JDK1.5 和 1.6 的區別總結

總結一下JDK1.5和1.6的區別。


jdk1.5的新特性: 

1. 泛型 

2 自動裝箱/拆箱 

3 for-each 

4 static import 

5 變長參數 

具體如下解釋:

1. 泛型 (避免類型強制轉換可能引起的運行錯誤) 
例如: 
ArrayList list=new ArrayList(); 
list.add(new Integer(3)); 
list.add(new Integer(4)); 
int i=((Integer)(list.get(0))).parseInt(); 
很麻煩 
ArrayList<Integer>list=new ArrayList<Integer>(); 
list.add(new Integer(3)); 
list.add(new Integer(4)); 

int i=list.get(0).parseInt(); 


2 自動裝箱/拆箱 
上面例子的最後一句可改爲: 
int i=list.get(0); 
因爲原始類型與對應的包裝類不用顯式轉換 
3 for-each 
循環的增強 
int a[]={........};//初始化 
for(int i:a) 

...... 

不用以前的i=0;i<a.length;i++ 

4 static import 
以前調Java.math 
Math.sqrt(); 
現在 static import java.lang.Math.sqrt; 
再 sqrt(); 
相當於你自己類裏有這個方法 

5 變長參數 
int sum(int ...intlist) 

int i,sum; 
sum=0; 
for(int i=0;i<intlist.length;i++) 

sum+=list[i]; 

return sum; 


有任意個參數,把他看作數組 

jdk6.0新特性
增強的for循環語句 

爲了迭代集合和數組,增強的for循環提供了一個簡單、兼容的語法

有兩點值得探討一下: 

一、在循環中,初始化表達式只計算一次。int表達式 
未增強的For: 
int sum = 0; 
Integer[] numbers = computeNumbers(); 
for (int i=0; i < numbers.length ; i++) 
sum += numbers[i]; 

增強後的For: 
int sum = 0; 
for ( int number: computeNumbers() ) 
sum += number; 

