Thinking In Java Part07(容器、簡單的容器分類、類加載器)

1、Collection和Iterator
	Collection是描述所有序列容器的共性的根接口,它可能會被認爲是一個“附屬接口”,即因爲要表示其他若干個接口的共性而出現的接口。AbstractCollection是Collection的默認實現。
	使用接口描述的一個理由是它可以使我們能夠創建更通用的代碼,通過針對接口而非具體實現來編寫代碼,我們的代碼可以應用於更多的對象類型。
2、適配器方法慣用法
	如果現有一個Iterable類,想要添加一個或多種在foreach語句中使用這個類的方法,,例如希望可以選擇以向前的方向或是向後的方向迭代一個單詞列表。如果直接繼承這個類,並覆蓋Iterator()方法,你只能替換先有的方法,而不能實現選擇。
	一種解決方案是所謂適配方法的慣用法。提供特定的接口滿足foreach語句,希望在默認的前向迭代器的基礎上,添加產生反向迭代器的能力,因此不能使用覆蓋,而是添加一個能夠產生Iterable對象的方法,該對象可以用於foreach語句,這使得我們可以提供多種使用foreach的方式。
	class ReversibleArrayList<T> extends ArrayList<T> {
	    public ReversibleArrayList(Collection<T> c) {
	        super(c);
	    }

	    public Iterable<T> reversed() {
	        return new Iterable<T>() {
	            @Override
	            public Iterator<T> iterator() {
	                return new Iterator<T>() {
	                    int current = size() - 1;

	                    @Override
	                    public boolean hasNext() {
	                        return current > -1;
	                    }

	                    @Override
	                    public T next() {
	                        return get(current--);
	                    }
	                };
	            }
	        };
	    }
	}

	public class AdapterMethod {
	    public static void main(String[] args) {
	        ReversibleArrayList<String> strings = new ReversibleArrayList<>(Arrays.asList("hh oo ll www".split(" ")));
	        for(String s:strings){
	            System.out.println(s + "");
	        }
	        System.out.println();
	        for (String s: strings.reversed()){
	            System.out.println(s + "");
	        }
	    }
	}
	如果直接將對象置於foreach當中,會得到默認的前向迭代器。但是如果在對象上調用reversed方法,會產生不同的行爲
	Arrays.asList()產生的List對象會使用底層數組作爲其物理實現,只要你執行的 操作會修改這個List,並且你不想原有的數組被修改,那麼就應該在另外的一個容器中創建一個副本。
3、持有對象的方式
	3.1、數組將數字與對象聯繫起來。它保存類型明確的對象,查詢對象時,不需要對結果做類型轉換。它可以是多維的,可以保存基本類型的數據。但是,數組一旦生成,其容量就不能改變。
	3.2、Collection保存單一的元素,而Map保存相關聯的鍵值對。有了Java的泛型,你就可以指定容器中存放的對象類型,因此你就不會將錯誤類型的對象放置到容器中,並且在從容器中獲取元素時,不必進行類型轉換。各種Collection和各種Map都可以在你向其中添加更多的元素時,自動調整其尺寸。容器不能持有基本類型,但是自動包裝機制會仔細地執行基本類型到容器中所持有的包裝器類型之間的雙向轉換。
	3.3、像數組一樣,List也建立數字索引與對象的關聯,因此,數組和List都是排好序的容器。List能夠自動擴充容量。
	3.4、如果要進行大量的隨機訪問,就使用ArrayList,如果要經常從表中間插入或刪除元素,則應該使用LinkedList。
	3.5、各種Queue以及棧的行爲,由LinkedList提供支持
	3.6、Map是一種將對象(而非數字)與對象相關聯的設計。HashMap設計用來快速訪問;而TreeMap保持“鍵”始終處於排序狀態,所以沒有HashMap快。LinkedHashMap保持元素插入的順序,但是也通過散列提供了快速訪問能力。
	3.7、Set不接受重複元素。HashSet提供最快的查詢速度,而TreeSet保持元素處於排序狀態。LinkedHashSet以插入順序保存元素。
	3.8、新程序中不應該使用過時的Vector、Hashtable和Stack。
