A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© Ralap军 中级黑马   /  2015-9-6 15:54  /  880 人查看  /  4 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 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
简单来说:
父类静态内容 ——> 子类静态内容 ——> 父类构造代码块 ——> 父类构造方法 ——> 子类构造代码块 ——> 子类构造方法
  1. /**
  2. *打印类,仅用于打印
  3. */
  4. class Print {
  5.     Print(Object s){
  6.         System.out.print(s+"\t");
  7.     }
  8. }
  9. /**
  10. *父类A
  11. */
  12. class A {
  13.     public static Print A_sv1 = new Print("A_sv1");
  14.     public Print A_cv1 = new Print("A_cv1");
  15.     public int num = 66;
  16.     public int num2 = 88;
  17.     static{
  18.         new Print("A_ss1");
  19.     }
  20.     public static Print A_sv2 = new Print("A_sv2");
  21.     {
  22.         new Print("A_cs1");
  23.     }
  24.     A(){
  25.         new Print("A_s");
  26.         show();
  27.     }
  28.     public Print A_cv2 = new Print("A_cv2");
  29.     {
  30.         new Print("A_cs2");
  31.     }
  32.     static{
  33.         new Print("A_ss2");
  34.     }
  35.     public void show(){
  36.         new Print(num);
  37.         new Print(num2);
  38.     }
  39. }
  40. /**
  41. *子类B
  42. */
  43. class B extends A {
  44.     public static Print B_sv1 = new Print("B_sv1");
  45.     public Print B_cv1 = new Print("B_cv1");
  46.     public int num = 99;
  47.     static{
  48.         new Print("B_ss1");
  49.     }
  50.     public static Print B_sv2 = new Print("B_sv2");
  51.     {
  52.         new Print("B_cs1");
  53.     }
  54.     B(){
  55.         new Print("B_s");
  56.         new Print(num);
  57.     }
  58.     public Print B_cv2 = new Print("B_cv2");
  59.     {
  60.         new Print("B_cs2");
  61.     }
  62.     static{
  63.         new Print("B_ss2");
  64.     }
  65.     public void show(){
  66.         new Print(num);
  67.         new Print(num2);
  68.         super.show();
  69.     }
  70. }/**
  71. *测试类Test
  72. */
  73. class Test {
  74.     public static void main(String[] args){
  75.         B b = new B();
  76.         System.out.println();
  77.         System.out.println("-----------------------------");
  78.         B b2 = new B();
  79.     }
  80. }
复制代码
根据上面的加载运行流程,应该可以分析出结果
其中,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步开始执行




hei.png (3.71 KB, 下载次数: 23)

hei.png

4 个回复

倒序浏览
沙发,支持一下
回复 使用道具 举报
共勉,加油努力
回复 使用道具 举报
                    点个赞
回复 使用道具 举报
很详细啊
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马