Java學(xué)習(xí)之繼承、內(nèi)部類(lèi)與多態(tài),包

一、繼承

當(dāng)兩個(gè)事物之間存在一定的所屬關(guān)系,即就像孩子從父母那里得到遺傳基因一樣,當(dāng)然,java要遺傳的更完美,這兩個(gè)類(lèi)中,一個(gè)繼承的類(lèi)(為子類(lèi)或者基礎(chǔ)類(lèi))可以從被繼承的類(lèi)(為父類(lèi)或者超類(lèi))中獲得一些屬性和方法,而不必再自己創(chuàng)建新方法(在不需要復(fù)寫(xiě)的情況等)。

1、特點(diǎn):

1、提高代碼復(fù)用性,定義在父類(lèi)中的成員(變量和方法),可以被子類(lèi)重復(fù)使用;

2、讓類(lèi)與類(lèi)之間產(chǎn)生關(guān)系,這樣就會(huì)有多態(tài)的特性。使得應(yīng)用起來(lái)更方便。

需要注意的是:
A.不可以只是為了獲取其他類(lèi)的功能,簡(jiǎn)化代碼就一味的使用繼承;必須是類(lèi)與類(lèi)之間有所屬關(guān)系,才可以用繼承,這種關(guān)系稱為“is-a”。
B.java語(yǔ)言中,只支持單繼承,不支持多繼承。究其原因,是因?yàn)槎嗬^承容易帶來(lái)安全隱患:
a.當(dāng)多個(gè)父類(lèi)中定義了相同的功能,且功能內(nèi)容不同時(shí),子類(lèi)對(duì)象并不確定要運(yùn)行哪一個(gè),這就造成了程序的混亂而導(dǎo)致異常出現(xiàn)。
b但是java保留了這種機(jī)制,并改良了多繼承為多實(shí)現(xiàn),而且接口就可以多繼承。

2、使用繼承體系中的功能

1、如果使用體系,要先對(duì)體系中父類(lèi)的描述進(jìn)行參閱,了解父類(lèi)中的共性功能,即可以使用該體系了。這樣既可以減少查閱時(shí)間,也能更系統(tǒng)的了解體系結(jié)構(gòu),以及父類(lèi)的功能。

2、具體調(diào)用父類(lèi)中的功能的時(shí)候,需要?jiǎng)?chuàng)建子類(lèi)對(duì)象,通過(guò)子類(lèi)對(duì)象對(duì)父類(lèi)方法調(diào)用,實(shí)現(xiàn)繼承。
為何要通過(guò)創(chuàng)建子類(lèi)對(duì)象調(diào)用父類(lèi)中的方法呢?原因如下:
a.父類(lèi)很可能不可以創(chuàng)建對(duì)象,如父類(lèi)是一個(gè)抽象類(lèi)或者接口,這就需要子類(lèi)創(chuàng)建對(duì)象,調(diào)用方法。
b.創(chuàng)建子類(lèi)對(duì)象,可以使用更多的功能,如父類(lèi)公有的,和子類(lèi)中自定義的特有功能。

3、子父類(lèi)出現(xiàn)后,類(lèi)成員的特點(diǎn):

類(lèi)成員:變量、函數(shù)、構(gòu)造函數(shù)
1、變量:
子父類(lèi)中出現(xiàn)非私有的同名成員變量是,子類(lèi)訪問(wèn)本類(lèi)的同名時(shí),要用this關(guān)鍵字(如果省略了this,仍是代表本類(lèi)的變量);子類(lèi)訪問(wèn)父類(lèi)同名變量時(shí),用super關(guān)鍵字。
補(bǔ)充:this和super兩者都存在與方法區(qū)中。
this:本類(lèi)對(duì)象引用,那個(gè)對(duì)象調(diào)用this所在的函數(shù),this就代表那個(gè)對(duì)象。
super:父類(lèi)對(duì)象引用,用于子類(lèi)初始化父類(lèi)構(gòu)造函數(shù)時(shí)等。

2、.函數(shù):(覆蓋(重寫(xiě)))
當(dāng)子類(lèi)同父類(lèi)出現(xiàn)相同函數(shù)時(shí),子類(lèi)對(duì)象調(diào)用該函數(shù)時(shí),會(huì)運(yùn)行子類(lèi)內(nèi)容,如同覆蓋谷類(lèi)函數(shù)一樣。實(shí)際上并沒(méi)有覆蓋父類(lèi)的函數(shù),如果還需要調(diào)用父類(lèi)的這個(gè)方法,需要用super.方法名即可。
舉例:

class Father  
{  
    int num = 3;  
    void show()  
    {  
        System.out.println(num);  
    }  
}  
  
class Son extends Father  
{  
    int num = 5;  
    //復(fù)寫(xiě)父類(lèi)函數(shù)  
    void show()  
    {  
        System.out.println(num);//this引用,此處省略this,結(jié)果是5  
        System.out.println(super.num);//super引用,結(jié)果是3.  
    }  
}  
  
class Demo  
{  
    public static void main(String [] args)  
    {  
        Son s = new Son();  
        s.show();  
    }  
}

