1. 程式人生 > >thinking in java 多型,介面,內部類,異常小結

thinking in java 多型,介面,內部類,異常小結

多型

1.類內private方法是禁止覆蓋的和繼承的,它預設為final。
在派生類中使用名字最好和基類名字不同

2.類方法中的資料域一般為private,我們呼叫getter它方法來訪問它,setter方法來設定它,如果你直接訪問某個域而不是傳引用,多型會在編譯進行解析

3.static方法不具有多型性,它是所有此類物件共有的

4.構造器預設為static的,構造方法是從基類構造到派生類,因為派生類有可能會用到基類的方法,java有GC回收,所以我們不用構建銷燬方法,但是如果我們想手動銷燬,必須是先銷燬派生類在銷燬基類,構造和銷燬順序相反。
並且我們要顯示呼叫銷燬方法。

5.如果存在多個引用時,我們不能直接呼叫銷燬方法,必須增加引用計數方法。

6.在派生類建構函式中呼叫基類的建構函式
運用super(引數列表)來顯示的呼叫基類的建構函式

7.在面向物件程式設計中,協變返回型別指的是子類中的成員函式的返回值型別不必嚴格等同於父類中被重寫的成員函式的返回值型別,而可以是更 “狹窄” 的型別。
Java 5.0添加了對協變返回型別的支援,即子類覆蓋(即重寫)基類方法時,返回的型別可以是基類方法返回型別的子類。協變返回型別允許返回更為具體的型別。

簡單的說協變返回型別就是基類的方法可以返回指向基類的引用,按理來說派生類覆蓋方法不能改變基類的返回型別,協變返回型別就允許我們將派生類返回型別變為指向派生類的返回型別。

8.java狀態模式

例如:一個類派生出兩個類,一個基類引用其中一個類,表現出一種形態,然後改變引用物件為另外一個派生類,則表現的行為也將變的不同。

9.java向上轉型是安全的, 因為基類不可能大於派生類,而向下轉型是危險的,因為派生類可能存在基類不存在的介面。

介面

1.包含抽象方法的類叫做抽象類,如果一個類包含一個或多個抽象方法,該類必須限定為抽象的。

2.java中除了抽象方法外,其他方法都必須定義,含有抽象方法的類是抽象基類,抽象基類不能例項化,只能作為介面,簡單理解就是定義了一個規範。

3.interface關鍵字代替class表明這個類作為介面,implements表示繼承這些介面,java中類只能繼承一個類,c++中有多重繼承(包括虛擬繼承),但是java中可以繼承多個介面。

4.friendly 就是預設訪問許可權(成員變數前面不加public protected 和 private)
重點看protected和fiendly兩種許可權的區別:對於protected成員變數,子孫類在任何地方都能訪問(包內或者包外),但是對於friendly或者說預設成員變數,其實是不存在子孫類訪問許可權的概念的,就是說如果子孫類在包內,則可以訪問,子孫類在包外則不可以訪問。
protected在其子類中可以訪問,無論是子類內部還是子類的例項,無論它們是在哪個包中,
但如果子類與父類不在同一個包中,在子類中用父類的例項去訪問的話不可以
java 介面中所有成員的屬性都為public static final,也就是說介面中宣告的變數都是常量
c++ class預設訪問許可權為private

5.java介面預設許可權為friendly,也就是包訪問許可權,如果在包外使用必須新增public

6.策略設計模式:建立一個能夠根據所傳遞引數物件的不同而具有不同行為的方法

7.只要一個方法操作的是類而非介面,那麼你就只能使用這個類及其子類,如果你想要將這個方法應用於不在此結構中的某個類,那麼你就會觸眉頭了,介面可以在很大程度上放鬆這種限制,因此它可以使得我們編寫複用性更好的程式碼

8.java代理和介面卡(iterface)?

9.java中多重繼承是繼承多個介面,由於每個介面沒有具體的實現,所以可以很容易的組合,但是c++多重繼承就揹負了很重的包袱。

