1、String的split(String regex)方法參數(shù)注意點使用這個方法時,,當我們直接以“.”為參數(shù)時,,是會出錯的,,如:
此時,我們得到的res是為空的(不是null),,即str = []; 因為String的split(Stringregex)根據給定的正則表達式的匹配來拆分此字符串,,而'.'是正則表達式中的關鍵字,沒有經過轉義split會把它當作一個正則表達式來處理的,,需要寫成str.split('.')進行轉義處理,。 2、關于hashCode方法【參考文章: http://www.cnblogs.com/dolphin0520/p/3681042.html 】 我們可以先通過HashMap中hashCode的作用來體驗一下,。 我們知道HashMap中是不允許插入重復元素的,,如果是插入的同一個元素,會將前面的元素給覆蓋掉,,那勢必在HashMap的put方法里對key值進行了判斷,,檢測其是否是同一個對象。其put源碼如下:
可以看到這里的判斷語句if(e.hash==hash&&((k=e.key)==key||key.equals(k))),,里面通過&&邏輯運算符相連,,先判斷e.hash==hash,即判斷傳進來的key的hashCode值與table中的已有的hashCode值比較,,如果不存在該key值,,也就不會再去執(zhí)行&&后面的equals判斷;當已經存在該key值時,,再調用equals方法再次確定兩個key值對象是否相同,。從這里可以看出,hashCode方法的存在是為了減少equals方法的調用次數(shù),,從而提高程序效率,。 可以看到,判斷兩個對象是否相同,,還是要取決于equals方法,,而兩個對象的hashCode值是否相等是兩個對象是否相同的必要條件。所以有以下結論: (1)如果兩個對象的hashCode值不等,,根據必要條件理論,,那么這兩個對象一定不是同一個對象,即他們的equals方法一定要返回false,; (2)如果兩個對象的hashCode值相等,,這兩個對象也不一定是同一個對象,即他們的equals方法返回值不確定,; 反過來,, (1)如果equals方法返回true,,即是同一個對象,它們的hashCode值一定相等,; (2)如果equals方法返回false,,hashCode值也不一定不相等,即是不確定的,; (hashCode返回的值一般是對象的存儲地址或者與對象存儲地址相關聯(lián)的hash散列值) 然而,,很多時候我們可能會重寫equals方法,來判斷這兩個對象是否相等,,此時,為了保證滿足上面的結論,,即滿足hashCode值相等是equals返回true的必要條件,,我們也需要重寫hashCode方法,以保證判斷兩個對象的邏輯一致(所謂的邏輯一致,,是指equals和hashCode方法都是用來判斷對象是否相等),。如下例子:
在Person里面重寫了equals方法,但是沒有重寫hashCode方法,,如果就我們平時正常來使用的話也不會出什么問題,,如:
上面是按照了我們重寫的equals方法,返回了我們想要的值,。但是當我們使用HashMap來保存Person對象的時候就會出問題了,,如下:
這是因為,,我們沒有重寫Person的hashCode方法,,使hashCode方法與我們equals方法的邏輯功能一致,此時的Person對象調用的hashCode方法還是父類的默認實現(xiàn),,即返回的是和對象內存地址相關的int值,,這個時候,p1對象和new Person('lly',18);對象因為內存地址不一致,,所以其hashCode返回值也是不同的,。故HashMap會認為這是兩個不同的key,故返回null,。 所以,,我們想要正確的結果,只需要重寫hashCode方法,,讓equals方法和hashCode方法始終在邏輯上保持一致性,。 “設計hashCode()時最重要的因素就是:無論何時,對同一個對象調用hashCode()都應該產生同樣的值,。如果在將一個對象用put()添加進HashMap時產生一個hashCdoe值,,而用get()取出時卻產生了另一個hashCode值,,那么就無法獲取該對象了。所以如果你的hashCode方法依賴于對象中易變的數(shù)據,,用戶就要當心了,,因為此數(shù)據發(fā)生變化時,hashCode()方法就會生成一個不同的散列碼”,。 如下一個例子:
此時我們繼續(xù)測試:
所以,,在設計hashCode方法和equals方法的時候,如果對象中的數(shù)據易變,,則最好在hashCode方法中不要依賴于該字段,。 3、Override和Overload的區(qū)別Override(重寫): 在子類中定義與父類具有完全相同的名稱和參數(shù)的方法,,通過子類創(chuàng)建的實例對象調用這個方法時,,將調用子類中的定義方法,這相當于把父類中定義的那個完全相同的方法給覆蓋了,,是子類與父類之間多態(tài)性的一種體現(xiàn),。特點如下: (1)子類方法的訪問權限只能比父類的更大,不能更?。梢韵嗤?; (2)如果父類的方法是private類型,那么,,子類則不存在覆蓋的限制,,相當于子類中增加了一個全新的方法; (3)子類覆蓋的方法所拋出的異常必須和父類被覆蓋方法的所拋出的異常一致,,或者是其子類,;即子類的異常要少于父類被覆蓋方法的異常; Overload(重載): (參考文章:http://developer.51cto.com/art/201106/266705.htm) 同一個類中可以有多個名稱相同的方法,,但方法的參數(shù)個數(shù)和參數(shù)類型或者參數(shù)順序不同,; 關于重載函數(shù)返回類型能否不一樣,需分情況: (1)如果幾個Overloaded的方法的參數(shù)列表不一樣(個數(shù)或類型),,它們的返回者類型當然也可以不一樣,; (2)兩個方法的參數(shù)列表完全一樣,則不能通過讓其返回類型的不同來實現(xiàn)重載,。 (3)不同的參數(shù)順序也是可以實現(xiàn)重載的,;如下:
我們可以用反證法來說明這個問題,因為我們有時候調用一個方法時也可以不定義返回結果變量,,即不要關心其返回結果,,例如,我們調用map.remove(key)方法時,雖然remove方法有返回值,,但是我們通常都不會定義接收返回結果的變量,,這時候假設該類中有兩個名稱和參數(shù)列表完全相同的方法,僅僅是返回類型不同,,java就無法確定編程者倒底是想調用哪個方法了,,因為它無法通過返回結果類型來判斷。 所以,,Overloaded重載的方法是可以改變返回值的類型,;只能通過不同的參數(shù)個數(shù)、不同的參數(shù)類型,、不同的參數(shù)順序來實現(xiàn)重載,。 4、ArrayList,、Vector,、LinkedList區(qū)別ArrayList、Vector,、LinkedList都實現(xiàn)了List接口,,其關系圖如下: 三者都可以添加null元素對象,,如下示例:
ArrayList和Vector相同點: ArrayList和Vector兩者在功能上基本完全相同,,其底層都是通過new出的Object[]數(shù)組實現(xiàn)。所以當我們能夠預估到數(shù)組大小的時候,,我們可以指定數(shù)組初始化的大小,,這樣可以減少后期動態(tài)擴充數(shù)組大小帶來的消耗。如下: ArrayList Vector 由于這兩者的數(shù)據結構為數(shù)組,,所以在獲取數(shù)據方面即get()的時候比較高效,,而在add()插入或者remove()的時候,由于需要移動元素,,效率相對不高,。(其實對于我們平常使用來說,由于一般使用add(String element)都是讓其加在數(shù)組末尾,,所以并不需要移動元素,,效率還是很好的,如果使用add(int index, String element)指定了插入位置,,此時就需要移動元素了,。) ArrayList和Vector區(qū)別: ArrayList的所有方法都不是同步的,而Vector的大部分方法都加了synchronized同步,,所以,,就線程安全來說,ArrayList不是線程安全的,而Vector是線程安全的,,也因此Vector效率方面相較ArrayList就會更低,,所以如果我們本身程序就是安全的,ArrayList是更好的選擇,。 大多數(shù)的Java程序員使用ArrayList而不是Vector,因為同步完全可以由程序員自己來控制,。 LinkedList: LinkedList其底層是通過雙向循環(huán)鏈表實現(xiàn)的,所以在大量增加或刪除元素時(即add和remove操作),,由于不需要移動元素有更好的性能,,但是在獲取數(shù)據(get操作)方面要差。 所以,,在三者的使用選擇上,,LinkedList適合于有大量的增加/刪除操作和較少隨機讀取操作,ArrayList適合于大規(guī)模隨機讀取數(shù)據,,而較少插入和刪除元素情景下使用,,Vector在要求線程安全的情況下使用。 String(since JDK1.0): 字符串常量,,不可更改,,因為其內部定義的是一個final類型的數(shù)組來保存值的,如下: privatefinalcharvalue[]; 所以,,當我們每次去“更改”String變量的值的時候(包括重新賦值或者使用String內部的一些方法),,其實是重新新建了一個String對象(new String)來保存新的值,然后讓我們的變量指向新的對象,。因此,,當我們需要頻繁改變字符串的時候,使用String會帶來較大的開銷,。 定義String的方法有兩種: (1)String str = 'abc'; (2)String str2 = new String('def'); 第一種方式創(chuàng)建的String對象“abc”是存放在字符串常量池中,,創(chuàng)建過程是,首先在字符串常量池中查找有沒有'abc'對象,,如果有則將str直接指向它,,如果沒有就在字符串常量池中創(chuàng)建出來“abc”,然后在將str指向它,。當有另一個String變量被賦值為abc時,,直接將字符串常量池中的地址給它。如下: 也就是說通過第一種方式創(chuàng)建的字符串在字符串常量池中,,是可共享的,。同時,,也是不可更改的,體現(xiàn)在: [java]view plaincopy 此時,,字符串常量池中存在了兩個對象“abc”和“abcdef”,。 第二種創(chuàng)建方式其實分為兩步: 第一步就是上面的第一種情況;第二步在堆內存中new出一個String對象,,將str2指向該堆內存地址,,新new出的String對象內容,是在字符串常量池中找到的或創(chuàng)建出“def”對象,,相當于此時存在兩份“def”對象拷貝,,一份存在字符串常量池中,一份被堆內存的String對象私有化管理著,。所以使用String str2 = new String('def');這種方式創(chuàng)建對象,,實際上創(chuàng)建了兩個對象。 StringBuffer(since JDK1.0)和StringBuilder(since JDK1.5): StringBuffer和StringBuilder在功能上基本完全相同,,它們都繼承自AbstractStringBuilder,,而AbstractStringBuilder是使用非final修飾的字符數(shù)組實現(xiàn)的,如:char[]value; ,,所以,,可以對StringBuffer和StringBuilder對象進行改變,每次改變還是再原來的對象上發(fā)生的,,不會重新new出新的StringBuffer或StringBuilder對象來,。所以,當我們需要頻繁修改字符串內容的時候,,使用StringBuffer和StringBuilder是很好地選擇,。 兩者的核心操作都是append和insert,,append是直接在字符串的末尾追加,,而insert(int index,String str)是在指定位置出插入字符串。StringBuffer和StringBuilder的最主要區(qū)別就是線程安全方面,,由于在StringBuffer內大部分方法都添加了synchronized同步,,所以StringBuffer是線程安全的,而StringBuilder不是線程安全的,。因此,,當我們處于多線程的環(huán)境下時,我們需要使用StringBuffer,,如果我們的程序是線程安全的使用StringBuilder在性能上就會更優(yōu)一點,。 三者的效率比較: 如上所述, (1)當我們需要頻繁的對字符串進行更改的時候,,使用StringBuffer或StringBuilder是優(yōu)先選擇,,對于StringBuffer和StringBuilder來說,只要程序是線程安全的,我們盡量使用StringBuilder來處理,,要求線程安全的話只能使用StringBuffer,。平常情況下使用字符串(不常更改字符串內容),String可以滿足需求,。 (2)有一種情況下使用String和StringBuffer或StringBuilder的效率是差不多的,,如下: 對于第一條語句,Java在編譯的時候直接把a編譯成 a = 'abcdef',,但是當我們拼接的字符串是其他已定義的字符串對象時,,就不會自動編譯了,如下: 根據String源碼中的解釋,,這種情況下是使用concatenation操作符(+),,內部是新創(chuàng)建StringBuffer或StringBuilder對象,利用其append方法進行字符串追加,,然后利用toString方法返回String串,。所以此時的效率也是不高的。 Java集合類用來保存對象集合,對于基本類型,,必須要使用其包裝類型,。Java集合框架分為兩種: (1)Collection 以單個對象的形式保存,其關系圖如下: 其中,,Statck類為Vector的子類,。由于Collection類繼承Iterable類,所以,,所有Collection的實現(xiàn)類都可以通過foreach的方式進行遍歷,。 (2)Map 以 List: List集合里面存放的元素有序,、可重復,。List集合的有序體現(xiàn)在它默認是按照我們的添加順序設置索引值(即我們可以通過get(索引值index)的方式獲取對象);可重復,,是由于我們給每個元素設置了索引值,,可以通過索引值找到相應的對象。 關于List集合下的具體實現(xiàn)類ArrayList,、Vector,、LinkedList可以參考上面的第四點總結,。對于Statck,它是Vector的子類,,模擬了棧后進先出的數(shù)據結構,。 Queue: 接口,模擬了隊列先進先出的數(shù)據結構,。 Set: Set里面的元素無序,、不可重復。由于無序性,,我們不能通過get方式獲取對象(因為set沒有索引值),。如下: 打印結果如下: ddd 777 4444 555 而對于不可重復性,Set的所有具體實現(xiàn)類其內部都是通過Map的實現(xiàn)類來保存對象的,。如HashSet內部就是通過HashMap來保存數(shù)據的,,如下源碼: 可以看到,new出一個HashSet的時候,,里面new出了一個HashMap對象,,在使用HashSet進行add添加數(shù)據的時候,HashSet將我們需要保存的數(shù)據作為HashMap 的key值保存了起來,,而key值是不允許重復的,,相當于HashSet的元素也是不可重復的。 Map: Map里面的元素通過 |
|