在內(nèi)存中的加載方式:



Son中的對(duì)象加載之后,才會(huì)出現(xiàn)this和super。

應(yīng)用:
a.當(dāng)子類(lèi)繼承父類(lèi)時(shí),就沿襲了父類(lèi)的功能,在子類(lèi)中,雖然具備該功能,,但是其內(nèi)容卻與父類(lèi)不同,此時(shí),可覆蓋父類(lèi)的方法(即保留),使用新特性。
注意:
第一、子類(lèi)權(quán)限必須大于或等于父類(lèi)的才能覆蓋;
第二、靜態(tài)只能覆蓋靜態(tài)。
b.用于擴(kuò)展(新功能或內(nèi)容等)
示例如下:

class Father  
{  
    void show()  
    {  
        System.out.println("Wathing TV");  
    }  
}  
  
class Son extends Father  
{  
    //復(fù)寫(xiě)父類(lèi)函數(shù)  
    void show()  
    {  
        System.out.println("Wathing TV");//父類(lèi)中的功能  
        System.out.println("Playing PC");//擴(kuò)展的功能  
    }  
}  
  
class Demo  
{  
    public static void main(String [] args)  
    {  
        Son s = new Son();  
        s.show();  
    }  
}

3、子父類(lèi)構(gòu)造函數(shù):子類(lèi)的構(gòu)造函數(shù)中有一個(gè)隱式的構(gòu)造函數(shù)
特點(diǎn):在對(duì)子類(lèi)對(duì)象進(jìn)行初始化時(shí),父類(lèi)構(gòu)造函數(shù)也在運(yùn)行,而且是在子類(lèi)之前運(yùn)行。因?yàn)樽宇?lèi)構(gòu)造函數(shù)默認(rèn)第一行有一條隱式語(yǔ)句:super();(沒(méi)有顯式的繼承父類(lèi)的類(lèi)中,是默認(rèn)繼承Object的,所以構(gòu)造函數(shù)第一條語(yǔ)句仍是super();),因此會(huì)首先訪問(wèn)父類(lèi)中空參數(shù)的構(gòu)造函數(shù)(在不手動(dòng)加入含有參數(shù)的super的語(yǔ)句的情況下),這是系統(tǒng)默認(rèn)的。所有的構(gòu)造函數(shù)均是如此。

需要注意的是:
a.如果父類(lèi)中無(wú)空參數(shù)構(gòu)造函數(shù)(即父類(lèi)中顯式的構(gòu)造了含有參數(shù)的構(gòu)造函數(shù)),必須手動(dòng)加入父類(lèi)中該構(gòu)造函數(shù)。
b.構(gòu)造函數(shù)不存在覆蓋,子類(lèi)中的構(gòu)造函數(shù)必定至少有一個(gè)會(huì)訪問(wèn)父類(lèi)中的構(gòu)造函數(shù)。
舉例:

為什么子類(lèi)一定要訪問(wèn)父類(lèi)的構(gòu)造函數(shù)呢?
第一、由于父類(lèi)中的數(shù)據(jù)可直接獲取,所以子類(lèi)對(duì)象建立時(shí),需要先查看父類(lèi)是如何對(duì)這些數(shù)據(jù)進(jìn)行初始化的。因此,子類(lèi)在初始化的時(shí)候須先訪問(wèn)父類(lèi)構(gòu)造函數(shù),如果父類(lèi)對(duì)這些數(shù)據(jù)初始化后,可以使用,子類(lèi)就無(wú)需在進(jìn)行多余的操作了。
第二、如果要訪問(wèn)指定的構(gòu)造函數(shù),可手動(dòng)定義super語(yǔ)句的方式來(lái)指定特定的構(gòu)造函數(shù)。

注意:
super語(yǔ)句一定要定義在子類(lèi)構(gòu)造函數(shù)的第一條語(yǔ)句,即第一行,因?yàn)橐瘸跏蓟割?lèi)構(gòu)造函數(shù),然后再初始化子類(lèi)構(gòu)造函數(shù),子類(lèi)構(gòu)造函數(shù)判斷是否父類(lèi)的可用,不可用則子類(lèi)將其覆蓋掉,這樣子類(lèi)在可以方便選擇。
構(gòu)造函數(shù)中,this和super語(yǔ)句都必須在第一語(yǔ)句出現(xiàn),因?yàn)閮烧叨际怯脕?lái)初始化的,既然是初始化,就要首先存在,就只能進(jìn)行一次,因此不能同時(shí)作為第一條語(yǔ)句出現(xiàn)。