4、容器分類
	其實只有四種容器:Map、List、Set、Queue 各有2個到三個實現版本.
	除了TreeSet之外的所有Set都擁有與Collection完全一樣的接口。List和Collection存在着明顯的不同,儘管List所要求的方法都在Collection中。另一方面,在Queue接口中的方法都是獨立的,在創建具有Queue功能的實現時,不需要使用Collection方法。最後,Map和Collection之間的唯一重疊就是Map可以使用entrySet()和values()方法來產生Collection。
	RandomAccess附着到了ArrayList上,而沒有附着到LinkedList上。可以根據所使用的特定List而動態修改其行爲的算法提供了信息。

簡單的容器分類

5、通過異常處理錯誤
	Java的基本理念是“結構不佳的代碼不能運行”。
	改進的錯誤恢復機制是提供代碼健壯性的最強有力的方式。Java使用異常來提供一致的錯誤報告模型,使得構建能夠與客戶端代碼可靠地溝通問題。
	Java中的異常處理的目的在於通過使用少於目前數量的代碼來簡化大型、可靠的程序的生成。
	使用異常所帶來的另一個相當明顯的好處是,它往往能降低錯誤處理代碼的複雜度。因爲使用異常,那就不必在方法調用出進行檢查,因爲異常機制將保證能夠捕獲這個錯誤。
	異常最重要的方面之一就是如果發生問題,它們將不允許程序沿着正常的路徑繼續走下去。C沒有任何辦法可以強制程序在出現問題時停止在某條路徑上運行下去,因此我們有可能會較長時間地忽略了問題。異常允許我們強制程序停止運行,並告訴我們出現了什麼問題,或者強制程序處理問題,並返回到穩定狀態。
6、終止與恢復
	異常處理理論上有兩種基本模型。Java支持終止模型(是Java和C++所支持的模型)。在這種模型中,將假設錯誤非常關鍵,以至於程序無法返回到異常發生的地方繼續執行。一旦異常被拋出,就表明錯誤已無法挽回,也不能回來繼續執行。
	另一種稱爲恢復模型。意思是異常處理程序的工作是修正錯誤,然後重新嘗試調用出問題的方法,並認爲第二次能成功。對於恢復模型,通常希望異常被處理之後能繼續執行程序。如果想要用Java實現類似恢復的行爲,那麼在遇見錯誤時就不能拋出異常,而是調用方法來修正該錯誤。或者,把try塊放在while循環中,這樣就不斷地進入try塊,直到得到滿意的結果。
	雖然恢復模型開始顯得很吸引人,但不是很實用。最主要的原因可能是它所導致的耦合:恢復性的處理程序需要了解異常拋出的地點,這勢必要包含依賴於拋出位置的非通用型代碼。這增加了代碼編寫和維護的難度,對於異常可能會從許多地方拋出的大型程序來說,更是如此。
7、棧軌跡
	printStackTrace()方法鎖提供的信息可以通過getStackTrace()方法來直接訪問,這個方法將返回一個由棧軌跡中的元素所構成的數組,其中每一個元素都表示棧中的一楨。元素0是棧頂元素,並且是調用序列中的租後一個方法調用(這個Throwable被創建和拋出之處)。數組中的最後一個元素和棧底是調用序列中的第一個方法調用。
8、異常鏈
	在捕獲一個異常後拋出另一個異常,並且希望把原始異常的信息保存下來,這杯稱爲異常鏈。在JDK1.4之前,從程序員必須自己編寫代碼來保存原始異常的信息。現在Throwable的子類在構造器中可以接受一個cause(因由)對象作爲參數。這個cause就用來表示原始異常,這樣通過把原始異常傳遞給新的異常,使得即使在當前位置創建並拋出了新的異常,也能聽過這個異常鏈追蹤到異常最初發生的位置。
	在Throwable的子類中,只有三種基本的異常類提供了帶cause參數的構造器。是Error(用於Java虛擬機報告系統錯誤)、Exception以及RuntimeException。如果要把其他類型的異常鏈接起來,應該使用InitCause()方法而不是構造器。
