文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java继承关系的类初始化和实例化顺序是怎样的

2023-06-17 08:46

关注

这期内容当中小编将会给大家带来有关java继承关系的类初始化和实例化顺序是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

就像之前的一个评论.我们学习的是思路. 很多人都知道继承关系的类的初始化和实例化的顺序,但如果忘记了怎么办? 如何找到自己的答案? 又如果遇到的问题是关于泛型的擦除问题,又该如何去分析?

思路,重点是思路.泛型擦除先不谈.看继承. 首先给出一个例子,看看它的输出是什么.

public class A {      private static String a = "NA";      private String i="NA";      {          i = "A";          System.out.println(i);      }            static {          a = "Static A";          System.out.println(a);      }            public A() {          System.out.println("Construct A");      }  }
public class B extends A {      private static String b = "NB";      private String j="NB";      {          j = "B";          System.out.println(j);      }            static {          b = "Static B";          System.out.println(b);      }            public B() {          System.out.println("Construct B");      }  }
public class C {      public static void main(String[] args) {          new B();      }   }

以上输出是:

Static A
Static B
A
Construct A
B
Construct B

一切都是java编译器搞得鬼. JVM只是负责解析字节码.字节码虽然不是最原始的原子汇编码,但字节码已经可以完全解释JVM的指令执行过程了.一般来说,字节码和java源码相差比较大,javac会做前期优化,修改增加删除源码产生jvm解释器可以理解的字节码. java语法带来的安全,易用,易读等功能让我们忽略了字节码会和java源码有出路.

当遇到new的时候,比如new B(),将会尝试去初始化B类.如果B已经初始化,则开始实例化B类.如果B类没有初始化,则初始化B类,但B类继承A,所以在初始化B类之前需要先初始化A类.所以类的初始化过程是:A->B. 类在初始化的时候会执行static域和块. 类的实例化在类初始化之后,实例化的时候必须先实例化父类.实例化会先执行域和块,然后再执行构造函数.

上面的理论如果靠这种死记硬背,总会忘记.哦,还有父类的构造函数必须放在子类构造函数的***行.为什么?

遇到这种语法问题的时候,看教科书不如自己找出答案.工具就在JDK中,一个名叫javap的命令. javap会打出一个class的字节码伪码. 我们只需要分析B的字节码,就可以找到答案.

joeytekiMacBook-Air:bin joey$ javap -verbose B  Compiled from "B.java" public class B extends A    SourceFile: "B.java"   minor version: 0   major version: 50   Constant pool:  const #1 = class    #2; //  B  const #2 = Asciz    B;  const #3 = class    #4; //  A  const #4 = Asciz    A;  const #5 = Asciz    b;  const #6 = Asciz    Ljava/lang/String;;  const #7 = Asciz    j;  const #8 = Asciz    <clinit>;  const #9 = Asciz    ()V;  const #10 = Asciz   Code;  const #11 = String  #12;    //  NB  const #12 = Asciz   NB;  const #13 = Field   #1.#14; //  B.b:Ljava/lang/String;  const #14 = NameAndType #5:#6;//  b:Ljava/lang/String;  const #15 = String  #16;    //  Static B  const #16 = Asciz   Static B;  const #17 = Field   #18.#20;    //  java/lang/System.out:Ljava/io/PrintStream;  const #18 = class   #19;    //  java/lang/System  const #19 = Asciz   java/lang/System;  const #20 = NameAndType #21:#22;//  out:Ljava/io/PrintStream;  const #21 = Asciz   out;  const #22 = Asciz   Ljava/io/PrintStream;;  const #23 = Method  #24.#26;    //  java/io/PrintStream.println:(Ljava/lang/String;)V  const #24 = class   #25;    //  java/io/PrintStream  const #25 = Asciz   java/io/PrintStream;  const #26 = NameAndType #27:#28;//  println:(Ljava/lang/String;)V  const #27 = Asciz   println;  const #28 = Asciz   (Ljava/lang/String;)V;  const #29 = Asciz   LineNumberTable;  const #30 = Asciz   LocalVariableTable;  const #31 = Asciz   <init>;  const #32 = Method  #3.#33; //  A."<init>":()V  const #33 = NameAndType #31:#9;//  "<init>":()V  const #34 = Field   #1.#35; //  B.j:Ljava/lang/String;  const #35 = NameAndType #7:#6;//  j:Ljava/lang/String;  const #36 = String  #2; //  B  const #37 = String  #38;    //  Construct B  const #38 = Asciz   Construct B;  const #39 = Asciz   this;  const #40 = Asciz   LB;;  const #41 = Asciz   SourceFile;  const #42 = Asciz   B.java;   {  static {};    Code:     Stack=2, Locals=0, Args_size=0    0:   ldc #11; //String NB     2:   putstatic   #13; //Field b:Ljava/lang/String;     5:   ldc #15; //String Static B     7:   putstatic   #13; //Field b:Ljava/lang/String;     10:  getstatic   #17; //Field java/lang/System.out:Ljava/io/PrintStream;     13:  getstatic   #13; //Field b:Ljava/lang/String;     16:  invokevirtual   #23; //Method java/io/PrintStream.println:(Ljava/lang/String;)V     19:  return   LineNumberTable:      line 3: 0    line 11: 5    line 12: 10    line 13: 19    public B();    Code:     Stack=2, Locals=1, Args_size=1    0:   aload_0     1:   invokespecial   #32; //Method A."<init>":()V     4:   aload_0     5:   ldc #11; //String NB     7:   putfield    #34; //Field j:Ljava/lang/String;     10:  aload_0     11:  ldc #36; //String B     13:  putfield    #34; //Field j:Ljava/lang/String;     16:  getstatic   #17; //Field java/lang/System.out:Ljava/io/PrintStream;     19:  aload_0     20:  getfield    #34; //Field j:Ljava/lang/String;     23:  invokevirtual   #23; //Method java/io/PrintStream.println:(Ljava/lang/String;)V     26:  getstatic   #17; //Field java/lang/System.out:Ljava/io/PrintStream;     29:  ldc #37; //String Construct B     31:  invokevirtual   #23; //Method java/io/PrintStream.println:(Ljava/lang/String;)V     34:  return   LineNumberTable:      line 15: 0    line 4: 4    line 6: 10    line 7: 16    line 16: 26    line 17: 34    LocalVariableTable:      Start  Length  Slot  Name   Signature     0      35      0    this       LB;  }