侷限性 
增強的for循環迭代期間不能訪問迭代器或下標 
請看下面的例子: 
for (int i=0; i < numbers.length ; i++) { 
if (i != 0) System.out.print(","); 
System.out.println(numbers[i]); 


這是另一個例子: 
for (Iterator<integer> it = n.iterator() ; it.hasNext() ; ) 

if (it.next() < 0) 

it.remove(); 

註釋 
註釋處理是一個很大的話題。因爲本文只關注核心的語言特性,所以我們不打算涵蓋它所有的可能形式和陷阱。 我們將討論內置的註釋(SuppressWarnings,Deprecated和Override)以及一般註釋處理的侷限性。 

Suppress Warnings 
該註釋關閉了類或方法級別的編譯器警告。有時候您比編譯器更清楚地知道,代碼必須使用一個被否決的方法或執行一些無法靜態確定是否類型安全的動作,而使用: 
@SuppressWarnings("deprecation") 
public static void selfDestruct() { 
Thread.currentThread().stop(); 


這可能是內置註釋最有用的地方。遺憾的是,1.5.0_04的javac不支持它。但是1.6支持它,並且Sun正在努力將其向後移植到1.5中。 
Eclipse 3.1中支持該註釋,其他IDE也可能支持它。這允許您把代碼徹底地從警告中解脫出來。如果在編譯時出現警告,可以確定是您剛剛把它添加進來——以幫助查看那些可能不安全的代碼。隨着泛型的添加,它使用起來將更趁手。 

Deprecated 
遺憾的是,Deprecated沒那麼有用。它本來旨在替換@deprecated javadoc標籤,但是由於它不包含任何字段,所以也就沒有方法來建議deprecated類或方法的用戶應該使用什麼做爲替代品。大多數用法都同時需要javadoc標籤和這個註釋。 

Override 
Override表示,它所註釋的方法應該重寫超類中具有相同簽名的方法: 
@Override 
public int hashCode() { 
... 


看上面的例子,如果沒有在hashCode中將“C”大寫,在編譯時不會出現錯誤,但是在運行時將無法像期望的那樣調用該方法。通過添加Override標籤,編譯器會提示它是否真正地執行了重寫。 
在超類發生改變的情況中,這也很有幫助。如果向該方法中添加一個新參數,而且方法本身也被重命名了,那麼子類將突然不能編譯,因爲它不再重寫超類的任何東西。 

其它註釋 
註釋在其他場景中非常有用。當不是直接修改行爲而是增強行爲時,特別是在添加樣板代碼的情況下,註釋在諸如EJB和Web services這樣的框架中運行得非常好。 
註釋不能用做預處理器。Sun的設計特別預防了完全因爲註釋而修改類的字節碼。這樣可以正確地理解該語言的成果,而且IDE之類的工具也可以執行深入的代碼分析和重構之類的功能。 
註釋不是銀彈。第一次遇到的時候,人們試圖嘗試各種技巧。請看下面這個從別人那裏獲得的建議: 
public class Foo {
@Property 
private int bar; 


其思想是爲私有字段bar自動創建getter和setter方法。遺憾的是,這個想法有兩個失敗之處:1)它不能運行,2)它使代碼難以閱讀和處理。 它是無法實現的,因爲前面已經提到了,Sun特別阻止了對出現註釋的類進行修改。 

即使是可能的,它也不是一個好主意,因爲它使代碼可讀性差。第一次看到這段代碼的人會不知道該註釋創建了方法。此外,如果將來您需要在這些方法內部執行一些操作,註釋也是沒用的。 總之,不要試圖用註釋去做那些常規代碼可以完成的事情。 

枚舉 
enum非常像public static final int聲明,後者作爲枚舉值已經使用了很多年。對int所做的最大也是最明顯的改進是類型安全——您不能錯誤地用枚舉的一種類型代替另一種類型,這一點和int不同,所有的int對編譯器來說都是一樣的。除去極少數例外的情況,通常都應該用enum實例替換全部的枚舉風格的int結構。 
枚舉提供了一些附加的特性。EnumMap和EnumSet這兩個實用類是專門爲枚舉優化的標準集合實現。如果知道集合只包含枚舉類型,那麼應該使用這些專門的集合來代替HashMap或HashSet。 
大部分情況下,可以使用enum對代碼中的所有public static final int做插入替換。它們是可比的,並且可以靜態導入,所以對它們的引用看起來是等同的,即使是對於內部類(或內部枚舉類型)。注意,比較枚舉類型的時候,聲明它們的指令表明了它們的順序值。 

“隱藏的”靜態方法 
兩個靜態方法出現在所有枚舉類型聲明中。因爲它們是枚舉子類上的靜態方法,而不是Enum本身的方法,所以它們在java.lang.Enum的javadoc中沒有出現。 
第一個是values(),返回一個枚舉類型所有可能值的數組。 
第二個是valueOf(),爲提供的字符串返回一個枚舉類型,該枚舉類型必須精確地匹配源代碼聲明。 
方法 
關於枚舉類型,我們最喜歡的一個方面是它可以有方法。過去您可能需要編寫一些代碼,對public static final int進行轉換,把它從數據庫類型轉換爲JDBC URL。而現在則可以讓枚舉類型本身帶一個整理代碼的方法。下面就是一個例子,包括DatabaseType枚舉類型的抽象方法以及每個枚舉實例中提供的實現: 
public enum DatabaseType { 
ORACLE { 
public String getJdbcUrl() {...} 

}, 


MYSQL { 
public String getJdbcUrl() {...} 
}; 

public abstract String getJdbcUrl(); 


現在枚舉類型可以直接提供它的實用方法。例如: 
DatabaseType dbType = ...; 
String jdbcURL = dbType.getJdbcUrl(); 
要獲取URL,必須預先知道該實用方法在哪裏。 

可變參數(Vararg) 
正確地使用可變參數確實可以清理一些垃圾代碼。典型的例子是一個帶有可變的String參數個數的log方法: 
Log.log(String code) 
Log.log(String code, String arg) 
Log.log(String code, String arg1, String arg2) 
Log.log(String code, String[] args) 

當討論可變參數時,比較有趣的是,如果用新的可變參數替換前四個例子,將是兼容的: 
Log.log(String code, String... args) 
所有的可變參數都是源兼容的——那就是說,如果重新編譯log()方法的所有調用程序,可以直接替換全部的四個方法。然而,如果需要向後的二進制兼容性,那麼就需要捨去前三個方法。只有最後那個帶一個字符串數組參數的方法等效於可變參數版本,因此可以被可變參數版本替換。 

類型強制轉換 
如果希望調用程序瞭解應該使用哪種類型的參數,那麼應該避免用可變參數進行類型強制轉換。看下面這個例子,第一項希望是String,第二項希望是Exception: 
Log.log(Object... objects) { 
String message = (String)objects[0]; 
if (objects.length > 1) { 

Exception e = (Exception)objects[1]; 
// Do something with the exception 



方法簽名應該如下所示,相應的可變參數分別使用String和Exception聲明: 

Log.log(String message, Exception e, Object... objects) {...} 

不要使用可變參數破壞類型系統。需要強類型化時纔可以使用它。對於這個規則,PrintStream.printf()是一個有趣的例外:它提供類型信息作爲自己的第一個參數,以便稍後可以接受那些類型。 

協變返回 
協變返回的基本用法是用於在已知一個實現的返回類型比API更具體的時候避免進行類型強制轉換。在下面這個例子中,有一個返回Animal對象的Zoo接口。我們的實現返回一個AnimalImpl對象,但是在JDK 1.5之前,要返回一個Animal對象就必須聲明。: 
public interface Zoo { 

public Animal getAnimal(); 



public class ZooImpl implements Zoo { 

public Animal getAnimal(){ 

return new AnimalImpl(); 
}


協變返回的使用替換了三個反模式: 

直接字段訪問。爲了規避API限制,一些實現把子類直接暴露爲字段: 

ZooImpl._animal 

另一種形式是,在知道實現的實際上是特定的子類的情況下,在調用程序中執行向下轉換: 

((AnimalImpl)ZooImpl.getAnimal()).implMethod(); 

我看到的最後一種形式是一個具體的方法,該方法用來避免由一個完全不同的簽名所引發的問題: 

ZooImpl._getAnimal(); 

這三種模式都有它們的問題和侷限性。要麼是不夠整潔,要麼就是暴露了不必要的實現細節。 

協變 
協變返回模式就比較整潔、安全並且易於維護,它也不需要類型強制轉換或特定的方法或字段: 
public AnimalImpl getAnimal(){ 
return new AnimalImpl(); 

使用結果: 
ZooImpl.getAnimal().implMethod(); 

使用泛型 
我們將從兩個角度來了解泛型:使用泛型和構造泛型。我們不討論List、Set和Map的顯而易見的用法。知道泛型集合是強大的並且應該經常使用就足夠了。 
我們將討論泛型方法的使用以及編譯器推斷類型的方法。通常這些都不會出問題,但是當出問題時,錯誤信息會非常令人費解,所以需要了解如何修復這些問題。 

泛型方法 
除了泛型類型,Java 5還引入了泛型方法。在這個來自java.util.Collections的例子中,構造了一個單元素列表。新的List的元素類型是根據傳入方法的對象的類型來推斷的: 
static <T> List<T> Collections.singletonList(T o) 
示例用法: 
public List<Integer> getListOfOne() { 

return Collections.singletonList(1); 


在示例用法中,我們傳入了一個int。所以方法的返回類型就是List<Integer>。編譯器把T推斷爲Integer。這和泛型類型是不同的,因爲您通常不需要顯式地指定類型參數。 
這也顯示了自動裝箱和泛型的相互作用。類型參數必須是引用類型:這就是爲什麼我們得到的是List<Integer>而不是List<int>。 

不帶參數的泛型方法 
emptyList()方法與泛型一起引入,作爲java.util.Collections中EMPTY_LIST字段的類型安全置換: 
static <T> List<T> Collections.emptyList() 

示例用法: 
public List<Integer> getNoIntegers() { 
return Collections.emptyList(); 

與先前的例子不同,這個方法沒有參數,那麼編譯器如何推斷T的類型呢?基本上,它將嘗試使用一次參數。如果沒有起作用,它再次嘗試使用返回或賦值類型。在本例中,返回的是List<Integer>,所以T被推斷爲Integer。 
如果在返回語句或賦值語句之外的位置調用泛型方法會怎麼樣呢?那麼編譯器將無法執行類型推斷的第二次傳送。在下面這個例子中,emptyList()是從條件運算符內部調用的: 
public List<Integer> getNoIntegers() { 
return x ? Collections.emptyList() : null; 


因爲編譯器看不到返回上下文,也不能推斷T,所以它放棄並採用Object。您將看到一個錯誤消息,比如:“無法將List<Object>轉換爲List<Integer>。” 
爲了修復這個錯誤,應顯式地向方法調用傳遞類型參數。這樣,編譯器就不會試圖推斷類型參數,就可以獲得正確的結果: 
return x ? Collections.<Integer>emptyList() : null; 

這種情況經常發生的另一個地方是在方法調用中。如果一個方法帶一個List<String>參數,並且需要爲那個參數調用這個傳遞的emptyList(),那麼也需要使用這個語法。 

集合之外 
這裏有三個泛型類型的例子,它們不是集合,而是以一種新穎的方式使用泛型。這三個例子都來自標準的Java庫: 

Class<T> 
Class在類的類型上被參數化了。這就使無需類型強制轉換而構造一個newInstance成爲可能。 
Comparable<T> 
Comparable被實際的比較類型參數化。這就在compareTo()調用時提供了更強的類型化。例如,String實現Comparable<String>。對除String之外的任何東西調用compareTo(),都會在編譯時失敗。 
Enum<E extends Enum<E>> 
Enum被枚舉類型參數化。一個名爲Color的枚舉類型將擴展Enum<Color>。getDeclaringClass()方法返回枚舉類型的類對象,在這個例子中就是一個Color對象。它與getClass()不同,後者可能返回一個無名類。 

通配符 
泛型最複雜的部分是對通配符的理解。我們將討論三種類型的通配符以及它們的用途。 
首先讓我們瞭解一下數組是如何工作的。可以從一個Integer[]爲一個Number[]賦值。如果嘗試把一個Float寫到Number[]中,那麼可以編譯,但在運行時會失敗,出現一個ArrayStoreException: 
Integer[] ia = new Integer[5]; 
Number[] na = ia; 
na[0] = 0.5; // compiles, but fails at runtime 

如果試圖把該例直接轉換成泛型,那麼會在編譯時失敗,因爲賦值是不被允許的: 
List<Integer> iList = new ArrayList<Integer>(); 
List<Number> nList = iList; // not allowed 
nList.add(0.5); 

如果使用泛型,只要代碼在編譯時沒有出現警告,就不會遇到運行時ClassCastException。 

上限通配符 
我們想要的是一個確切元素類型未知的列表,這一點與數組是不同的。 
List<Number>是一個列表,其元素類型是具體類型Number。 
List<? extends Number>是一個確切元素類型未知的列表。它是Number或其子類型。 


上限 

如果我們更新初始的例子,並賦值給List<? extends Number>,那麼現在賦值就會成功了: 
List<Integer> iList = new ArrayList<Integer>(); 
List<? extends Number> nList = iList; 
Number n = nList.get(0); 
nList.add(0.5); // Not allowed 

我們可以從列表中得到Number,因爲無論列表的確切元素類型是什麼(Float、Integer或Number),我們都可以把它賦值給Number。 
我們仍然不能把浮點類型插入列表中。這會在編譯時失敗,因爲我們不能證明這是安全的。如果我們想要向列表中添加浮點類型,它將破壞iList的初始類型安全——它只存儲Integer。 
通配符給了我們比數組更多的表達能力。 

爲什麼使用通配符 
在下面這個例子中,通配符用於向API的用戶隱藏類型信息。在內部,Set被存儲爲CustomerImpl。而API的用戶只知道他們正在獲取一個Set,從中可以讀取Customer。 
此處通配符是必需的,因爲無法從Set<CustomerImpl>向Set<Customer>賦值: 
public class CustomerFactory { 
private Set<CustomerImpl> _customers; 
public Set<? extends Customer> getCustomers() { 
return _customers; 



通配符和協變返回 
通配符的另一種常見用法是和協變返回一起使用。與賦值相同的規則可以應用到協變返回上。如果希望在重寫的方法中返回一個更具體的泛型類型,聲明的方法必須使用通配符: 
public interface NumberGenerator { 
public List<? extends Number> generate(); 

public class FibonacciGenerator extends NumberGenerator { 
public List<Integer> generate() { 
... 



如果要使用數組,接口可以返回Number[],而實現可以返回Integer[]。 

下限 
我們所談的主要是關於上限通配符的。還有一個下限通配符。List<? super Number>是一個確切“元素類型”未知的列表,但是可能是Mnumber,或者Number的超類型。所以它可能是一個List<Number>或一個List<Object>。 
下限通配符遠沒有上限通配符那樣常見,但是當需要它們的時候,它們就是必需的。 

下限與上限 
List<? extends Number> readList = new ArrayList<Integer>(); 
Number n = readList.get(0); 

List<? super Number> writeList = new ArrayList<Object>(); 
writeList.add(new Integer(5)); 

第一個是可以從中讀數的列表。 
第二個是可以向其寫數的列表。 

無界通配符 
最後,List<?>列表的內容可以是任何類型,而且它與List<? extends Object>幾乎相同。可以隨時讀取Object,但是不能向列表中寫入內容。 

公共API中的通配符 
總之,正如前面所說,通配符在向調用程序隱藏實現細節方面是非常重要的,但即使下限通配符看起來是提供只讀訪問,由於remove(int position)之類的非泛型方法,它們也並非如此。如果您想要一個真正不變的集合,可以使用java.util.Collection上的方法,比如unmodifiableList()。 
編寫API的時候要記得通配符。通常,在傳遞泛型類型時,應該嘗試使用通配符。它使更多的調用程序可以訪問API。 
通過接收List<? extends Number>而不是List<Number>,下面的方法可以由許多不同類型的列表調用: 
void removeNegatives(List<? extends Number> list); 

構造泛型類型 
現在我們將討論構造自己的泛型類型。我們將展示一些例子,其中通過使用泛型可以提高類型安全性,我們還將討論一些實現泛型類型時的常見問題。 

集合風格(Collection-like)的函數 
第一個泛型類的例子是一個集合風格的例子。Pair有兩個類型參數,而且字段是類型的實例: 
public final class Pair<A,B> { 

public final A first; 

public final B second; 

public Pair(A first, B second) { 
this.first = first; 
this.second = second; 



這使從方法返回兩個項而無需爲每個兩種類型的組合編寫專用的類成爲可能。另一種方法是返回Object[],而這樣是類型不安全或者不整潔的。 
在下面的用法中,我們從方法返回一個File和一個Boolean。方法的客戶端可以直接使用字段而無需類型強制轉換:
public Pair<File,Boolean> getFileAndWriteStatus(String path){ 
// create file and status 
return new Pair<File,Boolean>(file, status); 


Pair<File,Boolean> result = getFileAndWriteStatus("..."); 

File f = result.first; 

boolean writeable = result.second; 

集合之外 
在下面這個例子中,泛型被用於附加的編譯時安全性。通過把DBFactory類參數化爲所創建的Peer類型,您實際上是在強制Factory子類返回一個Peer的特定子類型: 
public abstract class DBFactory<T extends DBPeer> { 

protected abstract T createEmptyPeer(); 

public List<T> get(String constraint) { 

List<T> peers = new ArrayList<T>(); 
// database magic 
return peers; 


通過實現DBFactory<Customer>,CustomerFactory必須從createEmptyPeer()返回一個Customer: 

public class CustomerFactory extends DBFactory<Customer>{ 
public Customer createEmptyPeer() { 
return new Customer(); 



泛型方法 
不管想要對參數之間還是參數與返回類型之間的泛型類型施加約束,都可以使用泛型方法: 
例如,如果編寫的反轉函數是在位置上反轉,那麼可能不需要泛型方法。然而,如果希望反轉返回一個新的List,那麼可能會希望新List的元素類型與傳入的List的類型相同。在這種情況下,就需要一個泛型方法: 
<T> List<T> reverse(List<T> list) 

具體化 
當實現一個泛型類時,您可能想要構造一個數組T[]。因爲泛型是通過擦除(erasure)實現的,所以這是不允許的。 
您可以嘗試把Object[]強制轉換爲T[]。但這是不安全的。 

具體化解決方案 
按照泛型教程的慣例,解決方案使用的是“類型令牌”,通過向構造函數添加一個Class<T>參數,可以強制客戶端爲類的類型參數提供正確的類對象: 
public class ArrayExample<T> { 
private Class<T> clazz; 
public ArrayExample(Class<T> clazz) { 
this.clazz = clazz; 

public T[] getArray(int size) { 

return (T[])Array.newInstance(clazz, size); 



爲了構造ArrayExample<String>,客戶端必須把String.class傳遞給構造函數,因爲String.class的類型是Class<String>。 

擁有類對象使構造一個具有正確元素類型的數組成爲可能。


JDK暫時沒有深入研究,只曉得switch 可以用 String類型了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章