Java学习点滴——对象实例化

基于《Java编程思想》第四版程序员

构造与析构

在C++中经过构造函数和析构函数来保证:对象在使用前被正确初始化,在使用后被正确回收。Java中一样存在构造函数,可是没有析构函数。之因此没有析构函数是由于对象实际的存储期由GC决定,程序员没法明确析构函数什么时候会被执行。编程

GC会在回收对象前执行Objectprotected void finalize()方法,子类能够经过重写finalize()方法来清理资源。可是由于GC回收对象时间的不肯定性,加上finalize()自己可能引入的问题,因此并不同意使用该方法。不过Java还提供下面的格式,保证在finally里的语句必然在函数正常返回或者抛异常返回前执行,所以能够将资源的回收放在该语句块中。函数

try{

}finally{

}

对象初始化

在C++中,类对象实例化时,是不会初始化对象空间的,彻底依靠构造函数将成员变量设置为正确的初值,可是在Java中,当获得对象的空间后会先所有清零,再开始初始化对应成员变量。code

C++初始化成员变量有两种方式对象

  • 初始化列表
  • 构造函数内赋值

Java则有三种方式资源

  • 变量定义处赋值
  • 初始化语句块:分为静态初始化语句块和非静态初始化语句块,静态初始化语句块只能包含静态成员变量,非静态初始化语句块能够包含静态成员变量和非静态成员变量
  • 构造函数内赋值

经过下面的代码来加深印象class

class MyType{
    MyType(String msg){
        System.out.println(msg);
    }
}

class Base{
    MyType b1;
    static MyType b2 = new MyType("Base static member use =");
    MyType b3 = new MyType("Base non-static member use =");
    MyType b4;
    static MyType b5;
    {
        b4 = new MyType("Base non-static member in {}");
        b5 =  new MyType("Base static member in {}");
    }
    static MyType b6;
    static {
        b6 =  new MyType("Base static member in static {}");
    }
    Base(){
        b1 = new MyType("Base non-static member in Base()");
        System.out.println("Base()");
    }
}

class Derived extends Base{
    MyType d1;
    static MyType d2 = new MyType("Derived static member use =");
    MyType d3 = new MyType("Derived non-static member use =");
    MyType d4;
    static MyType d5;
    {
        d4 = new MyType("Derived non-static member in {}");
        d5 =  new MyType("Derived static member in {}");
    }
    static MyType d6;
    static {
        d6 =  new MyType("Derived static member in static {}");
    }
    Derived(){
        d1 = new MyType("Derived non-static member in Derived()");
        System.out.println("Derived()");
    }
}
public class Main {
    public static void main(String[] args) {
      Derived d1 = new Derived();
      System.out.println("----");
      Derived d2 = new Derived();
    }
}

代码的输出顺序以下,将分析也包含在输出中了变量

// 初始化子类时,会先初始化父类,此时先初始化静态成员变量。顺序:定义时赋值>初始化语句块。
Base static member use =
Base static member in static {}
// 父类的静态成员初始化结束后,开始初始化子类的静态成员变量。顺序:定义时赋值>初始化语句块。
Derived static member use =
Derived static member in static {}
// 以上对静态成员表初始化只在第一次加载class时执行的,后续都不会再执行了。

// 开始初始化父类的非静态成员变量。顺序:定义时赋值>初始化语句块>构造函数
Base non-static member use =
Base non-static member in {}
Base static member in {} // 此处的静态成员变量赋值会在每次类实例化对象时,执行
Base non-static member in Base()
Base()
// 开始初始化子类的非静态成员变量。顺序:定义时赋值>初始化语句块>构造函数
Derived non-static member use =
Derived non-static member in {}
Derived static member in {}
Derived non-static member in Derived()
Derived()
----
// 第二次实例化对象时,只会执行非静态成员变量的定义赋值、非静态初始化语句以及构造函数内赋值
Base non-static member use =
Base non-static member in {}
Base static member in {}
Base non-static member in Base()
Base()
Derived non-static member use =
Derived non-static member in {}
Derived static member in {}
Derived non-static member in Derived()
Derived()

整体顺序就是构造函数

  • 在首次加载class时,执行静态成变量的定义赋值和静态初始化语句块内的赋值。
  • 在每次实例化对象时,执行非静态成员变量的定义赋值、非静态初始化语句块内的赋值、构造函数内的赋值。

若是不实例化对象,而是直接访问静态成员变量,那么只会执行父类静态成变量的定义赋值和静态初始化语句块内的赋值程序

public class Main {
    public static void main(String[] args) {
        System.out.println( Derived.b5 );
    }
}
/// 输出以下
Base static member use =
Base static member in static {}
null

由于子类可能使用到父类的东西,因此老是先初始化父类再初始化子类。一样的初始化方法和类型(static或者non-static)的成员变量的初始化顺序与其定义顺序一致。这些和C++都是同样的。加载class的细节应该是被隐藏在Java解释器里了,经过字节码看不到完整的流程,这和C++就不同了。

相关文章
相关标签/搜索