如何创办网站,建设网站的申请,小型企业网站建设的背景,网站链接太多怎么做网站地图从JVM角度看继承
最近重读了周志明老师的《深入理解JAVA虚拟机》一书#xff0c;看完大有收获#xff0c;但仍对继承情况下对象内存布局有所疑惑#xff0c;所以查阅资料#xff0c;结合本书进行分析
参考文档#xff1a;
【深入理解JVM】#xff1a;Java类继承关系中…从JVM角度看继承
最近重读了周志明老师的《深入理解JAVA虚拟机》一书看完大有收获但仍对继承情况下对象内存布局有所疑惑所以查阅资料结合本书进行分析
参考文档
【深入理解JVM】Java类继承关系中的初始化顺序
从JVM内存机制理解 java 的继承
继承的对象内存布局
对象在堆内存中的存储布局可以划分为三个部分
对象头Header
实例 数据Instance Data
对齐填充Padding 书中原文 实例数据部分是对象真正存储的有效信息即我们在程序代码里面所定义的各种类型的字段内容无论是从父类继承下来的还是在子类中定义的字段都必须记录起来。这部分的存储顺序会 受到虚拟机分配策略参数-XXFieldsAllocationStyle参数和字段在Java源码中定义顺序的影响。 HotSpot虚拟机默认的分配顺序为longs/doubles、ints、shorts/chars、bytes/booleans、oopsOrdinary Object PointersOOPs从以上默认的分配策略中可以看到相同宽度的字段总是被分配到一起存 放在满足这个前提条件的情况下在父类中定义的变量会出现在子类之前。如果HotSpot虚拟机的 XXCompactFields参数值为true默认就为true那子类之中较窄的变量也允许插入父类变量的空 隙之中以节省出一点点空间。 可以看到继承下来的非静态成员变量是存在于子类的而静态成员变量存在于方法区
初始化的时机
原文 关于在什么情况下需要开始类加载过程的第一个阶段“加载”《Java虚拟机规范》中并没有进行 强制约束这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段《Java虚拟机规范》 则是严格规定了有且只有六种情况必须立即对类进行“初始化”而加载、验证、准备自然需要在此之 前开始 1遇到new、getstatic、putstatic或invokestatic这四条字节码指令时如果类型没有进行过初始化则需要先触发其初始化阶段。能够生成这四条指令的典型Java代码场景有 · 使用new关键字实例化对象的时候。 · 读取或设置一个类型的静态字段被final修饰、已在编译期把结果放入常量池的静态字段除外的时候。 · 调用一个类型的静态方法的时候。 2使用java.lang.reflect包的方法对类型进行反射调用的时候如果类型没有进行过初始化则需要先触发其初始化。 3当初始化类的时候如果发现其父类还没有进行过初始化则需要先触发其父类的初始化。 4当虚拟机启动时用户需要指定一个要执行的主类包含main()方法的那个类虚拟机会先初始化这个主类。 5当使用JDK 7新加入的动态语言支持时如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄并且这个方法句柄对应的类没有进行过初始化则需要先触发其初始化。 6当一个接口中定义了JDK 8新加入的默认方法被default关键字修饰的接口方法时如果有这个接口的实现类发生了初始化那该接口要在其之前被初始化。 对于这六种会触发类型进行初始化的场景《Java虚拟机规范》中使用了一个非常强烈的限定语——“有且只有”这六种场景中的行为称为对一个类型进行主动引用。除此之外所有引用类型的方式都不会触发初始化称为被动引用。 执行顺序
父类静态代码区和父类静态成员子类静态代码区和子类静态成员父类非静态代码区和普通成员父类构造函数子类非静态代码区和普通成员子类构造函数 原文 ()方法与类的构造函数(即在虚拟机视角中的实例构造器()方法) 不同它不需要显式地调用父类构造器Java虚拟机会保证在子类的()方法执行前父类的0方法已经执行完毕。因此在Java虚拟机中第一个被执行的()方法的类型肯定是java.lang.Object。 由于父类的()方法先执行也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作 ok 12步的顺序没问题 原文 例如前文多次登场的实例构造器()方法和类构造器()方法就是在这个阶段被添加到语 法树之中的。请注意这里的实例构造器并不等同于默认构造函数如果用户代码中没有提供任何构造 函数那编译器将会添加一个没有参数的、可访问性public、protected、private或与当前 类型一致的默认构造函数这个工作在填充符号表阶段中就已经完成。()和()这两个构造 器的产生实际上是一种代码收敛的过程 编译器会把 语句块对于实例构造器而言是“{}”块对于类构造器而言是“static{}”块、 变量初始化实例变量和类变量、 调用父类的实例构造器仅仅是实例构造器()方法中无须调用父类的()方法Java虚拟机会自动保证父类构造器的正确执行但在()方法中经常会生成调用java.lang.Object的()方法的代码 等操作收敛到()和()方法之中并且保证无论源码中出现的顺序如何都一定是按先执行父类的实例构造器然后初始化变量最后执行语句块的顺序进行上面所述的动作由Gen::normalizeDefs()方法来实现。 除了生成构造器以外还有其他的一些代码替换工作用于优化程序某些逻辑的实现方式如把字符串的加操作替换为StringBuffer或StringBuilder取决于目标代码的版本是否大于或等于JDK 5的append()操作等等。 由上文可以看到和的包括范围有些人认为阶段仅仅是执行构造方法这明显是错误的原文中已经表明()和()方法是一些操作的集合
方法:()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块static{}块中的语句合并产生的
方法:除了()方法包括的操作剩下的包括实例变量初始化非静态代码块构造方法先执行父类再执行子类
45步的顺序也ok
先执行父类的实例构造器然后初始化变量最后执行语句块的顺序
1235步骤中代码块和成员变量的初始化也有先后顺序一般是先初始化变量后静态代码块
至于和的顺序则是因为是执行在类加载的的初始化流程中可以说初始化的过程就是而的执行必须在类加载之后所以这两者的顺序也是确定的
默认构造器 自定义构造器 可以看到无论是默认构造器还是用户定义构造器均先调用父类的构造函数Object
L0LINENUMBER 6 L0ALOAD 0INVOKESPECIAL java/lang/Object.init ()V调用构造函数是否一定会产生实例
我认为不是
比如子类调用抽象父类的构造函数父接口的构造函数会产生子类实例但会产生父级的实例吗?
这显然是不会的
抽象类的构造方法为自定义构造器时子类需要显式调用 关于java构造函数 的错误 there is no default constructor available in
B类继承A类在B类的构造器中会隐式存在 super()用来调用父类无参构造器 而父类A中没有无参构造器因为A中已经定义了有参构造器在A中如果没有定义有参构造器就会有默认的无参构造器但如果定义了有参构造器就没有默认的无参构造器。所以会显示上述错误。
解决方法
**法一**在A类即父类中添加一个无参构造器 **法二**在B类子类的有参构造器中添加一个super(m);即可 B(int m){ super(m); i 2; } 、