成员初始化顺序
Java初始化的原则是:保证成员在使用前都能得到初始化。
下面用代码来理解初始化的顺序:
public class Demo{
public static void main(String[] args) {
System.out.println("+++++++调用Main方法+++++++");
new B();
System.out.println("=======第二次初始化======");
new B();
System.out.println("+++++++结束Main方法+++++++");
}
}
class A{
public A(int i) {
System.out.println("this is A"+i);
}
}
class B_Father{
A aa;
{
aa = new A(11);
System.out.println("非静态初始化:"+aa);
}
public B_Father() {
System.out.println("this is B_Father");
}
static A bb;
static{
bb = new A(22);
System.out.println("静态初始化:"+bb);
}
}
class B extends B_Father{
A a;
{
a = new A(1);
System.out.println("非静态初始化:"+a);
}
public B() {
System.out.println("this is B");
}
static A b;
static{
b = new A(2);
System.out.println("静态初始化:"+b);
}
}
打印结果分析
拓展: 自动初始化
自动初始化发生在类的加载时期,如果知道jvm加载类的过程,在类进行初始化之前,自动初始化已经发生;这里的类初始化只包括指定值对类的初始化。
自动初始化是不会初始化局部变量的,下面例子说明未对局部变量初始化就是用,会抛出编译期错误:
class A{
public void method(){
//方法中定义的变量为局部变量
int a;
System.out.println(a);
}
}
/*output:
Demo.java:5: 错误: 可能尚未初始化变量a
System.out.println(a);
^
1 个错误*/
- 初始化的时机
当类被主动引用时,类便会获得初始化的时机,有以下四种:
- 使用new关键字创建对象
- 调用类中的静态成员
- Java反射获得class对象
- main方法所在的类
除了new关键字来创建一个对象,会导致类进行一次完整的初始化;其他的初始化时机只会引起Java类中的静态成员初始化。
public class Demo{
public static void main(String[] args) throws Exception{
System.out.println("+++++++调用Main方法+++++++");
System.out.println("1.调用类中的静态成员,引起初始化:");
A a = B.b;
System.out.println("2.使用Java反射获得class对象的引用:");
Class b = Class.forName("B");
System.out.println("3.main方法所在的类:略");
System.out.println("+++++++结束Main方法+++++++");
}
}
打印结果分析
Tips: 这三种情况都有一个共同点,都是对类作用域进行操作,只需要初始化静态成员。
特殊情况,当类被动引用时,不会进行初始化:
- 调用类中的final常量
- 实例化对象数组
- 通过子类调用父类静态成员,只会初始化父类
public class Demo{
public static void main(String[] args) throws Exception{
System.out.println("+++++++调用Main方法+++++++");
B b = new B();
System.out.println("1.调用类中的常量。");
int a = b.cc;
System.out.println("2.数组来容纳对象");
B[] bb = new B[10]; //这里只给bb分配空间,而不将类B对象放入,不会引起初始化
System.out.println("3.调用父类中的静态变量,不会引起初始化");
A aa = b.aa;
System.out.println("+++++++结束Main方法+++++++");
}
}
- 静态初始化的特殊性
Java中的static关键字定义静态变量,也有全局变量的说法,因为静态变量是属于类的,一个类无论实例化多少次,都静态变量都只占一份内存;Java中并没有全局变量,称它为全局变量不符合面向对象语言的多态性和封装特性。
static变量在类初始化中,只会初始化一次,除非我们直接调用静态变量。
静态初始化可以使用静态块来帮助静态变量初始化
static int a;
static{
a = 10;
}
Tips: 静态块要么单独使用,要么只能和要初始化的变量绑定在一起。
- 指定值来初始化
指定值初始化的普遍方式有三种:
- 在定义时进行初始化
- 通过方法return来进行初始化
- 通过构造方法来进行初始化
对于静态变量,我们还可以使用静态块来初始化;非静态变量,我们可以使用类似静态块的方式初始化,也称实例初始化。
拓展: 数组初始化
public class Demo{
//1.利用for循环初始化
Integer[] a1 = new Integer[5];
for(int i = 0; i < a1.length; i++){
a1[i] = i;
}
//2.聚集初始化
Integer[] a2 = {1,2,3,4,5};
//3.动态聚集初始化
Integer[] a3 = new Integer[]{1,2,3,4,5};
}
有关例题解析
题 1 :分析打印结果
打印结果:YXYZ
解析:第一步初始化main方法所在的类 Z,因为没有静态方法,所以跳过 ;第二步,初始化类Z,先初始化父类 X 中的 非静态成员创建对象 Y并 打印一个 Y ,再调用父类构造方法,打印一个 X;最后初始化类 Z 中的非静态成员创建对象 Y 并 打印一个 Y,之后调用类 Z 的构造方法并 打印一个 Z。
总结
- 本文只讲了成员变量的初始化,并没有说方法的初始化,我们只谈方法的调用,成员初始化可以通过打印查看到初始化轨迹,方法却无法直观看到,我们只能调用方法。
- 成员方法的初始化顺序:在成员变量之后初始化,遵循Java初始化的原则,static方法在普通方法之前。
- 构造方法是不是静态方法?
如果说他是静态方法,那么他就不能调用非静态的方法了,反证,构造方法不是静态方法。