java 包装类可以被继承_【Java基本功】一文了解Java中继承、封装、多态的细节...
本節主要介紹Java面向對象三大特性:繼承 封裝 多態,以及其中的原理。
本文會結合虛擬機對引用和對象的不同處理來介紹三大特性的原理。
繼承
Java中的繼承只能單繼承,但是可以通過內部類繼承其他類來實現多繼承。public?class?Son?extends?Father{public?void?go?()?{System.out.println("son?go");}public?void?eat?()?{System.out.println("son?eat");}public?void?sleep()?{System.out.println("zzzzzz");}public?void?cook()?{//匿名內部類實現的多繼承new?Mother().cook();//內部類繼承第二個父類來實現多繼承Mom?mom?=?new?Mom();mom.cook();}private?class?Mom?extends?Mother?{@Overridepublic?void?cook()?{System.out.println("mom?cook");}}}
封裝
封裝主要是因為Java有訪問權限的控制。public > protected > package = default > private。封裝可以保護類中的信息,只提供想要被外界訪問的信息。
類的訪問范圍A、public????包內、包外,所有類中可見B、protected?包內所有類可見,包外有繼承關系的子類可見(子類對象可調用)C、(default)表示默認,不僅本類訪問,而且是同包可。D、private???僅在同一類中可見
多態
多態一般可以分為兩種,一個是重寫,一個是重載。重寫是由于繼承關系中的子類有一個和父類同名同參數的方法,會覆蓋掉父類的方法。重載是因為一個同名方法可以傳入多個參數組合。
注意,同名方法如果參數相同,即使返回值不同也是不能同時存在的,編譯會出錯。
從jvm實現的角度來看,重寫又叫運行時多態,編譯時看不出子類調用的是哪個方法,但是運行時操作數棧會先根據子類的引用去子類的類信息中查找方法,找不到的話再到父類的類信息中查找方法。
而重載則是編譯時多態,因為編譯期就可以確定傳入的參數組合,決定調用的具體方法是哪一個了。
向上轉型和向下轉型:public static void main(String[] args) {
Son son = new Son();
//首先先明確一點,轉型指的是左側引用的改變。
//father引用類型是Father,指向Son實例,就是向上轉型,既可以使用子類的方法,也可以使用父類的方法。
//向上轉型,此時運行father的方法
Father father = son;
father.smoke();
//不能使用子類獨有的方法。
// father.play();編譯會報錯
father.drive();
//Son類型的引用指向Father的實例,所以是向下轉型,不能使用子類非重寫的方法,可以使用父類的方法。
//向下轉型,此時運行了son的方法
Son son1 = (Son) father;
//轉型后就是一個正常的Son實例
son1.play();
son1.drive();
son1.smoke();
//因為向下轉型之前必須先經歷向上轉型。
//在向下轉型過程中,分為兩種情況:
//情況一:如果父類引用的對象如果引用的是指向的子類對象,
//那么在向下轉型的過程中是安全的。也就是編譯是不會出錯誤的。
//因為運行期Son實例確實有這些方法
Father f1 = new Son();
Son s1 = (Son) f1;
s1.smoke();
s1.drive();
s1.play();
//情況二:如果父類引用的對象是父類本身,那么在向下轉型的過程中是不安全的,編譯不會出錯,
//但是運行時會出現java.lang.ClassCastException錯誤。它可以使用instanceof來避免出錯此類錯誤。
//因為運行期Father實例并沒有這些方法。
Father f2 = new Father();
Son s2 = (Son) f2;
s2.drive();
s2.smoke();
s2.play();
//向下轉型和向上轉型的應用,有些人覺得這個操作沒意義,何必先向上轉型再向下轉型呢,不是多此一舉么。其實可以用于方法參數中的類型聚合,然后具體操作再進行分解。
//比如add方法用List引用類型作為參數傳入,傳入具體類時經歷了向下轉型
add(new LinkedList());
add(new ArrayList());
//總結
//向上轉型和向下轉型都是針對引用的轉型,是編譯期進行的轉型,根據引用類型來判斷使用哪個方法
//并且在傳入方法時會自動進行轉型(有需要的話)。運行期將引用指向實例,如果是不安全的轉型則會報錯。
//若安全則繼續執行方法。
}
public static void add(List list) {
System.out.println(list);
//在操作具體集合時又經歷了向上轉型
// ? ? ? ?ArrayList arr = (ArrayList) list;
// ? ? ? ?LinkedList link = (LinkedList) list;
}
總結:
向上轉型和向下轉型都是針對引用的轉型,是編譯期進行的轉型,根據引用類型來判斷使用哪個方法。并且在傳入方法時會自動進行轉型(有需要的話)。運行期將引用指向實例,如果是不安全的轉型則會報錯,若安全則繼續執行方法。
編譯期的靜態分派
其實就是根據引用類型來調用對應方法。public static void main(String[] args) {
Father father ?= new Son();
靜態分派 a= new 靜態分派();
//編譯期確定引用類型為Father。
//所以調用的是第一個方法。
a.play(father);
//向下轉型后,引用類型為Son,此時調用第二個方法。
//所以,編譯期只確定了引用,運行期再進行實例化。
a.play((Son)father);
//當沒有Son引用類型的方法時,會自動向上轉型調用第一個方法。
a.smoke(father);
//
}
public void smoke(Father father) {
System.out.println("father smoke");
}
public void play (Father father) {
System.out.println("father");
//father.drive();
}
public void play (Son son) {
System.out.println("son");
//son.drive();
}
方法重載優先級匹配public static void main(String[] args) {
方法重載優先級匹配 a = new 方法重載優先級匹配();
//普通的重載一般就是同名方法不同參數。
//這里我們來討論當同名方法只有一個參數時的情況。
//此時會調用char參數的方法。
//當沒有char參數的方法。會調用int類型的方法,如果沒有int就調用long
//即存在一個調用順序char -> int -> long ->double -> ..。
//當沒有基本類型對應的方法時,先自動裝箱,調用包裝類方法。
//如果沒有包裝類方法,則調用包裝類實現的接口的方法。
//最后再調用持有多個參數的char...方法。
a.eat('a');
a.eat('a','c','b');
}
public void eat(short i) {
System.out.println("short");
}
public void eat(int i) {
System.out.println("int");
}
public void eat(double i) {
System.out.println("double");
}
public void eat(long i) {
System.out.println("long");
}
public void eat(Character c) {
System.out.println("Character");
}
public void eat(Comparable c) {
System.out.println("Comparable");
}
public void eat(char ... c) {
System.out.println(Arrays.toString(c));
System.out.println("...");
}
// ? ?public void eat(char i) {
// ? ? ? ?System.out.println("char");
// ? ?}
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的java 包装类可以被继承_【Java基本功】一文了解Java中继承、封装、多态的细节...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java无法实例化类型_java – 无
- 下一篇: 珍爱网java高级等通知?_珍爱网独家J