10.java抽象類中可以有非抽象方法,如果是抽象方法在繼承中必須要被實現。

11.介面可以作為型別,且繼承該介面的可以轉型。使用介面的核心原因之一:為了能夠向上轉型為多個基型別,第二個原因防止程式設計師建立該類物件。

12.抽象類和介面的選擇
如果要建立不帶任何方法定義和成員變數的基類,那麼就應該選擇介面,如果知道某個類會成為基類,優先選擇成為介面。

13.繼承任何一個類然後在實現介面,那麼就是為這個類新增功能。

14.在Java語言中,介面可以巢狀在類或其它介面中。由於Java中interface內是不可以巢狀class的,所以介面的巢狀就共有兩種方式:class巢狀interface、interface巢狀interface。
- 1. class巢狀interface
這時介面可以是public,private和package的。重點在private上,被定義為私有的介面只能在介面所在的類被實現。可以被實現為public的類也可以被實現為private的類。當被實現為public時,只能在被自身所在的類內部使用。只能夠實現介面中的方法,在外部不能像正常類那樣上傳為介面型別。
- 2. interface巢狀interface

由於介面的元素必須是public的,所以被巢狀的介面自動就是public的,而不能定義成private的。在實現這種巢狀時,不必實現被巢狀的介面。

15.簡單的java工廠模式,工廠生產物品,傳遞同一個介面的不同物品的工廠,從而產生不同的物品以及狀態。

public class JavaFactory {
    public  static void runCycle(CycleFactory cf){
        Cycle cc = cf.getCycle();
        cc.move();
    }
    public static void main(String[] args){
        runCycle(new UnicycleFactory());
        runCycle(new BicyleFactory());
    }
}

interface Cycle{
    void move();
}

interface CycleFactory{
    Cycle getCycle();
}

class Unicycle implements Cycle{
    @Override
    public void move(){
        System.out.println("Unicycle run");
    }
}

class UnicycleFactory implements CycleFactory{
    @Override
    public Cycle getCycle(){
        return new Unicycle();
    }
}

class Bicyle implements Cycle{
    @Override
    public void move(){
        System.out.println("Bicyle run");
    }
}

class BicyleFactory implements CycleFactory{
    @Override
    public Cycle getCycle() {
        return new Bicyle();
    }
}

16.恰當的原則應該是優先選擇類而不是介面,從類開始,如果介面的必須性變得非常明確,那麼就進行重構,介面是一種重要的工具,但是它們非常容易被濫用。

內部類

1.內部類有外部類的所有元素的訪問許可權(包括private),而c++巢狀類只是單純的名字隱藏機制,與外圍物件沒有聯絡
內部類自動擁有外部類所有成員的訪問許可權,是因為當外部類物件建立一個內部類物件時,內部類物件就會祕密的捕獲一個指向那個外部類物件的引用,使用時就通過引用。

2.必須使用外部類物件才能建立內部類,在擁有外部類物件之前是不可能建立內部類物件的,這是因為內部類會暗暗連線到外部類物件來建立內部類,如果你建立的是靜態內部類,那麼就不需要對外部類進行引用。
生成對外部類物件的引用,外部類名字加.加this

3.

public class JavaInner {
    public static void main(String[] args) {
        Outer o = new Outer();
        Outer.Inner i = o.new Inner();
        i.f();
    }
}

class Outer{
    static int i = 0;
    class Inner{
        void f(){
            System.out.println("haha:" + i);
        }
    }
}

4.將內部類向上轉型為基類時,尤其是轉型為一個介面的時候,內部類就有用了,基類內部函式返回建立一個內部類從而達到轉型的目的

5.內部類分為成員內部類,區域性內部類,靜態內部類,匿名內部類。

