習題解答chapter04

題目:

  1. 實驗:利用IDE的debug功能給例6.4和例6.6的new語句設置斷點,使用單步調試(step into/step over)跟蹤子類對象實例化(初始化)的執行順序,並總結該過程。(教材:Java面向對象程式設計,第二版,袁紹欣主編)
  2. 如何實現兩個對象之間互發消息,請舉例說明。
  3. 談談組合與繼承的區別以及兩者的使用場景(即什麼時候宜用組合?什麼時候宜用繼承? )。
  4. Java中的運行時多態的含義是什麼?有什麼作用?請舉例說明。
  5. 使用介面改寫例6.8中的程式。
  6. 簡述運算符instanceof的使用場景。

1. 實驗:利用IDE的debug功能給例6.4和例6.6的new語句設置斷點,使用單步調試(step into/step over)跟蹤子類對象實例化(初始化)的執行順序,並總結該過程。

下面給出6.4調試動圖(大小限制,該動圖尺寸不大)

下面給出6.6調試動圖

程式說明:

package bookcode.ex6.part6_6;

class Pare {
    int i = 3;

    Pare() {
        super();
    }
};

class Construct extends Pare {
    int i = 8;

    Construct() {
    }

    Construct(int num) {
        this();
    }

    public static void main(String args[]) {
        Construct ct = new Construct(9);
        System.out.println(ct.i);
        System.out.println(ct.getSuper());
    }

    int getSuper() {
        return super.i;
    }
}

先執行的是子類的this(),Construct()中有一個隱含的super(),使得類Construct在生成對象時,父類 i=3 顯式初始化能夠執行。

實例化執行順序總結:

  • 為子類對象分配記憶體空間,對域變數進行默認初始化。
  • 綁定構造方法,將new對象中的參數傳遞給構造方法的形式參數。
  • 調用this或super語句,二者必居其一,也只有一。
  • 進行實例變數的顯式初始化操作。
  • 執行當前構造方法體中的程式程式碼。

2. 如何實現兩個對象之間互發消息,請舉例說明。

使用引用的屬性或方法其實都是調用對象的屬性或方法,而消息概念的引入就是為了說明這樣的過程。消息的實質就是引用向對象發出的服務請求,是對數據成員和成員方法的調用。下面列舉能否發送消息的三個條件:

  • 引用必須真實引用了特定的對象,否則會拋出NullPointerException異常。
  • 訪問對象必須定義了相應的屬性或方法,否則編譯不會通過。
  • 被訪問的屬性或方法必須具有可訪問的許可權。

消息也就是相當於在遙控器和顯示器之間架起溝通的橋樑。在面向對象語言中,消息把不同對象相互聯繫起來,共同完成特定功能。
實例程式碼:

class FighterPlane {
    String name;
    int missileNum;

    public FighterPlane(String _name, int _missleNum) {
        this.name = _name;
        this.missileNum = _missleNum;
    }

    public void fire() {
        if (this.missileNum > 0) {
            System.out.println("now fire a missile !");
            this.missileNum -= 1;
        } else {
            System.out.println("No missile left !");
        }

    }
}
class A {
    FighterPlane fp;

    public A(FighterPlane fpp) {
        this.fp = fpp;      //A對象中擁有了FighterPlane對象的引用
    }

    public void invoke() {
         //A對象發送消息給FighterPlane的對象
        System.out.println(fp.name);
    }
}public class Run {
    public Run() {
    }

    public static void main(String[] var0) {
        FighterPlane ftp = new FighterPlane("su35", 10);
         //產生A對象,並將ftp作為對象引用傳入
        A a= new A(ftp);
        a.invoke();
    }
}

3. 談談組合與繼承的區別以及兩者的使用場景(即什麼時候宜用組合?什麼時候宜用繼承? )。

  • 組合:通過對象內部的屬性引用來實現。使對象之間的耦合性較為鬆散。
  • 繼承:從已有的類派生出新的類。在不同的類中也可能會有共同的特徵和動作,可以把這些共同的特徵和動作放在一個類中,讓其它類共享。因此可以定義一個通用類,然後將其擴展為其它多個特定類,這些特定類繼承通用類中的特徵和動作。繼承是 Java 中實現軟體重用的重要手段,避免重複,易於維護,易於理解。
  • 組合就像房間裡面的窗戶、牆壁、地板、桌子、椅子等,他們之間並不存在結構上的相似性,只是功能上組合可以發揮更大的作用,但是單獨是可以獨立運行的。繼承就像對房間進行拓展成為一棟樓,前面的零部件它都具備,但是如果沒有房間,大樓是無法構建的,具有結構和功能上的關聯。
  • 顯而易見,在不具有結構和功能上的相似性時,使用繼承可以減少程式碼重複率,易於維護;在結構實現不同、功能「可疊加」時,使用組合無疑是優於繼承的。

4. Java中的運行時多態的含義是什麼?有什麼作用?請舉例說明。

Java提供了兩種多態機制——重載和覆蓋。運行時多態指的是覆蓋,在運行時根據輸入參數動態選擇不同的 成員方法執行,體現了一個類本身的多態性,使程式碼具有良好的拓展性。
舉例:同樣的紅燒魚,廚師老師的紅燒方法傳給廚師徒弟後,廚師徒弟在紅燒方法上做了改動,這是紅燒方法的重寫,就相當於 java 的方法重寫。

重寫程式碼如下:

class Ct{
    void hongshao(int a){
        System.out.println("這是廚師老師的紅燒int的方法");
    }
}

