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 mainA constructorB constructor-------------AB可爱分割线-------------A constructorB constructor
---------小可爱分割线-----------
A constructorB constructor ,num:1-------------AB可爱分割线-------------A constructorB constructor ,num:2解析: Java继承中对构造函数是不继承的,只是显式或者隐式调用,并且必须是在构造函数第一行。这里是隐式调用了super()。 子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。 如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。 所以先执行父类构造函数,再执行子类构造函数。
2、静态块、构造方法
静态代码块<在java中使用static关键字声明的代码块>在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 mainA constructor before staticA constructor after staticB constructor before staticB constructor after staticA constructorB constructor-------------AB可爱分割线-------------A constructorB constructor
---------小可爱分割线-----------
A constructorB constructor ,num:1-------------AB可爱分割线-------------A constructorB constructor ,num:2解析: 静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。 由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。 如果类中包含多个静态代码块,那么将按照”先定义的代码先执行,后定义的代码后执行”。 静态代码块不能存在于任何方法体内。 静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。 当父类与子类都有静态代码块和构造函数的时候,执行顺序如下:
- 父类静态代码块 > 子类静态代码块(Java虚拟机加载类时,就会执行该块代码)。
- 父类构造函数 > 子类构造函数 (先有父亲,后有孩子)
- 如果是多级继承关系的话,高层的父类首先执行,然后依次递减。
总结:静态优先执行,父类优先于子类执行。 静态代码块是在JVM加载类的时候执行的,而且静态代码块执行且仅执行一次
实例2:
子类包含静态属性---子类的实例化。 父类A<同实例1>同实例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 mainA constructor before staticA constructor after staticA constructorB constructor ,num:3A constructor ,num:2A constructorB constructor ,num:1B constructor before staticB constructor after staticA constructorB constructor解析: 进入main函数,先执行
System.out.println("F main");得到
F main执行
A a = new B();后,先执行父类的静态块得到
A constructor before staticA 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 constructorB constructor ,num:3A constructor ,num:2A constructorB constructor ,num:1由于之后子类含有静态块,故继续执行静态块中的打印:
B constructor before staticB constructor after static最后执行最终的无参构造函数:
A constructorB 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 mainA constructorB constructor ,num:12A constructor before staticA constructor after staticA constructorB constructor ,num:3A constructor ,num:2A constructorB constructor ,num:1B constructor before staticB constructor after staticA constructorB 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 mainA constructor before staticA constructor after staticB constructor before staticB constructor after staticA constructorB constructorget A static Functionget 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 mainA constructor before staticA constructor after staticB constructor before staticB constructor after staticA constructor before static before 构造块A constructor after static after 构造块A constructorB constructor before static before 构造块B constructor after static after 构造块B constructor-------------AB可爱分割线-------------A constructor before static before 构造块A constructor after static after 构造块A constructorB constructor before static before 构造块B constructor after static after 构造块B constructor
---------小可爱分割线-----------
A constructor before static before 构造块A constructor after static after 构造块A constructorB 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 constructorB constructor before static before 构造块B constructor after static after 构造块B constructor ,num:2解析: 先静态后构造块,先父类后子类。
实例2:
对于非继承类J
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=02:j i=1 n=13:构造块 i=2 n=24:t1 i=3 n=35:j i=4 n=46:构造块 i=5 n=57:t2 i=6 n=68:i i=7 n=79:静态块 i=8 n=9910:j i=9 n=10011:构造块 i=10 n=10112:init i=11 n=102解析: 典型的暂停类加载。即:类加载过程中,可能调用了实例化过程(因为static可以修饰方法,属性,代码块,内部类),此时则会暂停类加载过程而先执行实例化过程(被打断),执行结束再进行类加载过程。 类加载过程,不涉及构造方法。 实例化过程,涉及构造方法。 先类加载,执行static修饰的部分,遇到属性实例化,中断去执行实例化,完成后继续类加载,如此循环直至结束。
小结
无继承的的初始化顺序
静态成员变量(静态代码块)→普通成员变量→构造器
有继承的初始化顺序
父类静态成员变量、静态代码块→子类静态成员变量、静态代码块→父类普通成员变量、普通代码块→父类构造器→子类普通成员变量、普通代码块→子类构造器。
其他
类加载过程,不涉及构造方法 实例化过程,涉及构造方法 1、类中所有属性的默认值(一举而成) 2、父类静态属性初始化,静态块,静态方法的声明(按出现顺序执行) 3、子类静态属性初始化,静态块,静态方法的声明 (按出现顺序执行) 4 、调用父类的构造方法, 首先父类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行) 然后父类构造方法 5、 调用子类的构造方法, 首先子类的非静态成员初始化,构造块,普通方法的声明(按出现顺序执行) 然后子类构造方法
参考资料
深入了解Java程序执行顺序 Java中普通代码块,构造代码块,静态代码块区别及代码示例 Java:构造器,构造代码块,静态代码块的执行顺序 Java 继承 在继承中的问题,关于静态代码块,子类和父类的静态代码块的执行情况 JAVA静态方法是否可以被继承?

评论