區域性內部類:區域性內部類定義在方法裡不能被private, public, protected修飾,這個方法可以是private,public,protected,它根據方法的許可權來限定自己的許可權,
區域性內部類中不可定義靜態變數,可以訪問外部類的區域性變數(即方法內的變數),但是變數必須是final的
在類外不可直接生成區域性內部類(保證區域性內部類對外是不可見的)。要想使用區域性內部類時需要生成物件,物件呼叫方法,在方法中才能呼叫其區域性內部類。通過內部類和介面達到一個強制的弱耦合,用區域性內部類來實現介面,並在方法中返回介面型別,使區域性內部類不可見,遮蔽實現類的可見性。

靜態內部類:生成(new)一個靜態內部類不需要外部類成員:這是靜態內部類和成員內部類的區別。
而不需要通過生成外部類物件來生成。這樣實際上使靜態內部類成為了一個頂級類。靜態內部類不可用private來進行定義
當類與介面(或者是介面與介面)發生方法命名衝突的時候,此時必須使用內部類來實現。用介面不能完全地實現多繼承,用介面配合內部類才能實現真正的多繼承。

匿名內部類:
1.一個類用於繼承其他類或是實現介面,並不需要增加額外的方法,只是對繼承方法的事先或是覆蓋。
2.只是為了獲得一個物件例項,不需要知道其實際型別。
3.類名沒有意義,也就是不需要使用到。
一個匿名內部類一定是在new的後面,用其隱含實現一個介面或實現一個類,沒有類名,根據多型,我們使用其父類名。因他是區域性內部類,那麼區域性內部類的所有限制都對其生效。匿名內部類是唯一一種無構造方法類。大部分匿名內部類是用於介面回撥用的。匿名內部類在編譯的時候由系統自動起名Out$1.class。如果一個物件編譯時的型別是介面,那麼其執行的型別為實現這個介面的類。因匿名內部類無構造方法,所以其使用範圍非常的有限。當需要多個物件時使用區域性內部類,因此區域性內部類的應用相對比較多。匿名內部類中不能定義構造方法。如果一個物件編譯時的型別是介面,那麼其執行的型別為實現這個介面的類。

6.
內部類總結:

1.首先,把內部類作為外部類的一個特殊的成員來看待,因此它有類成員的封閉等級:private ,protected,預設(friendly),public
它有類成員的修飾符: static,final,abstract
2.非靜態內部類nested inner class,內部類隱含有一個外部類的指標this,因此,它可以訪問外部類的一切資源(當然包括private)
外部類訪問內部類的成員,先要取得內部類的物件,並且取決於內部類成員的封裝等級。
非靜態內部類不能包含任何static成員.
3.靜態內部類:static inner class,不再包含外部類的this指標,並且在外部類裝載時初始化.
靜態內部類能包含static或非static成員.
靜態內部類只能訪問外部類static成員.
外部類訪問靜態內部類的成員,循一般類法規。對於static成員,用類名.成員即可訪問,對於非static成員,只能
用物件.成員進行訪問
4.對於方法中的內部類或塊中內部類只能訪問塊中或方法中的final變數。

類成員有兩種static , non-static,同樣內部類也有這兩種
non-static 內部類的例項,必須在外部類的方法中建立或通過外部類的例項來建立(OuterClassInstanceName.new innerClassName(ConstructorParameter)),並且可直接訪問外部類的資訊,外部類物件可通過OuterClassName.this來引用
static 內部類的例項, 直接建立即可,沒有對外部類例項的引用。
內部類不管static還是non-static都有對外部類的引用
non-static 內部類不允許有static成員

方法中的內部類只允許訪問方法中的final區域性變數和方法的final引數列表,所以說方法中的內部類和內部類沒什麼區別。但方法中的內部類不能在方法以外訪問,方法中不可以有static內部類
匿名內部類如果繼承自介面,必須實現指定介面的方法,且無引數
匿名內部類如果繼承自類,引數必須按父類的建構函式的引數傳遞

7.優先使用類而不是介面,如果你的設計中需要某個幾口,你必須瞭解它, 否則不到迫不得已,不要將其放到你的設計中去。

