java 构造函数内部的多态方法 完全剖析
我们先来看一个例子,如果你读过《java编程思想》的话 应该会有印象
1 package com.test.zj; 2 3 public class PolyConstructors { 4 5 public static void main(String[] args) { 6 // TODO Auto-generated method stub 7 new RoundGlyph(5); 8 } 9 10 } 11 12 class RoundGlyph extends Glyph { 13 private int radius = 1; 14 15 public RoundGlyph(int r) { 16 // TODO Auto-generated constructor stub 17 radius = r; 18 System.out.println("RoundGlyph radius==" + radius); 19 } 20 21 @Override 22 void draw() { 23 // TODO Auto-generated method stub 24 System.out.println("RoundGlyph draw() radius==" + radius); 25 } 26 27 } 28 29 class Glyph { 30 void draw() { 31 System.out.println("print glyph.draw()"); 32 } 33 34 Glyph() { 35 System.out.println("Glyph() before draw()"); 36 draw(); 37 System.out.println("Glyph() after draw()"); 38 39 } 40 41 }
对于java基础一般的同学来说 这里你可能会认为输出是如下:
1 Glyph() before draw() 2 RoundGlyph draw() radius==1 3 Glyph() after draw() 4 RoundGlyph radius==5
但实际上你运行完毕以后 你会发现他的输出是这样的:
可能有的人读到这里还是不太明白我要表述什么,那我再写一个简单的例子。先定义一个父类SuperClass
1 package com.test.zj; 2 3 public class SuperClass 4 { 5 private int superValue; 6 7 public SuperClass() 8 { 9 setSuperValue(100); 10 11 } 12 13 public void setSuperValue(int x) 14 { 15 superValue=x; 16 } 17 18 }
然后我们定义它的子类:
1 //这个子类继承自父类superclass 2 public class SubClass extends SuperClass 3 { 4 private int subValue=10; 5 6 public SubClass() 7 { 8 9 } 10 //这个方法重写了父类的方法 11 public void setSuperValue(int x) 12 { 13 //先调用父类的方法 14 super.setSuperValue(x); 15 //然后把值赋给自己的变量 16 subValue=x; 17 18 } 19 20 public void printSubValue() 21 { 22 System.out.println("subclass subvalue=="+subValue); 23 } 24 25 }
最后写个main函数 就可以了
1 package com.test.zj; 2 3 public class MainClass { 4 5 public static void main(String[] args) { 6 // TODO Auto-generated method stub 7 SubClass sc=new SubClass(); 8 sc.printSubValue(); 9 } 10 11 }
好,现在我相信很多人都会认为第二个例子输出的结果应该是100
但其实并没有什么卵用,他的实际结果是:
那到底这两个例子都发生了什么呢,我们直接来看字节码好了,这个字节码肯定不会有错,字节码怎么写的 jvm就怎么执行。
我们就先看看第一个例子。
这里应该很明显的能看到 我们的main函数 一开始就是new了RoundGlyph这个对象。那我们看看这个类-c的结果吧
可以看到这个类的构造函数
先执行的是这个:
也就是说 先执行了glyph的构造方法 然后当glyph的构造函数执行完毕以后 才执行的赋值语句
我们的radius 作为一个int变量 在被执行之前 jvm自动初始化他的值为0!
所以你这里隐隐约约应该都能猜到一个大概了,先执行的glyph的 构造函数,然后再给自己的成员变量radius赋值。
那我们看看glyph 都做了什么吧:
你看glyph的构造函数, 在中间的时候13:invokevirtual #31 这里,去执行了draw方法,但是子类我们重写了这个draw方法
所以你看 在glyph的构造函数里 调用子类的draw方法的时候 子类的radius赋值语句并没有被执行到,所以子类的这个方法
输出的值当然是0!
当父类glyph的构造函数执行完毕以后 ,我们的子类的赋值语句才终于得到执行。所以到这里 你应该能明白第一个例子了。
那我们现在就可以去研究一下第二个例子,其实都是大同小异的。我们还是先看第二个例子的manclass和main函数
你看这里main函数 先是new了一个subclass 子类的对象 对吧。那我们当然就要去看看subclass init方法
实际上这个地方就是Subclass的构造函数了。
这里很清楚的可以看到 在subclass的构造函数里 我们是先执行的superclass的构造函数,然后才给自己的subValue赋值为10.
那我们就去看看superclass里都做了什么。但实际上走到这里我们已经能想到了无论你在superclass做了什么 当你做完以后
subValue的值都必定为10.
所以当你subclass的对象构造完毕以后 此时他的成员变量subvalue的值就是10了,所以你当然打印出来这个变量的值 就一定是10了。
当然为了更清晰一点 我还是把superclass构造函数里做了什么稍微讲一下,虽然这里面做了什么不会影响到我们的结论,但还是讲一下吧,
即使这并没有什么卵用。。。
你看这里就是调用了一下setSuperValue这个方法么,对吧,因为子类重写了这个方法 所以我们肯定要看看子类
这个方法干嘛的:
你看不就是又调用了父类的setSupervalue方法吗,然后调用以后 你看有个iload putfield
这2个操作不就是给我们子类的subvalue 赋值的吗,对吧。一直到这里,我们子类的对象构造函数的第一步:
调用父类的构造函数 就算是走完了,走完了以后 才终于执行了自己的赋值语句:
好,这2个例子到这里就算分析完毕了。
实际上最终的结论就是java编程思想里说的那样:
父类static成员 -> 子类static成员 -> 父类普通成员初始化和初始化块 -> 父类构造方法 -> 子类普通成员初始化和初始化块 -> 子类构造方法
如果你们有兴趣的话,可以写一个稍微更复杂一点的程序,验证一下 上面的这个结论是否成立,废话。。。。这结论肯定是成立的。但是
你如果用javap -c 这个命令 去看他们的字节码的话 相信你能理解的更深了!
最后多说一句,平常我们在写代码的时候,尽量避免 上述2个例子这样的写法,因为这种情况造成的bug 很难被发现。。。即:
尽量不要在父类的构造函数里 操作子类的成员变量。如果一定要把初始化写的很麻烦的话,请考虑使用初始化块 这样一目了然的方法!
别问我为什么会研究到这,因为tmd 有一个bug 找了好久 发现是这个原因啊!所以以后你们发现有人这么写,请直接写邮件抄送全组投诉他啊!
转载于:https://www.cnblogs.com/punkisnotdead/p/4936432.html
java 构造函数内部的多态方法 完全剖析相关推荐
- java中多态案例工厂类,Java中构造器内部的多态方法的行为实例分析
本文实例讲述了Java中构造器内部的多态方法的行为操作.分享给大家供大家参考,具体如下: 这篇文章主要讨论的是,若在一个构造器中调用正在构造的对象的某个动态绑定的方法时会出现的情况.在此之前,我们需要 ...
- java构造函数内部调用_具有内部类构造函数参数的Java Reflection奇数
java构造函数内部调用 关于Java内部类 Java允许成员类(在其他类内定义的类),局部类(在语句块内定义的类)和匿名类(无名称的类): class Outer {Object anonymous ...
- java判断闰年通过多态方法_04748《Java语言程序设计》实验指导书.doc
04748<Java语言程序设计>实验指导书.doc Java语言程序设计 实验指导书 前言 一.上机实验目的 上机实验的目的是提高学生的分析问题.解决问题的能力和动手能力,通过实践环节理 ...
- 构造器内部的多态方法的行为(在构造器内调用正在构造的对象的某个动态绑定方法)
初始化的实际过程: 1.在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零. 2.先调用基类的构造器.此时,调用被覆盖后的draw()方法(注意这一步是在调用子类RoundGlyph的构 ...
- java中不允许一个方法在自身定义的内部调用自己_Java面向对象三大特性(基础篇)...
面向对象简称 OO(Object Oriented),20 世纪 80 年代以后,有了面向对象分析(OOA). 面向对象设计(OOD).面向对象程序设计(OOP)等新的系统开发方式模型的研究. 对语言 ...
- Java构造函数的使用_java中构造函数的使用方法
java中构造函数的使用方法 发布时间:2020-06-26 18:08:07 来源:亿速云 阅读:113 作者:Leah 本篇文章为大家展示了java中构造函数的使用方法,代码简明扼要并且容易理解, ...
- java draw 方法调用_java,_Java,在构造器里调用多态方法,java - phpStudy
Java,在构造器里调用多态方法 class Glyph { void draw() { System.out.println("Glyph.draw()"); } Glyph() ...
- JAVA构造函数(方法)
一.什么是构造函数 Java构造函数,也叫构造方法,是JAVA中一种特殊的函数.与函数名相同,无返回值. 作用:一般用来初始化成员属性和成员方法的,即new对象产生后,就调用了对象的属性和方法. 在现 ...
- 【一起来学Java】继承,多态,抽象,接口
一起来认识Java--继承 我们发现,一些相似的类会用到一些相同的功能. 所以,为了方便起见,就把这些常用到的类的功能单独放在一起,就可以被其他的类所调用了. 继承的定义 父类和子类 父类是被继承的类 ...
最新文章
- GPT-3获NeurIPS 2020最佳论文奖
- Qt工作笔记-在Graphics上写文本(QGraphicsSimpleTextItem与QGraphicsTextItem的基本使用)
- 【codevs2452】【SCOI2005】【BZOJ1088】扫雷Mine
- 微型计算机在机械设计中的应用,浅谈计算机技术在机械设计制造及自动化中的应用.docx...
- raspberry ubuntu 修改源为清华_在Windows7基础上安装Ubuntu系统,实现双系统操作
- BOM知识点全面总结(附实例、图解)
- Android HandlerThread 消息循环机制之源代码解析
- 某新闻App sign签名算法解析(一)
- 20 周年会上的这条视频,看哭了 10 万阿里人
- 不同图像锐化算子提取的图像信息有哪些不同_opencv数字图像处理(3)- 图像平滑与锐化...
- c++学习——(6)数组对象与对象成员
- RGB的三维模型与渐变色-颜色系列之一
- 微服务09——中间件
- 微信朋友圈怎么发文字?微信怎么发朋友圈只发文字?
- kicad 基本使用
- composer init 命令详解
- 【Java】三款经典游戏 java版本(开源)提升代码水平
- 京东店铺的所有商品API接口-(item_search_shop-获得店铺的所有商品接口),京东API接口
- MacOS M1芯片 安装eclipse和SVN
- 外服剑灵卡刀宏使用教程
热门文章
- 鸿蒙智慧屏和pro有什么区别,体验揭秘荣耀智慧屏pro评测怎么样?荣耀智慧屏pro和普通版区别有什么不同?...
- java requiredstringvalidator_如何访问自定义组件中的Validator.required
- mysql分表搜索引擎_MySql分库分表总结(转)
- Spring 面试多频 18问
- Spring Cloud Alibaba 之 服务治理:Nacos 如何实现微服务服务治理
- 操作所有的数据库免费可视化界面靠它就够了,告别付费,告别白嫖,卸载Navicat!
- 获得虚拟服务器相对路径,在web应用中获取相对路径和绝对路径
- 用Python实现简单的人脸识别,10分钟(附源码)
- java datarow_DataSet DataTable DataRow 深入浅出
- 冷热分离和直接使用大数据库_中台有“数”:大数据技术为苏宁818保驾护航