總結(jié):子類(lèi)實(shí)例化過(guò)程
1、子類(lèi)所有的構(gòu)造函數(shù)默認(rèn)均訪問(wèn)父類(lèi)中的空參數(shù)構(gòu)造函數(shù),因?yàn)樽宇?lèi)每個(gè)構(gòu)造函數(shù)內(nèi)的第一行均有一句隱式的super();語(yǔ)句。
2、當(dāng)父類(lèi)中悟空參數(shù)構(gòu)造函數(shù)時(shí),子類(lèi)必須手動(dòng)通過(guò)super語(yǔ)句的形式指定要訪問(wèn)的父類(lèi)中的構(gòu)造函數(shù)
3、子類(lèi)構(gòu)造函數(shù)也可手動(dòng)指定this語(yǔ)句,來(lái)訪問(wèn)本類(lèi)中的構(gòu)造函數(shù),但子類(lèi)中至少要有一個(gè)構(gòu)造函數(shù)訪問(wèn)父類(lèi)的構(gòu)造函數(shù)。

還有一個(gè)問(wèn)題,就是繼承是對(duì)封裝性的一個(gè)挑戰(zhàn),這個(gè)問(wèn)題是用final解決的。具體關(guān)于final在此不再贅述了。

二、內(nèi)部類(lèi)

先來(lái)看一段簡(jiǎn)單的代碼:

//情況一:創(chuàng)建兩個(gè)常規(guī)類(lèi)A和B  
class A{  
    int a = 5;  
    void  fun(){  
        C c = new C();  
        c.method();  
        System.out.println("A:" + a);  
    }  
  
    //情況二:將C類(lèi)放入A類(lèi)中  
    class C{  
        void method(){  
            System.out.println("C訪問(wèn)A:" + a);//可直接訪問(wèn)  
        }  
    }  
}  
class B{  
    void fu(){  
        int a = 6;  
        A me = new A();  
        System.out.println("訪問(wèn)A中a:" + me.a);//必須建立對(duì)象后才能訪問(wèn)  
    }  
}  
  
class  Demo  
{  
    public static void main(String[] args)   
    {  
        A x = new A();  
        B y = new B();  
        x.fun();  
        y.fu();  
    }  
}

從代碼中就可以看出,內(nèi)部類(lèi)的好處。
內(nèi)部類(lèi):將一個(gè)類(lèi)定義在另一個(gè)類(lèi)之中。

1、訪問(wèn)規(guī)則(即使用內(nèi)部類(lèi)的好處):

1、內(nèi)部類(lèi)方法可以訪問(wèn)該類(lèi)所在的類(lèi)中作用域的成員,包括私有成員變量。
2、內(nèi)部類(lèi)可以對(duì)同一個(gè)包中的其他類(lèi)隱藏起來(lái),從而不被訪問(wèn)
3、使用匿名內(nèi)部類(lèi)是比較便捷簡(jiǎn)單的,這種情況較多的用于AWT設(shè)計(jì)中。
注:
為何內(nèi)部類(lèi)可直接訪問(wèn)外部類(lèi):
由于成員可被對(duì)象訪問(wèn),內(nèi)部類(lèi)中持有了一個(gè)外部類(lèi)的引用,,因此就可以直接用:外部類(lèi)名.this.成員

2、訪問(wèn)格式:

1當(dāng)內(nèi)部類(lèi)定義在外部類(lèi)的成員位置上,而且非私有,可在外部其他類(lèi)中直接建立內(nèi)部類(lèi)對(duì)象
格式:外部類(lèi)名.內(nèi)部類(lèi)名 變量名 = 外部類(lèi)對(duì)象.內(nèi)部類(lèi)對(duì)象

2、當(dāng)內(nèi)部類(lèi)在成員變量的位置上,可被成員修飾符修飾
·private:將內(nèi)部類(lèi)在外部類(lèi)中進(jìn)行封裝
·static:內(nèi)部類(lèi)具備了static的特性,當(dāng)內(nèi)部類(lèi)被static修飾(下面說(shuō)到的靜態(tài)內(nèi)部類(lèi)),只可訪問(wèn)外部類(lèi)中的static的成員,訪問(wèn)則受限。

注:(Outer為外部類(lèi)名,Inner為內(nèi)部類(lèi)名,function為非靜態(tài)成員)
在其他類(lèi)中,如何訪問(wèn)靜態(tài)內(nèi)部類(lèi)中非靜態(tài)成員:new Outer.Inner.function();(建立對(duì)象訪問(wèn))
在其他類(lèi)中,如何訪問(wèn)靜態(tài)內(nèi)部類(lèi)中的靜態(tài)成員:Outer.Inner.function();(可直接訪問(wèn))

注意:
1、當(dāng)內(nèi)部類(lèi)中定義了靜態(tài)成員,該內(nèi)部類(lèi)必須為靜態(tài)的
2、當(dāng)外部類(lèi)中靜態(tài)方法訪問(wèn)內(nèi)部類(lèi)時(shí),內(nèi)部類(lèi)必須為靜態(tài)的
3、內(nèi)部類(lèi)一般定義為private的,而很少定義為public的。

3、內(nèi)部類(lèi)定義原則:------->多在程序設(shè)計(jì)中使用

1、當(dāng)描述事物時(shí),事物的內(nèi)部還有事物,則該事物使用內(nèi)部類(lèi)來(lái)描述,因?yàn)閮?nèi)部事物要使用外部事物內(nèi)容
舉例來(lái)說(shuō):
樓房?jī)?nèi)部還有家庭住戶,家庭住戶中還有房間,這就可以用內(nèi)部類(lèi)描述。(總不能訪問(wèn)一個(gè)住戶,就建立一個(gè)樓房對(duì)象吧,那就可真是有錢(qián)人的風(fēng)范了。)

