国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java 抽象類、接口、內部類

miguel.jiang / 638人閱讀

摘要:即使抽象類里不包含抽象方法,這個抽象類也不能創建實例抽象類可以包含成員變量方法普通方法和抽象方法構造器初始化塊內部類接口枚舉種成分。

抽象類

當編寫一個類時,常常會為該類定義一些方法,這些方法用以描述該類的行為方式,那么這些方法都有具體的方法體。但在某些情況下,某個父類只是知道其子類應該包含怎樣的方法,但無法準確地知道這些子類如何實現這些方法。使用抽象方法即可滿足該要求:抽象方法是只有方法簽名,沒有方法實現的方法

抽象方法和抽象類

抽象方法和抽象類必須使用abstract修飾符來定義,有抽象方法的類只能被定義成抽象類,抽象類里可以沒有抽象方法

抽象方法和抽象類的規則如下:

抽象類必須使用abstract修飾符來修飾,抽象方法也必須使用abstract修飾符來修飾,抽象方法不能有方法體

抽象類不能被實例化,無法使用new關鍵字來調用抽象類的構造器創建抽象類的實例。即使抽象類里不包含抽象方法,這個抽象類也不能創建實例

抽象類可以包含成員變量、方法(普通方法和抽象方法)、構造器、初始化塊、內部類(接口、枚舉)5種成分。抽象類的構造器不能用于創建實例,主要是用于被其子類調用

含有抽象方法的類(包括直接定義了一個抽象方法;或繼承了一個抽象父類,但沒有完全實現父類包含的抽象方法;或實現了一個接口,但沒有完全實現接口包含的抽象方法三種情況)只能被定義成抽象類

抽象類與空方法體的方法:public abstract void test(); public void test(){};

抽象類不能用于創建實例,只能當作父類被其他子類繼承

當使用abstract修飾類時,表明這個類只能被繼承;當使用abstract修飾方法時,表明這個方法必須由子類提供實現(即重寫)。而final修飾的類不能被繼承,final修飾的方法不能被重寫。因此final和abstract永遠不能同時使用

abstract不能用于修飾成員變量,不能用于修飾局部變量,即沒有抽象變量、沒有抽象成員變量等說法;abstract也不能用于修飾構造器,沒有抽象構造器,抽象類里定義的構造器只能是普通構造器

當使用static修飾一個方法時,表明這個方法屬于該類本身,即通過類就可調用該方法,但如果該方法被定義成抽象方法,則將導致通過該類來調用該方法時出現錯誤(調用了一個沒有方法體的方法肯定會引起錯誤)。因此static和abstract不能同時修飾某個方法,即沒有所謂的類抽象方法。但static和abstract可以同時修飾內部類

abstract關鍵字修飾的方法必須被其子類重寫才有意義,否則這個方法將永遠不會有方法體,因此abstract方法不能定義為private訪問權限,即private和abstract不能同時修飾方法

抽象類的方法

抽象類體現的就是一種模板模式的設計,抽象類作為多個子類的通用模板,子類在抽象類的基礎上進行擴展、改造,但子類總體上會大致保留抽象類的行為方式。

模板模式在面向對象的軟件中很常用,其原理簡單,實現也很簡單。下面是使用模板模式的一些簡單規則:

抽象父類可以只定義需要使用的某些方法,把不能實現的部分抽象成抽象方法,留給其子類去實現

父類中可能包含需要調用其他系列方法的方法,這些被調用方法既可以由父類實現,也可以由其子類實現。父類里提供的方法只是定義了一個通用算法,其實現也許并不完全由自身實現,而必須依賴于其子類的輔助

Java8改進的接口

抽象類是從多個類中抽象出來的模板,如果將這種抽象進行得更徹底,則可以提煉出一種更加特殊的“抽象類”——接口(interface),接口里不能包含普通方法,接口里的所有方法都是抽象方法。Java8對接口進行了改進,允許在接口中定義默認方法,默認方法可以提供方法實現

接口的概念

接口定義的是多個類共同的公共行為規范,這些行為是與外部交流的通道,這意味著接口里通常是定義一組公用方法。

Java8中接口的定義
[修飾符]    interface    接口名    extends    父接口1,父接口2...
{
    零個到多個常量定義...
    零個到多個抽象方法定義...
    零個到多個內部類、接口、枚舉定義...
    零個到多個默認方法或類方法定義...
}

修飾符可以是public或者省略,如果省略了public訪問控制符,則默認采用包權限訪問控制符,即只有在相同包結構下才可以訪問該接口

