Java程式設計思想(2nd)學習筆記(6) (轉)

worldblog發表於2007-08-17
Java程式設計思想(2nd)學習筆記(6) (轉)[@more@]

第6章  重複運用classes

一.繼承(inheritance)

1.  在derived class中overriding某個時,只能覆寫base class中的介面,即base class中的public或protected或friendly函式。如果試圖overriding一個private函式,雖然編譯透過,但實際上你只是在derived class中新增了一個函式。如

class Cleanser{   

  private void prt(){//(b)

  System.out.println("Cleanser.prt()");

  }

 }

public class ExplicitStatic extends Cleanser{

  public void prt(){

  System.out.println("ExplicitStatic.prt()");

  }

  public static void main(String[] args){

  Cleanser x = new ExplicitStatic();

  x.prt();//(a)

  }

}

因為Cleanser中的prt()是private,所以不能在其derived class中被覆寫。ExplicitStatic中的prt()只是ExplicitStatic中的一個函式,所以當試圖在(a)處透過多型來prt()時,會發生錯誤。如果把(b)處的private去掉,則結果為

ExplicitStatic.prt()

2.  Super的使用

1)透過關鍵字super可以呼叫當前class的superclass(父類)。

例6.1.1.1

class Base{

  Base(){System.out.println("Base()");}

  public void scrub() { System.out.println(" Base.scrub()"); }

}

class Cleanser extends Base{

  private String s = new String("Cleanser");

  public void append(String a) { s+=a; }

  public void dilute() { append(" dilute()"); }

  public void apply() { append(" apply()"); }

  public void scrub() { append(" scrub()"); }

  public void print() { System.out.println(s); }

  Cleanser(){

  System.out.println("Cleanser(): " + s);

  }

  public static void testStatic(){

  System.out.println("testStatic()");

  }

  public static void main(String[] args){

  Cleanser x = new Cleanser();

  x.dilute(); x.apply(); x.scrub(); x.print();

  }

 }

public class ExplicitStatic extends Cleanser{

  ExplicitStatic(){

  System.out.println("ExplicitStatic()");

  }

  public void scrub(){

  append(" Detergen.scrub()");

  super.testStatic();

  super.scrub();//呼叫的是Cleanser.scrub()

  }

  public void foam() { append(" foam()"); }

  public static void main(String[] args){

  ExplicitStatic x = new ExplicitStatic();

  x.dilute(); x.apply(); x.scrub(); x.foam();

  x.print(); System.out.println("Test base class:");

  Cleanser.main(args); 

  testStatic();

  }

}

執行結果:

Base()

Cleanser(): Cleanser

ExplicitStatic()

testStatic()

Cleanser dilute() apply() Detergen.scrub() scrub() foam()

Test base class:

Base()

Cleanser(): Cleanser

Cleanser dilute() apply() scrub()

testStatic()

2)透過super來呼叫superclass中的成員時,呼叫的是最近成員。

例6.1.1.2

class Base{

  protected String baseS = "Base";//(a)

  //private String baseS = "Base";

  Base(){System.out.println("Base()");}

}

class Cleanser extends Base{

  protected String baseS = "Cleanser";//(b)

  public String s = new String("Cleanser");

  Cleanser(){

  System.out.println("Cleanser(): " + s);

  }

  Cleanser(String a){

  System.out.println("Cleanser(" + a + "): s = " + s );

  }

 }

public class ExplicitStatic extends Cleanser{

  String s2 = s;

  String baseS = super.baseS; //(c)

  ExplicitStatic(){

  super("ExplicitStatic");

  System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " + baseS + "super.baseS = " + super.baseS);

  baseS = "ExplicitStatic";

  System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);

  }

  public static void main(String[] args){

  ExplicitStatic x = new ExplicitStatic();

  }

}

結果1:

Base()

Cleanser(ExplicitStatic): s = Cleanser

ExplicitStatic():s2 = Cleanser, baseS = Cleanser,super.baseS = Cleanser

baseS = ExplicitStatic , super.baseS = Cleanser

在上面例子中,在三個class中都存在String bases例項。在ExplicitStatic中如果直接呼叫baseS,則實際呼叫的是當前類ExplicitStatic中的baseS(即(c)處的成員);如果透過super.bases來呼叫baseS,則呼叫的是離當前類ExplicitStatic最近的baseS成員,即Cleanser class中的baseS例項(即(b)處),產生的結果如結果1所示。如果把(b)處語句註釋掉,則將呼叫Base class中的baseS,結果如結果2所示。

結果2:

Base()

Cleanser(ExplicitStatic): s = Cleanser

ExplicitStatic():s2 = Cleanser, baseS = Base,super.baseS = Base