2、何時(shí)定義內(nèi)部類(lèi):
當(dāng)一個(gè)類(lèi)需要直接訪問(wèn)另一個(gè)類(lèi)中的成員時(shí),則當(dāng)這個(gè)類(lèi)放入另一個(gè)類(lèi)中,并將內(nèi)部類(lèi)封裝。

3、內(nèi)部類(lèi)生成文件:
我們?cè)诰幾g一個(gè)源文件是,如果代碼中有內(nèi)部類(lèi),你會(huì)發(fā)現(xiàn),生成的class文件中含有例如這樣的文件:AC.class。這是為什么呢?因?yàn)閮?nèi)部類(lèi)是一種編譯現(xiàn)象,與虛擬機(jī)無(wú)關(guān)。編譯器將會(huì)把內(nèi)部類(lèi)翻譯成用(美元符號(hào))分隔外部類(lèi)名和內(nèi)部類(lèi)名的常規(guī)類(lèi)文件,而虛擬機(jī)對(duì)此卻一無(wú)所知。

4、局部?jī)?nèi)部類(lèi)(也稱局部類(lèi),方法中)

看下面一段代碼:

class A{  
    private int a = 5;  
    void  fun(){  
        class B{//在方法中創(chuàng)建一個(gè)內(nèi)部類(lèi)  
            void method(){  
                System.out.println("C訪問(wèn)A:" + a);//可直接訪問(wèn)  
            }  
        }  
        B c = new B();//建立一個(gè)對(duì)象  
        c.method();  
    }  
}  
class  Demo  
{  
    public static void main(String[] args)   
    {  
        A x = new A();  
        x.fun();  
    }  
}

局部?jī)?nèi)部類(lèi):當(dāng)內(nèi)部類(lèi)只在外部類(lèi)中的某個(gè)方法中,創(chuàng)建了這個(gè)類(lèi)型的對(duì)象時(shí),且僅使用了一次,那么可在這個(gè)方法中定義局部類(lèi)。

注:
1、局部?jī)?nèi)部類(lèi)不可用public或者private訪問(wèn)修飾符聲明,它的作用域被限定在了聲明這個(gè)局部類(lèi)的代碼塊中
2、局部類(lèi)的優(yōu)勢(shì):
a.對(duì)外界完全隱藏,即使此方法所在的類(lèi)也不可訪問(wèn),也就是說(shuō),除此方法外,無(wú)任何方法知道它的存在。
b.可訪問(wèn)包含他們的外部類(lèi),因還持有外部類(lèi)的引用;還可訪問(wèn)局部變量,但是局部變量必須被聲明為final。

需要注意:局部?jī)?nèi)部類(lèi)不可被成員修飾符修飾,如static
代碼如下:

/* 
局部?jī)?nèi)部類(lèi)測(cè)試: 
*/  
  
class Outer//類(lèi)不能用private,但是內(nèi)部類(lèi)是可以的  
{  
    int x = 3;  
    private static class InnerOuter//可在外部類(lèi)的任意有效的地方,可以用private和static  
    {       //......  
    }  
    //含局部變量時(shí)  
    void method(final int a)//需訪問(wèn)加final  
    {  
        final int y = 4;//y需要被聲明為最終類(lèi)型,即final才可被局部?jī)?nèi)部類(lèi)中的成員訪問(wèn)  
        class Inner//在方法中,相當(dāng)于局部成員,不能為static或private  
        {  
            void fun()//不能定義靜態(tài)成員(變量和方法)  
            {  
                System.out.println("訪問(wèn)外部類(lèi)成員變量:+ Outer.this :" + Outer.this.x);  
                System.out.println("局部?jī)?nèi)部類(lèi)訪問(wèn)本地變量定義為final:" + y);  
                System.out.println("方法中的參數(shù)需定義為final:" + a);  
            }  
        }  
        new Inner().fun();  
    }  
}  
  
class InnerClassDemo0  
{  
    public static void main(String[] args)   
    {  
        Outer ou = new Outer();  
        ou.method(7);//打印3、4、7  
        ou.method(8);//打印3、4、8  
    }  
}
  • 說(shuō)明:
    method 方法中聲明了Inner的內(nèi)部類(lèi),同時(shí)也產(chǎn)生了Inner 的內(nèi)部類(lèi)實(shí)例化對(duì)象,并調(diào)用其內(nèi)部的方法。
    method 方法參數(shù)a 必須定義為final,在傳入7后,運(yùn)行method方法結(jié)束后,在棧內(nèi)存中局部變量a就會(huì)隨即消亡,然后就可以再傳入8,運(yùn)行下一個(gè)method方法。

5、匿名內(nèi)部類(lèi):

