一、多态
多态可以了解為事物存在的多種展現形态。
例:動物中貓,狗。貓這個對象對應的類型是貓類型,如:貓 x = new貓(); 同時貓也是動物中的一種,也可以把貓稱為動物。動物 y = new貓(); 那麼動物就是貓和狗具體事物中抽取出來的父類型。父類型引用指向了子類對象。
1、多态的展現:父類型的引用或者接口類型的引用指向子類類型的對象。
例如:Person p = new Student();
2、産生多态的前提:
a、類與類之間存在繼承關系,類與接口之間存在實作關系。
b、通常會有覆寫操作。
3、多态的利與弊:
利:提高了程式的可擴充性和後期可維護性。
弊:隻能使用父類中的引用通路父類中的成員。也就是說使用了多态,父類型的引用在使用功能時,不能直接調用子類中的特有方法。如:Animal a = new Cat(); 這就是多态的展現,假設子類Cat中有特有的抓老鼠功能,父類型的 a就不能直接調用。這上面的代碼中,可以了解為Cat類型提升為Animal類型,向上轉型。
如果此時父類的引用想要調用Cat中特有的方法,就需要強制将父類的引用,轉成子類類型,向下轉型。如:Cat c = (Cat)a;
注意:如果父類可以建立對象,如:Animal a = new Animal(); 此時,就不能向下轉型了,Cat c = (Cat)a; 這樣的代碼就變得不容許,編譯時會報錯。是以千萬不能出現這樣的操作,就是将父類對象轉成子類類型。
我們能轉換的是父類引用指向了自己的子類對象時,該引用可以被提升,也可以被強制轉換。多态至始至終都是子類對象在做着變化。
多态的代碼示例:
//父類————動物
abstract class Animal
{
public abstract void eat();
}
//子類————貓
class Cat extends Animal
{
//複寫父類中的抽象功能
public void eat()
{
System.out.println("吃魚");
}
//Cat特有的功能
public static void catchMouse()
{
System.out.println("抓老鼠");
}
}
class Demo
{
public static void main(String[] args)
{
Animal a = new Cat();
a.eat();
Cat c = (Cat)a;
c.catchMouse();
}
}
4、多态的特點
1)多态中非靜态成員函數的特點:
在編譯時期:參閱引用型變量所屬的類中是否有調用的方法。如果有,編譯通過,如果沒有編譯失敗。
如:在上面的示例中,如果用a.catchMouse();編譯就會報錯。這時隻能通過強轉向下類型轉換後,才可以使用子類的特有功能。
在運作時期:參閱對象所屬的類中是否有調用的方法。這就是說,如果父類中有一個非抽象的方法,而子類繼承後又将其複寫了,在多态運作時,父類的引用調用這個同名函數時,被運作的将是父類中的方法。
簡單總結就是:成員函數在多态調用時,編譯看左邊,運作看右邊。
2)多态中成員變量的特點
無論編譯和運作,都參考左邊(引用變量所屬的類)。如:多态中的父類引用調用成員變量時,如果父類和子類有同名的成員變量,那麼被調用的是父類中的成員變量。
3)多态中靜态成員函數的特點
無論編譯和運作,都參考左邊。也就是父類引用在調用靜态同名函數時,被調用的是父類中的靜态函數。這是因為,當類一被加載,靜态函數就随類綁定在了記憶體中。此時,不需要建立對象,就可以使用類名直接調用。同時,父類中的靜态成員函數一般是不被複寫的。
5、多态的出現思想上也做着變化:以前是建立對象并指揮對象做事情。有了多态以後,我們可以找到對象的共性類型,直接操作共性類型即可,這樣可以指揮一批對象做事情,即通過操作父類或接口實作。
二、内部類
1,概述:
如果A類需要直接通路B類中的成員,而B類又需要建立A類的對象。這時,為了友善設計和通路,直接将A類定義在B類中。A類就稱為内部類。内部類可以直接通路外部類中的成員。而外部類想要通路内部類,必須要建立内部類的對象。
編譯時,如果代碼中有内部類,生成的class檔案中會含有這樣的檔案:Test$1.class。編譯器将會把内部類翻譯成用$(美元符号)分隔外部類名和内部類名的正常類檔案。這是内部類的一種編譯現象。
2、内部類的通路規則:
1、内部類可以直接通路外部類中的成員,包括私有。
之是以可以直接通路外部類中的成員,是因為内部類中持有了一個外部類的引用,格式: 外部類名.this。
2、外部類要通路内部類,必須建立内部類對象。
3、通路格式:
當内部類定義在外部類的成員位置上,而且非私有,可以在外部其他類中直接建立内部類對象。
格式:
外部類名.内部類名 變量名 =外部類對象.内部類對象;
如: Outer.Inner in =new Outer().new Inner();
當内部類在外部類中的成員位置上時,可以被成員修飾符所修飾。比如:
private:将内部類在外部類中進行封裝。
static:内部類就局部static的特性。但是當内部類被static修飾後,隻能直接通路外部類中的static成員。出現了通路局限。
在外部其他類中,直接通路static内部類的非靜态成員的格式為:
new 外部類名.内部類名().方法名();
如:new Outer.Inner().function();
在外部其他類中,直接通路static内部類的靜态成員格式為:
外部類名.内部類名.方法名();
如:Outer.Inner.function();
注意:
1)當内部類中定義了靜态成員時,該内部類必須是static的。
2)當外部類中的靜态方法通路内部類時,内部類也必須是static的。
3)在實際應用中,内部類通常被定義為private,而很少定義為public。
4,為什麼内部類可以直接通路外部類中的成員呢?
那是因為内部中都持有一個外部類的引用。這個引用是 外部類名.this
内部類可以定義在外部類中的成員位置上,也可以定義在外部類中的局部位置上。
當内部類被定義在局部位置上,隻能通路局部中被final修飾的局部變量。
三、匿名内部類:
1、概述:沒有名字的内部類。就是内部類的簡化形式。一般隻用一次就可以用這種形式。匿名内部類其實就是一個匿名子類對象。想要定義匿名内部類:前提是内部類必須繼承一個類或者實作接口。
2、匿名内部類的格式:new 父類名&接口名(){ 定義子類成員或者覆寫父類方法 }.方法。
3、匿名内部類的使用場景:
當函數的參數是接口類型引用時,如果接口中的方法不超過3個。可以通過匿名内部類來完成參數的傳遞。
其實就是在建立匿名内部類時,該類中的封裝的方法不要過多,最好兩個或者兩個以内。
4,代碼示例:
interface Inter
{
void method();
}
class InnerClassTest
{
public static void main(String[] args)
{
show(new Inter()
{//匿名内部類
public void method()
{
System.out.println("method show run");
}
});
}
public static void show(Inter in)
{
in.method();
}
}
5,匿名内部類的一個小練習:
interface Inter
{
void method();
}
class Test
{
//通過匿名内部類,補足代碼。
}
class InnerClassDemo
{
public static void main(String[] args)
{
Test.function().method();
}
}
分析:
Test.function().method();//相當于Inter in=Test.function();
// in.method();
Test.function():Test類中有一個靜态的方法function。
.method():說明調用function這個方法運算後的結果是一個對象。而且是一個Inter類型的對象,因為隻有Inter類型的對象,才可以調用内部類中的method方法。
完整的代碼如下:
interface Inter
{
void method();
}
class Test
{
static Inter function()
{
return new Inter()
{
public void method()
{
System.out.println("内部類練習");
}
};
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Test.function().method();
}
}