每種語言都很強大,不管你是像我一樣的初學者還是有過N年項目經驗的大神,總會有你不知道的東西。就其語言本身而言,比如Java,也許你用Java開發了好幾年,對其可以說是爛熟於心,但你能保證Java所有的用法你都知道嗎?今天沒事就來整理下Java中有哪些隱藏的特性呢?知道的可以舉手哦~~~
一、雙括號初始化語法(DoubleBraceInitialization)(這里指的是大括號{})
主要指的是集合類(List,Map,Set等),我們創建一個常量集合或傳遞一個常量集合作為參數,往往都會這么做(以Set為例):
Set<String> validCodes = new HashSet<String>(); validCodes.add("XZ13s"); validCodes.add("AB21/X"); validCodes.add("YYLEX"); validCodes.add("AR2D"); removeProductsWithCodeIn(validCodes);
或在類中初始化一個常量集合:
private static final Set<String> VALID_CODES = new HashSet<String>(); static { validCodes.add("XZ13s"); validCodes.add("AB21/X"); validCodes.add("YYLEX"); validCodes.add("AR2D"); }
會不會覺得每次都這樣很費時費力,其實,有更好的辦法,那就是雙括號語法,像下面這樣:
private static final Set<String> VALID_CODES = new HashSet<String>() {{ add("XZ13s"); add("AB21/X"); add("YYLEX"); add("AR2D"); }}; // Or: removeProductsWithCodeIn(new HashSet<String>() {{ add("XZ13s"); add("AB21/X"); add("YYLEX"); add("AR5E"); }});
這里解釋下這兩個括號:第一個括號創建了一個新的匿名內部類,相信這個大家都知道;第二個括號聲明了匿名內部類實例化時運行的實例初始化塊。
使用雙括號語法需要注意兩點:
1. 如果要在匿名內部類中要建立匿名子類,那么只能用於非final的類,這很明顯,且不僅局限於集合類,可以用來實例化任何對象,例如用於GUI對象,如下:
add(new JPanel() {{ setLayout(...); setBorder(...); add(new JLabel(...)); add(new JSpinner(...)); }});
2. 這種語法與常用的equals(Object o)方法不兼容。例如Example類有這樣的方法:
public boolean equals(final Object o) { if (o == null) { return false; } else if (!getClass().equals(o.getClass())) { return false; } else { Example other = (Example) o; // Compare this to other. } }
那么,使用雙括號初始化語法創建的任何對象都不會與未使用雙括號語法創建的對象相等。因此,建議大家:如果類中需要equals(Object o)方法,那就老老實實不要使用這種語法了。不過集合類沒有這種問題,應該是因為集合內部優化了的原因。
那么什么時候建議大家使用雙括號語法呢?
如果你只是要創建並初始化一個實例而不是創建一個新類,或者創建任何不添加字段屬性或重載方法的匿名類時,用雙括號語法就很nice了。
3. 如果你用的是集合類且該類有構造器參數接受另一個集合生成該集合的實例,那么有個更好的更慣用的替代方法,如大家都知道的List初始化可以用Arrays.asList(),如下:
List<String> myList = new ArrayList<String>(Arrays.asList("One", "Two", "Three"));
但需要注意:asList返回的是一個長度不可變的列表。數組是多長,轉換成的列表是多長,我們是無法通過add、remove來增加或者減少其長度的。
二、類型參數的與操作&(TypeParameterJointUnion)
就是參數綁定多個類型,如:
public class ClassName<T extends Class & Interface1 & Interface2 & ...> {}
注意:這里extends后面只有第一個為類Class,后面&的全部都是接口Interface,而且類Class的聲明或定義必須在Interface之前。
舉個例子:如果你想要一個既是 Comparable類又是Collection類的參數,實現的功能是:兩個給定的集合是否相等或兩個集合中的任一個是否包含指定的元素,那么你可以用下面的函數實現:
public static <A, B extends Collection<A> & Comparable<B>> boolean foo(B b1, B b2, A a) { return (b1.compareTo(b2) == 0) || b1.contains(a) || b2.contains(a); }
這里b1和b2可以同時具有類型Collection和Comparable類型,因此可以使用Collection類的contains方法也可以使用Comparable類的compareTo方法。
三、VisualVM監控工具(Java VisualVM)
這是JDK6.0 update 7 中自帶的監控工具(java啟動時不需要特定參數,該工具在bin/jvisualvm.exe),能夠監控線程,內存情況,查看方法的CPU時間和內存中的對象,已被GC的對象,反向查看分配的堆棧(如100個String對象分別由哪幾個對象分配出來的)。
雙擊打開,從UI上來看,這個軟件是基於NetBeans開發的了。
四、Classpath支持通配符(Setting the class path)
java -classpath ./lib/log4j.jar:./lib/commons-codec.jar:./lib/commons-httpclient.jar:./lib/commons-collections.jar:./lib/myApp.jar so.Main
比較復雜,還容易出錯,其實可以用通配符更加簡潔方便:
java -classpath ./lib/* so.Main
五、協變返回類型( covariant return types)
這是Java 5添加的功能,在Java5之前我們在子類中覆蓋基類的方法時是不能改變被覆蓋方法的返回類型的,就是基類和父類的方法必須一模一樣,想要改變只能在創建對象時Cast。Java 5過后,我們就可以改變了,不過需要注意的是:改變后的類型必須是原類型的子類型。舉個例子就一目了然了。
public class CovariantReturnTypesTest { public static void main(String[] args) { // TODO Auto-generated method stub Mill m = new Mill(); Grain g = m.process(); System.out.println(g); // output: Grain m = new WheatMill(); g = m.process(); System.out.println(g); // output: Wheat } } class Grain { public String toString() { return "Grain"; } } class Wheat extends Grain { public String toString() { return "Wheat"; } } class Mill { Grain process() { return new Grain(); } } class WheatMill extends Mill { // 這里返回類型改為了Grain的子類型Wheat Wheat process() { return new Wheat(); } }
