Java作为一种面向对象语言。支持以下基本概念:
多态
继承
封装
抽象
类
对象
实例
方法
重载
Java一些重要概念
普通类和抽象类
抽象类不能被实例化;
抽象类可以有抽象方法,只需申明,无须实现;
有抽象方法的类一定是抽象类;
抽象类的子类必须实现抽象类中的所有抽象方法,否则子类仍然是抽象类;
抽象方法不能声明为静态、不能被static、final修饰。
接口和抽象类
(1)接口
接口使用interface修饰;
接口不能实例化;
类可以实现多个接口;
java8之前,接口中的方法都是抽象方法,省略了public abstract。②java8之后;接口中可以定义静态方法,静态方法必须有方法体,普通方法没有方法体,需要被实现;
(2)抽象类
抽象类使用abstract修饰;
抽象类不能被实例化;
抽象类只能单继承;
抽象类中可以包含抽象方法和非抽象方法,非抽象方法需要有方法体;
如果一个类继承了抽象类,①如果实现了所有的抽象方法,子类可以不是抽象类;②如果没有实现所有的抽象方法,子类仍然是抽象类。
类
类的创建者负责在创建新的类时,只暴露必要的接口给客户程序员,同时隐藏其它所有不必要的信息。
为什么这么做呢?
(1)因为如果这些信息对于客户程序员而言是不可见的,那么类的创建者就可以任意修改隐藏信息,而无需担心对其它任何人造成影响。隐藏的代码通常代表着一个对象内部脆弱的部分,如果轻易暴露给粗心或经验不足的客户程序员,就可能在顷刻之间被破坏殆尽。所以,隐藏代码的具体实现可以有效减少程序bug。
(2)让类库的设计者在改变类的内部工作机制时,不用担心影响到使用该类的客户程序员。
Java提供了三个显示关键字来控制访问权限。
成员变量和局部变量
(1)在类中的位置不同
成员变量:类中方法外;
局部变量:方法定义中或者方法声明上;
(2)在内存中的位置不同
成员变量:在堆中
局部变量:在栈中
(3)生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
(4)初始化值不同
成员变量:有默认值
局部变量:没有默认值,必须定义,赋值,然后才能使用
对象的创建和声明周期
对象的创建需要消耗一些资源,尤其是内存资源。
当我们不再需要一个对象时,就要及时清理它,这样占用的资源才能被释放并重复使用。
如果要最大化运行时效率,可以通过栈区(局部变量)来保存对象,或者将对象保存在静态区里,这样在编写程序时就可以明确的知道对象的内存分配和生命周期,这种做法会优先考虑分配和释放内存的速度。但是代价就是牺牲了灵活性,因为你必须在编写代码时就明确对象的数量、生命周期以及类型,但是这种写法的限制性很大。
还有一种方案是在内存池中动态创建对象,这个内存池也就堆。如果使用这个方案,直到运行时你才能知道需要多少对象,以及它们的生命周期和确切的类型是什么。如果需要创建一个新对象,可以直接通过堆来创建。因为堆是在运行时动态管理内存的,所以堆分配内存所花费的时间通常会比栈多一些。栈通常利用汇编指令向下或向上移动栈指针来管理内存,而堆何时分配内存则取决于内存机制的实现方式。
Java只允许动态分配内存,每当要创建一个对象时,都需要使用new来创建一个对象的动态实例。
如果在栈中创建对象,编译器会判断对象存在时间以及负责自动销毁该对象。
如果在堆中创建对象,编译器就无法得知对象的生命周期。
Java支持垃圾回收机制,它会自动找到没用的对象将其销毁。
final 和 static
都可以修饰类、方法、成员变量
static可以修饰类的代码块,final不可以
static不可以修饰方法内局部变量,final可以
static修饰表示静态或全局
static修饰的代码块表示静态代码块,当JVM加载类时,只会被创建一次
static修饰的变量可以重新赋值
static方法中不能用this和super关键字
因为this代表的是调用这个函数的对象的引用,而静态方法是属于类的,不属于对象,静态方法成功加载后, 对象还不一定存在。 this代表对本类对象的引用,指向本类已创建的对象。 super代表对父类对象的引用,指向父类对象。 静态优先于对象存在,方法被static修饰之后,方法先存在,所需的父类引用对象晚于该方法的出 现,也就是super所指向的对象还没出现,当然就会报错。
static方法必须被实现,而不能是抽象的abstract
static方法只能被static方法覆盖
final修饰表示常量、一旦创建不可被修改
final标记的成员变量必须在声明的同时赋值,或在该类的构造方法中赋值,不可重新赋值
final方法不能被子类重写
final类不能被继承,没有子类,final类中的方法默认是final的
final不能用于修饰构造方法
private类型的方法默认是final类型的
final、finally、finalize
final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写
finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。
finalize方法用于垃圾回收。
一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。
但是当调用finalize方法后,并不意味着gc会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以,不推荐使用finalize方法。
Java运算符
算术运算符
关系运算符
位运算符
逻辑运算符
赋值运算符
其他运算符
循环结构
for
while
do...while
10、条件表达式
if...else...
switch case
super与this
super关键字可以在子类的构造方法中显示地调用父类的构造方法,super()必须为子类构造函数中的第一行。
super可以用来访问父类的成员方法或变量,当子类成员变量或方法与父类有相同的名字时也会覆盖父类的成员变量或方法,这个时候要想访问父类的成员变量或方法只能通过super关键字来访问,子类方法中的super.方法名()不需要位于第一行。
this关键字指向的是当前对象的引用,用来区分成员变量和局部变量(重名问题)
this() 不能使用在普通方法中 只能写在构造方法中。
必须是构造方法中的第一条语句。
方法的重写
父类的功能无法满足子类的需求时,则需要用到重写;
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
重写的注意事项:
参数列表必须完全与被重写方法的相同;
返回类型必须完全与被重写方法的返回类型相同;
访问权限不能比父类中被重写的方法的访问权限更低;
声明为final的方法不能被重写;
声明为static的方法不能被重写,但是能够被再次声明;
构造方法不能被重写;
子类和父类在同一个包中,那么子类可以重写父类所有除了声明为private和final 的方法;
如果不能继承一个方法,则不能重写这个方;
子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和 protected的非final方法;
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是, 重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性 异常,反之则可以。
在 Java 中,什么时候用重载,什么时候用重写?
(1)重载是多态的集中体现,在类中,要以统一的方式处理不同类型数据的时候,可以用重载。
(2)重写的使用是建立在继承关系上的,子类在继承父类的基础上,增加新的功能,可以用重写。
(3)简单总结:
重载是多样性,重写是增强剂;
目的是提高程序的多样性和健壮性,以适配不同场景使用时,使用重载进行扩展;
目的是在不修改原方法及源代码的基础上对方法进行扩展或增强时,使用重写;
抽象类和接口
接口和抽象类都遵循”面向接口而不是实现编码”设计原则,它可以增加代码的灵活性,可以适应不断变化的需求。下面有几个点可以帮助你回答这个问题:在 Java 中,你只能继承一个类,但可以实现多个接口。所以一旦你继承了一个类,你就失去了继承其他类的机会了。
接口通常被用来表示附属描述或行为如: Runnable 、 Clonable 、 Serializable 等等,因此当你使用抽象类来表示行为时,你的类就不能同时是 Runnable 和 Clonable( 注:这里的意思是指如果把 Runnable 等实现为抽象类的情况 ) ,因为在 Java 中你不能继承两个类,但当你使用接口时,你的类就可以同时拥有多个不同的行为。
在一些对时间要求比较高的应用中,倾向于使用抽象类,它会比接口稍快一点。如果希望把一系列行为都规范在类继承层次内,并且可以更好地在同一个地方进行编码,那么抽象类是一个更好的选择。有时,接口和抽象类可以一起使用,接口中定义函数,而在抽象类中定义默认的实现。
克隆
(1)什么要使用克隆?
想对一个对象进行复制,又想保留原有的对象进行接下来的操作,这个时候就需要克隆了。
(2)如何实现对象克隆?
实现Cloneable接口,重写clone方法;
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。
BeanUtils,apache和Spring都提供了bean工具,只是这都是浅克隆。
(3)深拷贝和浅拷贝区别是什么?
浅拷贝:仅仅克隆基本类型变量,不克隆引用类型变量;
深克隆:既克隆基本类型变量,又克隆引用类型变量;
javac
(1)javac是什么
javac是一种编译器,能够将一种语言规范转化成另外一种语言规范
javac的任务就是将Java源代码转化成JVM能够识别的一种语言(Java字节码),这种字节码不是针对某种机器、某种平台的
(2)javac编译器编译程序的步骤
① 词法分析
首先是读取源代码,找出这些字节中哪些是我们定义的语法关键词,如Java中的if、else、for等关键词。
语法分析的结果:从源代码中找出一些规范化的token流。
注:token是一种认证机制
② 语法分析
检查关键词组合在一起是不是Java语言规范,如if后面是不是紧跟着一个布尔表达式。
语法分析的结果:形成一个符合Java语言规范的抽象语法树
③ 语义分析
把一些难懂的、复杂的语法转化为更加简单的语法。
语义分析的结果:完成复杂语法到简单语法的简化,如将foreach语句转化成for循环结果,还有注解等。最后形成一个注解过后的抽象语法树,这颗语法树更接近目标语言的语法规则。
④ 生成字节码
通过字节码生成器生成字节码,根据经过注解的抽象语法树生成字节码,也就是将一个数据结构转化成另一个数据结构。
代码生成器的结果:生成符合Java虚拟机规范的字节码。
注:抽象语法树
在计算机科学中,抽象语法树是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
(3)javac编译器的基本结构
按照上述编译步骤来看,可以将javac编译器分为4个模块:词法分析器、语法分析器、语义分析器和代码生成器。