告訴你在java中為什么要慎重使用繼承

告訴你在java中為什么要慎重使用繼承

深圳達(dá)內(nèi)教育      2022-04-11 13:07:01     6

告訴你在java中為什么要慎重使用繼承,  Java繼承是面向?qū)ο蟮淖铒@著的一個(gè)特征,今天深圳達(dá)內(nèi)教育java培訓(xùn)機(jī)構(gòu)小編告訴大家在java中為什么要慎重使用繼承,本文主要

課程價(jià)格 請咨詢

上課時(shí)段: 授課校區(qū):

詳細(xì)介紹

  

Java繼承是面向?qū)ο蟮淖铒@著的一個(gè)特征,今天深圳達(dá)內(nèi)教育java培訓(xùn)機(jī)構(gòu)小編告訴大家在java中為什么要慎重使用繼承,本文主要通過示例代碼介紹,希望通過此文大家能夠在使用java繼續(xù)時(shí)慎重選擇,避開可能遇到的坑。下面就隨小編一起來了解一下java中為什么要慎重使用繼承?



  JAVA中使用到繼承就會有兩個(gè)無法回避的缺點(diǎn):


  (1)打破了封裝性,子類依賴于超類的實(shí)現(xiàn)細(xì)節(jié),和超類耦合。


 ?。?)超類更新后可能會導(dǎo)致錯(cuò)誤。


  繼承打破了封裝性


  關(guān)于這一點(diǎn),下面是一個(gè)詳細(xì)的例子:


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);

 }

}


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


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


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());

 }

}


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


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



  問題的根源


  將這種情況抽象一下,可以發(fā)現(xiàn)出錯(cuò)是因?yàn)槌惖目筛采w的方法存在自用性(即超類里可覆蓋的方法調(diào)用了別的可覆蓋的方法),這時(shí)候如果子類覆蓋了其中的一些方法,就可能導(dǎo)致錯(cuò)誤。


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


  關(guān)鍵在于,子類的寫法很可能從表面上看來沒有問題,但是卻會出錯(cuò),這就迫使開發(fā)者去了解超類的實(shí)現(xiàn)細(xì)節(jié),從而打破了面向?qū)ο蟮姆庋b性,因?yàn)榉庋b性是要求隱藏實(shí)現(xiàn)細(xì)節(jié)的。更危險(xiǎn)的是,錯(cuò)誤不一定能輕易地被測出來,如果開發(fā)者不了解超類的實(shí)現(xiàn)細(xì)節(jié)就進(jìn)行重寫,那么可能就埋下了隱患。



  超類更新時(shí)可能產(chǎn)生錯(cuò)誤,主要有以下幾種可能:


  1、超類更改了已有方法的簽名。會導(dǎo)致編譯錯(cuò)誤。


  2、超類新增了方法:


 ?。?)和子類已有方法的簽名相同但返回類型不同,會導(dǎo)致編譯錯(cuò)誤。


 ?。?)和子類的已有方法簽名相同,會導(dǎo)致子類無意中復(fù)寫,回到了第一種情況。


 ?。?)和子類無沖突,但可能會影響程序的正確性。比如子類中元素加入集合必須要滿足特定條件,這時(shí)候如果超類加入了一個(gè)無需檢測就可以直接將元素插入的方法,程序的正確性就受到了威脅。



  設(shè)計(jì)可以用來繼承的類時(shí),應(yīng)該注意:


  1、對于存在自用性的可覆蓋方法,應(yīng)該用文檔精確描述調(diào)用細(xì)節(jié)。


  2、盡可能少的暴露受保護(hù)成員,否則會暴露太多實(shí)現(xiàn)細(xì)節(jié)。


    3、構(gòu)造器不應(yīng)該調(diào)用任何可覆蓋的方法。


  詳細(xì)解釋下第三點(diǎn)。它實(shí)際上和繼承打破了封裝性里討論的問題很相似,假設(shè)有以下代碼:


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());

 }

}


  上述代碼在運(yùn)行測試時(shí)就會拋出NullPointerException :


public class SonTest {

 private Son  son = new Son();

 

 @Test

 public void test() {

  son.someMethod();

 }

}


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


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



  結(jié)論


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


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



以上就是深圳達(dá)內(nèi)教育java培訓(xùn)機(jī)構(gòu)小編介紹的“在java中為什么要慎重使用繼承”的內(nèi)容,希望對大家有幫助,更多java最新資訊請繼續(xù)關(guān)注深圳達(dá)內(nèi)教育java培訓(xùn)機(jī)構(gòu)官網(wǎng),每天會有精彩內(nèi)容分享與你。


相關(guān)免費(fèi)視頻教程推薦


JAVA繼承視頻教程下載——為什么使用繼承:http://www.bjpowernode.com/xiazai/2616.html


培訓(xùn)啦提醒您:交易時(shí)請核實(shí)對方資質(zhì),對于過大宣傳或承諾需謹(jǐn)慎!任何要求預(yù)付定金、匯款等方式均存在風(fēng)險(xiǎn),謹(jǐn)防上當(dāng)。