<示例1>

請寫出運行結(jié)果
1 abstract class Glyph
{
2 Glyph()
{
3 System.out.println( " Glyph() before draw() " );
4 draw(); // 不明白的地方是這里.
5 System.out.println( " Glyph() after draw() " );
6 }
7 abstract void draw();
8 }
9
10 class RoundGlyph extends Glyph
{
11 private int radius = 1 ;
12 RoundGlyph( int r)
{
13 radius = r;
14 System.out.println( " RoundGlyph.RoundGlyph(), radius = " + radius);
15 }
16
17 void draw()
{
18 // radius = z;
19 System.out.println( " RoundGlyph.draw(), radius = " + radius);
20 }
21 }
22

23 public class PolyConstructors
{

24 public static void main(String[] args)
{
25 new RoundGlyph( 5 );
26 }
27 }


答案
1 Glyph() before draw()
2 RoundGlyph.draw(), radius = 0
3 Glyph() after draw()
4 RoundGlyph.RoundGlyph(), radius = 5

分析
1
類初始化時構(gòu)造函數(shù)調(diào)用順序:
2
首先加載類,遇到extends字樣會停止加載當前類,加載父類,之后再繼續(xù)加載。
3
當把一個類完全加載后,這個類的靜態(tài)成員將被先加載。之后進行如下流程:
4
(1)初始化對象的存儲空間為零或null值;
5
(2)調(diào)用父類構(gòu)造函數(shù);
6
(3)按順序分別調(diào)用類成員變量和實例成員變量的初始化表達式;
7
(4)調(diào)用本身構(gòu)造函數(shù)
8
9
構(gòu)造函數(shù)的初始化順序:
10
1 .當執(zhí)行new RoundGlyph( 5 )時,首先會加載‘RoundGlyph. class ’,但是這個時候JVM發(fā)現(xiàn) RoundGlyph extends (繼承于) Glyph ,所以他會加載Glyph. class
11
2 .然后會初始化Glyph類的static成員數(shù)據(jù),這個類沒有static成員數(shù)據(jù),所以沒有這一步。
12
3 .會執(zhí)行Glyph類的構(gòu)造函數(shù)Glyph()
13
4 .接著初始化RoundGlyph類的成員數(shù)據(jù),最后調(diào)用RoundGlyph()構(gòu)造函數(shù)來生產(chǎn)對象
<示例2>

題目
1
public class Test2 extends Test1
{
2
{
3
System.out.print( " 1 " );
4
}
5
Test2()
{
6
System.out.print( " 2 " );
7
}
8
static
{
9
System.out.print( " 3 " );
10
}
11
{
12
System.out.print( " 4 " );
13
}
14
public static void main(String[] args)
{
15
new Test2();
16
}
17
}
18
19
class Test1
{
20
Test1()
{
21
System.out.print( " 5 " );
22
}
23
static
{
24
System.out.print( " 6 " );
25
}
26
}

分析
1
( 1 ) 虛擬機試圖加載Test2,其父類為Test1
2
( 2 ) 虛擬機加載Test1
3
( 3 ) Test1靜態(tài)初始化 -----> 輸出 " 6 "
4
( 4 ) 虛擬機加載Test2
5
( 3 ) Test2靜態(tài)初始化 -----> 輸出 " 3 "
6
( 5 ) new Test2()構(gòu)造Test2首先構(gòu)造父類Test1
7
( 6 ) Test1構(gòu)造 -----> 輸出 " 5 "
8
( 7 ) Test2執(zhí)行初始化語句塊(虛擬機調(diào)用Test2初始化方法init) -----> 輸出 " 1 " 和 " 4 "
9
( 8 ) Test2構(gòu)造 -----> 輸出 " 2 "
10
11
=================================
12
先輸出6:
13
因為父類的Static的變量 / 常量會被子類共享
14
所以,首先輸出6
15
接著,會先初始化自身的static的變量 / 常量
16
17
然后,在構(gòu)造函數(shù)中,會首先調(diào)用父類的默認構(gòu)造函數(shù)(如果沒有顯示調(diào)用super(args)的話)
18
在調(diào)用父類構(gòu)造函數(shù)時,在執(zhí)行構(gòu)造函數(shù)體中的數(shù)據(jù)內(nèi)容時,先執(zhí)行方法外的程序段,如變量申明,或者程序段。然后再調(diào)用構(gòu)造函數(shù)中的程序段。
19
然后再執(zhí)行自身的構(gòu)造函數(shù),執(zhí)行前,也如上述說:在執(zhí)行本構(gòu)造函數(shù)體中的數(shù)據(jù)內(nèi)容時,先執(zhí)行方法外的程序段,如變量申明,或者程序段。然后再調(diào)用構(gòu)造函數(shù)中的程序段。
20
21
這就是一個對象的實例化過程。
類初始化時構(gòu)造函數(shù)調(diào)用順序:
首先加載類,遇到extends字樣會停止加載當前類,加載父類,之后再繼續(xù)加載。
當把一個類完全加載后,這個類的靜態(tài)成員將被先加載。之后進行如下流程:
(1)初始化對象的存儲空間為零或null值;
(2)調(diào)用父類構(gòu)造函數(shù);
(3)按順序分別調(diào)用類成員變量和實例成員變量的初始化表達式;
(4)調(diào)用本身構(gòu)造函數(shù)
<示例3>this指針

