1、仅构造方法
每个类都有构造方法。
如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
若定义了有参构造方法,编译器将不会提供默认的无参构造,如需要,需要自己构造。
Java继承中对构造函数是不继承的,只是显式或者隐式调用。
实例
父类A:
public class A { public A(){ System.out.println("A constructor"); } public A(int a){ System.out.println("A constructor ,num:"+a); } }
子类B:
public class B extends A { public B (){ System.out.println("B constructor"); } public B(int b){ System.out.println("B constructor ,num:"+b); } }
测试类E:
public class E { public static void main(String[] args) { System.out.println("E main"); A a = new B(); System.out.println("-------------AB可爱分割线-------------"); B b = new B(); System.out.println("\n---------小可爱分割线-----------\n"); A a1 = new B(1); System.out.println("-------------AB可爱分割线-------------"); B b1 = new B(2); } }
测试结果:
E main A constructor B constructor -------------AB可爱分割线------------- A constructor B constructor ---------小可爱分割线----------- A constructor B constructor ,num:1 -------------AB可爱分割线------------- A constructor B constructor ,num:2
解析:
Java继承中对构造函数是不继承的,只是显式或者隐式调用,并且必须是在构造函数第一行。这里是隐式调用了super()。
子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。
所以先执行父类构造函数,再执行子类构造函数。
2、静态块、构造方法
静态代码块:在java中使用static关键字声明的代码块。
实例1:
父类A:
public class A { static { System.out.println("A constructor before static"); } public A(){ System.out.println("A constructor"); } public A(int a){ System.out.println("A constructor ,num:"+a); } static { System.out.println("A constructor after static"); } }
子类B:
public class B extends A { static { System.out.println("B constructor before static"); } public B (){ System.out.println("B constructor"); } public B(int b){ System.out.println("B constructor ,num:"+b); } static { System.out.println("B constructor after static"); } }
测试类E:
public class E { public static void main(String[] args) { System.out.println("E main"); A a = new B(); System.out.println("-------------AB可爱分割线-------------"); B b = new B(); System.out.println("\n---------小可爱分割线-----------\n"); A a1 = new B(1); System.out.println("-------------AB可爱分割线-------------"); B b1 = new B(2); } }
测试结果:
E main A constructor before static A constructor after static B constructor before static B constructor after static A constructor B constructor -------------AB可爱分割线------------- A constructor B constructor ---------小可爱分割线----------- A constructor B constructor ,num:1 -------------AB可爱分割线------------- A constructor B constructor ,num:2
解析:
静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。
由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。
如果类中包含多个静态代码块,那么将按照"先定义的代码先执行,后定义的代码后执行"。
静态代码块不能存在于任何方法体内。
静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。
当父类与子类都有静态代码块和构造函数的时候,执行顺序如下:
父类静态代码块 > 子类静态代码块(Java虚拟机加载类时,就会执行该块代码)。
父类构造函数 > 子类构造函数 (先有父亲,后有孩子)
如果是多级继承关系的话,高层的父类首先执行,然后依次递减。
总结:静态优先执行,父类优先于子类执行。 静态代码块是在JVM加载类的时候执行的,而且静态代码块执行且仅执行一次
实例2:
子类包含静态属性---子类的实例化
父类A:同实例1
子类B:
public class B extends A { public static A b = new B(3); public static A a = new A(2); public static B c = new B(1); static { System.out.println("B constructor before static"); } public B (){ System.out.println("B constructor"); } public B(int b){ System.out.println("B constructor ,num:"+b); } static { System.out.println("B constructor after static"); } }
测试类F:
public class F { public static void main(String[] args) { System.out.println("F main"); A a = new B(); } }
测试结果:
F main A constructor before static A constructor after static A constructor B constructor ,num:3 A constructor ,num:2 A constructor B constructor ,num:1 B constructor before static B constructor after static A constructor B constructor
解析:
进入main函数,先执行
System.out.println("F main");
得到: F main
A a = new B();
A constructor before static A constructor after static
执行子类静态成员变量(方法块),由于子类包含静态属性和方法,按照“先定义的代码先执行,后定义的代码后执行”的规则,先执行静态属性部分,即:
public static A b = new B(3); public static A a = new A(2); public static B c = new B(1);
此时由于父类静态部分已经执行,故执行相应的构造函数得到(感觉说是中断类加载过程,执行实例初始化过程更贴切些,这部分可以考虑参考看最后关于“暂停类加载”部分):
A constructor B constructor ,num:3 A constructor ,num:2 A constructor B constructor ,num:1
由于之后子类含有静态块,故继续执行静态块中的打印:
B constructor before static B constructor after static
最后执行最终的无参构造函数:
A constructor B constructor
若父类A,改成如下会更直观:
public class A { public static A a = new B(12); static { System.out.println("A constructor before static"); } public A(){ System.out.println("A constructor"); } public A(int a){ System.out.println("A constructor ,num:"+a); } static { System.out.println("A constructor after static"); } }
此时结果:
F main A constructor B constructor ,num:12 A constructor before static A constructor after static A constructor B constructor ,num:3 A constructor ,num:2 A constructor B constructor ,num:1 B constructor before static B constructor after static A constructor B constructor
实例3:
子类和父类存在相同的静态方法和非静态方法时
父类A:
public class A { static { System.out.println("A constructor before static"); } public static void getClassStaticFunc(){ System.out.println("get A static Function"); } public void getClassFunc(){ System.out.println("get A Function"); } public A(){ System.out.println("A constructor"); } public A(int a){ System.out.println("A constructor ,num:"+a); } static { System.out.println("A constructor after static"); } }
子类B:
public class B extends A { static { System.out.println("B constructor before static"); } public static void getClassStaticFunc(){ System.out.println("get B static Function"); } public void getClassFunc(){ System.out.println("get B Function"); } public B (){ System.out.println("B constructor"); } public B(int b){ System.out.println("B constructor ,num:"+b); } static { System.out.println("B constructor after static"); } }
测试类F:
public class F { public static void main(String[] args) { System.out.println("F main"); A a = new B(); a.getClassStaticFunc(); a.getClassFunc(); } }
测试结果:
F main A constructor before static A constructor after static B constructor before static B constructor after static A constructor B constructor get A static Function get B Function
解析:
java中静态属性和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏。
原因:
1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。
2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。
3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。
3、构造代码块、静态块、构造方法
构造块:直接在类中定义且没有加static关键字的代码块称为{}构造代码块。
构造代码块在创建对象时被调用,每次创建对象都会被调用,并且构造代码块的执行次序优先于类构造函数。
实例1
父类A:
public class A { { System.out.println("A constructor before static before 构造块"); } static { System.out.println("A constructor before static"); } public A(){ System.out.println("A constructor"); } public A(int a){ System.out.println("A constructor ,num:"+a); } static { System.out.println("A constructor after static"); } { System.out.println("A constructor after static after 构造块"); } }
子类B:
public class B extends A { { System.out.println("B constructor before static before 构造块"); } static { System.out.println("B constructor before static"); } public B (){ System.out.println("B constructor"); } public B(int b){ System.out.println("B constructor ,num:"+b); } static { System.out.println("B constructor after static"); } { System.out.println("B constructor after static after 构造块"); } }
测试类E:
public class E { public static void main(String[] args) { System.out.println("E main"); A a = new B(); System.out.println("-------------AB可爱分割线-------------"); B b = new B(); System.out.println("\n---------小可爱分割线-----------\n"); A a1 = new B(1); System.out.println("-------------AB可爱分割线-------------"); B b1 = new B(2); } }
测试结果:
E main A constructor before static A constructor after static B constructor before static B constructor after static A constructor before static before 构造块 A constructor after static after 构造块 A constructor B constructor before static before 构造块 B constructor after static after 构造块 B constructor -------------AB可爱分割线------------- A constructor before static before 构造块 A constructor after static after 构造块 A constructor B constructor before static before 构造块 B constructor after static after 构造块 B constructor ---------小可爱分割线----------- A constructor before static before 构造块 A constructor after static after 构造块 A constructor B constructor before static before 构造块 B constructor after static after 构造块 B constructor ,num:1 -------------AB可爱分割线------------- A constructor before static before 构造块 A constructor after static after 构造块 A constructor B constructor before static before 构造块 B constructor after static after 构造块 B constructor ,num:2
解析:
先静态后构造块,先父类后子类。
实例2:
对于非继承类
public class J { public static int k = 0; static { print("静态块2"); } public static J t1 = new J("t1"); public static J t2 = new J("t2"); public static int i = print("i"); public static int n = 99; public int j = print("j"); { print("构造块"); } static { print("静态块"); } public J(String str) { System.out.println((++k) + ":" + str + " i=" + i + " n=" + n); ++i; ++n; } public static int print(String str) { System.out.println((++k) + ":" + str + " i=" + i + " n=" + n); ++n; return ++i; } public static void main(String args[]) { J t = new J("init"); } }
测试结果:
1:静态块2 i=0 n=0 2:j i=1 n=1 3:构造块 i=2 n=2 4:t1 i=3 n=3 5:j i=4 n=4 6:构造块 i=5 n=5 7:t2 i=6 n=6 8:i i=7 n=7 9:静态块 i=8 n=99 10:j i=9 n=100 11:构造块 i=10 n=101 12:init i=11 n=102
解析:
典型的暂停类加载。即:类加载过程中,可能调用了实例化过程(因为static可以修饰方法,属性,代码块,内部类),此时则会暂停类加载过程而先执行实例化过程(被打断),执行结束再进行类加载过程。
类加载过程,不涉及构造方法。
实例化过程,涉及构造方法。
先类加载,执行static修饰的部分,遇到属性实例化,中断去执行实例化,完成后继续类加载,如此循环直至结束。
小结
无继承的的初始化顺序
静态成员变量(静态代码块)→普通成员变量→构造器
有继承的初始化顺序
父类静态成员变量、静态代码块→子类静态成员变量、静态代码块→父类普通成员变量、普通代码块→父类构造器→子类普通成员变量、普通代码块→子类构造器。
其他
类加载过程,不涉及构造方法
实例化过程,涉及构造方法
1、类中所有属性的默认值(一举而成)
2、父类静态属性初始化,静态块,静态方法的声明(按出现顺序执行)
3、子类静态属性初始化,静态块,静态方法的声明 (按出现顺序执行)
4 、调用父类的构造方法,
首先父类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
然后父类构造方法
5、 调用子类的构造方法,
首先子类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行)
然后子类构造方法