1、匿名內(nèi)部類(lèi):就是內(nèi)部類(lèi)的一種簡(jiǎn)寫(xiě)格式。
當(dāng)只創(chuàng)建該類(lèi)的一個(gè)對(duì)象,可不用再為其命名。所以稱之為匿名內(nèi)部類(lèi)。

代碼示例:

class Outer2  
{  
    int x = 3;  
    public  void fun()  
    {  
        /* 
        其中Inner()被改寫(xiě)為AbsDemo(){}  (其實(shí)就是對(duì)父類(lèi)的復(fù)寫(xiě)) 
        */  
        new AbsDemo()//看這里  
        {  
            void show()  
            {  
                System.out.println("匿名show:" + x);  
            }  
        }.show();  
    }  
}

2、定義前提:
內(nèi)部類(lèi)必須繼承一個(gè)類(lèi)或?qū)崿F(xiàn)接口。

但是有一種很特殊,可以不直接繼承一個(gè)父類(lèi),仍可定義一個(gè)匿名內(nèi)部類(lèi)。因?yàn)槿魏晤?lèi)都是Object的子類(lèi)。
如:

new Object()  
{  
         類(lèi)中的內(nèi)容  
         fun()方法  
}.fun();  

3、格式:

new    父類(lèi)或接口(參數(shù)){定義子類(lèi)的內(nèi)容};

4、要點(diǎn)說(shuō)明:
A.其實(shí)匿名內(nèi)部類(lèi)就是一個(gè)匿名子類(lèi)對(duì)象,可以理解為帶有內(nèi)容的對(duì)象。
B.匿名內(nèi)部類(lèi)中的方法最好少于3個(gè),方法少,比較方便簡(jiǎn)單,匿名內(nèi)部類(lèi)一定要簡(jiǎn)化,否則就違背了初衷。

6、靜態(tài)內(nèi)部類(lèi)

1、概述:
上面提到當(dāng)內(nèi)部類(lèi)在成員變量的位置上,可被成員修飾符static修飾,這就是靜態(tài)內(nèi)部類(lèi)

2、使用前提:
某些情況下,會(huì)把內(nèi)部類(lèi)作為一個(gè)隱藏的類(lèi),不需要使用內(nèi)部類(lèi)引用外部類(lèi)的對(duì)象。因此,可以將外部類(lèi)聲明為static,就可以消除產(chǎn)生的引用。在內(nèi)部類(lèi)不需要訪問(wèn)外部類(lèi)對(duì)象的時(shí)候,應(yīng)該使用靜態(tài)內(nèi)部類(lèi)。

下面是示例:

/* 
靜態(tài)內(nèi)部類(lèi)測(cè)試:最大值最小值 
*/  
  
class ArrayAlg  
{  
    public static class Pair  
    {  
        private double first;  
        private double second;  
        //構(gòu)造函數(shù)獲得兩個(gè)值  
        public Pair(double first,double second)  
        {  
            this.first = first;  
            this.second = second;  
        }  
          
        //訪問(wèn)器:訪問(wèn)私有變量first和second  
        public double getFirst()  
        {  
            return first;  
        }  
        public double getSecond()  
        {  
            return second;  
        }  
    }  
    //定義一個(gè)獲得最大值和最小值的功能  
    public static Pair minmax(double[] arr)  
    {  
        //min和max要分別設(shè)置最大和最小,然后才能和數(shù)組中的數(shù)比較  
        double min = Double.MAX_VALUE;  
        double max = Double.MIN_VALUE;  
  
        for (int i=0;i<arr.length;i++)  
        {  
            if (min > arr[i])  
                min = arr[i];  
            if (max < arr[i])  
                max = arr[i];  
        }  
        return new Pair(min,max);  
    }  
}  
  
class ArrayZDemo  
{  
    public static void main(String[] args)   
    {  
        double[] d = new double[20];  
        for (int i=0;i<d.length;i++)  
            d[i] = 100*Math.random();  
        ArrayAlg.Pair p = ArrayAlg.minmax(d);  
        System.out.println("min = " + p.getFirst());  
        System.out.println("max = " + p.getSecond());  
    }  
}

練習(xí):

interface Inter  
{  
    void method();  
}  
class Test  
{  
    //補(bǔ)足代碼,用匿名內(nèi)部類(lèi)  
}  
class NInnerClassTest  
{  
    public static void main(String[] args)   
    {  
        Test.function().method();  
    }  
}

對(duì)于Test.function().method();的理解:
Test.function():類(lèi)中有一個(gè)靜態(tài)的方法function。
method():function這個(gè)方法運(yùn)算后的結(jié)果是一個(gè)對(duì)象,而且是一個(gè)Inter類(lèi)型的對(duì)象。
--->因?yàn)橹挥惺荌nter類(lèi)型的對(duì)象,才可以調(diào)用method方法。
結(jié)果:

interface Inter  
{  
    void method();  
}  
  
class Test  
{  
    public static Inter function()  
    {  
        return new Inter()  
        {  
            public void method()  
            {  
                System.out.println("使用靜態(tài)");  
            }  
        };  
    }  
}  
//測(cè)試結(jié)果  
class NInnerClassTest  
{  
    public static void main(String[] args)   
    {  
        Test.function().method();  
    }  
}

