多態 的內存分析
多態:父類的引用類型變量指向了子類的對象
或者是接口 的引用類型變量指向了接口實現類的對象
(因爲接口的方法都是非static的,所以必須要用實現類將接口方法實現)
Animal a =new mouse();
多態要注意 的細節:
1. 多態情況下,子父類存在同名的成員變量時,訪問的是父類的成員變量。
2. 多態情況下,子父類存在同名的非靜態的成員函數時,訪問的是子類的成員函數。
3. 多態情況下,子父類存在同名的靜態的成員函數時,訪問的是父類的成員函數。
4. 多態情況下,不能訪問子類特有的成員。
總結:多態情況下,子父類存在同名的成員時,訪問的都是父類的成員,除了在同名非靜態函數時纔是訪問子類的。
如果要訪問①子類特有的成員 ②同名的靜態成員函數 ③同名的成員變量
則需要進行類型強轉。
爲什麼會這樣呢?
java的特點: 編譯看左邊, 運行不一定看右邊
如果引用類型的變量不具備指定的成員,就報錯。
實際上
Person worker = new Worker();
等價於
Worker w = new Worker(); Person worker = w;
所以堆內存中其實已經存在一個Worker對象, 因爲是繼承關係,所以Worker對象中有一塊用於繼承的內存空間(其實是一個Person對象),把這個對象的引用賦給a。所以顯然a調用的屬性和方法都是父類Person的。
只有非靜態的被子類重寫的方法例外 , 爲什麼呢?
這裏要重點要搞清楚堆內存中的調用。
圖片引用自:http://blog.csdn.net/thinking_in_android
在方法區中含有 類代碼,static變量和 常量池,
常量池中有一些 字符串, 已經方法的入口。
①首先因爲運行demo.class 裏面有關鍵字 Person和 Worker,所以這三個類的類代碼加載進方法區
②將static變量和static代碼塊加載進方法區 ,所有的類只維護一份
③因爲new Worker(),在堆中創建一個Worker對象,並且把Worker對象中的一些常量放入方法區中的常量池,其中就包括Worker類中重寫的方法。
④Person Worker調用say方法,去哪裏找呢? 首先在堆內存的Worker對象中的Person內存空間找, 找到say()方法索引,於是順着這個索引去方法區中的常量池中找say()方法的入口,因爲此時只new了子類Worker對象,所以這個常量池中的say()方法入口其實是因爲創建Worker對象而存入常量池的,於是Person類對象順着這個入口調用了子類重寫的方法。
內部類
分爲: 成員內部類, 局部內部類
成員內部類
1. 成員內部類: 與成員變量等價,是一個成員屬性。
成員內部類的訪問方式:
方式一:在**外部類提供一個方法創建內部類的對象**進行訪問。!!!!!!注意在本類的主方法中野不能創建。
方式2二:在其他類直接創建內部類的對象。 格式:外部類.內部類 變量名 = new 外部類().new 內部類();
注意: 如果是一個靜態內部類,那麼在其他類創建 的格式:
外部類.內部類 變量名 = new 外部類.內部類();
內部類可以直接訪問外部類的所有成員
內部類要注意的細節:
1. 如果外部類與內部類存在同名的成員變量時,在內部類中默認情況下是訪問內部類的成員變量。
可以通過”外部類.this.成員變量名” 指定訪問外部類的 成員。
因爲this不知道指向誰!!!
2. 私有的成員內部類只能在外部類提供一個方法創建內部類的對象進行訪問,不能在其他類創建對象了。
3. 成員內部類一旦出現了靜態的成員,那麼該類也必須 使用static修飾。
局部內部類
局部內部類: 在一個類 的方法內部定義另外一個類,那麼另外一個類就稱作爲局部內部類。
局部內部類要注意的細節:
. 如果局部 內部類訪問了一個局部變量,那麼該局部變量必須使用final修飾、
class Outer{
String name= "外部類的name";
public void test(){
//局部變量
final int y =100; // y 什麼時候從內存中消失? 方法執行完畢之後y消失。
//局部內部類
class Inner{ /*
當test方法執行完畢之後,那麼y馬上從內存中消失,而Inner對象在方法
執行完畢的時候還沒有從內存中消失,而inner對象的print方法還在訪問着
y變量,這時候的y變量已經消失了,那麼就給人感覺y的生命變量已經被延長了
.
解決方案: 如果一個局部內部類訪問一個局部變量的時候,那麼就讓該局部內部類訪問這個局部 變量 的複製品。(所以要用final修飾)
*/
int x = 10;
public void print(){
System.out.println("這個是局部內部類的print方法.."+y);
}
}
Inner inner = new Inner(); //這個inner對象什麼時候消失? Inner對象的生命週期比局部變量y的生命週期要長。
inner.print();
}
}
匿名內部類
前提:必須要有繼承或者實現 關係
匿名內部類 只是沒有類名,其他的成員都具備。
因爲沒有類名 要借用父類的名字,其實是創建Animal子類對象
abstract class Animal{
public abstract Animal run();
public abstract void sleep();
}
new Animal(){ //多態
//匿名內部的成員
public Animal run(){
System.out.println("狗在跑..");
return this;
}
public void sleep(){
System.out.println("狗趴在睜開眼睛睡..");
}
//特有的方法
public void bite(){
System.out.println("狗在咬人..");
}
};
如果要調用一個方法 直接new Animal{xxxx}.run() 即可
如果要調用兩個方法怎麼辦呢?
①將其中一個方法的返回值設爲Animal , 然後return this;
②將這個對象賦給一個Animal變量, 用多態的特點運行子類重寫的方法,但是如果要調用的是新增的方法, 就只能用①中的方法。。或者換成局部內部類