最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關于為何說JAVA中要慎重使用繼承詳解

 更新時間:2018年08月16日 08:58:47   作者:Decouple  
Java繼承是面向對象的最顯著的一個特征,然而下面這篇文章主要給大家介紹了關于為何說JAVA中要慎重使用繼承的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧

前言

這篇文章的主題并非鼓勵不使用繼承,而是僅從使用繼承帶來的問題出發(fā),討論繼承機制不太好的地方,從而在使用時慎重選擇,避開可能遇到的坑。

JAVA中使用到繼承就會有兩個無法回避的缺點:

  • 打破了封裝性,子類依賴于超類的實現(xiàn)細節(jié),和超類耦合。
  • 超類更新后可能會導致錯誤。

繼承打破了封裝性

關于這一點,下面是一個詳細的例子(來源于Effective Java第16條)

public class MyHashSet<E> extends HashSet<E> {
 private int addCount = 0;

 public int getAddCount() {
  return addCount;
 }

 @Override
 public boolean add(E e) {
  addCount++;
  return super.add(e);
 }

 @Override
 public boolean addAll(Collection<? extends E> c) {
  addCount += c.size();
  return super.addAll(c);
 }
}

這里自定義了一個HashSet,重寫了兩個方法,它和超類唯一的區(qū)別是加入了一個計數(shù)器,用來統(tǒng)計添加過多少個元素。

寫一個測試來測試這個新增的功能是否工作:

public class MyHashSetTest {
 private MyHashSet<Integer> myHashSet = new MyHashSet<Integer>();

 @Test
 public void test() {
  myHashSet.addAll(Arrays.asList(1,2,3));
  
  System.out.println(myHashSet.getAddCount());
 }
}

運行后會發(fā)現(xiàn),加入了3個元素之后,計數(shù)器輸出的值是6。

進入到超類中的addAll()方法就會發(fā)現(xiàn)出錯的原因:它內(nèi)部調(diào)用的是add()方法。所以在這個測試里,進入子類的addAll()方法時,數(shù)器加3,然后調(diào)用超類的addAll(),超類的addAll()又會調(diào)用子類的add()三次,這時計數(shù)器又會再加三。

問題的根源

將這種情況抽象一下,可以發(fā)現(xiàn)出錯是因為超類的可覆蓋的方法存在自用性(即超類里可覆蓋的方法調(diào)用了別的可覆蓋的方法),這時候如果子類覆蓋了其中的一些方法,就可能導致錯誤。

 

比如上圖這種情況,F(xiàn)ather類里有可覆蓋的方法A和方法B,并且A調(diào)用了B。子類Son重寫了方法B,這時候如果子類調(diào)用繼承來的方法A,那么方法A調(diào)用的就不再是Father.B(),而是子類中的方法Son.B()。如果程序的正確性依賴于Father.B()中的一些操作,而Son.B()重寫了這些操作,那么就很可能導致錯誤產(chǎn)生。

關鍵在于,子類的寫法很可能從表面上看來沒有問題,但是卻會出錯,這就迫使開發(fā)者去了解超類的實現(xiàn)細節(jié),從而打破了面向對象的封裝性,因為封裝性是要求隱藏實現(xiàn)細節(jié)的。更危險的是,錯誤不一定能輕易地被測出來,如果開發(fā)者不了解超類的實現(xiàn)細節(jié)就進行重寫,那么可能就埋下了隱患。

超類更新時可能產(chǎn)生錯誤

這一點比較好理解,主要有以下幾種可能:

1、超類更改了已有方法的簽名。會導致編譯錯誤。

2、超類新增了方法:

  • 和子類已有方法的簽名相同但返回類型不同,會導致編譯錯誤。
  • 和子類的已有方法簽名相同,會導致子類無意中復寫,回到了第一種情況。
  • 和子類無沖突,但可能會影響程序的正確性。比如子類中元素加入集合必須要滿足特定條件,這時候如果超類加入了一個無需檢測就可以直接將元素插入的方法,程序的正確性就受到了威脅。

設計可繼承的類

設計可以用來繼承的類時,應該注意:

  • 對于存在自用性的可覆蓋方法,應該用文檔精確描述調(diào)用細節(jié)。
  • 盡可能少的暴露受保護成員,否則會暴露太多實現(xiàn)細節(jié)。
  • 構造器不應該調(diào)用任何可覆蓋的方法。

詳細解釋下第三點。它實際上和 繼承打破了封裝性 里討論的問題很相似,假設有以下代碼:

public class Father {
 public Father() {
  someMethod();
 }

 public void someMethod() {
 }
}
public class Son extends Father {
 private Date date;

 public Son() {
  this.date = new Date();
 }

 @Override
 public void someMethod() {
  System.out.println("Time = " + date.getTime());
 }
}

