Java中的前向引用与类初始化顺序
一.什么是向前引用?
有过C++编程经验的就会知道,一个变量或者方法总是需要先声明再使用。那么Java里面如下的代码是可以的吗?
public class GoFirst{
int m = n;//illegal forward reference,无法编译,报错
int n = 1;
}
也许我们可以做一些迷惑编译器的代码以达到前向引用的目的。
public class GoFirst{
int m = method();;//可以编译,此时n已被初始化为0
int n = 1;
int method() {return n; }
}
public static void main(String[] args) {
GoFirst goFirst=new GoFirst();
System.out.println(goFirst.m);//0,首先按照成员声明顺序装载成员字段,m初始化时n未被初始化,为默认值0
System.out.println(goFirst.method());//1,此时所有字段初始化已经完毕
System.out.println(goFirst.n);//1
}
在GoFirst类被初始化的时候,第一次的初始化:此时的GoFirst的所有成员变量均被初始化为各种数据类型的初始值,此时的成员变量已经为默认值(int类型的默认值为0,此次初始值均为编译器给定的默认值),第二次的初始化:按照成员变量声明的顺序设置我们想要初始值。如m先被设置为method()的返回值,再初始化n的值为1。
下面是《Java编程思想第四版》中对象创建过程的描述,假设有一个Dog类:
1.首次创建Dog的对象时或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件
2.载入Dog.class(这将会创建一个Class对象)文件,有关静态初始化的所有动作都会执行,静态初始化只在Class对象首次加载的时候进行一次
3.当用new Dog()创建对象的时候,首次将在堆上为Dog对象分配足够的存储空间
4.将这片存储空间清零,这就自动的将Dog对象的基本数据类型都设置为了默认值,而引用则被设置为了null
5.执行所有出现于字段定义处的初始化动作
6.执行构造器
二.类初始化顺序
Ⅰ.单个类加载顺序
/**
* 〈类的初始化顺序测试〉
*
* @author 龙
* @create 2018/9/10 15:40
* @since 1.0.0
*/
public class InitOrder {
public int number=initNumber();
{
System.out.println("初始化代码块!!!");
}
public static int staticA=initA();
static {
System.out.println("静态初始化代码块!!!");
}
public static int initA(){
System.out.println("初始化静态字段!!!");
return 5;
}
public int initNumber(){
System.out.println("初始化成员字段!!!");
return 5;
}
public static void main(String[] args) {
System.out.println("main函数开始执行!!!");
InitOrder initOrder=new InitOrder();
}
}
运行结果如下:
初始化静态字段!!!
静态初始化代码块!!!
main函数开始执行!!!
初始化成员字段!!!
初始化代码块!!!
静态字段和静态代码块,初始化顺序只是按照代码顺序执行,初始化成员字段和初始化代码块同理。由于main函数为这个类的静态成员,所以在main函数执行前依然需要先初始化该类的其它静态字段和静态代码块。如果在main方法里面不新建InitOreder对象,则成员字段和代码块将不会被初始化。即不会打印后面两句
Ⅰ.多个类加载顺序
public class Parent {
public int numberP=initNumberP();
public static int staticP=initStaticP();
public Parent(){
System.out.println("父类无参构造函数!!!");
}
public Parent(String s){
System.out.println("父类有参构造函数!!!");
}
public static int initStaticP(){
System.out.println("父类初始化静态字段!!!");
return 5;
}
public int initNumberP(){
System.out.println("父类初始化成员字段!!!");
return 5;
}
}
class Son extends Parent{
public int numberS=initNumberS();
public static int staticS=initStaticS();
public Son(){
super("string");//没有该句则默认调用父类的无参构造函数
System.out.println("子类构造函数!!!");
}
public static int initStaticS(){
System.out.println("子类初始化静态字段!!!");
return 5;
}
public int initNumberS(){
System.out.println("子类初始化成员字段!!!");
return 5;
}
public static void main(String[] args) {
System.out.println("main函数开始执行!!!");
Son son=new Son();
}
}
运行结果如下:
父类初始化静态字段!!!
子类初始化静态字段!!!
main函数开始执行!!!
父类初始化成员字段!!!
父类有参构造函数!!!
子类初始化成员字段!!!
子类构造函数!!!
就继承而言,父类静态成员>子类静态成员>父类实例成员>子类实例成员
参考资料:
《Java编程思想第四版》