class Cs extends Ct{
    void hongshao(int a) {
        System.out.println("這是廚師徒弟的紅燒int的方法");
    }
}

重載程式碼如下:

class Cs extends Ct{
    void hongshao(int a) {
        System.out.println("這是廚師徒弟的紅燒int的方法");
    }
    void hongshao(float b) {
        System.out.println("這是廚師徒弟紅燒float的方法");
    }
    void hongshao(int a,float b) {
        System.out.println("這是廚師徒弟紅燒int和float的方法");
    }
}

5. 使用介面改寫例6.8中的程式。

創建介面

package bookcode.ex6.part6_8;

public interface Shape {
    double getArea();
    double getPerimeter();
    void show();
}

圓類

package bookcode.ex6.part6_8;

public class Circle implements Shape{
    private int r;
    public Circle(int _r){
        r = _r;
    }

    @Override
    public double getArea() {
        return r * r * Math.PI;
    }

    @Override
    public double getPerimeter() {
        return 2 * Math.PI * r;
    }

    @Override
    public void show() {
        System.out.println("Circle Area:" + getArea());
        System.out.println("Circle Perimeter:" + getPerimeter());
    }
}

矩形類

package bookcode.ex6.part6_8;

public class Rect implements Shape {

    private int k;
    private int m;

    public Rect(int width, int height) {
        k = width;
        m = height;
    }

    public double getArea() {
        return (k * m);
    }

    public double getPerimeter() {
        return (2 * k + 2 * m);
    }

    @Override
    public void show() {
        System.out.println("Rect Area:" + getArea());
        System.out.println("Rect Perimeter:" + getPerimeter());
    }
}

三角形類

package bookcode.ex6.part6_8;

import com.sun.tools.javac.file.SymbolArchive;

public class Triangle implements Shape{
    private int x, y, z, m;
    public Triangle(int _x, int _y, int _z){
        x = _x;
        y = _y;
        z = _z;
        m = (x + y +z) / 2;
    }

    @Override
    public double getArea() {
        return (Math.sqrt(m *(m - x) * (m - y) * (m - z)));
    }

    @Override
    public double getPerimeter() {
        return 2 * m;
    }

    @Override
    public void show() {
        System.out.println("Triangle Area:" + getArea());
        System.out.println("Triangle Perimeter:" + getPerimeter());
    }
}

運行

package bookcode.ex6.part6_8;

public class RunShape{
    public static void main(String args[]){
        Rect rect = new Rect(5 , 6);
        Triangle triangle = new Triangle(3, 4, 5);
        Circle circle = new Circle(5);

        rect.show();
        System.out.println();
        triangle.show();
        System.out.println();
        circle.show();
    }
}

6. 簡述運算符instanceof的使用場景。

instanceof 是 Java 的一個二元操作符,instanceof 是 Java 的保留關鍵字。它的作用是測試它左邊的對象是否是它右邊的類或子類的實例,返回 boolean 的數據類型。

package bookcode.ex6.part6_15;

class Uncle{}
class Pare{}
class Pare1 extends Pare{}
class Pare2 extends Pare1{}

public class InstanceofTest {

    public static void main(String args[]) {
        Uncle uncle = new Uncle();
        Pare pare = new Pare();
        Pare1 pare1 = new Pare1();
        Pare2 pare2 = new Pare2();

        //驗證pare
        if (pare instanceof Pare) {
            System.out.println("pare instanceof Pare");
        }
        if (!(pare instanceof Pare1)){
            System.out.println("pare not instanceof Pare1");
        }else if (pare instanceof Pare1){
            System.out.println("pare instanceof Pare1");
        }
        if(!(pare instanceof Pare2)){
            System.out.println("pare not instanceof Pare2");
        }else if (pare instanceof Pare2){
            System.out.println("pare instanceof Pare2");
        }
        System.out.println();

        //驗證pare1
        if (pare1 instanceof Pare1) {
            System.out.println("pare instanceof Pare");
        }
        if (!(pare1 instanceof Pare)){
            System.out.println("pare1 not instanceof Pare");
        }else if(pare1 instanceof Pare){
            System.out.println("pare1 instanceof Pare");
        }
        if(!(pare1 instanceof Pare2)){
            System.out.println("pare1 not instanceof Pare2");
        }else if(pare1 instanceof Pare2){
            System.out.println("pare1 instanceof Pare2");
        }
        System.out.println();

        //驗證pare2
        if (pare2 instanceof Pare2) {
            System.out.println("pare instanceof Pare");
        }
        if (!(pare2 instanceof Pare)){
            System.out.println("pare2 not instanceof Pare");
        }else if (pare2 instanceof Pare){
            System.out.println("pare2 instanceof Pare");
        }
        if(!(pare2 instanceof Pare1)){
            System.out.println("pare2 not instanceof Pare1");
        }else if (pare2 instanceof Pare1){
            System.out.println("pare2 instanceof Pare1");
        }
        System.out.println();

        //驗證uncle
        if (uncle instanceof Uncle){
            System.out.println("uncle instanceof Uncle");
        }
        // 語法錯誤,無法編譯通過
        /*
        if (uncle instanceof Pare) {
            System.out.println("uncle instanceof Pare");
        }else {
            System.out.println("uncle instanceof Pare");
        }
        if (!(uncle instanceof Pare1)){
            System.out.println("uncle not instanceof Pare1");
        }else {
            System.out.println("uncle instanceof Pare1");
        }
        if(!(uncle instanceof Pare2)){
            System.out.println("uncle not instanceof Pare2");
        }else {
            System.out.println("uncle instanceof Pare2");
        }
        */


    }
}