發現了一本《Java深入解析》,其中涉及了很多平常沒有註意的一些誤區,也許開發的時候用不上,但是對這些概念卻不能模糊。書中的內容還是很有用處,這裡摘要了一些筆記。
1、在Java中,沒有goto語句。因為大量使用goto語句會降低程式的可讀性和可維護性,所以Java語言取消了goto的使用。同時,為了避免程式設計師自行使用goto所帶來的混亂,Java語言仍將goto定義為一個關鍵字,但沒有定義任何語法,故稱為「保留字」。
2 、true、false和null在IDE中雖然以不同的顏色顯示,但是並不是關鍵字,而是“字面常數”,就和String類型的abc一樣。
3 、定義名稱時盡量避免使用$,因為編譯器在對.java檔進行編譯的時候,會將”$”編譯成頂層類型與底層類型的連接符。請參閱下例:
package com.laixintao.Test; public class Outer$Inner { public static void main(String[] args) { Outer o = new Outer(); Outer.Inner i = o.new Inner(); i.innerPrint(); } } class Outer { class Inner { void innerPrint() { System.out.println("Inner Print!"); } } }
在編譯(javac Test3.java)這段程式碼的時候,編譯器會報以下錯誤:Test.java:12: 錯誤: 類別重複: com.laixintao.Test.Outer.Inner class Inner { ^
4、Unicode轉義字元處理的非常早,在解析程式之前。例如:
// char c1 = 'u00a'; // char c2 = 'u00d';
在程式中出現這兩行程式碼編譯報錯。這兩個Unicode碼分別表示”換行”和”回車”,所以,在編譯器編譯的時候,程式碼是這樣的:
// char c1 = ' '; // char c2 = ' ';
5、Unicode碼使用16位元字元編碼,在Java中用char類型來表示。現在Unicode已經擴展到一百萬個字符,超出16位元限制的成為增補字符。所有增補字元都不能用字元常數來表示。
6、當short,byte,char參加運算時,結果為int型,而非與較高的類型相同。如果變數是byte,short,byte類型,當其賦予編譯時期的常數,而該常數又沒有超過變數的取值範圍時,編譯器就可以進行隱式的收縮轉換。這種隱式的收縮轉換是安全的,因為此收縮轉換只適用於變數的賦值,而不適用於方法呼叫語句,即不適用於方法呼叫時的參數傳遞。 (詳見java中預設型別轉換的小問題)
7、注意char類型,這是無符號型別。因此,char與short或char與byte之間的轉換必須顯示地使用型別轉換。 從byte到char的轉換為擴展收縮轉換,該轉換比較特殊,即先將byte擴展轉換到int,然後再收縮到char。
8、在整數資料間的擴展轉換中,如果運算元是char型別(無符號型別),則進行無符號擴展,副檔位為0.如果運算元是byte,short或int(有符號型),則進行有符號擴展,擴展位為該變數的符號位元。
9、整型資料之間的收縮轉換,只是截斷並丟棄高位,不做任何其他處理。
10、0.1+0.2不等於0.3.System.out.println((double)0.1+(double)0.2);這語句的輸出結果是0.30000000000000004。因為電腦使用二進制來儲存數據,而許多小數都不能夠準確地使用二進制來表示(事實上,大多數地小數都是近似的),就像使用十進制小數不能準確地表示1/3這樣地分數一樣。大多數地浮點型,在電腦中只是近似地儲存其值,而不像整數類型那樣準確地儲存。又例,這是一個死循環:for(float f = 10.1f;f != 11;f+=0.1f){}
11、float類型可以保留7~8個有效數字,而double類型可以保留15 ~16個有效數字,因而當int類型或long類型數值多於double或float地有效數字時,該值的一些最低有效位就會丟失,從而造成精度丟失,這時,就會採用IEEE754最近舍入模式,提取與該整數值最接近的浮點值。儘管整數型向浮點型的轉換屬於擴展轉換,但當數值很大或很小(絕對值很大)時,就會產生一定的精度損失。
12、i+++j如何計算? (這個問題在C/C++)中討論是沒有多大意義的,因為C/C++依賴實現的硬體結構,不同的環境結果也會不同。不過在Java中,這個結果是固定的,不受其運作的硬體環境與平台的影響) 答:根據貪心規則,前置++優於後置++,結果是(i++)+j
13、i++和++i其實都是先+1,再賦值。 ++i,沒什麼好說的;i++,以j=i++;為例在底層的實作是:temp = i;i = i + 1; j = temp; 所以,i=15;i=i++;這個表達式的結果是15.(因為加一之後又執行了一次賦值,從16變回15)
14、+0與-0在浮點類型變數儲存中,符號位是不同的。當-0和+0參與浮點類型的相關運算(例如相除與求餘運算)時,可以產生不同的結果。
15、浮點的相除與求餘運算不同與整數的相除與求餘運算,當除數為0時,浮點運算不會產生ArithmeticException異常。
16、String類別是非可變類,其物件一旦創建,就不可銷毀。 String類別那些看似修改字元序列的方法其實都是傳回新建的String對象,而不是修改自身物件。
17、由於String物件是不可改變的,因此具有執行緒安全性,可以自由地實現共享。
18 在String類別內部,是使用字元陣列(char[])來維護字元序列的。 String的最大長度也就是字元陣列的最大長度,理論上最大長度為int類型的最大值,即2147483647.實際中,一般可取得的最大值小於理論最大值。
19、main()方法在表現行為上,與其他方法基本上相同,可以重載,由其他方法調用,繼承,隱藏,也可以拋出異常,帶有類型參數。我們也可以在一個程式中透過反射來呼叫main方法(或其他方法)。
20、當兩個或多個方法的名稱相同,而參數列表不同時,這幾個方法就構成了重載。重載方法可以根據參數列表對應的類型與參數的個數來區分,但是,參數的名稱、方法的返回類型,方法的異常列表與類型參數不能作為區分重載方法的條件。
21、究竟選擇哪個方法調用,順序是這樣的:
在第一階段,自動裝箱(拆箱)與可變參數不予考慮,搜尋對應形參類型可以匹配實參類型並且形參個數與實參數相同的方法;
如果在步驟一不存在符合條件的方法,在第二階段,自動裝箱與拆箱將會執行。
如果在步驟二中不存在符合條件的方法,在第三階段,可變參數的方法將會考慮。
如果3個階段都沒有搜尋到符合條件的方法,將會產生編譯錯誤。如果如何條件的方法多於一個,將會選擇最明確的方法。最明確的方法定義為:如果A方法的形參列表類型對應的都可以賦值給B方法的形參列表類型,則A方法比B方法明確。如果無法選出最明確的方法,則會產生編譯錯誤。
22 、重寫和隱藏的本質區別是:重寫是動態綁定的,根據運行時引用所指向物件的實際類型來決定呼叫相關類別的成員。而隱藏是靜態綁定的,根據編譯時引用的靜態型別來決定呼叫的相關成員。換句話說,如果子類別重寫了父類別的方法,當父類別的引用指向子類別物件時,透過父類別的參考呼叫的是子類別方法。如果子類別隱藏了父類別的方法(成員變數),透過父類別的參考呼叫的仍是父類別的方法(成員變數)。
23、建構器是遞歸呼叫的,子類別的建構器會呼叫父類別的建構器,直到呼叫Object類別的建構器。
24 建構器沒有建立對象,建構器是使用new建立對象時由系統呼叫的,用來初始化類別的實例成員。從順序上說,先是創建對象,然後再呼叫構造器的。 (建構器並沒有產生新的物件)
25、預設的建構器不為空,而建構器會呼叫父類別的無參構器,並可能執行實例成員變數的初始化。所以,預設的構造器至少呼叫了父類別的建構器,它所做的工作還可能更多,包括實例變數宣告初始化與實例初始化區塊,都是在建構器中執行的。
26 當==或!=運算符的兩個操作數的類型一個是基本資料型,另一個是包裝類別引用型別時,將引用型別拆箱轉換為基本資料型別,然後比較兩個基本數據類型的值是否相等。
27、在Java中,數組也是類,數組宣告的引用變數指向數組類型的物件。所有的陣列都繼承Object類,實作了java.lang.Cloneable與java.io.Serializable介面。陣列的成員包括變數length(隱式存在)與從Object類別繼承的成員。 Cloneable與Serializable是兩個標記的接口,這兩個接口中沒有明確宣告任何成員。
28、介面是完全抽象的設計,不能實例化。使A用new方式創建的藉口類型,實際上是創建了一個匿名類,該匿名類實作了介面類型。
29、如果兩個接口聲明了相同的變數x,則當某接口同時繼承這兩個接口,或者某類同時實現這兩個接口時,通過簡單名稱訪問會產生編譯錯誤。
30、如果兩個接口中聲明了相同名稱的方法m,並且兩個方法沒有構成重載,則當某接口能夠同時繼承這兩個接口,或者某類能夠同時繼承這兩個接口時,必須存在一種方法簽名,使得該簽名同時為兩個m方法簽名的子簽名,並且在方法的返回類型上,必須存在一種類型,使得該類型同時為兩個m方法返回類型的可替換類型。
以上就是Java的常見迷思與細節的內容,更多相關內容請關注PHP中文網(m.sbmmt.com)!