匿名內(nèi)部類(lèi)的應(yīng)用:

class NInnerClassTest  
{  
    public static void main(String[] args)   
    {  
        show(new Inter()  
        {  
            public void method()  
            {  
                System.out.println("method show run");  
            }  
     
  
    }  
    public static void show(Inter a)  
    {  
        a.method();  
    }  
}

三、多態(tài)

多態(tài):可理解為事物存在的多種體現(xiàn)形態(tài)。又稱為動(dòng)態(tài)綁定,是java的核心機(jī)制之一。

理解:多態(tài)是在運(yùn)行期間,判斷引用實(shí)際類(lèi)型,根據(jù)實(shí)際類(lèi)型調(diào)用相應(yīng)的方法。
比如說(shuō)人又男女之分,動(dòng)物有貓、狗等之分

1、多態(tài)的體現(xiàn)形式:

1、父類(lèi)的引用指向了自己子類(lèi)的對(duì)象。

2、父類(lèi)的引用可接收子類(lèi)的對(duì)象。

比如說(shuō)Person p = new Student();中p指向了Student中的一個(gè)對(duì)象。
如圖:

Paste_Image.png

2、多態(tài)的前提:

1、必須是類(lèi)與類(lèi)之間的關(guān)系,如繼承和實(shí)現(xiàn)關(guān)系

2、要存在覆蓋的操作,父類(lèi)中必須由方法被子類(lèi)覆蓋,即重寫(xiě)

3、有父類(lèi)引用指向子類(lèi)對(duì)象

3、多態(tài)的利弊:

1、好處:大大提高了程序的擴(kuò)展性

2、弊端:雖然提高擴(kuò)展性,但是只能使用父類(lèi)的引用訪問(wèn)父類(lèi)中的成員,不可預(yù)先使用子類(lèi)。這是由于,子類(lèi)在父類(lèi)之后加載,此時(shí)還沒(méi)有子類(lèi)被加載。

在下面的代碼中,Animal a = new Cat();是一種“類(lèi)型提升,向上轉(zhuǎn)型”的過(guò)程。如果要實(shí)現(xiàn)Cat中的其他Animal沒(méi)有的功能,那么就需要強(qiáng)制將父類(lèi)的引用轉(zhuǎn)換成子類(lèi)類(lèi)型,然后再調(diào)用子類(lèi)的方法:

Cat c = (Cat) a;
c.catchM();

注意:
1、一定不能將父類(lèi)的對(duì)象轉(zhuǎn)換成子類(lèi)類(lèi)型。
2、可轉(zhuǎn)換的:父類(lèi)引用指向自己子類(lèi)的對(duì)象時(shí),該引用可被提升,也可被強(qiáng)制轉(zhuǎn)化。
3、多態(tài)自始至終均為子類(lèi)對(duì)象在做變化。

例如:

/** 
分析: 
    1、定義一個(gè)動(dòng)物類(lèi)這個(gè)超類(lèi)類(lèi)型(抽象),含有一個(gè)吃飯的抽象方法 
    2、定義子類(lèi)(如貓,狗等),含有自己的方法(貓:抓老鼠;狗:看家) 
    3、用多態(tài)實(shí)現(xiàn)子類(lèi)方法 
*/  
abstract class Animal   //抽象類(lèi)  
{  
    abstract void eat();  
}  
  
class Cat extends Animal  //Cat繼承父類(lèi)Animal  
{     
    //復(fù)寫(xiě)父類(lèi)的抽象方法  
    void eat(){  
  
        System.out.println("吃魚(yú)");  
    }  
  
    //定義自身功能  
    void catchM(){  
  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal  
{  
      
    //復(fù)寫(xiě)父類(lèi)的抽象方法  
    void eat(){  
        System.out.println("啃骨頭");  
    }  
    //定義自身功能  
    void kanJ(){  
        System.out.println("看家");  
    }  
}  
class DuoTaiTest  
{  
    public static void main(String[] args)  
    {  
        Animal a1 = new Cat();//多態(tài)運(yùn)用a2.eat();  
        Dosome(a1);  
        Cat c = (Cat)a1;  
        Dosome(c);  
        c.catchM();  
        Animal a2 = new Dog();  
        a2.eat();  
        Dosome(a2);  
        Dog d = (Dog)a2;  
        Dosome(d);  
        d.kanJ();  
    }  
    public static void Dosome(Animal a)  
    {  
        a.eat();  
    }  
}

4、多態(tài)的特點(diǎn):

1、在多態(tài)中成員函數(shù)的特點(diǎn):

·編譯時(shí)期:參閱引用型變量所屬的類(lèi)中,是否有調(diào)用的方法,如果有,則編譯通過(guò),若沒(méi)有,則編譯失敗。如上面的代碼中,引用型變量a是父類(lèi)Animal的類(lèi)型,其中有eat 的方法,所以沒(méi)問(wèn)題,但是如果調(diào)用a.catchM()就會(huì)編譯失敗。

·運(yùn)行時(shí)期:參與對(duì)象所屬的類(lèi)中,是否有調(diào)用的方法。如引用型變量c是所屬于Cat類(lèi)型的,可以調(diào)用c.catchM()

總結(jié):成員函數(shù)在多態(tài)調(diào)用時(shí),編譯看左邊,運(yùn)行看右邊。子類(lèi)中局部有變量就訪問(wèn)局部的,沒(méi)有就訪問(wèn)成員的變量,成員中沒(méi)有的就在父類(lèi)中找;如果父類(lèi)中沒(méi)有,編譯失敗。

2、在多態(tài)中成員變量(和靜態(tài)成員)的特點(diǎn):
·無(wú)論編譯和運(yùn)行,都參考左邊的,即引用型變量所屬的類(lèi)型。也就是說(shuō)父類(lèi)中有自己的變量,則先找父類(lèi)自己的成員。

·非靜態(tài)有重寫(xiě)的特點(diǎn),靜態(tài)一般不被重寫(xiě)。

原因:因?yàn)楫?dāng)調(diào)用靜態(tài)方法時(shí),只要建立子類(lèi)對(duì)象,父類(lèi)與子類(lèi)中的靜態(tài)方法都會(huì)隨之加載入內(nèi)存,是不需要調(diào)用就可直接用類(lèi)名.方法名的,而不需要對(duì)象。只要引用還存在,就看引用變量的類(lèi)型。

class Father  
{  
    int num = 3;  
    void method1()  
    {  
        System.out.println("father-1");  
    }  
      
    void method2()  
    {  
        System.out.println("father-2");  
    }  
      
    static void method4()  
    {  
        System.out.println("father-4");  
    }  
}  
  
class Son extends Father  
{  
    int num = 5;  
    void method1()  
    {  
        System.out.println("son-1");  
    }  
      
    void method3()  
    {  
        System.out.println("son-3");  
    }  
      
    static void method4()  
    {  
        System.out.println("son-4");  
    }  
}  
class DuotaiYYDemo  
{  
    public static void main(String[] args)   
    {  
        Father f = new Son();  
        Son s = new Son();  
        f.method1();//son-1  
        s.method1();//son-1  
        f.method2();//father-2  
        s.method3();//son-3  
        //f.method3();報(bào)錯(cuò)  
        f.method4();//father-4  
        s.method4();//son-4  
        System.out.println(f.num);//3  
        System.out.println(s.num);//5  
    }  
}

注:
如果在每次調(diào)用方法時(shí)都要進(jìn)行搜索的話,效率是非常低的。因此,JVM虛擬機(jī)會(huì)預(yù)先為每個(gè)類(lèi)在方法區(qū)創(chuàng)建一個(gè)方法表,列出了所有方法的參數(shù)和實(shí)際調(diào)用的方法。因此,在真正調(diào)用的時(shí)候,虛擬機(jī)只查找這個(gè)方法表就可行了。而且,方法區(qū)中會(huì)有靜態(tài)方法區(qū)和非靜態(tài)方法區(qū)之分(如圖)。其中像this和super這兩個(gè)關(guān)鍵字存在于非靜態(tài)方法區(qū)中,而被static修飾的,存在于靜態(tài)方法區(qū)中。

5、多態(tài)的應(yīng)用

1、定義好工具類(lèi),即將共同行為封裝在一個(gè)類(lèi)中。
2、對(duì)類(lèi)型進(jìn)行抽取,---->多態(tài)的產(chǎn)生。
3、操作同一個(gè)大類(lèi)型,對(duì)其中的小類(lèi)型均可操作

四、包

1、包概述:

java中的包就相當(dāng)于系統(tǒng)中的文件夾。
當(dāng)我們需要將各個(gè)類(lèi)分門(mén)別類(lèi)的存放的時(shí)候,或者含有不同功能的相同類(lèi)名的時(shí)候,需要用到包(package)。包可以將相關(guān)的類(lèi)放在一起,并可以將兩個(gè)或多個(gè)相同文件名的文件放在不同包中。

2、包的作用:

1、為避免多個(gè)類(lèi)重名的情況,如果出現(xiàn)兩個(gè)相同名字的類(lèi),可通過(guò)包將兩者區(qū)分,從而避免沖突
2、對(duì)類(lèi)文件進(jìn)行分類(lèi)管理,可以將相關(guān)的一些類(lèi)放在同一個(gè)包中。
3、給類(lèi)提供多層命名空間,如a包中的Demo.class文件即packagea.Demo.class

3、規(guī)則:

1、寫(xiě)在程序的第一行,因?yàn)槭前碌念?lèi),先有包了,才能將類(lèi)放進(jìn)去。
2、類(lèi)名的全稱:包名.類(lèi)名
3、包是一種封裝形式,是java封裝性的體現(xiàn)。

