Java学习笔记--构造器的调用顺序
2017-10-06 19:01
393 查看
前言
工作之余回头巩固一下Java知识,只有基础牢固了才能走的更远。
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次逐渐向上链接, 以使每个基类的构造器都能得到调用。导出类只能访问自己的成员,访问不了基类中private的成员,只有基类的构造器有权限对自己的元素进行初始化。因此,必须令所有的构造器都得到调用,否则就不可能正确的构造完整的对象。
示例:
class Meal{ Meal(){ System.out.println("Meal()"); } } class Chesses{ Chesses(){ System.out.println("Chesses()"); } } class Bread{ Bread(){ System.out.println("Bread()"); } } class Lettuce{ Lettuce(){ System.out.println("Lettuce()"); } } class Lunch extends Meal{ Lunch() { System.out.println("Lunch()"); } } class ProtableLunch extends Lunch{ ProtableLunch() { System.out.println("ProtableLunch"); } } public class Sandwich extends ProtableLunch{ private Bread bread = new Bread(); private Chesses chess = new Chesses(); private Lettuce lettuce = new Lettuce(); public Sandwich() { System.out.println("Sandwich()"); } public static void main(String[] args) { Sandwich sandwich = new Sandwich(); } } --------------------------------------------------- Output: Meal() Lunch() ProtableLunch Bread() Chesses() Lettuce() Sandwich
由输出结果可见,构造器的大致初始化顺序如下:
1)调用基类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一层的导出类,等等,知道最底层的导出类。2) 按照声明顺序调用成员的初始化方法。
3)调用导出类构造器的主体。
按照这样的规则就能保证所有的基类成员以及当前对象的成员对象都得到了初始化。但是有一种特殊情况。
示例:
class Glyph{ void draw(){ System.out.println("Glyph.Draw()"); } Glyph(){ System.out.println("Glyph() before draw()"); draw(); System.out.println("Glyph() after draw()"); } } class RoundGlyph extends Glyph{ private int radius = 1; RoundGlyph(int i){ radius = i; System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius); } @Override void draw() { System.out.println("RoundGlyph.draw(), radius = " + radius); } } public class PloyConstructors { public static void main(String[] args) { new RoundGlyph(5); } } ------------------------------------------------------- Output: Glyph() before draw() RoundGlyph.draw(), radius = 0 Glyph() after draw() RoundGlyph.RoundGlyph(), radius = 5
在输出结果当中radius的输出结果是0,并不是默认的初始值1。所以上面所说的初始化顺序并不完整。
构造器的实际初始化顺序如下:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零。2)如前所述那样调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤2的缘故,我么此时会发现radius的值为0。
3)按照声明顺序调用成员的初始化方法。
4)调用导出类构造器的主体。
因为我们在Glyph的构造器中调用draw()方法的时候,RoundGlyph的构造器还没有得到调用,所以他的存储空间此时是初始化为二进制0的,所以radius的输出结果是0。这样的错误编译器不会报错,我们也很难发现。编写构造器时有一条有效的准则:“用尽可能简单的方法使对象进入正常状态;如果可以的话,尽量避免调用其他方法。”在构造器当中唯一能够安全调用的那些方法就是基类当中的final方法(也适用于private方法,他们自动属于final方法)。这些方法不会被覆盖,因此不会出现上面的问题。
原文地址:http://blog.csdn.net/Liuhe_5656/article/details/78166486
相关文章推荐
- java学习笔记---构造器的多态和调用顺序
- 《Thinking in Java》笔记之调用构造器顺序
- Java学习笔记(1) 对象初始化顺序
- think in java之构造器的真正调用顺序
- java多态中构造器的调用顺序研究
- think in java之构造器的真正调用顺序
- C++ Primer 学习笔记(8): 构造函数、析构函数调用顺序
- C++学习笔记(5)——基类、派生类的构造函数、析构函数的调用顺序
- Java学习笔记——子类调用父类属性方法
- Thinking in Java [Java编程机制] 学习笔记 -- 初始化顺序Order of initialization
- JNI学习之---用jni API 访问java对象的属性,方法,调用构造器。
- 马士兵java视频学习笔记第二章:递归调用
- JNI学习笔记:(1)开篇(2)本地代码访问Java代码 (3)本地方法取得Java属性/调用java方法 (4)本地代码创建Java对象(包括javaString) (5) 本地方法处理java数组
- 黑马程序员之asp.net学习笔记:巧用C#做中间语言 实现java调用.net
- Java 学习笔记 (10) - Java 函数的递归调用
- 新手JAVA学习笔记2——解读构造器
- .net调用java webservice基于JBOSS服务器 学习笔记(一)
- java学习笔记3:对象的初始化顺序
- lua学习笔记 3 android调用Lua。Lua脚本使用LoadLib回调Java,并传多个参数
- java学习笔记4:对象的初始化顺序(官方实例)