构造器是OOP的重要组成部分,很多人认为它很容易。只不过是new了一个对象而已。而think in java的作者却告诉我们,其实这并不容易。
先看下面这个例子。在你没看结果之前,你觉得你的答案是对的么。
package com.tudou.t1;
class Meal {
Meal() {
System.out.println("meal");
}
}
class Bread {
Bread() {
System.out.println("Bread");
}
}
class Cheese {
Cheese() {
System.out.println("Cheese");
}
}
class Lettuce {
Lettuce() {
System.out.println("Lettuce");
}
}
class Lunch extends Meal{
Lunch() {
System.out.println("Lunch");
}
}
class PortableLunch extends Lunch{
PortableLunch() {
System.out.println("PortableLunch");
}
}
public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() {
System.out.println("Sandwich");
}
public static void main(String[] args) {
new Sandwich();
}
}
控制台的打印结果为:
meal
Lunch
PortableLunch
Bread
Cheese
Lettuce
Sandwich
复杂对象调用构造器的顺序应该遵循下面的原则:
1,调用基类[即父类]构造器。这个步骤会不断反复递归下去,首先是构造器这种层次结构的根,然后是下一层导出类[即子类],等等。直到最底层的导出类。[从最上层的meal一直递归到PortableLunch]
2,按声明顺序调用成员的初始化方法。[即上面的Bread,Cheese,Lettuce]
3,调用导出类构造器的主体[即Sandwich]
可见,调用类本身是最后完成初始化的,最先完成初始化的是最顶级的基类,所谓没有父亲,哪来的儿子。处于它们中间的是调用类本身拥有的子对象。因为你不可能在子对象初始化之前用本类调用它,所以它一定在本类调用之前,父类调用之后完成初始化的。
那么这个说法是不是一定成立呢。结果是否定的。你必须知道JVM的编绎原理才可能知道,它究竟是如何工作的。
我们来看下面这个例子,来解释为什么它不一定。因为在继承和重写的时候,这种情况变得有点诡异。
深入探究:
package com.tudou.t1;
public class ConstrcutorTest2 {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
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 r) {
radius = r;
System.out.println("RoundGlyph(),radius:" + radius);
}
void draw() {
System.out.println("RoundGlyph.draw(),radius:" + radius);//此处打印是0,而不是1
}
}
控制台打印结果:
Glyph before draw();
RoundGlyph.draw(),radius:0
Glyph after draw();
RoundGlyph(),radius:5
为什么RoundGlyph.draw(),radius:0这里会是0呢。
默认的1哪去了?值自己会变么。其实上面的讲述并不完整。,而这正是解决谜题的关键所在。初始化的实际过程之前,实际在还有一步。
0:在其他任何事物发生之前,将分配对象的存舍得空间初始化为二进制的零。
而它后面的初始化顺序就是上面的3步。
1,调用基类[即父类]构造器。这个步骤会不断反复递归下去,首先是构造器这种层次结构的根,然后是下一层导出类[即子类],等等。直到最底层的导出类。
2,按声明顺序调用成员的初始化方法。
3,调用导出类构造器的主体
也就是说,实际上有4步,知道这些你对对象初始化构造器才可能有个清楚的认识。
JAVA有更多的精髓等着人们去挖掘,而不仅仅是知道如何去使用它。
因为你不知道什么时候它会出现意想不到的后果,而这个错误,可能你根本就想不出来。
编写构造器时有一条准则:
用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其它方法。
在构造器内唯一能够安全调用的那些方法是基类中的final或者private方法,这些方法不能被覆盖,因此也就不会出现令人惊讶的问题。
你可能无法总是遵循这条准则,但是应该朝着它努力。
学任何语言,请打好基础,它是你以后扩展的人生基石。
分享到:
相关推荐
Think in java 源码构建编译
think in java 源码整理,应该算是比较全面的,有需要的朋友可以下下来看下
Think in java 的代码源码,里面很详细的习题详解
java think in java (英文第四版)至今为止 被认为是java教程之中权威作品之一
Think Data Structures in Java 英文azw3 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除
think in java 第四版 源码以及作业 eclipse版本 包含jar包 可以直接导入eclipse
这是java编程思想中的练习题,自己感觉不错,上传上去给大家分享~~~~~
Think in Java 作者的文章 精辟见解
Think in Java(美)Bruce Eckel 著 陈昊鹏 译 引言 同人类任何语言一样,Java为我们提供了一种表达思想的方式。如操作得当,同其他方式相 比,随着问题变得愈大和愈复杂,这种表达方式的方便性和灵活性会显露无遗。 ...
Think in java 教程 Think in java 教程
Think In Enterprise Java v1.1
抽象的进步 1.2 对象的接口 1.3 实现方案的隐藏 1.4 方案的重复使用 1.5 继承:重新使用接口 1.5.1 改善基础类 1.5.2 等价和类似关系 1.6 多形对象的互换使用 1.6.1 动态绑定 ...1.11 Java和因特网
编程思想第四版完整中文高清版(免费)TXT格式
学习think in java 时的第9章的例题及练习答案。 eclipse 工程包,运行可用!全自己敲的!
从一程序员朋友手里tiang来的,看看对你有帮助
经典的think in java4,所有源码,帮你早日攻克java学习的堡垒!!!
think in Java答案
think in java Chinese 个人整理
java进阶学习不可不看的一本书。不过这是thinkin java2
1.7.1 集合与继承器 1.7.2 单根结构 1.7.3 集合库与方便使用集合 1.7.4 清除时的困境:由谁负责清除? 1.8 违例控制:解决错误 1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 ...