4、包與包之間的訪問(wèn):

1、要訪問(wèn)其他包中的類(lèi),需要定義類(lèi)名的全名:包名.類(lèi)名
2、如果包不再當(dāng)前目錄下,需要設(shè)置classpath,告知虛擬機(jī)包的執(zhí)行路徑
3、有了包,范圍變大,一個(gè)包中的類(lèi)要被訪問(wèn),必須有足夠大的權(quán)限。

注:
a.包與包間進(jìn)行訪問(wèn),被訪問(wèn)的包中的類(lèi)以及類(lèi)中的成員,需要public修飾。
b.不同包中的子類(lèi)還可直接訪問(wèn)父類(lèi)中被protected權(quán)限修飾的成員。包與包間可使用的權(quán)限有兩種:public和protected。
c.其中protected只用于覆蓋。

示例:

package cn.conpany.test;  
  
public class FatherDemo {  
    //父類(lèi)受保護(hù)方法  
    protected void show(){  
        System.out.println("protected father");  
    }  
    //父類(lèi)公有方法  
    public void fuc(){  
        System.out.println("public father");  
    }  
}
package cn.conpany.test.array;  
  
public class SonDemo extends cn.conpany.test.FatherDemo{  
    public void showSon(){  
        //自定義方法  
        System.out.println("son show");  
        //調(diào)用父類(lèi)方法  
        show();  
    }  
}
package cn.conpany.test.object;  
  