类的生命周期,将经历类的装载,链接,初始化,使用,卸载. 装载是将字节码读入到内存的方法区中, 而类的初始化则会在线程栈中执行static{}块的code. 在之前,这个块有另一个名字<cinit>即类初始化方法.现在改名为static{}了. 类的初始化只进行一次. 但是,每当一个类在装载和链接完毕以后,通过字节码的分析,JVM解析器已经知道B是继承A的,于是在初始化B类前,A类会先初始化.这是一个递归过程. 所以,B类的初始化会导致A类static{}执行,然后是B的static{}执行.让我们看看B的static{}块中执行了什么.

static {};    Code:     Stack=2, Locals=0, Args_size=0 栈深为2,本地变量0个,参数传递0个.     0:   ldc #11; //String NB  将常量池中#11放到栈顶.#11="NB".     2:   putstatic   #13; //Field b:Ljava/lang/String;  将栈顶的值 "NB" 赋予常量池中的#13,也就是 static b="NB".     5:   ldc #15; //String Static B  将#15放入栈顶. #15="static B".     7:   putstatic   #13; //Field b:Ljava/lang/String;  赋值static b = "static B".     10:  getstatic   #17; //Field java/lang/System.out:Ljava/io/PrintStream;  将PrintStream引用压栈.     13:  getstatic   #13; //Field b:Ljava/lang/String;  将static b的值压栈.     16:  invokevirtual   #23; //Method java/io/PrintStream.println:(Ljava/lang/String;)V  调用虚函数PrintStream.println("static B")     19:  return 退出函数,销毁函数栈帧.

通过注释,我们看到类B中的static域赋值和static块均被放到了类的初始化函数中.

当我们进行类的实例化的时候,会调用类的构造函数.我们看看类B的构造函数做了什么.

public B();    Code:     Stack=2, Locals=1, Args_size=1 栈深为2,本地变量1个(其实就是this),参数为1个(就是this).     0:   aload_0  将***个参数压栈.也就是this压栈.     1:   invokespecial   #32; //Method A."<init>":()V  在this上调用父类的构造函数.在B的构造函数中并没有声明super(),但是java编译器会自动生成此字节码来调用父类的无参构造函数.如果在B类中声明了super(int),编译器会使用对应的A类构造函数来代替.JVM只是执行字节码而已,它并不对super进行约束,约束它们的是java的编译器.this出栈.     4:   aload_0  将this压栈.     5:   ldc #11; //String NB  将"NB"压栈.     7:   putfield    #34; //Field j:Ljava/lang/String;  给j赋值this.j="NB". this和"NB"出栈.     10:  aload_0  将this压栈.     11:  ldc #36; //String B  把"B"压栈     13:  putfield    #34; //Field j:Ljava/lang/String;  给j赋值this.j="B". this和"B"出栈.栈空     16:  getstatic   #17; //Field java/lang/System.out:Ljava/io/PrintStream;  压栈PrintStream     19:  aload_0  压栈this    20:  getfield    #34; //Field j:Ljava/lang/String;  this出栈,调用this.j,压栈this.j.     23:  invokevirtual   #23; //Method java/io/PrintStream.println:(Ljava/lang/String;)V  调用PrintStream.println(this.j).栈空.     26:  getstatic   #17; //Field java/lang/System.out:Ljava/io/PrintStream;  压栈PrintStream     29:  ldc #37; //String Construct B  压栈"Construct B"    31:  invokevirtual   #23; //Method java/io/PrintStream.println:(Ljava/lang/String;)V  调用PrintStream.println("Construct B")     34:  return

从上面的字节码可以看出,java编译器在编译产生字节码的时候,将父类的构造函数,域的初始化,代码块的执行和B的真正的构造函数按照顺序组合在了一起,形成了新的构造函数. 一个类的编译后的构造函数字节码一定会遵循这样的顺序包含以下内容:

父类的构造函数->

当前类的域初始化->(按照书写顺序)

代码块->(按照书写顺序)

当前类的构造函数.

到这里,应该彻底明白继承类的初始化和实例化顺序了.

上述就是小编为大家分享的java继承关系的类初始化和实例化顺序是怎样的了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