baseS = ExplicitStatic , super.baseS = Base

3.  Base class的初始化

2.1  當你產生derived class時,其中會包含base class子物件(sub)。這個子物件就和你另外產生的base class物件一模一樣。

2.2  透過super()可呼叫base class的建構函式,但必須放在建構函式的第一行,並且只能在建構函式中運用。

2.3  初始化順序為:

1)  載入程式碼(.class)

2)  初始化class的靜態成員,初始化順序了“從裡到外”,即從base class開始。

3)  在derived class的建構函式中呼叫base class的建構函式。

如果在derived class的建構函式中沒有透過super()顯式呼叫呼叫base class的建構函式,會呼叫bass class的default建構函式並自動生成相應的呼叫語句,從而產生一個base class例項。如果在derived class的建構函式中透過super()顯示呼叫了父類的建構函式,則呼叫所指定的建構函式。呼叫建構函式的呼叫順序是“從裡到外”。

4)  呼叫derived class的建構函式。

**:當base class沒有default建構函式時,必須在derived class的建構函式中透過super顯示呼叫base class的建構函式。

例:下面程式碼的初始化過程為:

1)  裝載ExplicitStatic的程式碼(裝載ExplicitStatic.class檔案)。

2)  發現ExplicitStatic有關鍵字extends,裝載ExplicitStatic的base class的程式碼(裝載Cleanser.class檔案)。

3)  發現Cleanser有關鍵字extends,裝載Cleanser的base class的程式碼(裝載Base.class檔案)。

4)  初始化Base class中的靜態成員。

5)  初始化Cleanser class中的靜態成員。

6)  初始化ExplicitStatic class中的靜態成員。

如果把(c)處的程式碼註釋掉,那麼初始化工作到此就結束了。

7)  為ExplicitStatic物件分配空間,並把儲存空間初始化為0。

8)  在ExplicitStatic class的構造中呼叫super("ExplicitStatic")(在ExplicitStatic class的建構函式中顯式呼叫父類的建構函式),試圖產生一個Cleanser class例項。

9)  為Cleanser物件分配儲存空間,並把儲存空間初始化為0。

10)  由於Cleanser class又是繼承自Base class,會在Cleanser class的建構函式中透過super()(由於沒有顯式呼叫父類的建構函式,所以自動呼叫父類的default建構函式)呼叫父類的建構函式,試圖產生一個Cleanser class例項。

11)  產生一個Base class例項。先初始化成員變數,再呼叫建構函式。

12)  回到Cleanser class,產生一個例項。首先初始化Cleanser class中的成員資料,再建構函式Cleanser(String a)中的其餘部分。

13)  回到ExplicitStatic class,產生一個例項。首先初始化ExplicitStatic class中的成員資料,再執行建構函式ExplicitStatic ()中的其餘部分(System.out.println(“ExplicitStatic()”))。

class Base{

  static int s1 = prt("s1 initialized.", 11);

  int i1 = prt("i1 initialized.", 12);

  Base(){

  System.out.println("Base()");

  System.out.println("s1 = " + s1 + " ,i1 = " + i1);

  draw();//(d)

  }

  void draw(){

  System.out.println("base.draw:s1 = " + s1 + " ,i1 = " + i1);

  }

  static int prt(String s, int num) {

  System.out.println(s);

  return num;

  }

}

class Cleanser extends Base{

  static int s2 = prt("s2 initialized.", 21);

  int i2 = prt("i2 initialized.", 22);

  Cleanser(){

   System.out.println("Cleanser()");

  System.out.println("s2 = " + s2 + " ,i2 = " + i2);

  }

  Cleanser(String a){

  //super();(b)

  System.out.println("Cleanser(" + a + ")");

  System.out.println("s2 = " + s2 + " ,i2 = " + i2);

  }

 

  void draw(){

  System.out.println("Cleanser.draw:s2 = " + s2 + " ,i2 = " + i2);

  }

 }

public class ExplicitStatic extends Cleanser{

  static int s3 = prt("s3 initialized.", 31);

  int i3 = prt("i3 initialized", 31);

  ExplicitStatic(){

  super("ExplicitStatic");//(a)

  System.out.println("ExplicitStatic()");

  System.out.println("s3 = " + s3 + " ,i3 = " + i3);

  }

  public static void main(String[] args){

  ExplicitStatic x = new ExplicitStatic();//(c)

  }

}

結果:

s1 initialized.

s2 initialized.

s3 initialized.

//如果把(c)處的程式碼註釋掉,輸出結果到此為止,不會輸出下面結果

i1 initialized.

Base()

s1 = 11 ,i1 = 12