import cn.conpany.test.FatherDemo;  
import cn.conpany.test.array.SonDemo;  
  
public class PackageDemo {  
  
    public static void main(String[] args) {  
        //創(chuàng)建子父類(lèi)對(duì)象  
        FatherDemo f = new FatherDemo();  
        SonDemo s = new SonDemo();  
        //類(lèi)PackageDemo可訪問(wèn)其他類(lèi)公有方法  
        f.fuc();//public father  
        //訪問(wèn)其他類(lèi)受保護(hù)方法,不可  
        //f.show();//報(bào)錯(cuò)  
        s.fuc();//public father  
        //子類(lèi)可訪問(wèn)父類(lèi)受保護(hù)方法  
        s.showSon();/*son show 
                    protected father*/  
    }  
}

5、包的導(dǎo)入:

1、為了簡(jiǎn)化類(lèi)名的書(shū)寫(xiě),使用import導(dǎo)入
如:import packa.packb.packc.*; 是將包packa下的包packb下的包packc下的所有類(lèi)導(dǎo)入進(jìn)來(lái)。
注:如果需要packb下還有類(lèi)需要導(dǎo)入,則還需在導(dǎo)入,
如: import packa.packb.*;

2、注意事項(xiàng):
a.兩個(gè)包中有相同類(lèi)名文件,當(dāng)再統(tǒng)一類(lèi)中創(chuàng)建對(duì)象時(shí),需要加上包名.類(lèi)名創(chuàng)建。
b.建議定義包名不要重復(fù),可以使用URL來(lái)定義,因?yàn)閁RL是唯一的。
如:
www.itcast.com---->package cn.itcast.Demo以及cn.itcast.Test

Java中各個(gè)主要包的作用(javax開(kāi)頭的都是擴(kuò)展包)
java.util是JAVA的utility工具包,包含一些使用工具類(lèi),如定義系統(tǒng)特性、使用與日期日歷相關(guān)的函數(shù)等的類(lèi)
java.lang是JAVA的language核心語(yǔ)言包;如String、Math、Integer、System、Thread,提供常用的功能。特殊之處是不需要導(dǎo)入,是作為默認(rèn)導(dǎo)入的包。
java.awt是JAVA的abstractwindow toolkit,抽象窗口工具包;包含了構(gòu)成抽象窗口共具體的多個(gè)類(lèi),這些類(lèi)用于構(gòu)建和管理應(yīng)用程序的圖形用戶(GUI)。
java.applet是創(chuàng)建APPLET的必須包;包含applet運(yùn)行時(shí)所需要的一些類(lèi)。
java.net是JAVA有關(guān)網(wǎng)絡(luò)操作的包。
java.io是JAVA的輸入輸出流的包。
java.sql是JAVA的數(shù)據(jù)庫(kù)操作包。
javax.swing是新的界面包。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一:java概述:1,JDK:Java Development Kit,java的開(kāi)發(fā)和運(yùn)行環(huán)境,java的開(kāi)發(fā)工...
    ZaneInTheSun閱讀 2,823評(píng)論 0 11
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,853評(píng)論 18 399
  • 一、多態(tài) 1. 概述 理解:多態(tài)可以理解為事物存在的多種體(表)現(xiàn)形態(tài)。例如:動(dòng)物中的貓和狗。貓這個(gè)對(duì)象對(duì)應(yīng)的是貓...
    陳凱冰閱讀 382評(píng)論 0 1
  • Unsupported major.minor version 51.0解決辦法 具體步驟 解決:項(xiàng)目------...
    晨星資源閱讀 902評(píng)論 0 1
  • 1.import static是Java 5增加的功能,就是將Import類(lèi)中的靜態(tài)方法,可以作為本類(lèi)的靜態(tài)方法來(lái)...
    XLsn0w閱讀 1,442評(píng)論 0 2

友情鏈接更多精彩內(nèi)容