多態、內部類
-----Java培訓、Android培訓、期待與您交流! -------
11 多態
多態: 一個對象具有多種形態。(父類的引用類型變量指向了子類的對象)
多態前提: 必須要存在繼承或者實現的關係。
多態要注意的細節:
1. 多態情況下,子父類存在同名的成員變量時,都是訪問父類的成員變量。 2. 多態情況下,子父類存在着同名的非靜態函數時,默認訪問的是子類的成員函數。 3. 多態情況下,子父類存在着同名的靜態函數時,默認訪問的是父類的成員函數。 4. 多態情況下,不能訪問或者調用子類特有的成員,如果真的需要訪問那麼需要進行強制類型轉換。 |
總結:多態情況下,子父類存在同名的成員時,默認都是訪問父類的成員,
只有子父類存在非靜態函數是纔是訪問子類的成員函數。
編譯看左邊,運行不一定看右邊。
編譯看左邊: java編譯器在編譯的時候,會檢查引用類型所屬的類是否具備指定的成員,如果不具備那麼編譯報錯。
運行:多態情況下,子父類存在同名的成員時,默認都是訪問父類的成員,
只有子父類存在非靜態函數是纔是訪問子類的成員函數。
多態的應用場景:
1.多態應用於形參類型的時候,方法可以接收更多類型的參數。
2.多態用於返回值類型的時候,可以返回更多類型的參數。
多態的好處:提高了程序的拓展性。
總結
1:當父類和子類具有相同的非靜態成員變量,那麼在多態下訪問的是父類的成員變量
2:當父類和子類具有相同的靜態成員變量,那麼在多態下訪問的是父類的靜態成員變量
所以:父類和子類有相同的成員變量,多態下訪問的是父類的成員變量。
3:當父類和子類具有相同的非靜態方法(就是子類重寫父類方法),多態下訪問的是子類的成員方法。
4:當父類和子類具有相同的靜態方法(就是子類重寫父類靜態方法),多態下訪問的是父類的靜態方法。
2:多態體現
1:父類引用變量指向了子類的對象
2:父類引用也可以接受自己的子類對象
3:多態前提
1:類與類之間有關係,繼承或者實現
4:多態弊端
1:提高擴展性,但是隻能使用父類引用指向父類成員。
5:多態特點
非靜態
1:編譯時期,參考引用型變量所屬的類是否有調用的方法,如果有編譯通過。沒有編譯失敗
2:運行時期,參考對象所屬類中是否有調用的方法。
3:總之成員函數在多態調用時,編譯看左邊,運行看右邊。
在多態中,成員變量的特點,無論編譯和運行參考左邊(引用型變量所屬的類)。
在多態中,靜態成員函數特點,無論編譯和運行都參考左邊
例子:
/* * 需求:定義一個函數可以接收任意類型的圖形對象。 */
//圖形類 abstract class MyShape{
public abstract void getArea();
public abstract void getLength(); }
//矩形 class Rect extends MyShape{ int width ; int height ;
public Rect(intwidth , intheight){ this.width =width; this.height =height; }
public void getArea(){ System.out.println("矩形面積是:"+width*height); }
public void getLength(){ System.out.println("矩形周長是:"+ 2*(width+height)); } } //圓形 class Circle extends MyShape{ public static final double PI = 3.14; int r; public Circle(intr){ this.r =r; }
public void getArea(){ System.out.println("圓形面積是:"+PI*r*r); } public void getLength(){ System.out.println("圓形周長是:"+ 2*PI*r); } }
public class Duotai { public static void main(String[] args) { /* Circle c = new Circle(4); Rect r = new Rect(3,4); print(r); */ MyShape c = getShape(1); c.getArea(); c.getLength(); }
//需求:定義一個函數可以接收任意類型的圖形對象。 public static void print(MyShape m){ // MyShape m = new Rect(3,4); m.getLength(); m.getArea(); }
//需求:編寫一個函數可以返回任意類型的圖形對象。 public static MyShape getShape(inti){ if (i==0){ return new Circle(4); }else{ return new Rect(4,5); } } } |
多態與數據類型轉換的例子
/* 引用類型數據轉換: 小數據類型數據---------->大數據類型 自動類型轉換。 大數據類型數據----------->小數據類型 強制類型轉換。 */
//動物 class Animal { String name; String color; static int x = 10; //構造函數 public Animal(Stringname,String color){ this.name =name; this.color =color; } public void eat(){ System.out.println("動物在喫飯..."); } }
//狗 class Dog extends Animal { static int x = 20; public Dog(Stringname,String color){ super(name,color); } public void eat(){ System.out.println("狗在喫狗糧..."); } //狗特有的方法咬人 public void bite(){ System.out.println(name+"狠狠的咬人!!"); } } //魚 class Fish extends Animal{ public Fish(Stringname,String color){ super(name,color); } public void eat(){ System.out.println("魚在喫草..."); } //魚特有的方法 public void swing(){ System.out.println("魚在游泳..."); } }
public class Duotai { public static void main(String[] args) { /*System.out.println("Hello World!"); Fish f = new Fish("草魚","墨綠色"); Dog d = new Dog("哈巴狗","白色"); print(d);
Animal a =getAnimal(0); a.swing(); Animal a = new Fish("草魚","墨綠色"); //在多態的情況下,就是不能訪問到子類特有的成員,如果需要訪問子類特有的成員,那麼需要做強制類型轉換。 Fish f = (Fish)a; //把動物又強制轉換成魚 f.swing(); */ Fish f = new Fish("草魚","墨綠色"); Dog d = new Dog("哈巴狗","白色"); print2(f); } //需求3:定義一個函數可以接收任意類型的動物對象,在方法內部調用動物對象特有的方法。 public static void print2(Animal a){ if(ainstanceof Fish){ Fish f = (Fish)a; //把動物強制轉換成魚 f.swing(); }else if(a instanceof Dog){ Dog d = (Dog)a; d.bite(); } } //需求1:定義一個函數可以接收任意類型的動物對象。 public static void print(Animal a){ a.eat(); } //需求2:編寫一個函數可以返回任意類型的動物對象。 public static Animal getAnimal(inti){ if(i==0){ return new Fish("草魚","墨綠色"); }else{ return new Dog("哈巴狗","白色"); } } } |
12 內部類
成員內部類:定義一個類在類的內部,方法的外部,稱作爲成員內部類。
成員內部類訪問方式:
方式1:在外部提供一個方法創建內部類 的對象進行訪問。
方式2: 在其他類直接創建內部類對象進行訪問。
創建對象的格式:
外部類.內部類 變量名= new 外部類().new 內部類();
注意:如果是靜態的成員內部類的在其他類創建對象:外部類.內部類 變量名= new 外部類.內部類();
Outer.Inner inner = new Outer.Inner(); //靜態內部類的訪問方式 |
內部類的好處:內部類的好處就是可以直接訪問外部類的成員。
內部類的寫法
class Outer{ class Inner { public void show(){ System.out.println("內部類的show方法"); } } public void print(){ new Inner().show(); } } |
12.1成員內部類要注意的細節:
1. 成員內部類可以直接訪問外部類的成員。 2. 如果外部類與內部類存在同名的成員變量時,在內部類中默認是訪問內部類的成員,可以通過"外部類.this.成員變量名" 指定訪問外部類 的成員變量。 3. 如果內部類使用了private修飾,那麼訪問該內部類的時候就只能在外部類提供一個方法進行訪問了。不能在其他類直接創建了。 4. 如果成員內部類出現了靜態的成員,那麼該成員內部類也需要使用static修飾。 疑問:如果成員內部類出現了靜態的成員,那麼該成員內部類也需要使用static修飾? 原因:假如沒有使用static修飾,那訪問非靜態成員時,得創建對象再調用new Outer().Inner.x,與Java規範有矛盾,使用成員內部類也需要使用static修飾。 java規範:靜態的數據不需要依賴於對象進行訪問。 |
細節的說明範例
//外部類 class Outer{
//成員變量. int x = 100;
//成員內部類 static class Inner{
static int x = 999;
//成員變量 int i = 10;
//成員函數 public void print(){ System.out.println("這個是內部類的print方法.. x = "+x); } }
//在外部提供一個方法創建內部類的對象進行訪問。 public void visitedInner(){ //創建一個內部的對象 Inner in = new Inner(); System.out.println("i = "+in.i); }
}
//其他類 class Way {
public static void main(String[] args) {
/* //創建一個外部類的對象 Outer outer = new Outer(); outer.visitedInner();
Outer.Inner inner = new Outer().new Inner(); System.out.println("i = "+inner.i); inner.print();
Outer.Inner inner = new Outer.Inner(); //靜態內部類的訪問方式 inner.print(); */ } } |
12.2局部內部類:
局部類要注意的細節:
局部內部類訪問局部變量的時候,局部變量是需要使用final修飾的。
//外部類 class Outer{ public void test(){ //局部變量 final int y = 100; /* y的生命週期:test方法執行完畢之後馬上從內存消失。 局部內部類訪問局部變量的時候,局部變量是需要使用final修飾,爲什麼? */ //局部內部類 class Inner{ //成員變量 intx = 10; //成員函數 public void print(){ System.out.println("這個是局部內部類的print方法..."+y); /* y變量當方法執行完畢的時候就已經從內存中消失了, 這時候test方法執行完畢的時候Inner對象還沒有消失,y消失了, 但是Innner對象的方法還在訪問着y變量,這時候給人的感覺就是y還沒有消失, y變量的生命週期被延長了。
解決方案:如果局部內部類訪問一個局部變量的時候,那麼就讓局部內部類訪問的是 一個變量複製品。源變量可以正常消失。 */ } } //創建局部內部類對象 Inner inner = new Inner();// Inner對象生命週期:反正Inner的對象當test執行完畢之後還不會馬上消失。 // Inner對象的生命週期是比y要長的。 inner.print(); } }
class Way { public static void main(String[] args) { Outer outer = new Outer(); outer.test(); } } |
12.3 匿名內部類
使用前提:必須要存在着繼承或者實現的關係
好處:簡化書寫
繼承關係下的匿名內部類
//動物類 abstract class Animal{ public abstract void run(); public abstract void sleep(); }
class Outer{ public void print(){ /* //需求:定義一個類繼承Animal,然後創建該類的對象調用run、sleep方法。 class Bird extends Animal{
public void run(){ System.out.println("鳥飛得更高!!"); } public void sleep(){ System.out.println("鳥在睡覺!!"); } }
//使用該內部類創建對象。 Bird b = new Bird(); b.run(); b.sleep(); */
//匿名內部類只是沒有類名,其他一個類該有的成員,匿名內部類都是具備的。
//匿名內部類實現 Animal b = new Animal(){ //匿名內部類與Animal的關係是繼承。 多態 //匿名內部的成員 public void run(){ System.out.println("鳥飛得更高!!"); } public void sleep(){ System.out.println("鳥在睡覺!!"); } //子類特有的方法... public void eat(){ System.out.println("鳥在喫!!"); //return this; } };
//b.eat(); b.run(); b.sleep(); } }
public class Demo { public static void main(String[] args) { Outer outer= new Outer(); outer.print(); } } |
接口關係下的匿名內部類
interface A{ public void add(); }
class Outer{ public void print(){ //實現關係下的匿名內部類 new A(){ //這個匿名內部類與A接口的關係是實現關係。 //匿名內部類的成員 public void add(){ System.out.println("這個是添加的方法..!"); } }.add(); } }
class Demo3 { public static void main(String[] args) { Outer outer = new Outer(); outer.print(); } } |