初始化和清理
1. 用构造器确保初始化
初始化需要解决的两个问题:
- 任何名字都可能与类的某个成员名称冲突
- 调用构造器是编译器的责任,所以必须让编译器知道调用哪个方法
在Java中采用这种方法:构造器与类有相同的名称
- 构造器不需要返回值
- 不接受任何参数的构造器叫默认构造器,也叫无参构造器
- 一个类,如果我们没有提供构造器,会有一个无参构造器。如果我们自定义了构造器,那么会顶替掉默认构造器,如果还想要无参构造器,得自己手动添加。
2. 方法重载
现在我们有多种属性的初始化需求,那么想用多种方式创建一个对象该怎么办?
重载
2.1 区分重载方法
要是几个方法有相同的名字,那么你指的是哪一个呢?
规则:每个重载的方法都必须有一个独一无二的参数列表。参数顺序不同,也算不同。
方法返回值不同,其他相同,即返回参数不同算重载吗?
如果调用方法时,不保存返回值,那么编译器不知道该调用哪个方法,所以编译不通过,不算。
2.2 涉及基本类型的重载
基本类型能从一个“较小”的类型自动提升至一个“较大的”类型。那么将一个较小类型传入到参数为较大类型的方法,会怎么样呢?
- 如果传入类型小于方法中声明的参数类型,实际数据类型会提升。char略有不同,会直接提升到int
- 如果传入类型大于方法中声明的参数类型,那就必须强转了。
3. this关键字
如果想在方法内部获得当前对象的引用,那么你需要this关键字
3.1 在构造器中调用构造器
3.2 static
static方法就是没有this的方法
4. 终结处理和垃圾回收
Java有自己的垃圾回收机制,为啥还要了解垃圾回收呢?
如果通过JNI创建了一些对象,那么这些对象Java是无法控制的,你怎么做?
4.1 finalize()
为了释放一些特殊的内存(如:natvie方法),java允许在类中定义一个名为finalize()的方法。
原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize()方法,并且在下一次垃圾回收动作是,才会真正回收对象占用的内存。
因此:我们就可以在finalize()中,调用free()等释放内存的方法。
如果对象中含有其他对象(普通java对象),finalize应该明确释放那么对象吗?
不,对于java中的对象,不管是如何创建的,垃圾回收器都会负责释放对象占据的内存,最好要在finalize中对一般java对象做释放操作。
4.2 垃圾回收器如何工作
- 首先理解其他系统中的垃圾回收机制
- 引用计数:每个对象都有一个引用计数器,有引用连接至对象那么计数器加一,当离开引用域或者置为null将计数器减一,当发现某个对象的引用计数为零的时候,就释放对象占用的内存。存在缺陷:如果对象间存在循环引用,那么应该被回收的对象,引用计数却不为零。
- Java中垃圾回收机制:自适用垃圾回收机制。
- 停止-复制:先停止程序的运行,然后将所有活着的对象复制到另一个堆,没有被复制的全部是垃圾。缺点
- 需要额外的操作空间(两个堆)
- 程序稳定后,只会产生少量的垃圾,甚至没有垃圾,直接复制的话,这很浪费。
- 标记--清扫:从堆栈和静态存储区出发,遍历所有引用,进而找到所有存活的对象。每当找到一个活的对象,就会给对象设一个标记,这个过程中不会回收任何对象。只有标记工作完成,才会开始清理动作。剩下的对空间是不连续的,如果垃圾回收器希望得到连续的空间,就得重新整理剩下的对象。
- 停止-复制:先停止程序的运行,然后将所有活着的对象复制到另一个堆,没有被复制的全部是垃圾。缺点
Java虚拟机会监视资源,如果所有对象都很稳定,垃圾回收器的效率降低的话,就切换到“标记-清扫”;同样,如果堆空间出现很多碎片就会切换回“停止-复制”方式。这就是“自适用”技术。
6. 成员初始化
Java尽力保证:所有变量在使用前都能得到恰当的初始化。
6.1 初始化顺序
成员变量的初始化,放在构造器初始化之前。比如:成员变量是有初始值默认值的,经过构造器赋值才变为新的值。所以 成员变量初始化--->构造器初始化
-
如果有多处成员变量初始化呢?
成员变量按上下顺序初始化后,开始构造器中的初始化
-
静态数据的初始化
静态对象先于非静态。
6.1.2 静态代码块和非静态实例初始化
Child继承自Parent,两者的初始化顺序。子类可能会用到父类中的属性,所以父类中的初始化会先于子类,但是Static比较特殊,先于两者的所有其他非静态属性。
顺序 | 代码块 |
---|---|
1 | Parent static block |
2 | Child static block |
3 | Parent block |
4 | Parent constructor |
5 | Child block |
6 | Child constructor |