上述代碼在運行測試時就會拋出NullPointerException :

public class SonTest {
 private Son  son = new Son();

 @Test
 public void test() {
  son.someMethod();
 }
}

因為超類的構造函數(shù)會在子類的構造函數(shù)之前先運行,這里超類的構造函數(shù)對someMethod()有依賴,同時someMethod()被重寫,所以超類的構造函數(shù)里調(diào)用到的將是Son.someMethod(),而這時候子類還沒被初始化,于是在運行到date.getTime()時便拋出了空指針異常。

因此,如果在超類的構造函數(shù)里對可覆蓋的方法有依賴,那么在繼承時就可能會出錯。

結論

繼承有很多優(yōu)點,但使用繼承時應該慎重并多加考慮。同樣用來實現(xiàn)代碼復用的還有復合,如果使用繼承和復合皆可(這是前提),那么應該優(yōu)先使用復合,因為復合可以保持超類對實現(xiàn)細節(jié)的屏蔽,上述關于繼承的缺點都可以用復合來避免。這也是所謂的復合優(yōu)先于繼承。

如果使用繼承,那么應該留意重寫超類中存在自用性的可覆蓋方法可能會出錯,即使不進行重寫,超類更新時也可能會引入錯誤。同時也應該精心設計超類,對任何相互調(diào)用的可覆蓋方法提供詳細文檔。

總結

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關文章

  • 基于Java利用static實現(xiàn)單例模式

    基于Java利用static實現(xiàn)單例模式

    這篇文章主要介紹了基于Java利用static實現(xiàn)單例模式,當在多個線程同時觸發(fā)類的初始化過程的時候static不會被多次執(zhí)行,下面我們一起進入文章看看具體要的原因
    2022-01-01
  • mybatis清除一級緩存的幾種方式

    mybatis清除一級緩存的幾種方式

    這篇文章主要介紹了mybatis清除一級緩存的幾種方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Java monitor機制使用方法解析

    Java monitor機制使用方法解析

    這篇文章主要介紹了Java monitor機制使用方法解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • kafka生產(chǎn)實踐(詳解)

    kafka生產(chǎn)實踐(詳解)

    下面小編就為大家?guī)硪黄猭afka生產(chǎn)實踐(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • java如何寫接口給別人調(diào)用的示例代碼

    java如何寫接口給別人調(diào)用的示例代碼

    這篇文章主要介紹了java如何寫接口給別人調(diào)用的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-09-09
  • Spring源碼之事件監(jiān)聽機制(實現(xiàn)EventListener接口方式)

    Spring源碼之事件監(jiān)聽機制(實現(xiàn)EventListener接口方式)

    這篇文章主要介紹了Spring源碼之事件監(jiān)聽機制(實現(xiàn)EventListener接口方式),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • SpringBoot請求轉發(fā)的方式小結

    SpringBoot請求轉發(fā)的方式小結

    本文主要介紹了SpringBoot請求轉發(fā)的方式,一共有兩大類,一種是controller控制器轉發(fā)一種是使用HttpServletRequest進行轉發(fā),本文就詳細的介紹一下,感興趣的可以了解一下
    2023-09-09
  • IDEA關閉git管理,文件變成紅色解決方案

    IDEA關閉git管理,文件變成紅色解決方案

    在軟件開發(fā)中,當一個文件夾內(nèi)的Java項目啟用Git版本控制,通常會導致該文件夾下所有項目同步開啟Git,這種做法有助于保持項目的一致性和可追溯性,但也可能帶來管理上的復雜性,如果需要解除某個項目的Git管理,可以通過IDE的設置選項進行調(diào)整
    2024-10-10
  • Java語言中flush()函數(shù)作用及使用方法詳解

    Java語言中flush()函數(shù)作用及使用方法詳解

    這篇文章主要介紹了Java語言中flush函數(shù)作用及使用方法詳解,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • 使用React和springboot做前后端分離項目的步驟方式

    使用React和springboot做前后端分離項目的步驟方式

    這篇文章主要介紹了使用React和springboot做前后端分離項目的步驟方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08

最新評論

吐鲁番市| 雷山县| 白水县| 陵水| 丹阳市| 嘉义市| 沾益县| 外汇| 鄂托克前旗| 老河口市| 遵义县| 甘孜| 墨竹工卡县| 马关县| 柳州市| 湛江市| 河源市| 叙永县| 琼结县| 宁夏| 虹口区| 西华县| 汉寿县| 周宁县| 常宁市| 漳平市| 宁津县| 卢氏县| 温州市| 辽源市| 沽源县| 得荣县| 东乡| 文化| 伽师县| 神池县| 桃园县| 喀喇沁旗| 郴州市| 喜德县| 大城县|