複用代碼是Java衆多引人注目的功能之一。這句話很通順,沒什麼問題,但問題在於不少人並不清楚「複用」是什麼。就好像我說「沉默王二是一個不止會寫代碼的程序員」,唉,沉默王二是誰?程序員
若是想學習Java工程化、高性能及分佈式、深刻淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友能夠加個人Java高級交流:854630135,裏面有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給你們。分佈式
咱們須要來給「複用」下一個定義。複用,說白了就是重複使用。微服務
舉個例子,不少名人說了不少名言,咱們在說話、寫做的時候,就常常有意無心的重複這些名言。好比說我,就特別喜歡重複使用王小波的那句名言:「從話語中,你不多能學到人性,從沉默中卻能。假如還想學得更多,那就要繼續一聲不吭 。」源碼分析
上面這個例子,只能說是「複用」的一種低級的應用,其實就是複製粘貼了。還有高級的複用方式嗎?性能
有,固然有。Java做爲一種優秀的面向對象設計的語言,在複用的應用上就高級得多了。學習
最多見的複用方法就是繼承——使用extends
關鍵字在基類的基礎上建立新類,新類能夠直接複用基類的非private
的屬性和方法;就像程序清單1-1那樣。this
程序清單1-1:spa
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中使用,代碼的複用工做就這樣輕鬆地完成了。設計
另一種常見的複用方法就是組合——在新類中建立已有類的對象,經過該對象來調用已有類中的非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中咱們能夠看得出,葵花點穴手雖然是白展堂的絕技,但做爲佟掌櫃的男友,佟掌櫃要展堂點個穴,展堂也是不敢辭讓的。你看,佟掌櫃雖然是個弱女子,但自從有了展堂這個武功首屈一指的男友,再沒有誰敢不聽話啊——厲害的組合啊。
須要注意的是,如何在繼承和組合之間作出選擇呢?
若是新類和已有類須要具備一些類似的方法和屬性時,就採用繼承的形式;若是新類只是爲了借用已有類的一些方法和屬性時,而二者沒有不少類似之處時就須要採用組合的形式。
還有一種複用方法是代理——在新類中建立代理,經過代理來操做已有類的非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的方法和屬性直接暴露在新類中);與此同時,代理拿到了足夠的好處。
做爲代碼的生產者來講,咱們有時候但願代碼被複用,有的時候又但願代碼不被複用。當咱們不想代碼被複用時,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("我就是最終形態,別繼承我!"); } }