接口名應與類名采用相同的命名規則,即如果僅從語法角度來看,接口名只要是合法的標識符即可;如果要遵守Java可讀性規范,則接口名應由多個有意義的單詞連綴而成,每個單詞首字母大寫,單詞與單詞之間無須任何分隔符。接口名通常能夠使用形容詞。

一個接口可以有多個直接父接口,但接口只能繼承接口,不能繼承類。

由于接口定義的是一種規范,因此接口里不能包含構造器和初始化塊定義。接口里可以包含成員變量(只能是靜態常量)、方法(只能是抽象實例方法、類方法或默認方法)、內部類(包括內部接口、枚舉)定義

接口里定義的是多個類共同的公共行為規范,因此接口里的所有成員,包括常量、方法、內部類和內部枚舉都是public訪問權限。定義接口成員時,可以省略訪問控制修飾符,如果指定訪問控制修飾符,則只能使用public訪問控制修飾符。

對于接口里定義的靜態常量而言,它們是接口相關的,因此系統會自動為這些成員變量增加static和final兩個修飾符。也就是說,在接口中定義成員變量時,不管是否使用public static final修飾符,接口里的成員變量總是使用這三個修飾符來修飾。而且接口里沒有構造器和初始化塊,因此接口里定義的成員變量只能在定義時指定默認值

接口里定義的內部類、內部接口、內部枚舉默認都采用public static兩個修飾符,不管定義時是否指定這兩個修飾符,系統都會自動使用public static對它們進行修飾

接口里定義的方法只能是抽象方法、類方法或默認方法,因此如果不是定義默認方法,系統將自動為普通方法增加abstract修飾符;定義接口里的普通方法時不管是否使用public abstract修飾符,接口里的普通方法總是public abstract來修飾。接口里的普通方法不能有方法實現(方法體);但類方法、默認(default)方法都必須有方法實現(方法體)

public interface Output 
{
    //接口里定義的成員變量只能是常量
    int MAX_CACHE_LINE = 50;
    //接口里定義的普通方法只能是public的抽象方法
    void out();
    void getData(String msg);
    //在接口里定義默認方法,需要使用default修飾
    default void print(String... msgs)
    {
        for (String msg : msgs) {
            System.out.println(msg);
        }
    }
    //在接口中定義默認方法,需要使用default修飾
    default void test()
    {
        System.out.println("默認的test()方法");
    }
    //在接口里定義類方法,需要使用static修飾
    static String staticTest()
    {
        return "接口里的類方法";
    }
}

Java8允許在接口中定義類方法,類方法必須使用static修飾,該方法不能使用default修飾,無論程序是否指定,類方法總是使用public修飾——如果開發者沒有指定public,系統會自動為類方法添加public修飾符。類方法可以直接使用接口來調用。

接口的繼承

接口的繼承和類繼承不一樣,接口完全支持多繼承,即一個接口可以有多個直接父接口。和類繼承相似,子接口擴展某個父接口,將會獲得父接口里定義的所有抽象方法、常量。
一個接口繼承多個父接口時,多個父接口排在extends關鍵字之后,多個父接口之間以英文逗號(,)隔開。

使用接口

接口不能用于創建實例,但接口可以用于聲明引用類型變量。當使用接口來聲明引用類型變量時,這個引用類型變量必須引用到其實現類的對象。除此之外,接口的主要用途就是被實現類實現。

定義變量,也可用于進行強制類型轉換

調用接口中定義的常量

被其他類實現
一個類可以實現一個或多個接口,繼承使用extends關鍵字,實現則使用implements關鍵字。

[修飾符]    class    類名    extends    父類    implements    接口1,接口2
{
    類體部分
}

一個類實現了一個或多個接口之后,這個類必須完全實現這些接口里所定義的全部抽象方法(也就是重寫這些抽象方法);否則,該類將保留從父接口那里繼承到的抽象方法,該類也必須定義成抽象類。
一個類實現某個接口時,該類將會獲得接口中定義的常量(成員變量)、方法等,因此可以把實現接口理解為一種特殊的繼承,相當于實現類繼承了一個徹底抽象的類(相當于除了默認方法外,所有方法都是抽象方法的類)。

