1. 程式人生 > >Java程式碼複用的三種常用方式:繼承、組合和代理

Java程式碼複用的三種常用方式:繼承、組合和代理

複用程式碼是Java眾多引人注目的功能之一。這句話很通順,沒什麼問題,但問題在於很多人並不清楚“複用”是什麼。就好像我說“沉默王二是一個不止會寫程式碼的程式設計師”,唉,沉默王二是誰?

如果想學習Java工程化、高效能及分散式、深入淺出。微服務、Spring,MyBatis,Netty原始碼分析的朋友可以加我的Java高階交流:854630135,裡面有阿里大牛直播講解技術,以及Java大型網際網路技術的視訊免費分享給大家。

我們需要來給“複用”下一個定義。複用,說白了就是重複使用。

舉個例子,很多名人說了很多名言,我們在說話、寫作的時候,就經常有意無意的重複這些名言。比如說我,就特別喜歡重複使用王小波的那句名言:“從話語中,你很少能學到人性,從沉默中卻能。假如還想學得更多,那就要繼續一聲不吭

 。”

上面這個例子,只能說是“複用”的一種低階的應用,其實就是複製貼上了。還有高階的複用方式嗎?

有,當然有。Java作為一種優秀的面向物件設計的語言,在複用的應用上就高階得多了。

01 繼承

最常見的複用方法就是繼承——使用extends關鍵字在基類的基礎上建立新類,新類可以直接複用基類的非private的屬性和方法;就像程式清單1-1那樣。

程式清單1-1:

public class Wangxiaosan extends Wangsan {
    public Wangxiaosan() {
        System.out.println("我是新類王小三");

        setName("王老三");

        System.out.println(getName());
    }

    public static void main(String[] args) {
        new Wangxiaosan();
    }
}

class Wangsan {
    private String name;

    Wangsan() {
        System.out.println("我是基類王三");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

從程式清單1-1中我們可以看得出,getName()和setName()方法雖然是在基類Wangsan中建立的,但可以在新類Wangxiaosan中使用,程式碼的複用工作就這樣輕鬆地完成了。

02 組合

另外一種常見的複用方法就是組合——在新類中建立已有類的物件,通過該物件來呼叫已有類中的非private的屬性和方法;就像程式清單2-1那樣。

程式清單2-1:

public class Tongxiangyu {
    private Baizhantang boyFriend = new Baizhantang();

    public Tongxiangyu() {
        System.out.println("我是同福客棧的掌櫃佟湘玉");

        boyFriend.pointHand("郭芙蓉");
    }

    public static void main(String[] args) {
        new Tongxiangyu();
    }
}

class Baizhantang {
    Baizhantang() {
        System.out.println("我是退隱江湖的盜聖白展堂");
    }

    public void pointHand(String name) {
        System.out.println("那誰" + name + ",準備一下——葵花點穴手");
    }
}

從程式清單2-1中我們可以看得出,葵花點穴手雖然是白展堂的絕技,但作為佟掌櫃的男朋友,佟掌櫃要展堂點個穴,展堂也是不敢推辭的。你看,佟掌櫃雖然是個弱女子,但自從有了展堂這個武功數一數二的男朋友,再沒有誰敢不聽話啊——厲害的組合啊。

需要注意的是,如何在繼承和組合之間做出選擇呢?

如果新類和已有類需要具有一些相似的方法和屬性時,就採用繼承的形式;如果新類只是為了借用已有類的一些方法和屬性時,而兩者沒有很多相似之處時就需要採用組合的形式。

03 代理

還有一種複用方法是代理——在新類中建立代理,通過代理來操作已有類的非private的屬性和方法;就像程式清單3-1那樣。

程式清單3-1:

public class Member {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        System.out.println("代理說一個藥丸十五塊");
        proxy.buy(15);
    }
}

class Proxy {
    private Shop shop = new Shop();

    public void buy(int money) {
        System.out.println("一個藥丸十五塊");
        shop.sale(money - 5);
    }
}

class Shop {
    public void sale(int money) {
        System.out.println("一個藥丸十塊錢");
    }
}

從程式清單3-1中我們可以看得出,代理的模式和組合有點類似,但又有差別——代理成功的隔開了新類(會員)和已有類(店鋪)的直接關係,使得已有類的方法不直接暴露在新類面前(組合的方式會將已有類的非private的方法和屬性直接暴露在新類中);與此同時,代理拿到了足夠的好處。

04 final

作為程式碼的生產者來說,我們有時候希望程式碼被複用,有的時候又希望程式碼不被複用。當我們不想程式碼被複用時,final關鍵字就派上用場了。final這個關鍵字很形象,它本身就說明了一切——最後的,最終的;決定性的;不可更改的。

使用final的場景有三種,分別是資料、方法和類。我們來稍作說明。

1)final 資料

最常見的final資料就是常量了,例如:

public class Consts {
    public static final String CMOWER = "沉默王二";
}

對於常量來說,它對於整個應用內的所有類都是可見的,因此是public的;它可以直接通過類名.常量名訪問,所以是static的;它是不可修改的,因此是final的。

另外一種常見的final資料就是引數了,參照程式清單4-1。

程式清單4-1:

public class Cmower {

    public void write(final String content) {
        // content += "猶未雪"; // final修飾的引數是無法在方法內部被再次修改的
        System.out.println(content);
    }

    public void write1(String content) {
        content += "猶未雪";
        System.out.println(content);
    }

    public static void main(String[] args) {
        Cmower cmower = new Cmower();
        cmower.write("精忠報國");
        cmower.write1("靖康恥");
    }
}

2)final 方法

在Java類中,所有的private方法都隱式地指定為final的(也就是說,如果你在private方法上加上final修飾符,其實是沒啥意義的)。在介紹繼承的時候,你應該注意到我強調的一句話,就是新類可以直接複用基類的非private的屬性和方法,也就是說private方法是無法被繼承者修改的,因為private方法是final的。

來看程式清單4-2,你會發現Wangsan型別的san引用是不能呼叫say(String words)方法的,因為private方法是無法被繼承者修改的,儘管Wangxiaosan中重新定義了say(String words)方法。

程式清單4-2:

public class Wangxiaosan extends Wangsan {
    public Wangxiaosan() {
        say("吃中飯沒");
    }

    public void say(String words) {
        System.out.println("王小三在說:" + words);
    }

    public static void main(String[] args) {
        Wangsan san = new Wangxiaosan();
        // san.say("吃晚餐沒"); // 無法訪問,並不會被覆蓋
    }
}

class Wangsan {
    public Wangsan() {
        say("吃早飯沒");
    }

    private void say(String words) {
        System.out.println("王三在說:" + words);
    }
}

3)final 類

當我們認為某個類就是最終的形態了,它很完美,不應該被繼承,就可以使用final關鍵字來修飾;參照程式清單4-3。

程式清單4-3:

// 無法繼承
public class Wangxiaosan extends Wangsan {
}

final class Wangsan {
    public Wangsan() {
        System.out.println("我就是最終形態,別繼承我!");
    }
}