java 重写方法 访问权限_为何Java中子类重写方法的访问权限不能低于父类中权限(内含里氏替换原则)...
為何Java中子類重寫方法的訪問(wèn)權(quán)限不能低于父類中權(quán)限
因?yàn)?向上轉(zhuǎn)型及Java程序設(shè)計(jì)維護(hù)的原因
例:
假設(shè)一個(gè)父類A 擁有的方法public void setXXX(){}可以被其他任意對(duì)象調(diào)用這個(gè)方法被子類B 覆寫后 為void setXXX(){}即默認(rèn)的訪問(wèn)權(quán)限只能被本包極其子類 所訪問(wèn)假設(shè)其他包中的對(duì)象 C調(diào)用 方法為:
get( A? a){
a.setXXX();}
而此時(shí)傳入的對(duì)象為B類對(duì)象,假設(shè)為b此時(shí)b將轉(zhuǎn)型為a但是b中的setXXX() 調(diào)用權(quán)限
已經(jīng)被縮小了這將造成錯(cuò)誤。所以子類對(duì)象不能比父類對(duì)象訪問(wèn)權(quán)限大
以上只是一個(gè)例子還有其他出于易維護(hù)、易代碼結(jié)構(gòu)設(shè)計(jì)的設(shè)計(jì)思想原因
其他:子類中重寫的方法拋出的異常應(yīng)該為父類中方法的子異常或相同異常,并且拋出的異常種類不能多于父類中的方法。
實(shí)際上是遵循里氏替換原則。
一、里氏替換原則的定義
1、里氏替換原則通俗的來(lái)講就是:子類可以擴(kuò)展父類的功能,但不能改變父類原有的功能。
2、里氏代換原則告訴我們,在軟件中將一個(gè)基類對(duì)象替換成它的子類對(duì)象,程序?qū)⒉粫?huì)產(chǎn)生任何錯(cuò)誤和異常,反過(guò)來(lái)則不成立,如果一個(gè)軟件實(shí)體使用的是一個(gè)子類對(duì)象的話,那么它不一定能夠使用基類對(duì)象。
3、里氏代換原則是實(shí)現(xiàn)開(kāi)閉原則的重要方式之一,由于使用基類對(duì)象的地方都可以使用子類對(duì)象,因此在程序中盡量使用基類類型來(lái)對(duì)對(duì)象進(jìn)行定義,而在運(yùn)行時(shí)再確定其子類類型,用子類對(duì)象來(lái)替換父類對(duì)象。
二、里氏替換原則包含的含義
1、子類可以實(shí)現(xiàn)父類的抽象方法,但是不能覆蓋父類的非抽象方法
在我們做系統(tǒng)設(shè)計(jì)時(shí),經(jīng)常會(huì)設(shè)計(jì)接口或抽象類,然后由子類來(lái)實(shí)現(xiàn)抽象方法,這里使用的其實(shí)就是里氏替換原則。子類可以實(shí)現(xiàn)父類的抽象方法很好理解,事實(shí)上,子類也必須完全實(shí)現(xiàn)父類的抽象方法,哪怕寫一個(gè)空方法,否則會(huì)編譯報(bào)錯(cuò)。
里氏替換原則的關(guān)鍵點(diǎn)在于不能覆蓋父類的非抽象方法。(盡量不要重寫)父類中凡是已經(jīng)實(shí)現(xiàn)好的方法,實(shí)際上是在設(shè)定一系列的規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類必須遵從這些規(guī)范,但是如果子類對(duì)這些非抽象方法任意修改,就會(huì)對(duì)整個(gè)繼承體系造成破壞。而里氏替換原則就是表達(dá)了這一層含義。
2、子類中可以增加自己特有的方法
在繼承父類屬性和方法的同時(shí),每個(gè)子類也都可以有自己的個(gè)性,在父類的基礎(chǔ)上擴(kuò)展自己的功能。前面其實(shí)已經(jīng)提到,當(dāng)功能擴(kuò)展時(shí),子類盡量不要重寫父類的方法,而是另寫一個(gè)方法。
3、當(dāng)子類覆蓋或?qū)崿F(xiàn)父類的方法時(shí),方法的前置條件(即方法的形參)要比父類方法的輸入?yún)?shù)更寬松。
4、當(dāng)子類覆蓋或?qū)崿F(xiàn)父類的方法時(shí),方法的后置條件(即方法的返回值)要比父類方法的返回值更嚴(yán)格。
現(xiàn)在我們可以對(duì)以上四層含義逐個(gè)講解。
子類可以實(shí)現(xiàn)父類的抽象方法,但是不能覆蓋父類的非抽象方法
在我們做系統(tǒng)設(shè)計(jì)時(shí),經(jīng)常會(huì)設(shè)計(jì)接口或抽象類,然后由子類來(lái)實(shí)現(xiàn)抽象方法,這里使用的其實(shí)就是里氏替換原則。子類可以實(shí)現(xiàn)父類的抽象方法很好理解,事實(shí)上,子類也必須完全實(shí)現(xiàn)父類的抽象方法,哪怕寫一個(gè)空方法,否則會(huì)編譯報(bào)錯(cuò)。
里氏替換原則的關(guān)鍵點(diǎn)在于不能覆蓋父類的非抽象方法。父類中凡是已經(jīng)實(shí)現(xiàn)好的方法,實(shí)際上是在設(shè)定一系列的規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類必須遵從這些規(guī)范,但是如果子類對(duì)這些非抽象方法任意修改,就會(huì)對(duì)整個(gè)繼承體系造成破壞。而里氏替換原則就是表達(dá)了這一層含義。
在面向?qū)ο蟮脑O(shè)計(jì)思想中,繼承這一特性為系統(tǒng)的設(shè)計(jì)帶來(lái)了極大的便利性,但是由之而來(lái)的也潛在著一些風(fēng)險(xiǎn)。就像開(kāi)篇所提到的那一場(chǎng)景一樣,對(duì)于那種情況最好遵循里氏替換原則,類C1繼承類C時(shí),可以添加新方法完成新增功能,盡量不要重寫父類C的方法。否則可能帶來(lái)難以預(yù)料的風(fēng)險(xiǎn),比如下面一個(gè)簡(jiǎn)單的例子:
public?class?C {
public?int?func(int?a, int?b){
return?a+b;
}
}
public?class?C1 extends?C{
@Override
public?int?func(int?a, int?b) {
return?a-b;
}
}
public?class?Client{
public?static?void?main(String[] args) {
C c = new?C1();
System.out.println("2+1="?+ c.func(2, 1));
}
}
運(yùn)行結(jié)果:2+1=1
上面的運(yùn)行結(jié)果明顯是錯(cuò)誤的。類C1繼承C,后來(lái)需要增加新功能,類C1并沒(méi)有新寫一個(gè)方法,而是直接重寫了父類C的func方法,違背里氏替換原則,引用父類的地方并不能透明的使用子類的對(duì)象,導(dǎo)致運(yùn)行結(jié)果出錯(cuò)。
子類中可以增加自己特有的方法
在繼承父類屬性和方法的同時(shí),每個(gè)子類也都可以有自己的個(gè)性,在父類的基礎(chǔ)上擴(kuò)展自己的功能。前面其實(shí)已經(jīng)提到,當(dāng)功能擴(kuò)展時(shí),子類盡量不要重寫父類的方法,而是另寫一個(gè)方法,所以對(duì)上面的代碼加以更改,使其符合里氏替換原則,代碼如下:
public?class?C {
public?int?func(int?a, int?b){
return?a+b;
}
}
public?class?C1 extends?C{
public?int?func2(int?a, int?b) {
return?a-b;
}
}
public?class?Client{
public?static?void?main(String[] args) {
C1 c = new?C1();
System.out.println("2-1="?+ c.func2(2, 1));
}
}
運(yùn)行結(jié)果:2-1=1
當(dāng)子類覆蓋或?qū)崿F(xiàn)父類的方法時(shí),方法的前置條件(即方法的形參)要比父類方法的輸入?yún)?shù)更寬松
import?java.util.HashMap;
public?class?Father {
public?void?func(HashMap m){
System.out.println("執(zhí)行父類...");
}
}
import?java.util.Map;
public?class?Son extends?Father{
public?void?func(Map m){//方法的形參比父類的更寬松
System.out.println("執(zhí)行子類...");
}
}
import?java.util.HashMap;
public?class?Client{
public?static?void?main(String[] args) {
Father f = new?Son();//引用基類的地方能透明地使用其子類的對(duì)象。
HashMap h = new?HashMap();
f.func(h);
}
}
運(yùn)行結(jié)果:執(zhí)行父類...
注意Son類的func方法前面是不能加@Override注解的,因?yàn)榉駝t會(huì)編譯提示報(bào)錯(cuò),因?yàn)檫@并不是重寫(Override)。
當(dāng)子類的方法實(shí)現(xiàn)父類的抽象方法時(shí),方法的后置條件(即方法的返回值)要比父類更嚴(yán)格
import?java.util.Map;
public?abstract?class?Father {
public?abstract?Map func();
}
import?java.util.HashMap;
public?class?Son extends?Father{
@Override
public?HashMap func(){//方法的返回值比父類的更嚴(yán)格
HashMap h = new?HashMap();
h.put("h", "執(zhí)行子類...");
return?h;
}
}
public?class?Client{
public?static?void?main(String[] args) {
Father f = new?Son();//引用基類的地方能透明地使用其子類的對(duì)象。
System.out.println(f.func());
}
}
執(zhí)行結(jié)果:{h=執(zhí)行子類...}
總結(jié)
繼承作為面向?qū)ο笕筇匦灾?#xff0c;在給程序設(shè)計(jì)帶來(lái)巨大便利的同時(shí),也帶來(lái)了一些弊端,它增加了對(duì)象之間的耦合性。因此在系統(tǒng)設(shè)計(jì)時(shí),遵循里氏替換原則,盡量避免子類重寫父類的方法,可以有效降低代碼出錯(cuò)的可能性。
我們都知道面向?qū)ο笥腥筇匦?#xff1a;封裝、繼承、多態(tài)。所以我們?cè)趯?shí)際開(kāi)發(fā)過(guò)程中,子類在繼承父類后,根據(jù)多態(tài)的特性,可能是圖一時(shí)方便,經(jīng)常任意重寫父類的方法,那么這種方式會(huì)大大增加代碼出問(wèn)題的幾率。比如下面場(chǎng)景:類C實(shí)現(xiàn)了某項(xiàng)功能F1。現(xiàn)在需要對(duì)功能F1作修改擴(kuò)展,將功能F1擴(kuò)展為F,其中F由原有的功能F1和新功能F2組成。新功能F由類C的子類C1來(lái)完成,則子類C1在完成功能F的同時(shí),有可能會(huì)導(dǎo)致類C的原功能F1發(fā)生故障。這時(shí)候里氏替換原則就閃亮登場(chǎng)了。
總結(jié)
以上是生活随笔為你收集整理的java 重写方法 访问权限_为何Java中子类重写方法的访问权限不能低于父类中权限(内含里氏替换原则)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: emf java_Java实现emf图片
- 下一篇: java pattern用法_Java