題目
1
public class CalC
{
2
void amethod()
{
3
System.out.println("CalC.amethod");
4
}
5
CalC()
{
6
amethod();
7
System.out.println("Hu?");
8
}
9
public static void main(String[] args)
{
10
// TODO Auto-generated method stub
11
CalC cc = new CalChild();
12
cc.amethod();
13
}
14
}
15
class CalChild extends CalC
{
16
void amethod()
{
17
System.out.println("CalChild.amethod");
18
}
19
}

答案
1
CalChild.amethod
2
Hu?
3
CalChild.amethod
實例方法專用于類的對象,但是在內(nèi)存中只有一份實例方法供類的所有實例共享,因為為每個對象都復制一份所有實例方法代價很昂貴。
所以系統(tǒng)中專門有這樣一種機制來保證:當你調(diào)用一個方法時,代碼是按專用于這個對象的方式進行,這種機制就是this。
每個實例方法都有一個名為this的變量,指的是調(diào)用該方法的當前對象。這種機制是當你的方法引用了類的一個實例變量是由編譯器隱含使用的。
即使對實例變量的每個引用實質(zhì)上是有 this. 前綴的
當調(diào)用一個實例方法時,this變量將被設(shè)置成引用它所起作用的特定類對象。
所以的對象都共享這個備份,為了區(qū)分開到底是哪個對象在調(diào)用這個方法,關(guān)鍵的地方就是this的使用。this把調(diào)用方法的上下文對應(yīng)到當前對象上。
本例分析:
調(diào)用java中的所有成員變量或者成員函數(shù)都隱含了this。所以這個地方就很明了了:構(gòu)造子類,this指針代表的當前對象是子類實例,子類實例為啥不調(diào)用自己overriding的方法呢?!

參考分析
CalC cc = new CalChild();這一步也叫“異類搜集”,以父類類型聲明變量作為子類對象的引用,此時子類的所有屬性方法都失效,只有子類中重寫父類的這樣一中方法可以被變量調(diào)用,而不調(diào)用父類的同名方法,從而實現(xiàn)多態(tài)!
此程序前后兩次打印都是一個原理。。
參考java基礎(chǔ)(一)
<示例4>

題目
1
class super1
{
2
{
3
System.out.println("super1 ok");
4
}
5
super1()
{
6
System.out.println("3");
7
}
8
}
9
10
class Employee extends super1
{
11
private String name;
12
private double salary=1500.00;
13
private Date birthday;
14
public Employee(String n,Date DoB)
{
15
System.out.println("2");
16
name=n;
17
birthday=DoB;
18
}
19
public Employee(String n)
{
20
this(n,null);
21
System.out.println("4");
22
}
23
}
24
class Manager extends Employee
{
25
{
26
System.out.println("Manager ok");
27
}
28
private String department;
29
public Manager(String n,String d)
{
30
super(n);
31
department=d;
32
}
33
}
34
35
public class test1
{
36
public static void main(String args[])
{
37
new Manager("Smith","sales");
38
}
39
}

答案
1
super1 ok
2
3
3
2
4
4
5
Manager ok
分析:
你總是可以從派生類構(gòu)造器中調(diào)用一個適當?shù)幕A(chǔ)類構(gòu)造器,對基礎(chǔ)類構(gòu)造器的調(diào)用必須放在派生類構(gòu)造器的首位。
如果派生類構(gòu)造器第一個語句不是對基礎(chǔ)類構(gòu)造器的調(diào)用,則編譯器將替你插入一個對默認基礎(chǔ)類構(gòu)造器的調(diào)用:super()
如果你自己定義了自己的構(gòu)造函數(shù),則編譯器認為是你會負責一個對象構(gòu)造的全部細節(jié)問題,包括對默認構(gòu)造器的任何要求,編譯器不將管理。
如果你構(gòu)造函數(shù)中要使用super() super(…) this() this(…)等字樣,則必須放在第一行。
如果你沒有顯示指定super() super(…)字樣,系統(tǒng)會在構(gòu)造函數(shù)第一行自動插入super()字樣;如果你指定了,系統(tǒng)將按你指定的進行而不是默認的了。
<練習1>