Cleanser.draw:s2 = 21 ,i2 = 0//(d)處結果

i2 initialized.

Cleanser(ExplicitStatic)//(a)處結果

s2 = 21 ,i2 = 22

i3 initialized

ExplicitStatic()

s3 = 31 ,i3 = 31

由於在Base()中呼叫draw()時,Cleanser中的i2還未進行初始化,而在為Cleanser物件分配儲存空間時,把儲存空間初始化為0,所以此時i2為0。

2.4  程式碼及結果中的(a)說明了是先產生當前class的base class的例項,否則在derived class中無法呼叫base class的成員。在呼叫Cleanser class的建構函式Cleanser(String a)時,在Cleanser(String a)中沒有用super顯式呼叫Base class的建構函式,所以會自動生成呼叫Base class的default建構函式的語句,如(b)。

4.  組合與繼承之間的快擇

.  1)繼承表示的是一種“is-a(是一個)”的關係,如貨車是汽車中的一種;組合表示的是一種“has-a(有一個)”的關係,如汽車有四個輪子。

  2)是否需要將新的class向上轉型為base class。

5.  在繼承中的訪問

protect變數能被子孫類所呼叫。如Base class中的baseS能被Cleanser class和ExplicitStatic class呼叫。

class Base{

  protected String baseS = "Base";

  //private String baseS = "Base";

  Base(){System.out.println("Base()");}

}

class Cleanser extends Base{

  protected String baseS = "Cleanser";

  public String s = new String("Cleanser");

  Cleanser(){

  System.out.println("Cleanser(): " + s);

  }

  Cleanser(String a){

  System.out.println("Cleanser(" + a + "): s = " + s );

  }

 }

public class ExplicitStatic extends Cleanser{

  String s2 = s;

  String baseS = super.baseS;

  ExplicitStatic(){

  super("ExplicitStatic");

  System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " + baseS + "super.baseS = " + super.baseS);

  baseS = "ExplicitStatic";

  System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);

  }

  public static void main(String[] args){

  ExplicitStatic x = new ExplicitStatic();

  }

}

結果:

Base()

Cleanser(ExplicitStatic): s = Cleanser

ExplicitStatic():s2 = Cleanser, baseS = Cleanser, super.baseS = Cleanser

baseS = ExplicitStatic , super.baseS = Cleanser

二.關鍵字final

1.Final data

  1.1 final data

1)當基本型別被定義為final,表示它的資料值不能被改變。如

  final int i = 9;

  i++;//編譯錯誤,不能改變I的值

2) 當object reference被定義為final時,不能改變的只是reference而不是物件本身。如

  class Value{ 

    int i = 1;

  }

public class ExplicitStatic extends Cleanser{

    public static void main(String[] args){

    final Value v = new Value();//v.i = 1

    v.i++;//v.i = 2

    //v = new Value();

    }

}

由於v為final,所以不能透過new Value()使v重新指向一個物件;但是v所指向的物件的值是可以改變的(v.i++)。

1.2 blank finals

我們可以將資料成員宣告為final但不給予初值,這就是blank finals。但blank finals必須且只能在建構函式中進行初始化。

  public class ExplicitStatic {

  final int ib;

  final int i = 1;

  ExplicitStatic()

  {

  ib = 2;//(a)

  //i = 3; (b)

  System.out.println("i = " + i  + ", ib = " + ib);

  }

  public static void main(String[] args){

  ExplicitStatic ex = new ExplicitStatic();

  }

}

ib為blank finals,所以可以在建構函式中進行初始化。如果把(a)處的程式碼註釋掉,則ib沒有初值,編譯出錯。而i在定義處已進行了初始化,則不能改變i的值,(b)處的程式碼編譯錯誤。

**:非blank finals成員即使在建構函式中也不能更改其值

2.Final methods

  1)被宣告為final的函式不能被覆寫

  2)class中所有private函式自然而然會是final。

1.  Final classes

1)當一個class被宣告為final時,表示它不能被繼承,但class的資料成員不是final,可以被改變。如

  class SmallBrain{}

 :namespace prefix = o ns = "urn:schemas--com::office" />

final class Dinosaur{

    int i = 7;

    int j = i;

    SmallBrain x = new SmallBrain();

    void f(){};

}

 

//不能繼承final函式

//class Further extends Dinosaur{}

 

public class ExplicitStatic{

    public static void main(String[] args){

    Dinosaur n = new Dinosaur();

    n.f();

    n.i = 40;//final class中的non-final資料成員可以被改變

      n.j++;

    }

}

2)final class中的所有函式也都自然是final,因為沒有人能夠加以覆寫。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-962957/,如需轉載,請註明出處,否則將追究法律責任。

相關文章