1. 回顾
先来回顾一下有关构造器的知识
构造器执行步骤之简要概述:
(1) binding 绑定参数
(2) this()
(3) super() --> 直到Object终止
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这里完成了上行过程,由于这个过程完全是系统自动完成的,所以我们可以忽略
但是一定要记住,到此为止所有的成员变量均已初始化为默认值
然后由Object自顶向下,下衍执行
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(4) 执行域变量的赋值语句
(5) 执行构造器内的语句
关于这个过程,我们很熟悉了
如果只把上行的过程简略的看作是初始化所有成员变量的过程,那么我们完全可以忽略它
(不过不要忘了 super() 过程的存在,这也是考点之一),而将精力放在下衍的过程
毕竟我们的代码都在这个过程中执行。
因此,我们可以将构造器的初始过程看作为从 Object 顶端逐步向下一级一级执行构造
器的过程(Marcus Green 语,参考 http://www.jchq.net)
现在再来看一些优秀的题目吧:
2. 范例讨论
(1) Q22 from Hardest Mock Exam
What will be written to the standard output
when the following program is run?
class Base {
int i;
Base() {
add(1);
}
void add(int v) {
i += v;
}
void print() {
System.out.println(i);
}
}
class Extension extends Base {
Extension() {
add(2);
}
void add(int v) {
i += v*2;
}
}
public class Qd073 {
public static void main(String args[]) {
bogo(new Extension());
}
static void bogo(Base b) {
b.add(8);
b.print();
}
}
1) 9
2) 18
3) 20
4) 21
5) 22
这个题目不仅考察了构造器的构造过程,同时考察了关于多态的知识
bogo(new Extention()); 这里 new 了一个 Extention 对象
明显的调用了构造器并初始化了一个对象,bogo() 将它绑定为 Base 类传入 bogo 方法
所以说实际上执行了类似下面的这个语句:Base v = new Extention(); bogo(v);
因此,Extention() 初始化的过程是:
上行 -- 初始化所有实例变量,此时,i=0
下衍 -- 自顶执行构造器,(Object 构造器为空,所以继续下衍)
先执行父类 Base 的构造器 add(1),注意,由于多态和动态绑定的原因
add(1) 将虚拟调用 Extention 类的 add() 方法,而不是 Base 类的 add() 方法
因此执行完 add(1) 之后,i = 2;
继续执行子类构造器 add(2),仍然是调用 Extention 类的 add() 方法,
执行完后,i = 6
构造器执行完毕
运行 b.add(8)之后,i = 22
答案是 5
(2)
What is the output of the following program
public class Test {
private int i = giveMeJ();
private int j = 10;
private int giveMeJ() {
return j;
}
public static void main(String args[]) {
System.out.println((new Test()).i);
}
}
Select one correct answer
a. Compiler error complaining about access restriction of private variables
of AQuestion.
b. Compiler error complaining about forward referencing.
c. No Compilation error - The output is 0;
d. No Compilation error - The output is 10;
e. No Compilation error, but Runtime error;
这个题目稍微简单一些,不过域变量由方法赋值很容易迷惑人
如果你不坚定自己的信念,就会困惑下去
分析步骤同前面的例子一样:
上行 -- 初始化全部成员变量 i = 0 , j = 0
下衍 -- 这里用到了构造器执行过程的第4步,就是执行显式的为域变量赋初值代码
i 调用 giveMeJ() 方法为自己赋值,此时 j = 0,因此,i = 0
然后 j 赋初值为 10 ;然后,执行 new 过程的最后一步,执行构造器
由于构造器是默认构造器,没有代码执行,new 过程结束。
所以答案是 c
(3)
将上面的例子中 i 赋值语句部分修改一下,改为:
private int i = new Test().giveMeJ();
以同样的步骤分析,你会发现,在构造过程的第4步为 i 指定值的时候
又 new 了一个 Test 对象,因此需要进入一个新的 new 过程
而新的过程在同样的步骤同样的地方又进入了一个新的 new 过程…………
好像没有尽头?没错,这个程序因为在堆栈中重复 new 自身
将导致堆栈溢出
所以答案是 e
先来回顾一下有关构造器的知识
构造器执行步骤之简要概述:
(1) binding 绑定参数
(2) this()
(3) super() --> 直到Object终止
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
这里完成了上行过程,由于这个过程完全是系统自动完成的,所以我们可以忽略
但是一定要记住,到此为止所有的成员变量均已初始化为默认值
然后由Object自顶向下,下衍执行
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(4) 执行域变量的赋值语句
(5) 执行构造器内的语句
关于这个过程,我们很熟悉了
如果只把上行的过程简略的看作是初始化所有成员变量的过程,那么我们完全可以忽略它
(不过不要忘了 super() 过程的存在,这也是考点之一),而将精力放在下衍的过程
毕竟我们的代码都在这个过程中执行。
因此,我们可以将构造器的初始过程看作为从 Object 顶端逐步向下一级一级执行构造
器的过程(Marcus Green 语,参考 http://www.jchq.net)
现在再来看一些优秀的题目吧:
2. 范例讨论
(1) Q22 from Hardest Mock Exam
What will be written to the standard output
when the following program is run?
class Base {
int i;
Base() {
add(1);
}
void add(int v) {
i += v;
}
void print() {
System.out.println(i);
}
}
class Extension extends Base {
Extension() {
add(2);
}
void add(int v) {
i += v*2;
}
}
public class Qd073 {
public static void main(String args[]) {
bogo(new Extension());
}
static void bogo(Base b) {
b.add(8);
b.print();
}
}
1) 9
2) 18
3) 20
4) 21
5) 22
这个题目不仅考察了构造器的构造过程,同时考察了关于多态的知识
bogo(new Extention()); 这里 new 了一个 Extention 对象
明显的调用了构造器并初始化了一个对象,bogo() 将它绑定为 Base 类传入 bogo 方法
所以说实际上执行了类似下面的这个语句:Base v = new Extention(); bogo(v);
因此,Extention() 初始化的过程是:
上行 -- 初始化所有实例变量,此时,i=0
下衍 -- 自顶执行构造器,(Object 构造器为空,所以继续下衍)
先执行父类 Base 的构造器 add(1),注意,由于多态和动态绑定的原因
add(1) 将虚拟调用 Extention 类的 add() 方法,而不是 Base 类的 add() 方法
因此执行完 add(1) 之后,i = 2;
继续执行子类构造器 add(2),仍然是调用 Extention 类的 add() 方法,
执行完后,i = 6
构造器执行完毕
运行 b.add(8)之后,i = 22
答案是 5
(2)
What is the output of the following program
public class Test {
private int i = giveMeJ();
private int j = 10;
private int giveMeJ() {
return j;
}
public static void main(String args[]) {
System.out.println((new Test()).i);
}
}
Select one correct answer
a. Compiler error complaining about access restriction of private variables
of AQuestion.
b. Compiler error complaining about forward referencing.
c. No Compilation error - The output is 0;
d. No Compilation error - The output is 10;
e. No Compilation error, but Runtime error;
这个题目稍微简单一些,不过域变量由方法赋值很容易迷惑人
如果你不坚定自己的信念,就会困惑下去
分析步骤同前面的例子一样:
上行 -- 初始化全部成员变量 i = 0 , j = 0
下衍 -- 这里用到了构造器执行过程的第4步,就是执行显式的为域变量赋初值代码
i 调用 giveMeJ() 方法为自己赋值,此时 j = 0,因此,i = 0
然后 j 赋初值为 10 ;然后,执行 new 过程的最后一步,执行构造器
由于构造器是默认构造器,没有代码执行,new 过程结束。
所以答案是 c
(3)
将上面的例子中 i 赋值语句部分修改一下,改为:
private int i = new Test().giveMeJ();
以同样的步骤分析,你会发现,在构造过程的第4步为 i 指定值的时候
又 new 了一个 Test 对象,因此需要进入一个新的 new 过程
而新的过程在同样的步骤同样的地方又进入了一个新的 new 过程…………
好像没有尽头?没错,这个程序因为在堆栈中重复 new 自身
将导致堆栈溢出
所以答案是 e