8.普通類繼承多個介面使用外部類繼承和內部類繼承沒區別,如果是抽象類或具體的類,而不是介面,那就只能使用內部類才能實現多重繼承。

9.java匿名內部類?

10.閉包
閉包是可以包含自由(未繫結)變數的程式碼塊;這些變數不是在這個程式碼塊或者任何全域性上下文中定義的,而是在定義程式碼塊的環境中定義。“閉包” 一詞來源於以下兩者的結合:要執行的程式碼塊(由於自由變數的存在,相關變數引用沒有釋放)和為自由變數提供繫結的計算環境(作用域)。在 Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby 和 Python 等語言中都能找到對閉包不同程度的支援。

閉包的價值在於可以作為函式物件 或者匿名函式,對於型別系統而言這就意味著不僅要表示資料還要表示程式碼。支援閉包的多數語言都將函式作為第一級物件,就是說這些函式可以儲存到變數中、作為引數傳遞給其他函式,最重要的是能夠被函式動態地建立和返回。

java回撥的價值在於它的靈活性,可以在執行時動態決定需要呼叫什麼方法。也就是閉包中的根據環境變數來變化。

如果是直接簡答的繼承是不具有這樣的靈活性的。

11.內部類把變化的事物和不變化的事物分離開來。

12.內部類的繼承,內部類的構造器必須連線到指向其外部類物件的引用,所以在繼承內部類的時候,指向外圍類物件的“祕密的”引用必須被初始化,而在派生類中不再存在可連線的預設物件。

所以要生成一個構造器時,必須使用enclosingclassreference.super( )才可以。

外部類內部包含內部類只是宣告而已,如果我們要使用必須例項化內部類,在外部類定義多個內部類可以使外部類表現出多種不同的樣子,類似閉包把

13.內部類可以被覆蓋嗎

當繼承了某個外部類的時候,內部類並沒有發生什麼特別變化,這兩個內部類是完全獨立的實體,各自在自己的名稱空間內。

14.區域性內部類的名字在方法外是不可見的,那麼我們有時使用區域性內部類的理由是我們需要一個已命名的構造器,或者需要過載構造器,而匿名內部類只能用於例項初始化,另一個理由就是需要不止一個該內部類的物件。

15.內部類也必須產生一個.class檔案,一般是外部類$內部類.class,如果內部類是匿名的,編譯器會簡單的產生一個數字作為其標識。

異常

1.異常引數
我們總是在堆上建立異常物件,也伴隨著儲存空間和構造器的呼叫。所有的標準異常都有兩個構造器,預設和帶字串引數的。我們也可以通過繼承異常基類來自己創造異常類

2.super()表示呼叫基類的構造器

3.異常和日誌記錄 thinking in java P253

4.異常說明,屬於方法宣告的一部分,緊跟在形式引數列表之後,關鍵字,throws,鼓勵我們列出可能丟擲的異常。

5.丟擲異常後會跳轉到catch程式碼塊繼續執行,
正常執行的程式碼如果出現異常,就不會執行出現異常語句後面的所有正常程式碼.
異常可能會被捕獲掉,比如上面catch宣告的是捕獲Exception,那麼所有Exception包括子類都會被捕獲,但如Error或者是Throwable但又不是Exception(Exception繼承Throwable)就不會被捕獲.
如果異常被捕獲,就會執行catch裡面的程式碼.如果異常沒有被捕獲,就會往外丟擲,相當於這整個方法出現了異常.
finally中的程式碼只要執行進了try catch永遠都會被執行.執行完finally中的程式碼,如果異常被捕獲就會執行外面跟這個try catch無關的程式碼.否則就會繼續往外丟擲異常.
return無論在哪裡,只要執行到就會返回,但唯一一點不同的是如果return在try或者catch中,即使返回了,最終finally中的程式碼都會被執行.這種情況最常用的是打開了某些資源後必須關閉,比如打開了一個OutputStream,那就應該在finally中關閉,這樣無論有沒有出現異常,都會被關閉.