9、“被檢查的異常”和強靜態類型檢查對開發健壯的程序的好處實際上來自:
	不在於編譯器是否會強制程序員去處理錯誤,而是要有一致的、使用異常來報告錯誤的模型。
	不在於什麼時候進行檢查,而是一定要有類型間抽查。也就是說,必須檢查程序使用正確的類型,至於這種強制施加於編譯時還是運行時,沒有關係。
10、異常使用情況
	10.1、在恰當的級別處理問題(在知道該如何處理的情況下才捕獲異常)
	10.2、解決問題並且重新調用產生異常的方法。
	10.3、進行少許修補,然後繞過異常發生的地方繼續執行。
	10.4、用別的數據進行計算,以代替方法預計會返回的值。
	10.5、把當前運行環境下能做的事情儘量做完,然後把相同的異常重拋到更高層。
	10.6、把當前運行環境下能做的事情儘量做完,然後把不同的異常拋到更高層。
	10.7、終止程序
	10.8、進行簡化(如果異常模式使問題變得動太複雜,那用起來會非常痛苦和煩人)
	10.9、讓類庫和程序更安全。(既是在調試做短期投資,也是在爲程序的健壯性做長期投資)
11、異常的處理優點
	其中之一是使得我們可以在某處集中精力處理你要解決的問題,而在另一處處理你編寫的這段代碼中產生的錯誤。
12、字符串
	字符串操作是計算機程序設計中最場景的行爲。
	String對象時不可變的,Stirng類中每一個看起來會修改String值的方法,實際上都是創建了一個全新的String對象,以包含修改後的字符串內容。而最初的String對象則絲毫未動。
13、重載"+"與StringBuilder
	在編譯String s= "abc"+"cc";的時候編譯器會自動引入java.lang.StringBuilder,雖然源代碼中沒有使用,但是編譯器自主使用了它,因爲更高效。
14、RTTI(Run-Time Type Identification)
	運行時類型信息使得我們可以在程序運行時發現和使用類型信息。
	Java有兩種方式讓我們在運行時識別對象和類的信息:
		傳統的RTTI:假定我們在編譯時已經知道了所有的類型
		反射機制:允許我們在運行時發現和使用類的信息。
15、類加載器
	類是程序的一部分,每個類都有一個Class對象。每當編寫並且編譯了一個新類,就會產生一個Class對象(更恰當地說是被保存在一個同名的.class文件中)。爲了生成這個類對象,運行這個程序的Java虛擬機(JVM)將使用被稱爲“類加載器”的子系統。
	類加載器子系統實際上可以包含一條類加載器鏈,但是隻有一個原生類加載器,它是JVM實現的一部分。原生類加載器加載的是所謂的可信類,包括Java API類,它們通常是從本地盤加載的。在這條鏈中,通常不需要添加額外的類加載器,但是如果你有特殊的需求(例如以某種特殊的方式加載類,以支持Web服務器應用,或者在網絡中下載類),那麼你可以掛接額外的類加載器。
	所有的類都是在對其第一次使用時,動態加載到JVM中的,當程序創建第一個對類的靜態成員的引用時,就會加載這個類。這個證明構造器也是類的靜態方法,即使在構造器之前沒有使用static關鍵字。因此,使用new操作符創建類的新對象也會被當中對類的靜態成員的引用。
	類加載器首先檢查這個類的Class對象是否已經加載。如果尚未加載,默認的類加載器就會根據類名查找.class文件(例如,某個附加類加載器可能會在數據庫中查找字節碼)。在這個類的字節碼被加載時,它們會接受驗證,以確保其沒有被破壞,並且不包含不良Java代碼(這個Java中用於安全防範目的的措施之一)。
	一旦某個類的Class對象被載入內存,它們就被用來創建這個類的所有對象。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章