Thinking In Java Part06(內部類/容器)

1、爲什麼需要內部類
	一般來說,內部類繼承自某個類或實現某個接口,內部類的代碼操作創建它的外圍類的對象。所以可以認爲內部類提供了某種進入其外圍類的窗口。
	如果只需要一個對接口的引用,那麼直接通過外圍類實現那個接口。
	那麼內部類實現一個接口和外圍類實現這個接口有什麼區別呢?
		後者不是總能享受到接口帶來的方便,又是需要用到接口的實現。所以。使用內部類的最重要的原因:
			每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對於內部類都沒有影響。
	內部類提供了、可以繼承多個具體的或抽象的類的能力,使得多重繼承的解決方案變得完整。接口解決了部分問題,而內部類有效地實現了“多重繼承”。內部類允許繼承多個非接口類型(類或抽象類)。
	如果擁有的是抽象的類或具體的類,而不是藉口,那就只能使用內部類才能實現多重繼承。
	如果使用內部類,還可以獲得其他一些特性:
		1.1、內部類可以有很多實例,每個實例都有自己的狀態信息,並且與其外圍類對象的信息相互獨立。
		1.2、在單個外圍類中,可以讓多個內部類以不同的方式實現同一個接口,或繼承同一個類。
		1.3、創建內部類對象的時刻並不依賴於外圍類對象的創建。
		1.4、內部類並沒有令人迷糊的"is-a"關係,它就是一個獨立的實體。
	使用內部內實現抽象的類或具體的類的多重繼承
	class D{

	}
	abstract class E{

	}
	class Z extends D{
	    E returnE(){
	        return new E(){};
	    }
	}
	public class MultiExtend {
	    static void takesD(D d){
	        System.out.println("d");
	    }
	    static void takesE(E e){
	        System.out.println("e");
	    }

	    public static void main(String[] args) {
	        Z z = new Z();
	        takesD(z);
	        takesE(z.returnE());
	    }
	}


	interface Incrementable {
    void increment();
}
2、閉包與調用
	閉包(closure)是一個可調用的對象,它記錄了一些信息,這些信息來自於創建他的作用域。通過這個定義,可以看出內部類是明向對象的閉包,因爲它不僅包含外圍類對象(創建內部類的作用域)的信息,還自動擁有一個指向此外圍類對象的引用,在此作用域內,內部類有權操作有所的成員,包括private成員。
	Java最引人爭議的問題之一爲:人們認爲Java應該包含某種類似指針的機制,以允許回調。通過回調,對象能夠攜帶一些信息,這些信息允許它在稍後的某個時刻調用初始的對象。如果回調是通過指針實現的,那麼就只能希望程序員不會誤用指針。Java更小心仔細,沒有在語言中包括指針。通過內部類提供閉包比指針更靈活、安全。
	通過內部類完成閉包
	class Calleel implements Incrementable {
	    private int i = 0;

	    @Override
	    public void increment() {
	        i++;
	        System.out.println(i);
	    }
	}
	class MyIncrement{
	    public void increment(){
	        System.out.println("other operation");
	    }
	    static void f(MyIncrement mi){
	        mi.increment();
	    }
	}
	class Calleel2 extends MyIncrement{
	    private int i =0;
	    @Override
	    public  void increment(){
	        super.increment();
	        i++;
	        System.out.println(i);
	    }
	    private class Closure implements Incrementable{
	        @Override
	        public void increment(){
	            Calleel2.this.increment();
	        }
	    }
	    Incrementable getCallbackReference(){
	        return new Closure();
	    }
	}
	class Caller{
	    private Incrementable callbackReference;
	    Caller(Incrementable cbh){callbackReference = cbh;}
	    void go(){callbackReference.increment();}
	}
	public class CallBacks {
	    public static void main(String[] args) {
	        Calleel calleel = new Calleel();
	        Calleel2 calleel2 = new Calleel2();
	        // other operation 1
	        MyIncrement.f(calleel2);
	        Caller caller = new Caller(calleel);
	        Caller caller1 = new Caller(calleel2.getCallbackReference());
	        // 1
	        caller.go();
	        //2
	        caller.go();
	        // other operation 2 這個2爲 calleel的1 +1 想要實現其他接口但是重名了部分 於是用內部類獨立實現接口而不用覆蓋原有方法
	        caller1.go();
	        caller1.go();
	    }
	}
	這個例子展示了外圍類實現一個接口與內部類實現此接口直接的區別。Callee2繼承MyIncrement,後者已經有了一個不同的increment()方法,並且與Incrementable接口期望的increment()方法完全不相關。所以如果Callee2繼承了MyIncrement,就不能爲了Incrementable的用途而覆蓋Increment()方法,於是只能使用內部類獨立地實現Incrementable。當創建一個內部類時,並沒有在外圍類的接口中添加東西,也沒有修改外圍類的接口。
	內部類Closure實現了Incrementable,以提供一個返回Caleel2的鉤子(hook)——而且是一個安全的鉤子。無論誰獲得此Incrementable的引用,都只能調用increment(),除此之外沒有其他功能。
	回調的價值在於它的靈活性——可以在運行時動態地決定需要調用什麼方法。
