本帖最后由 Ralap军 于 2015-9-6 15:53 编辑
看了网友的子父类静态变量、静态代码块、构造代码块、构造方法的执行代码分析
现结合亮哥讲解的子父类加载执行流程,自己写了一个验证代码,望各位大神指点
首先,加载执行流程:
1、在栈内存空间中开辟一个空间给引用数据类型变量b用
2、加载父类.class字节码文件 ——> 父类的静态内容进方法区的静态区
——> 开辟静态成员变量空间,并显示初始化。执行静态代码块(静态成员变量与静态代码块的执行顺序与代码顺序有关)
3、加载子类.class字节码文件 ——> 子类的静态内容进方法区的静态区
——> 开辟静态成员变量空间,并显示初始化。执行静态代码块
4、通过new开辟子类在堆中的内存空间,为对象 ——> 子类的成员变量进堆中,并赋系统默认值(整型-0,浮点型-0.0,布尔型-false,字符型-'0u0000',引用型-null)
——> 把方法加载到方法区的非静态区(成员变量与构造代码块的执行顺序有代码顺序有关)
5、在堆中另开辟一个父类的内存空间,非对象 ——> 父类的成员变量进堆中,并赋系统默认值 ——> 把父类的方法加载到方法区的非静态区
6、对父类的成员变量进行显式初始化。执行父类构造代码块 ——> 父类的构造方法
7、把父类的内存空间标识给子类
8、对子类的成员变量进行显式初始化。执行子类构造代码块 ——> 子类的构造方法
9、把子类在堆中的地址值给栈中的变量b
简单来说:
父类静态内容 ——> 子类静态内容 ——> 父类构造代码块 ——> 父类构造方法 ——> 子类构造代码块 ——> 子类构造方法
- /**
- *打印类,仅用于打印
- */
- class Print {
- Print(Object s){
- System.out.print(s+"\t");
- }
- }
- /**
- *父类A
- */
- class A {
- public static Print A_sv1 = new Print("A_sv1");
- public Print A_cv1 = new Print("A_cv1");
- public int num = 66;
- public int num2 = 88;
- static{
- new Print("A_ss1");
- }
- public static Print A_sv2 = new Print("A_sv2");
- {
- new Print("A_cs1");
- }
- A(){
- new Print("A_s");
- show();
- }
- public Print A_cv2 = new Print("A_cv2");
- {
- new Print("A_cs2");
- }
- static{
- new Print("A_ss2");
- }
- public void show(){
- new Print(num);
- new Print(num2);
- }
- }
- /**
- *子类B
- */
- class B extends A {
- public static Print B_sv1 = new Print("B_sv1");
- public Print B_cv1 = new Print("B_cv1");
- public int num = 99;
- static{
- new Print("B_ss1");
- }
- public static Print B_sv2 = new Print("B_sv2");
- {
- new Print("B_cs1");
- }
- B(){
- new Print("B_s");
- new Print(num);
- }
- public Print B_cv2 = new Print("B_cv2");
- {
- new Print("B_cs2");
- }
- static{
- new Print("B_ss2");
- }
- public void show(){
- new Print(num);
- new Print(num2);
- super.show();
- }
- }/**
- *测试类Test
- */
- class Test {
- public static void main(String[] args){
- B b = new B();
- System.out.println();
- System.out.println("-----------------------------");
- B b2 = new B();
- }
- }
复制代码 根据上面的加载运行流程,应该可以分析出结果
其中,sv-静态成员变量,cv-普通成员变量,ss-静态代码块,cs-构造代码块,s-构造方法
1、B b表示b是B的引用。将在栈内存中开辟空间给b用
2、加载B的父类A的字节码文件,执行静态内容。将打印:A_sv1 A_ss1 A_sv2 A_ss2
3、加载B类的字节码文件,执行静态内容。将打印:B_sv1 B_ss1 B_sv2 B_ss2
4、new B()代表创建B类的对象,将在堆中开辟空间。
成员变量赋系统默认值:B_cv1=null,num=0,B_cv2=null。同时方法show()进方法区
5、堆中另给父类A开辟一个内存空间。
成员变量赋系统默认值:A_cv1=null,num=0,num2=0,A_cv2=null。同时方法show()进方法区
6、父类A成员变量显示初始化num=66,num2=88,同时执行构造代码块,将打印:A_cv1 A_cs1 A_cv2 A_cs2
7、执行父类A的构造方法,将打印:A_s
执行show()方法,注意,此时show()已经被子类B重写,
根据就近原则,num为B类中的成员变量,num2是从父类A中继承过来的,所以打印:0 88
再执行父类的show()方法,此时num和num2都为父类中的,且已被初始化,所以打印:66 88
8、把父类的内存空间标识给子类B的对象
9、B类的成员变量显示初始化num=99,同时执行构造代码块,将打印:B_cv1 B_cs1 B_cv2 B_cs2
10、执行B类的构造方法,根据就近原则,此时num为99,所以,将打印:B_s 99
11、把在堆中创建的B类对象的地址值赋给b
12、B b2 = new B()。因为已经加载过子父类的字节码,所以静态内容不再执行,方法也不用再加载到方法区。重复从第4步开始执行
|
|