//定義一個Product接口
interface Product
{
    int getProduceTime();
}
//讓Printer類實現Output和Product接口
public class Printer implements Output, Product
{
    private String[] printData = new String[MAX_CACHE_LINE];
    //用以記錄當前需打印的作業數
    private int dataNum = 0;
    public void out() 
    {
        //只要有作業,就繼續打印
        while(dataNum >0)
        {
            System.out.println("打印機打?。?+printData[0]);
            //把作業隊列整體前移一位,并將剩下的作業數減1
            System.arraycopy(printData, 1, printData, 0, --dataNum);
        }
    }
    public void getData(String msg)
    {
        if (dataNum >= MAX_CACHE_LINE) 
        {
            System.out.println("輸出隊列已滿,添加失敗");
        }
        else {
            //把打印數據添加到隊列里,已保存數據的數量加1
            printData[dataNum++] = msg;
        }
    }
    public int getProduceTime() 
    {
        return 45;
    }
    public static void main(String[] args) 
    {
        //創建一個Printer對象,當成Output使用
        Output o = new Printer();
        o.getData("Pringles品客薯片");
        o.getData("酸乳酪洋蔥味薯片");
        o.out();
        o.getData("樂天熊仔餅");
        o.getData("小熊餅");    
        o.out();
        //調用Output接口中定義的默認方法
        o.print("大天狗","妖刀姬","一目連");
        o.test();
        //創建一個Printer對象,當成Product使用
        Product p = new Printer();
        System.out.println(p.getProduceTime());
        //所有接口類型的引用變量都可直接賦給Object類型的變量
        Object object = p;
    }
}

實現接口方法時,必須使用public訪問控制修飾符,因為接口里的方法都是public的,而子類(相當于實現類)重寫父類方法時訪問權限只能更大或者相等,所以實現類實現接口里的方法時只能使用public訪問權限

接口和抽象類

接口和抽象類很像,它們都具有如下特征。

接口和抽象類都不能被實例化,它們都位于繼承樹的頂端,用于被其他類實現和繼承。

接口和抽象類都可以包含抽象方法,實現接口或繼承抽象類的普通子類都必須實現這些抽象方法。

接口作為系統與外界交互的窗口,接口體現的是一種規范
抽象類作為系統中多個子類的共同父類,它所體現的是一種模板式設計

接口和抽象類在用法上的差別:

接口里只能包含抽象方法和默認方法,不能為普通方法提供方法實現;抽象類則完全可以包含普通方法

接口里不能定義靜態方法;抽象類里可以定義靜態方法

接口里只能定義靜態常量,不能定義普通成員變量;抽象類里則既可以定義普通成員變量,也可以定義靜態常量

接口里不包含構造器;抽象類里可以包含構造器,抽象類里的構造器并不是用于創建對象,而是讓其子類調用這些構造器來完成屬于抽象類的初始化操作

接口里不能包含初始化塊,但抽象類則完全包含初始化塊

一個類最多只能有一個直接父類,包括抽象類;但一個類可以直接實現多個接口,通過實現多個接口可以彌補Java單繼承的不足。

面向接口編程

接口體現的是一種規范和實現分離的設計哲學,充分利用接口可以極好地降低程序各模塊之間的耦合,從而提高系統的可擴展性和可維護性

1.簡單工廠模式

所謂設計模式,就是對經常出現的軟件設計問題的成熟解決方案。

Computer類組合一個Output類型的對象,將Computer類與Printer類完全分離。Computer對象實際組合的是Printer對象還是BetterPrinter對象,對Computer而言完全透明。當Printer對象切換到BetterPrinter對象時,系統完全不受影響

public class Computer 
{
    private Output output;
    public Computer(Output output)
    {
        this.output = output;
    }
    // 定義一個模擬獲取字符串輸入的方法
    public void keyIn(String msg) 
    {
        output.getData(msg);
    }
    //定義一個模擬打印的方法
    public void print() 
    {
        output.out();
    }
}

Computer類已經完全與Printer類分離,只是與Output接口耦合。Computer不再負責創建Output對象,系統提供一個Output工廠來負責生成Output對象

public class OutputFactory 
{
    public Output getOutput() 
    {
        return new Printer();
    }
    public static void main(String[] args)
    {
        OutputFactory outputFactory = new OutputFactory();
        Computer computer = new Computer(outputFactory.getOutput());
        computer.keyIn("眼前的黑是什么黑,你說的白是什么白");
        computer.keyIn("人們說的天空藍,是我記憶中那團白云背后的藍天");
        computer.print();
    }
}

在該OutputFactory類中包含了一個getOutput()方法,該方法返回一個Output實現類的實例,該方法負責創建Output實例,具體創建哪一個實現類的對象由該方法決定(具體由該方法中的粗體部分控制,當然也可以增加更復雜的控制邏輯)。如果系統需要將Printer改為BetterPrinter實現類,只需讓BetterPrinter實現Output接口,并改變OutputFactory類中的getOutput()方法即可。

public class BetterPrinter implements Output
{
    private String[] printData = new String[MAX_CACHE_LINE];
    //用以記錄當前需打印的作業數
    private int dataNum = 0;
    public void out() 
    {
        //只要有作業,就繼續打印
        while (dataNum > 0) 
        {
            System.out.println("高速打印機正在打?。?+printData[0]);
            //把作業隊列整體前移一位,并將剩下的作業數減1
            System.arraycopy(printData, 1, printData, 0, --dataNum);
        }
    }
    public void getData(String msg)
    {
        if (dataNum >= MAX_CACHE_LINE * 2) 
        {
            System.out.println("輸出隊列已滿,添加失敗");
        }
        else {
            //把打印數據添加到隊列里,已保存數據的數量加1
            printData[dataNum++] = msg;
        }
    }
}

上面的BetterPrinter類也實現了Output接口,因此也可當成Output對象使用,于是只要把OutputFactory工廠類的getOutput()方法中部分改為如下代碼:

return new BetterPrinter();

通過這種方式,即可把所有生成Output對象的邏輯集中在OutputFactory工廠類中管理,而所有需要使用Output對象的類只需與Output接口耦合,而不是與具體的實現類耦合。即使系統中有很多類使用了Printer對象;只需OutputFactory類的getOutput()方法生成的Output對象是BetterPrinter對象,則它們全部都會改為使用BetterPrinter對象,而所有程序無須修改,只需要修改OutputFactory工廠類的getOutput()方法實現即可

2.命令模式

某個方法需要完成某一個行為,但這個行為的具體實現無法確定,必須等到執行該方法時才可以確定。具體一點:假設有個方法需要遍歷某個數組元素,但無法確定在遍歷數組元素時如何處理這些元素,需要在調用該方法時指定具體的處理行為。

使用一個Command接口來定義一個方法,用這個方法來封裝“處理行為”,但這個方法沒有方法體——因為現在還無法確定這個處理行為。

public interface Command 
{
    //接口里定義的process方法用于封裝“處理行為”
    void process(int[] target);
}

下面是需要處理數組的處理類,在這個處理中包含了一個process()方法,這個方法無法確定處理數組的處理行為,所以定義該方法時使用了一個Command參數,這個Command參數負責對數組的處理行為。

public class ProcessArray 
{
    public void process(int[] target, Command cmd) 
    {
        cmd.process(target);
    }
}

通過一個Command接口,就實現了讓ProcessArray類和具體“處理行為”的分離,程序使用Command接口代表了對數組的處理行為。Command接口也沒有提供真正的處理,只要等到調用ProcessArray對象的process()方法時,才真正傳入一個Command對象,才確定對數組的處理行為。

public class CommandTest 
{
    public static void main(String[] args) 
    {
        ProcessArray pa = new ProcessArray();
        int[] target = {3, -4, 6, 4};
        //第一次處理數組,具體處理行為取決于PrintCommand
        pa.process(target, new PrinterCommand());
        System.out.println("-------");
        pa.process(target, new AddCommand());
    }
}
public class PrinterCommand implements Command 
{
    public void process(int[] target) 
    {
        for(int tmp :target)
        {
            System.out.println("迭代輸出目標數組的元素:"+tmp);
        }
    }
}
public class AddCommand implements Command 
{
    public void process(int[] target) 
    {
        int sum = 0;
        for (int tmp : target) 
        {
            sum += tmp;
        }
        System.out.println("數組元素的總和是"+sum);
    }
}
內部類

一個類放在另一個類的內部定義,這個定義在其他類內部的類就被稱為內部類,包含內部類的類也被稱為外部類。內部類主要有如下作用:

內部類提供了更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中的其他類訪問該類。

內部類成員可以直接訪問外部類的私有數據,因為內部類被當成其外部類成員,同一個類的成員之間可以互相訪問。但外部類不能訪問內部類的實現細節,例如內部類的成員變量。

匿名內部類適合用于創建那些僅需要一次使用的類。

內部類與外部類的區別:

內部類比外部類可以多使用三個修飾符:private、protected、static——外部類不可以使用這三個修飾符。

非靜態內部類不能擁有靜態成員。

非靜態內部類

定義內部類非常簡單,只要把一個類放在另一個類的內部定義即可。此次的“類內部”包括類中的任何位置,甚至在方法中也可以定義內部類(方法里定義的內部類被稱為局部內部類)。內部類定義語法格式如下:

public class OuterClass
{
    //此處可以定義內部類
}

大部分時候,內部類都被作為成員內部類定義,而不是作為局部內部類。成員內部類是一種與成員變量、方法、構造器和初始化塊相似的類成員:局部內部類和匿名內部類則不是類成員。

成員內部類分為兩種:靜態內部類和非靜態內部類,使用static修飾的成員內部類

外部類的上一級程序單元是包,所以它只有2個作用域:同一包內和任何位置。因此只需2種訪問權限:包訪問和公開訪問權限,正好對應省略訪問控制符和public訪問控制符。省略訪問控制符是包訪問權限,即同一包中的其他類可以訪問省略訪問控制符的成員。因此如果一個外部類不使用任何訪問控制符,則只能被同一個包中其他類訪問。而內部類的上一級程序單元是外部類,他就具有4個作用域:同一個類、同一個包、父子類和任何位置,因此可以使用4種訪問控制權限。

成員內部類(包括靜態內部類、非靜態內部類)的class文件總是這種形式:OuterClass$InnerClass.class

在非靜態內部類里可以直接訪問外部類的private成員。因為在非靜態內部類對象里,保存了一個它所寄生的外部類對象的引用(當調用非靜態內部類的實例方法時,必須有一個非靜態內部類實例,非靜態內部類實例必須寄生在外部類實例里)

當在非靜態內部類的方法訪問某個變量時,系統優秀在該方法內查找是否存在該名字的局部變量,如果存在就使用該變量;如果不存在,則到該方法所在的內部類中查找是否存在該名字的成員變量,如果存在則使用該成員變量;如果不存在,則在該內部類所在的外部類中查找是否存在該名字的成員變量,如果存在則使用該成員變量;如果依然不存在,系統將出現編譯錯誤;提示找不到該變量。
因此,如果外部類成員變量、內部類成員變量與內部類里方法的局部變量同名,則可通過使用this、外部類類名.this作為限定來區分。

public class DiscernVariable 
{
    private String prop = "外部類的實例變量";
    private class InClass
    {
        private String prop = "內部類的實例變量";
        public void info()
        {
            String prop = "局部變量";
            // 通過外部類類名.this.varName 訪問外部類實例變量
            System.out.println("外部類的實例變量值:"+DiscernVariable.this.prop);
            // 通過this.varName 訪問內部類實例變量
            System.out.println("內部類的實例變量值:"+this.prop);
            // 直接訪問局部變量
            System.out.println("局部變量值:"+prop);
        }
    }
    public void test() 
    {
        InClass inClass = new InClass();
        inClass.info();
    }        
    public static void main(String[] args) 
    {
        new DiscernVariable().test();
    }
}

非靜態內部類的成員可以訪問外部類的private成員,但反過來就不成立了。非靜態內部類的成員只在非靜態內部類范圍內是可知的,并不能被外部類直接使用。如果外部類需要訪問非靜態內部類的成員,則必須顯式創建非靜態內部類對象來調用訪問其實例成員。

public class Outer 
{
    private int outProp = 9;
    class Inter
    {
        private int inProp = 5;
        public void accessOuterProp() 
        {
            //非靜態內部類可以直接訪問外部類的private成員變量
            System.out.println("外部類的outProp值:"+outProp);
        }
    }
    public void accessInterProp() 
    {
        //外部類不能直接訪問非靜態內部類的實例變量
        //下面代碼出現編譯錯誤
        //System.out.println("內部類的inProp值:"+inProp);
        //如需訪問內部類的實例變量,必須顯式創建內部類對象
        System.out.println("內部類的inProp值:"+new Inter().inProp);
    }
        public static void main(String[] args) 
    {
        //執行下面代碼,只創建了外部類對象,還未創建內部類對象
        Outer outer = new Outer();
        outer.accessInterProp();
    }
}

非靜態內部類對象必須寄生在外部類對象里,而外部類對象則不必一定有非靜態內部類對象寄生其中。簡單地講,如果存在一個非靜態內部類對象,則一定存在一個被他寄生的外部類對象。但外部類對象存在時,外部類對象里不一定寄生了非靜態內部類對象。因此外部類對象訪問非靜態內部類成員時,可能非靜態普通內部類對象根本不存在!而非靜態內部類對象訪問外部類成員時,外部類對象一定存在。

根據靜態成員不能訪問非靜態成員的規則,外部類的靜態成員、靜態代碼塊不能訪問非靜態內部類,包括不能使用非靜態內部類定義變量、創建實例等??傊辉试S在外部類的靜態成員中直接使用非靜態內部類。
Java不允許在非靜態內部類定義靜態成員。非靜態內部類里不能有靜態方法、靜態成員變量、靜態初始化塊。

靜態內部類

如果使用static來修飾一個內部類,則這個內部類就屬于外部類本身,而不屬于外部類的某個對象。因此使用static修飾的內部類被稱為類內部類,有的地方也稱為靜態內部類。

static關鍵字的作用是把類的成員變成類相關,而不是實例相關,即static修飾的成員屬于整個類,而不屬于單個對象。外部類的上一級程序單元是包,所以不可使用static修飾;而內部類的上一級程序單元是外部類,使用static修飾可以將內部類變成外部類相關,而不是外部類實例相關。因此static關鍵字不可修飾外部類,但可修飾內部類。

靜態內部類是外部類的類相關的,而不是外部類的對象相關的。也就是說,靜態內部類對象不是寄生在外部類的實例中,而是寄生在外部類的類本身中。當靜態內部類對象存在時,并不存在一個被它寄生的外部類對象,靜態內部類對象只持有外部類的類引用,沒有持有外部類對象的引用。如果允許靜態內部類的實例方法訪問外部類的實例成員,但找不到被寄生的外部類對象,這將引起錯誤。

靜態內部類是外部類的一個靜態成員,因此外部類的所有方法、所有初始化塊中可以使用靜態內部類來定義變量、創建對象等。
外部類依然不能直接訪問靜態內部類的成員,但可以使用靜態內部類的類名作為調用者來訪問靜態內部類的類成員,也可以使用靜態內部類對象作為調用者來訪問靜態內部類的實例成員。

使用內部類

1.在外部類使用內部類

可以直接通過內部類類名來定義變量,通過new調用內部類構造器來創建實例。
區別:不要在外部類的靜態成員(包括靜態方法和靜態初始化塊)中使用非靜態內部類,因為靜態成員不能訪問非靜態成員。

2.在外部類以外使用內部類

希望在外部類以外的地方訪問內部類(靜態、非靜態),則內部類不能使用private訪問控制權限,private修飾的內部類只能在外部類內部使用。
▲ 省略訪問控制符的內部類,只能被與外部類處于同一個包中的其他類訪問。
▲ 使用protected修飾的內部類,可被與外部類處于同一個包中的其他類和外部類的子類所訪問。
▲ 使用public修飾的內部類,可以在任何地方被訪問。

在外部類以外的地方定義內部類(包括靜態和非靜態)變量的語法格式如下:
OuterClass.InnerClass varName

在外部類以外的地方創建非靜態內部類實例的語法:
OuterInstance.new InnerConstructor()
class Out
{
    //定義一個內部類,不使用訪問控制符
    //即只與同一個包中的其他類可訪問該內部類
    class In
    {
        public In(String msg)
        {
            System.out.println(msg);
        }
    }
}
public class CreateInnerInstance 
{
    public static void main(String[] args) 
    {
        Out.In in = new Out().new In("瞅啥瞅");
        /*
         上面代碼可改為如下三行代碼
         使用OuterClass.InerClass的形式定義內部類變量
         Out.In in;
         創建外部類實例,非靜態內部類實例將寄生在該實例中
         Out out = new Out();
         通過外部類實例和new來調用內部類構造器創建非靜態內部類實例
         in = out.new In("瞅啥瞅");
         */
    }
}

定義一個子類繼承Out類的非靜態內部類In類

public class SubClass2 extends Out.In
{
    //顯式定義SubClass2的構造器
    public SubClass2(Out out)
    {
        //通過傳入的Out對象顯式調用In的構造器
        out.super("瞅你咋地");
    }
}

非靜態內部類In類的構造器必須使用外部類對象來調用,代碼中super代表調用In類的構造器,而out則代表外部類對象(上面的Out、In兩個類直接來自前一個CreateInnerInsatence.java)??梢钥闯鋈绻枰獎摻⊿ubClass2對象時,必須先創建一個Out對象。因為SubClass2是非靜態內部類In類的子類,非靜態內部類In對象里必須與一個對Out對象的引用,其子類SubClass2對象里也應該持有對Out對象的引用。當創建SubClass2對象時傳給該構造器的Out對象,就是SubClass2對象里Out對象引用所指向的對象。

非靜態內部類In對象和SubClass2對象都必須持有指向Outer對象的引用,區別是創建兩種對象時傳入Out對象的方式不同:當創建非靜態內部類In類的對象時,必須通過Outer對象來調用new關鍵字;當創建SubClass2類的對象時,必須使用Outer對象作為調用者來調用In類的構造器。

3.在外部類以外使用靜態內部類

在外部類以外的地方創建靜態內部類實例的語法:
new OutClass.InnerConstructor()

class StaticOut
{
    //定義一個靜態內部類,不使用訪問控制符
    //即同一個包中的其他類可訪問該內部類
    static class StaticIn
    {
        public StaticIn()
        {
            System.out.println("靜態內部類的構造器");
        }
    }
}

public class CreateStaticInnerInstance 
{
    public static void main(String[] args) 
    {
        StaticOut.StaticIn in = new StaticOut.StaticIn();
        /*
         上面代碼可改為如下兩行代碼
         使用OuterClass.InnerClass的形式定義內部類變量
         StaticOut.StaticIn in;
         通過new來調用內部構造器創建靜態內部類實例
         in = new StaticOut.StaticIn();
         */
    }
}

不管是靜態內部類還是非靜態內部類,它們聲明變量的語法完全一樣。區別只是在創建內部類對象時,靜態內部類只需使用外部類即可調用構造器,而非靜態內部類必須使用外部類對象來調用構造器。

使用靜態內部類比使用非靜態內部類要簡單很多,只要把外部類當成靜態內部類的包空間即可。因此當程序需要使用內部類時,應該優先考慮使用靜態內部類。

局部內部類

如果把一個內部類放在方法里定義,則這個內部類就是一個局部內部類,局部內部類僅在該方法里有效。由于局部內部類不能在外部類的方法以外的地方使用,因此局部內部類也不能使用訪問控制符和static修飾符修飾。

對于局部成員而言,不管是局部變量還是局部內部類,它們的上一級程序單元都是方法,而不是類,使用static修飾它們沒有任何意義。因此,所有的局部成員都不能使用static修飾。

Java8改進的匿名內部類

匿名內部類適合創建只需要一次使用的類。創建匿名內部類時會立即創建一個該類的實例,這個類定義立即消失,匿名內部類不能重復使用。

new 實現接口() | 父類構造器(實參列表)
{
    //匿名內部類的類體部分
}

關于匿名內部類規則

匿名內部類不能是抽象類,因為系統在創建匿名內部類時,會立即創建匿名內部類的對象。因此不允許將匿名內部類定義成抽象類

匿名內部類不能定義構造器。由于匿名內部類沒有類名,所以無法定義構造器,但匿名內部類可以定義初始化塊,可以通過實例初始化塊來完成構造器需要完成的事情。

最常用的創建匿名內部類的方式是需要創建某個接口類型的對象

interface Product
{
    public double getPrice();
    public String getName();
}
public class AnonymousTest 
{
    public void test(Product p) 
    {
        System.out.println("購買了一個"+p.getName()
                +",花掉了"+p.getPrice());
    }
    public static void main(String[] args) 
    {
        AnonymousTest ta = new AnonymousTest();
        //調用test()方法時,需要傳入一個Product參數
        //此處傳入其匿名實現類的實例
        ta.test(new Product() 
        {
            public double getPrice() 
            {
                return 567.8;
            }
            public String getName() 
            {
                return "AGP顯卡";
            }
        });
    }
}

上面程序中的AnonymousTest類定義了一個test()方法,該方法需要一個Product對象作為參數,但Product是接口,無法直接創建對象,因此此次創建一個Product接口實現類的對象傳入該方法

如果這個Product接口實現類只需使用一次,則采用上述方式,定義匿名內部類

當通過實現接口來創建匿名內部類時,匿名內部類也不能顯式創建構造器,因此匿名內部類只有一個隱式的無參數構造器,故new接口名后的括號里不能傳入參數值

如果通過繼承父類來創建匿名內部類時,匿名內部類將擁有和父類相似的構造器,此次的相似指的是擁有相同的形參列表

當創建匿名內部類時,必須實現接口或抽象父類里的所有抽象方法

abstract class Device
{
    private String name;
    public abstract double getPrice();
    public Device(){};
    public Device(String name) 
    {
        this.name =name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class AnonymousInner 
{
    public void test(Device d) 
    {
        System.out.println("購買了一個"+d.getName()+",花掉了"+d.getPrice());
    }
    public static void main(String[] args) 
    {
        AnonymousInner ai = new AnonymousInner();
        //調用有參數的構造器創建Device匿名內部類的對象
        ai.test(new Device("電子示波器") 
        {
            public double getPrice() {
                return 67.8;
            }
        });
    //調用無參數的構造器創建Device匿名內部類的對象
    Device d = new Device() 
    {
        //初始化塊
        {
            System.out.println("匿名內部類的初始化塊...");
        }
        //實現抽象方法
        public double getPrice() {
            return 56.2;
        }
        public String getName() 
        {
            return "鍵盤";
        }
    };
    ai.test(d);
    }
}

當創建Device為父類的匿名內部類時,既可以傳入參數,代表父類帶參數的構造器;也可以不傳入參數,代表調用父類無參數的構造器。

interface A
{
    void test();
}
public class ATest 
{
    public static void main(String[] args) 
    {
        int age = 8;
        A a = new A() 
        {
            public void test() 
            {
                //在Java 8以前下面語句將提示錯誤:age必須使用final修飾
                //從Java 8開始,匿名內部類、局部內部類允許訪問非final局部變量
                System.out.println(age);
            }
        };
        a.test();
    }
}

如果局部變量被匿名內部類訪問,那么局部變量相當于自動使用了final修飾。

默認方法

Java 8 新增了接口的默認方法。簡單說,默認方法就是接口可以有實現方法,而且不需要實現類去實現其方法,只需在方法名前面加個default關鍵字即可實現默認方法。

默認方法語法格式如下:

public interface PPAP 
{
    default void print()
    {
        System.out.println("I have an apple, I have a pen~");
    }
}

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/66284.html

相關文章

  • Java 面向對象

    摘要:自從轉到前段,差不多有一年時間沒有動了,今天老弟問我關于的面向對象,自己不敢直接回答了,也許自己真的應該去做相關的考究了。要有父類引用指向子類對象。外部類要訪問內部類的成員,必須要建立內部類的對象。 自從轉到前段,差不多有一年時間沒有動Java了,今天老弟問我關于Java的面向對象,自己不敢直接回答了,也許自己真的應該去做相關的考究了。然后在告訴他吧,記在這里當做一個筆記吧。  什么...

    cncoder 評論0 收藏0
  • java學習(三) —— 面對對象

    摘要:前言是面對對象的語言,因此有必要單獨紀錄下對象的各種定義和理解。面對對象基本概述概述是基于面向過程的變成思想,是對面向過程的一種封裝。面對對象開發就是不斷的創建對象,使用對象,指揮對象做事情。面對對象設計其實就是在管理和維護對象之間的關系。 前言 java是面對對象的語言,因此有必要單獨紀錄下對象的各種定義和理解。 面對對象,主要包括:面向對象思想,類與對象及其使用,對象的內存圖,成...

    褰辯話 評論0 收藏0
  • Java Lambda表達式

    摘要:表達式的主要作用就是代替匿名內部類的煩瑣語法。從這點來看,表達式的代碼塊與匿名內部類的方法體是相同的。與匿名內部類相似的是,由于表達式訪問了局部變量,該局部變量相當于與一個隱式的修飾,因此不允許對局部變量重新賦值。 函數式接口 函數式接口(Functional Interface)就是一個只有一個抽象方法(可以包含多個默認方法或多個static方法)的普通接口,可以被隱式轉換為lamb...

    lewif 評論0 收藏0
  • 【筆記】Java核心技術卷一讀書筆記

    摘要:構造器沒有返回值一個對象變量并沒有實際包含一個對象,而僅僅引用一個對象,如有兩個部分。子類重寫方法的返回值范圍必須小于等于父類方法的返回值。枚舉類型中可以添加一些構造器方法和域。 第三章 Java是一種強類型語言。 https://blog.csdn.net/qq_3619... 在Java中,整型的范圍與機器無關。 int 4字節 short 2字節 long ...

    weizx 評論0 收藏0
  • java面向對象(中)

    摘要:使用創建的字符串對象是運行時創建出來的,它被保存在運行時內存區,即堆內存,不會放入常量池中。類成員創建的對象實例中根本不會擁有對應類的類變量。抽象類的構造器不能用于創建實例,主要是用于被其子類調用。 Java提供了final關鍵字來修飾變量,方法和類,系統不允許為final變量重新賦值,子類不允許覆蓋父類的final方法,final類不能派生子類。 Abstract 和 inte...

    孫吉亮 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<