題目
1
class B
{
2
B()
{
3
System.out.println("B()");
4
}
5
}
6
class C extends B
{
7
C()
{
8
System.out.println("C()");
9
}
10
11
public class A
{
12
public static void main(String[] arg)
{
13
new C();
14
}
15
}
<練習2>

題目
1
class A
{
2
public A()
{
3
System.out.println("A's c is here ");
4
}
5
void println()
{
6
System.out.println("A's v is here ");
7
}
8
}
9
class B extends A
{
10
public B()
{
11
System.out.println("B's c is here ");
12
}
13
void println()
{
14
System.out.println("B's v is here ");
15
}
16
}
17
public class Chp_4_2
{
18
public static void main(String[] args)
{
19
B b = new B();
20
}
21
}

分析
子類的構(gòu)造函數(shù),除非顯示的寫上super或者super(Object),第一行會被插入super()的

因為class B extends class A ,所以你可以這樣看B的構(gòu)造方法

public B()
{
super();
System.out.println("B's c is here");
}
所以A's c is here會輸出
<練習3>

題目
1
public class JRun1
{
2
public JRun1()
{
3
System.out.println(" 構(gòu)造函數(shù)");
4
}
5
static
{
6
System.out.println("static{}");
7
}
8

{
9
System.out.println("{}");
10
}
11
public static void main(String[] args)
{
12
System.out.println("main()");
13
new JRun1();
14
}
15
}

答案

static
{}
main()


{}
構(gòu)造函數(shù)

題目
import java.util.Calendar;
public class SingletonTest{
private static final SingletonTest instance = new SingletonTest();
private static final int BIZZ = 100;
{
System.out.println(BIZZ);
}
private int size = 0;
private static int YEAR = Calendar.getInstance().get(Calendar.YEAR);
{
System.out.println("after generate instance line");
}
private SingletonTest() {
System.out.println("Constructor
");
size = YEAR - 1930;
}
public int getSize() {
return size;
}
public static void main(String[] args) {
System.out.println(instance.getSize());
}
}

答案
100
after generate instance line
Constructor

-1930

題目
1 import java.util.Calendar;
2
3 public class SingletonTest{
4 private static final SingletonTest instance = new SingletonTest();
5 private int size = 0;
6 private static int YEAR = Calendar.getInstance().get(Calendar.YEAR);
7 {
8 System.out.println("after generate instance line");
9 }
10 private SingletonTest() {
11 System.out.println("Constructor
");
12 size = YEAR - 1930;
13 }
14 public int getSize() {
15 return size;
16 }
17 public static void main(String[] args) {
18 System.out.println(instance.getSize());
19 }
20 }

答案
1 after generate instance line
2 Constructor

3 -1930

題目
1 import java.util.Calendar;
2
3 public class SingletonTest{
4 private static final int BIZZ = 100;
5 {
6 System.out.println(BIZZ);
7 }
8 private int size = 0;
9 private static int YEAR = Calendar.getInstance().get(Calendar.YEAR);
10 private static final SingletonTest instance = new SingletonTest();
11 {
12 System.out.println("after generate instance line");
13 }
14 private SingletonTest() {
15 System.out.println("Constructor
");
16 size = YEAR - 1930;
17 }
18 public int getSize() {
19 return size;
20 }
21 public static void main(String[] args) {
22 System.out.println(instance.getSize());
23 }
24 }
<練習>

題目
1
public class Parent
{
2
public void test()
{ }
3
public Parent()
{
4
test();
5
}
6
public static void main(String[] args)
{
7
new Child();
8
}
9
}
10
11
class Child extends Parent
{
12
private int instanceValue = 20;
13
public void test()
{
14
System.out.println("instance value is: " + instanceValue);
15
}
16
}
17
看輸入結(jié)果是什么?

分析
1
(網(wǎng)友)花之劍:定義實例變量instanceValue(默認為0)->調(diào)用父類無參構(gòu)造方法->調(diào)用子類的test()(因多態(tài)性,test被重寫)->構(gòu)造方法結(jié)束->實例變量的初始化,此時instanceValue=20
2
3
(網(wǎng)友)花之劍:
4
1.如果子類無自己的構(gòu)造方法,子類默認繼承父類無形參構(gòu)造方法
5
順序應(yīng)該是:
6
定義實例變量instanceValue(默認為0)->調(diào)用父類無參構(gòu)造方法->調(diào)用子類的test()(因多態(tài)性,test被重寫)->構(gòu)造方法結(jié)束->實例變量的初始化,此時instanceValue=20
7
2.如果子類有自己的構(gòu)造方法,子類構(gòu)造方法第一行默認super().
8
順序應(yīng)該是:
9
定義實例變量instanceValue(默認為0)->調(diào)用父類無參構(gòu)造方法->調(diào)用子類的test()(因多態(tài)性,test被重寫)->父類無形參構(gòu)造方法結(jié)束->實例變量的初始化,此時instanceValue=20 -->子類的構(gòu)造方法。
10
11
public class Parent
{
12
public void test()
{}
13
public Parent()
{
14
test();
15
}
16
public static void main(String[] args)
{
17
new Child();
18
}
19
}
20
21
class Child extends Parent
{
22
private int instanceValue = 20;
23
public Child()
{
24
//super();
25
System.out.println("instance value is: " + instanceValue);
26
}
27
public void test()
{
28
System.out.println("instance value is: " + instanceValue);
29
}
30
}