6.想要在捕獲一個異常以後丟擲另外一個異常,並且依舊儲存舊異常的資訊叫做異常鏈

7.使用finally的情況,已經開啟的檔案或網路連線等等。

8.當覆蓋方法的時候,只能丟擲在基類方法的異常說明列出的那些異常。

9.在構造器中丟擲異常,通過finally不能解決問題,因為finally是清楚構造完的物件,構造器丟擲異常可能是構造了一半的物件。
解決:對於可能構造失敗的物件,我們使用try-catch巢狀,在try的內部try來執行關閉銷燬操作,確保是在物件構造完畢的情況下銷燬。
簡單來說就是,在建立需要清理的物件之後,立即進入一個try-finally語句塊。
我們還需要判斷此物件的構造可不可能失敗

10.異常匹配
丟擲異常的時候,異常處理系統會按照最近的catch進行匹配,要注意派生類物件也可以撇皮基類的處理程式,但是派生類處理程式不能放在基類的處理程式之前,因為這樣基類會永遠得不到執行會報錯。

11.和java不同,c++不會在編譯時進行檢查以確定函式或方法是不是真的丟擲異常,或者異常說明是不是完整,這樣的檢查只發生在執行期間,如果丟擲的異常與異常說明不符,c++會呼叫標準庫的unexpected函式。

12.執行時系統從發生異常的方法開始,依次回查呼叫棧中的方法,直至找到含有合適異常處理器的方法並執行。當執行時系統遍歷呼叫棧而未找到合適 的異常處理器,則執行時系統終止。同時,意味著Java程式的終止。
也就是丟擲異常,如果當層不能捕獲繼續向上層丟擲,但是需要注意try後面必須跟一個catch或多喝catch塊,如果不很catch塊就必須根一個finally塊。

13.java什麼情況下必須用throws丟擲異常?
答:在程式中丟擲了非RuntimeException異常卻沒有對其處理(用try catch塊處理)的情況下,必須在方法頭throws該異常。

14.
try 塊:用於捕獲異常。其後可接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。

try語句塊中,出現異常之後的語句也不會被執行,catch語句塊執行完後,執行finally語句塊裡的語句,最後執行finally語句塊後的語句

catch 塊:用於處理try捕獲到的異常。
finally 塊:無論是否捕獲或處理異常,finally塊裡的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。在以下4種特殊情況下,finally塊不會被執行:
1)在finally語句塊中發生了異常。
2)在前面的程式碼中用了System.exit()退出程式。
3)程式所在的執行緒死亡。
4)關閉CPU。

15.
如果一個方法可能會出現異常,但沒有能力處理這種異常,可以在方法宣告處用throws子句來宣告丟擲異常。例如汽車在執行時可能會出現故障,汽車本身沒辦法處理這個故障,那就讓開車的人來處理。

 throws語句用在方法定義時宣告該方法要丟擲的異常型別,如果丟擲的是Exception異常型別,則該方法被宣告為丟擲所有的異常。多個異常可使用逗號分割。

Throws丟擲異常的規則:

1) 如果是不可查異常(unchecked exception),即Error、RuntimeException或它們的子類,那麼可以不使用throws關鍵字來宣告要丟擲的異常,編譯仍能順利通過,但在執行時會被系統丟擲。

2)必須宣告方法可丟擲的任何可查異常(checked exception)。即如果一個方法可能出現受可查異常,要麼用try-catch語句捕獲,要麼用throws子句宣告將它丟擲,否則會導致編譯錯誤

3)僅當丟擲了異常,該方法的呼叫者才必須處理或者重新丟擲該異常。當方法的呼叫者無力處理該異常的時候,應該繼續丟擲,而不是囫圇吞棗。

4)呼叫方法必須遵循任何可查異常的處理和宣告規則。若覆蓋一個方法,則不能宣告與覆蓋方法不同的異常。宣告的任何異常必須是被覆蓋方法所宣告異常的同類或子類。

throw 丟擲的只能夠是可丟擲類Throwable 或者其子類的例項物件