java继承中类的初始化过程
2017-09-24 11:59
274 查看
我们先看代码,然后再分析。
输出:
Insect
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle
Beetle constructor
i=9,j=0
Beetle.k initialized
k=63
j=47
java中类的class文件只有在初次使用时才会加载到jvm中。类的静态成员、静态代码块和静态方法会随着类的加载而加载,并且只加载一次。初次使用包含两种情况:
静态调用。就是“类名.静态成员”或“类名.静态方法”。
使用构造器。
上面的程序先从Beetle类的main方法形如执行(可以理解成jvm执行Beetle.main(),也就是静态调用),于是类加载器开始加载Beetle类的字节码文件。在此过程中,编译器发现了“extends”关键字,即Beetle类还继承了Insect类,于是接着加载Insect类。上面的例子包含静态初始化和对象初始化两个过程。首先是开始Insect类(父类)的静态初始化。
静态代码块和静态成员的执行顺序由书写顺序决定,所以开始输出:
Insect
static Insect.x1 initialized
于是父类(Insect)的静态初始化结束。紧接着,类加载器继续加载子类(Beetle)的静态初始化,输出:
static Beetle.x2 initialized
Beetle
现在父类和子类的静态初始化操作全部完成。以上步骤仅仅是jvm试图调用Beetle类的main方法的准备动作,并没有真正执行main方法中的代码,现在开始真正执行:
第一行输出
Beetle constructor
这没有疑问,显然易见的事。第二行开始实例化Beetle类,即开始对象的初始化了。我们看看Beetle类的构造器:
它等价于:
也就是在构造Beetle对象时需要先构造父类(Insect)对象。于是jvm又去执行Insect类的构造器:
里面使用到了成员变量,其实成员变量的初始化也是放在构造器中的,也就是Insect类中对象成员属性定义等价于:
这样,实例化Insect对象时会输出:
i=9,j=0
注意,j的值,因为静态方法虽然能被子类继承但不能被重写,所以j的值是47而不是63,Insect类对象初始化完成。然后jvm回到Beetle构造器继续运行,完成Insect类的对象初始化,依次输出:
k=63
j=47
类初始华过程总结:
父类静态初始化
子类静态初始化
父类对象初始化
子类对象初始化
其他总结:
当类第一次被静态调用或是通过构造器调用时会被加载到内存
对象的成员属性初始化是在构造器中完成的(紧跟在supper()语句后面)
静态方法可以被继承,但是不能被重写,因为静态方法属于类,而不是属于对象
package se; public class Beetle extends Insect{ int k = prt("Beetle.k initialized"); Beetle(){ prt("k="+k); prt("j="+j); } static int x2=prt("static Beetle.x2 initialized"); static{ System.out.println("Beetle"); } static int prt(String s){ System.out.println(s); return 63; } public static void main(String[] args) { prt("Beetle constructor"); Beetle b = new Beetle(); } } class Insect{ int i=9; int j; Insect(){ j=prt("i="+i+",j="+j); } static{ System.out.println("Insect"); } static int xl = prt("static Insect.x1 initialized"); static int prt(String s){ System.out.println(s); return 47; } }
输出:
Insect
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle
Beetle constructor
i=9,j=0
Beetle.k initialized
k=63
j=47
java中类的class文件只有在初次使用时才会加载到jvm中。类的静态成员、静态代码块和静态方法会随着类的加载而加载,并且只加载一次。初次使用包含两种情况:
静态调用。就是“类名.静态成员”或“类名.静态方法”。
使用构造器。
上面的程序先从Beetle类的main方法形如执行(可以理解成jvm执行Beetle.main(),也就是静态调用),于是类加载器开始加载Beetle类的字节码文件。在此过程中,编译器发现了“extends”关键字,即Beetle类还继承了Insect类,于是接着加载Insect类。上面的例子包含静态初始化和对象初始化两个过程。首先是开始Insect类(父类)的静态初始化。
static{ System.out.println("Insect"); } static int xl = prt("static Insect.x1 initialized");
静态代码块和静态成员的执行顺序由书写顺序决定,所以开始输出:
Insect
static Insect.x1 initialized
于是父类(Insect)的静态初始化结束。紧接着,类加载器继续加载子类(Beetle)的静态初始化,输出:
static Beetle.x2 initialized
Beetle
现在父类和子类的静态初始化操作全部完成。以上步骤仅仅是jvm试图调用Beetle类的main方法的准备动作,并没有真正执行main方法中的代码,现在开始真正执行:
public static void main(String[] args) { prt("Beetle constructor"); Beetle b = new Beetle(); }
第一行输出
Beetle constructor
这没有疑问,显然易见的事。第二行开始实例化Beetle类,即开始对象的初始化了。我们看看Beetle类的构造器:
Beetle(){ prt("k="+k); prt("j="+j); }
它等价于:
Beetle(){ supper(); k=prt("Beetle.k initialized"); prt("k="+k); prt("j="+j); }
也就是在构造Beetle对象时需要先构造父类(Insect)对象。于是jvm又去执行Insect类的构造器:
Insect(){ j=prt("i="+i+",j="+j); }
里面使用到了成员变量,其实成员变量的初始化也是放在构造器中的,也就是Insect类中对象成员属性定义等价于:
class Insect{ int i; int j; Insect(){ super(); i=9; j=0; j=prt("i="+i+",j="+j); } //其他部分省略 }
这样,实例化Insect对象时会输出:
i=9,j=0
注意,j的值,因为静态方法虽然能被子类继承但不能被重写,所以j的值是47而不是63,Insect类对象初始化完成。然后jvm回到Beetle构造器继续运行,完成Insect类的对象初始化,依次输出:
k=63
j=47
类初始华过程总结:
父类静态初始化
子类静态初始化
父类对象初始化
子类对象初始化
其他总结:
当类第一次被静态调用或是通过构造器调用时会被加载到内存
对象的成员属性初始化是在构造器中完成的(紧跟在supper()语句后面)
静态方法可以被继承,但是不能被重写,因为静态方法属于类,而不是属于对象
相关文章推荐
- 继承的时候,java对象初始化过程
- java 了解继承在内的初始化全过程
- java包括继承在内的初始化全过程 笔记
- java继承中父类、子类的初始化过程
- Java 关于含有继承类的成员初始化过程讲解
- 继承与初始化过程(摘自Thinking in java 3th edition)
- Java语言 -- 带有继承的初始化过程
- 《Thinking in Java》——静态、非静态成员变量、子句的初始化顺序(含继承)及对象创建过程
- JAVA继承初始化过程
- 含继承在内的初始化过程 ——《Thinking in java》 读书笔记之一
- java 继承 static 初始化过程分析
- 一个Java小程序,帮助理解Java继承中的初始化过程
- java中继承的初始化过程
- Java的初始化过程
- Java 继承与初始化
- [转载]解析 Java 类和对象的初始化过程
- Java实例初始化过程
- Java:验证在类继承过程中equals()、 hashcode()、toString()方法的使用
- JAVA基础:子类、父类继承关系 静态块、非静态块、构造函数初始化顺序性、静态非静态方法覆盖
- JAVA基础-子类继承父类实例化对象过程