Java编程思想拾遗(4) 对象的初始化与清理

199 阅读4分钟

构造器

可以假想为编写的每个类都定义一个initialize()方法,该方法的名称提醒你在使用其对象之前,应首先调用initialize(),然而这意味着用户必须记得自己去调用此方法,为此Java提供了构造器,“初始化”和“创建”被捆绑在一起,两者不能分离。

默认构造器又名无参构造器,它的作用是创建一个“默认对象”,如果你写的类中没有构造器,则编译器会自动帮你创建一个默认构造器。如果已经定义了一个构造器(无论是否有参数),编译器就不会帮你自动创建默认构造器。

初始化

除了显式的静态初始化,还有一种非静态实例初始化的语法,用来初始化每一个对象的非静态变量,在重载构造器下,保证无论调用了哪个显式构造器,某些操作都会发生。

每个类的编译代码都存在于它自己的独立的文件中,该文件只在需要使用程序代码时才会被加载,类的代码在初次使用时才加载,这通常是指加载发生于创建类的第一个对象之时,但是当访问static域或者static方法时,也会发生加载。(构造器也是static方法,尽管static关键字是隐式的,因此更准确地讲,类是在其任何static成员被访问时加载的)

重载

重载与类型提升并不冲突,可共存

为什么重载的依据跟返回类型无关?因为方法调用的返回值是可忽略的,在编译器上可选,所以编译器无法据此区分。

{
    void f() {}
    int f() {return 1;}
    
    f();
}

this关键字

为了能用简便、面向对象的语法来编写代码--即“发送消息给对象”,编译器做了一些幕后工作,它暗自把“所操作对象的引用”作为第一个参数传递给方法。

{
    Banana a = new Banana();
    a.peel(1);
    
    // equals to 
    Banana.peel(a, 1);
}

方法内部调用同类方法,不需要使用this,原则上能不用就不用。

清理

Java允许在类中定义一个名为finalize()的方法,一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用器finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。然而Java里的对象不能保证一定会被垃圾回收,所以如果要做一些清理工作,必须自己手动创建一个执行清理工作的普通方法。

使用垃圾回收器的唯一原因是为了回收程序不再使用的内存,所以对于与垃圾回收有关的任何行为来说(尤其是finalize()方法),它们也必须同内存机器回收有关。因为垃圾回收器都会负责释放对象占据的所有内存,这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配了存储空间。

垃圾回收器并非基于引用记数技术,对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用,由此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。

Java虚拟机中有许多附加技术用以提升速度,尤其是与加载器操作有关的被称为即时编译器的技术,这种技术可以把程序全部或部分翻译成本地机器码(这本来是Java虚拟机的工作),程序运行速度因此得以提升。当需要装载某个类(通常是在为该类创建第一个对象)时,编译器会先找到其.class文件,然后将该类的字节码装入内存。此时,有两种方案可供选择,一种是让即时编译器编译所有代码,但这样做有两个缺陷:这种加载动作散落在整个程序生命周期内,累加起来要花更多时间,并且会增加可执行代码的长度(字节码要比即时编译器展开后的本地机器码小很多),这将导致页面调度,从而降低程序速度。另一种做法称为惰性评估,意思是即时编译器只在必要的时候才编译代码,这样,从不会被执行的代码也许就压根不会被JIT所编译,新版JDK中的Java HotSport技术就采用了类似方法,代码每次被执行的时候都会做一些优化,所以执行的次数越多,它的速度就越快。