3、內部類與控制框架
	應用程序框架(application framework)就是被設計用以解決某類特定問題的一個類或一組類。要運用某個應用程序框架,通常是繼承一個或多個類,並覆蓋某些方法。在覆蓋後的方法中,編寫代碼定製應用程序框架提供的通用解決方案,以解決你的特定問題。模版方法包含算法的基本結果,並且會調用一個或多個可覆蓋的方法,以完成算法的動作。設計模式總是將變化的事物與保持不變的事物分離開。
	控制框架是一類特殊的應用程序框架,用來解決響應事件的需求。主要用來響應事件的系統被稱作事件驅動系統。
	一個用來管理並觸發事件的實際控制框架
	public class Controller {
	    private List<Event> eventList = new ArrayList<Event>();
	    public void addEvent(Event c){eventList.add(c);}
	    public void run(){
	        while(eventList.size()>0){
	            for(Event e: new ArrayList<Event>(eventList)){
	                if(e.ready()){
	                    System.out.println(e);
	                    e.action();
	                    eventList.remove(e);
	                }
	            }
	        }
	    }
	}
	在這個設計當中 你並不知道Event到底做了什麼。這正是此設計的關鍵所在,“使變化的事物與不變的事物相互分離”。“變化向量”就是各種不同的Event對象所具有的不同行爲,而你通過創建不同的Event來表現不同的行爲。
	這正是內部類要做的事情,內部類允許:
		控制框架的完整實現是由單個的類創建的,從而使得實現的細節被封裝了起來,內部類用來表示解決問題所必需的各種不同的action().
		內部類能很容易地訪問外圍類的任意成員,所以可以避免這種實現變得笨拙。如果沒有這種能力,代碼將變得令人討厭。
4、內部類的標識符
	每個類都會產生一個.class文件,其中包含了如何創建該類型的對象的全部信息。內部類也必鬚生成一個.class文件以包含它們的class對象信息。命名規則:外圍類的名字加上‘$’,再加上內部類的名字。例如:LocalClass$InnerLocalCounter
	如果內部類是匿名的,編譯器會簡單地產生一個數字作爲其標識符,如果內部類是嵌套在別的內部類之中,只需要將他們的名字加在其外圍類標識符與‘$’的後面。
5、容器
	程序總是根據運行時才知道的某些條件去創建新對象。需要在任意時刻和任意位置創建任意數量的對象。Java提供了一套完整的容器類來解決這個問題,其中基本的類型是List、Set、Queue和Map。這些對象類型也稱爲集合類,但由於類庫使用Collection這個名字來指代該類庫的一個特殊子集,所以用範圍更廣的“容器”稱呼他們。
6、Java容器基本概念
	Collection。一個獨立元素的序列,這些元素都服從一條或多條規則。List必須按照插入的順序保存元素,而Set不能有重複元素。Queue按照排隊規則來確定對象的產生的順序(通常與他們被插入的順序相同)
	Map。一組成對的鍵值對對象,允許你使用鍵查找值,ArrayList允許你使用數字來查找值,某種意義上,將數字與對象關聯在一起。映射表允許我們使用另一個對象來查找某個對象,也被稱爲“關聯數組”,因爲它將某些對象與另外一些對象關聯在了一起,或者被稱爲字典,因爲你可以使用鍵對象來查找值對象,就像在字典中使用單詞來定義一樣。
	TreeSet按照比較結果的升序保存對象,LinkedHashSet按照被添加的順序保存對象
7、List
	List承諾將元素維護在特定的序列中。
	有兩種類型的List
		基本的ArrayList 長於隨機訪問元素,但是在List的中間插入和移除元素時較慢
		LinkedList,通過代價較低的在List中間進行的插入和刪除操作,提供了優化的順序訪問。LinkedList在隨機訪問方面相對比較慢,但是特性集比ArrayList更大。
8、迭代器
	只是使用容器,不知道或者不關心容器的類型,想要做到不重寫代碼就可以應用到不同類型的容器,使用迭代器來完成。
	迭代器是一個對象,它的工作是遍歷並選擇序列中的對象,而客戶端程序員不必知道或關心該序列底層的結果。此外,迭代器通常被稱爲輕量級對象:創建它的代價小。因此,迭代器有些奇怪的限制,例如java的Iterator只能單向移動。有子類ListIterator可以雙向移動,但是隻能用於List類的訪問。
9、Stack
	棧 通常是指後進先出(LIFO)的容器。有時棧也被稱爲疊加棧,因爲最後壓入棧的元素,第一個彈出棧。
10、Set
	Set不保存重複的元素,最常用來測試歸屬性,可以很容易地詢問某個對象是否在某個Set中,查找成爲了Set中最重要的操作,HashSet也專門對快速查找進行了優化。
	Set具有與Collection完全一樣的接口,因此沒有任何額外的功能,不像之前的兩個不同的List,實際上Set就是Collection,只是行爲不同。(繼承與多態思想的典型應用:表現不同的行爲)Set是基於對象的值來確定歸屬性的。
	出於速度原因,HashSet使用了散列。TreeSet將元素存儲在紅-黑樹數據結構中,HashSet使用散列函數。LinkedHashList因爲查詢速度的原因也使用了散列,但是看起來它使用了鏈表來維護元素的插入順序。
11、Queue	
	隊列是一個典型的先進先出(FIFO)的容器。即從容器的一端放入事物,從另一端取出,並且事物放入容器的順序與取出的順序是相同的。隊列常被當作一種可靠的對象從程序的某個區域傳輸到另一個區域的途徑。隊列在併發編程中很重要,因爲它們可以安全地將對象從一個任務傳輸給另一個任務。
	LinkedList提供了方法以支持隊列的行爲,並且實現了Queue接口,因此LinkedList可以用作Queue的一種實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章