jdk1.5新特性
1.泛型 2.foreach 3.自動拆箱裝箱 4.枚舉
5.靜態導入(Static import) 6.元數據(Metadata) 7.線程池 8.Java Generics
下面詳細討論每一個新特性:
一、泛型(Generics) javascript
泛型是JDK1.5中一個最「酷」的特徵。引入泛型,咱們將得到編譯時類型的安全和運行時更小地拋出 ClassCastExceptions的可能。java
JDK1.5中,你能夠聲明一個集合將接收/返回的對象的類型。程序員
JDK1.4中,建立僱員名字的清單 (List)須要一個集合對象,像下面的語句: spring
List listOfEmployeeName = new ArrayList();
在JDK1.5中,你將使用下面語句
List<String> listOfEmployeeName = new ArrayList<String>();
最「酷」的是,若你試圖插入非string類型的值,你將在編譯時發現而且修正這類問題。沒有泛型,你會發現這樣一個bug,當你的客戶調用後會告 訴你,你所編寫的程序拋出ClassCastException異常而崩潰。
另外,當你從集合中獲得一個元素時你無需進行強制轉換。故原先爲:
String employeeName = ((String) listOfEmployee.get(i));
而下面的語句將比上面的更加簡單:
String employeeName = listOfEmployee.get(i);
不清楚對象的類型而強制轉換對象是不合理的,且更重要的是,它將在運行時失敗。假使用戶無心間傳入一個包含string buffers類型而非string類型的集合,那結果會怎樣呢。在Listing A中,客戶被要求傳入一個編譯器沒法強制的strings類型集合。Listing B中顯示了一樣的方法使用泛型是如何實現的。
Listing A
static boolean checkName(Collection employeeNameList, String name) {
for (Iteratori = employeeNamList.iterator(); i.hasNext(); ) {
String s = (String) i.next();
if(s.equals(name)){
return true;
//print employee name here ......
}
}
return false;
}
Listing B
static boolean checkName(Collection<String> employeeNameList, String name) {
for (Iteratori = employeeNamList.iterator(); i.hasNext(); ) {
if(i.next().equals(name)){
return true;
//print employee name here ......
}
}
return false;
}
如今,經過方法簽名能夠清楚知道輸入集合必須只能包含strings。若是客戶試圖傳入一個包含string buffers的集合,程序將不會編譯。同時注意,該方法不包含任何強制轉換。它只須要短短一行,一旦你習慣泛型後,它也更加清晰。
二、在JDK當前版本下的For循環語法以下:
void printAll(Collection c) {
for (Iteratori = c.iterator(); i.hasNext(); ) {
Employee emp = (Employee)i.next();
System.out.println(emp.getName());
}
}
如今,用加強的For語句實現相同方法:
void printAll(Collection c) {
for (Object o : c)
System.out.println((TimerTask)o).getName());
}
在這類For循環中,你應該將":"當作"in",因此,在該例中能夠當作"for Object o in c"。你能夠發現這種For循環更具可讀性。
三、自動置入/自動取出(Autoboxing/unboxing)
Java有基本數據類型,在這些基本數據類型周圍又有包裝類。一般,編程人員須要將一種類型轉換成另外一種。看看Listing C.中的代碼片段。
Listing C
public class Employee {
private static final Integer CHILD = new Integer(0);
public static void main(String args[]) {
//code for adding n to an Integer
int n=10;
Integer age= new Integer(30);
Integer ageAfterTenYear= new Integer(age.intValue +10);
}
}
請注意,用於計算ageAfterTenYear的內循環代碼看上去是多麼雜亂。如今,在Listing D.中看看相同的程序使用autoboxing重寫後的樣子。
Listing D
public class Employee {
public static void main(String args[]) {
int n=10;
Integer age= new Integer(30);
Integer ageAfterTenYear= age +10;
}
}
值得注意的是:先前,若你取出(unbox)Null值,它將變爲0。在次代碼中,編譯器將自動轉換Integer爲int而後加上10後將其轉換回Integer
四、類型安全的枚舉(Typesafeenums)
類型安全枚舉提供下列特性:
他們提供編譯時類型安全。
他們都是對象,所以你不須要將他們放入集合中。
他們做爲一種類的實現,所以你能夠添加一些方法。
他們爲枚舉類型提供了合適的命名空間。
他們打印的值具備情報性(informative)― 若是你打印一個整數枚舉(intenum),你只是看見一個數字,它可能並不具備情報性。
例一:
enum Season { winter, spring, summer, fall }
例二:
public enum Coin {
penny(1), nickel(5), dime(10), quarter(25); 數據庫
Coin(int value) { this.value = value; } 編程
private final int value;
public int value() { return value; }
}
五、靜態導入(Static import)
靜態導入使代碼更易讀。一般,你要使用定義在另外一個類中的常量(constants),像這樣:
import org.yyy.pkg.Increment;
class Employee {
public Double calculateSalary(Double salary{
return salary + Increment.INCREMENT * salary;
}
}
當時使用靜態導入,咱們無需爲常量名前綴類名就能使用這些常量,像這樣:
import static org.yyy.pkg.Increment;
class Employee {
public Double calculateSalary(Double salary{
return salary + INCREMENT * salary;
}
}
注意,咱們能夠調用INCREMENT這一常量而不要使用類名Increment.。
六、元數據(Metadata)
元數據特徵志於使開發者們藉助廠商提供的工具能夠進行更簡易的開發。看一看Listing E.中的代碼。
Listing E
import org.yyy.hr;
public interface EmployeeI extends Java.rmi.Remote {
public String getName() throwsJava.rmi.RemoteException;
public String getLocation () throwsJava.rmi.RemoteException;
}
public class EmployeeImpl implements EmployeeI {
public String getName(){
}
public String getLocation (){
}
}
經過元數據的支持,你能夠改寫Listing E中的代碼爲:
import org.yyy.hr;
public class Employee {
@Remote public String getName() {
...
}
@Remote public public String getLocation() {
...
}
}
7線程池
Java5中,對Java線程的類庫作了大量擴展,其中線程池就是Java5的新特徵之一,除了線程池以外,還有不少多線程相關內容,爲多線程的編程帶來了極大便利。爲了編寫高效穩定可靠的多線程程序,線程部分的新增內容顯得尤其重要。數組
有關Java5線程新特徵的內容所有在Java.util.concurrent下面,裏面包含數目衆多的接口和類,熟悉這部分API特徵是一項艱難的學習過程。目前有關這方面的資料和書籍都少之又少,大所屬介紹線程方面書籍還停留在java5以前的知識層面上。固然新特徵對作多線程程序沒有必須的關係,在java5以前通用能夠寫出很優秀的多線程程序。只是代價不同而已。瀏覽器
線程池的基本思想仍是一種對象池的思想,開闢一塊內存空間,裏面存放了衆多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣能夠避免反覆建立線程對象所帶來的性能開銷,節省了系統的資源。
Java5以前,要實現一個線程池是至關有難度的,如今Java5爲咱們作好了一切,咱們只需按照提供的API來使用,便可享受線程池帶來的極大便利。
Java5的線程池分好多種:固定尺寸的線程池、可變尺寸鏈接池、。
在使用線程池以前,必須知道如何去建立一個線程池,在Java5中,須要瞭解的是java.util.concurrent.Executors類的API,這個類提供大量建立鏈接池的靜態方法,是必須掌握的。
1、固定大小的線程池
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
/**
* Java線程:線程池
*/
public class Test {
public static void main(String[] args) {
//建立一個可重用固定線程數的線程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//建立實現了Runnable接口對象,Thread對象固然也實現了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
//將線程放入池中進行執行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
//關閉線程池
pool.shutdown();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在執行。。。");
}
}
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-2正在執行。。。
Process finished with exit code 0安全
2、單任務線程池ruby
在上例的基礎上改一行建立pool對象的代碼爲:
//建立一個使用單個 worker 線程的 Executor,以無界隊列方式來運行該線程。
ExecutorService pool = Executors.newSingleThreadExecutor();
輸出結果爲:
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
Process finished with exit code 0
對於以上兩種鏈接池,大小都是固定的,當要加入的池的線程(或者任務)超過池最大尺寸時候,則入此線程池須要排隊等待。
一旦池中有線程完畢,則排隊等待的某個線程會入池執行。
3、可變尺寸的線程池
與上面的相似,只是改動下pool的建立方式:
//建立一個可根據須要建立新線程的線程池,可是在之前構造的線程可用時將重用它們。
ExecutorService pool = Executors.newCachedThreadPool();
pool-1-thread-5正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-4正在執行。。。
pool-1-thread-3正在執行。。。
pool-1-thread-2正在執行。。。
Process finished with exit code 0
4、延遲鏈接池
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* Java線程:線程池
*/
public class Test {
public static void main(String[] args) {
//建立一個線程池,它可安排在給定延遲後運行命令或者按期地執行。
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
//建立實現了Runnable接口對象,Thread對象固然也實現了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
//將線程放入池中進行執行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
//使用延遲執行風格的方法
pool.schedule(t4, 10, TimeUnit.MILLISECONDS);
pool.schedule(t5, 10, TimeUnit.MILLISECONDS);
//關閉線程池
pool.shutdown();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在執行。。。");
}
}
pool-1-thread-1正在執行。。。
pool-1-thread-2正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-2正在執行。。。
Process finished with exit code 0
5、單任務延遲鏈接池
在四代碼基礎上,作改動
//建立一個單線程執行程序,它可安排在給定延遲後運行命令或者按期地執行。
ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-1正在執行。。。
Process finished with exit code 0
6、自定義線程池
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Java線程:線程池-自定義線程池
*/
public class Test {
public static void main(String[] args) {
//建立等待隊列
BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20);
//建立一個單線程執行程序,它可安排在給定延遲後運行命令或者按期地執行。
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue);
//建立實現了Runnable接口對象,Thread對象固然也實現了Runnable接口
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
Thread t6 = new MyThread();
Thread t7 = new MyThread();
//將線程放入池中進行執行
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
pool.execute(t7);
//關閉線程池
pool.shutdown();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在執行。。。");
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
pool-1-thread-1正在執行。。。
pool-1-thread-2正在執行。。。
pool-1-thread-2正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-2正在執行。。。
pool-1-thread-1正在執行。。。
pool-1-thread-2正在執行。。。
Process finished with exit code 0
建立自定義線程池的構造方法不少,本例中參數的含義以下:
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
用給定的初始參數和默認的線程工廠及處理程序建立新的 ThreadPoolExecutor。使用 Executors 工廠方法之一比使用此通用構造方法方便得多。
參數:
corePoolSize - 池中所保存的線程數,包括空閒線程。
maximumPoolSize - 池中容許的最大線程數。
keepAliveTime - 當線程數大於核心時,此爲終止前多餘的空閒線程等待新任務的最長時間。
unit - keepAliveTime 參數的時間單位。
workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute 方法提交的 Runnable 任務。
拋出:
IllegalArgumentException - 若是 corePoolSize 或 keepAliveTime 小於零,或者 maximumPoolSize 小於或等於零,或者 corePoolSize 大於 maximumPoolSize。
NullPointerException - 若是 workQueue 爲 null
自定義鏈接池稍麻煩些,不過經過建立的ThreadPoolExecutor線程池對象,能夠獲取到當前線程池的尺寸、正在執行任務的線程數、工做隊列等等。
8.Java Generics
在JDK1.5以前的版本中,對於一個Collection類庫中的容器類實例,可將任意類型
對象加入其中(都被看成Object實例看待);從容器中取出的對象也只是一個Object實例,須要將其強制轉型爲期待的類型,這種強制轉型的運行時正確性由程序員自行保證。
例如如下代碼片段:
List intList = new ArrayList(); //建立一個List,準備存放一些Integer實例
intList.add(new Integer(0));
intList.add(「1」); //不當心加入了一個字符串;但在編譯和運行時都不報錯,只有仔細的代碼走才能揪出
Integer i0 = (Integer)intList.get(0);
Integer i1 = (Integer)intList.get(1); //編譯經過,直到運行時才拋ClassCastException
而在JDK1.5中,能夠建立一個明確只能存放某種特定類型對象的容器類實例,例如以下代碼:
List<Integer> intList = new ArrayList<Integer>(); //intList爲存放Integer實例的List
intList.add(new Integer(0));
Integer i0 = intList.get(0); //無需(Integer)強制轉型;List<Integer>的get()返回的就是Integer類型對象
intList.add(「1」); //編譯不經過,由於List<Integer>的add()方法只接受Integer類型的參數
「List<Integer> intList = new ArrayList<Integer>();」就是最簡單且最經常使用的Generic應用;顯然,運用Generic後的代碼更具可讀性和健壯性。
2 Generic類
JDK1.5中Collection類庫的大部分類都被改進爲Generic類。如下是從JDK1.5源碼中
截取的關於List和Iterator接口定義的代碼片段:
public interface List<E> {
void add(E x);
Iterator<E> iterator;
}
public interface Iterator<E> {
E next();
boolean hasNext();
}
以List爲例,「public interface List<E>」中的E是List的類型參數,用戶在使用List時可爲類型參數指定一個肯定類型值(如List<Integer>)。
類型值爲Java編譯器所用,確保用戶代碼類型安全。例如,編 譯器知道List<Integer>的add()方法只接受Integer類型的參數,所以若是你在代碼中將一個字符串傳入add()將致使 編譯錯誤;
編譯器知道Iterator<Integer>的next()方法返回一個Integer的實例,你在代碼中也就無需對返回結果進 行(Integer)強制轉型。
代碼檢驗經過(語法正確且不會致使運行時類型安全問題)後,編譯器對現有代碼有一個轉換工做。簡單的說,就是去除代碼中的 類型值信息,在必要處添加轉型代碼。例如以下代碼:
public String demo() {
List<String> strList = new ArrayList<String>();
strList.add(「Hello!」);
return strList.get(0);
}
編譯器在檢驗經過後,將其轉換爲:
public String demo() {
List strList = new ArrayList(); //去除類型值<String>
strList.add(「Hello!」);
return (String)strList.get(0); //添加轉型動做代碼(String)
}
可見,類型值信息只爲Java編譯器在編譯時所用,確保代碼無類型安全問題;驗證經過以後,即被去除。
對於JVM而言,只有如JDK1.5以前版本同樣的 List,並沒有List<Integer>和List<String>之分。這也就是Java Generics實現中關鍵技術Erasure的基本思想。如下代碼在控制檯輸出的就是「true」。
List<String> strList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();
System.out.println(strList.getClass() == intList.getClass());
能夠將Generic理解爲:爲提升Java代碼類型安全性(在編譯時確保,而非等到運行時才暴露),Java代碼與Java編譯器之間新增的一種約定 規 範。Java編譯器在編譯結果*.class文件中供JVM讀取的部分裏沒有保留Generic的任何信息;JVM看不到Generic的存在。
對於Generic類(設爲GenericClass)的類型參數(設爲T):
1) 因爲對於JVM而言,只有一個GenericClass類,因此GenericClass類的靜態字段和靜態方法的定義中不能使用T。
T只能出如今 GenericClass的非靜態字段或非靜態方法中。也即T是與GenericClass的實例相關的信息;
2)T只在編譯時被編譯器理解,所以也就不能與運行時被JVM理解並執行其表明的操做的操做符(如instanceof 和new)聯用。
class GenericClass<T> {
T t1;
public void method1(T t){
t1 = new T(); //編譯錯誤,T不能與new聯用
if (t1 instanceof T) {}; //編譯錯誤,T不能與instanceof聯用
};
static T t2; //編譯錯誤,靜態字段不能使用T
public static void method2(T t){};//編譯錯誤,靜態方法不能使用T
}
Generic類能夠有多個類型參數,且類型參數命名通常爲大寫單字符。例如Collection類庫中的Map聲明爲:
public interface Map<K,V> {
……;
}
3 Generic類和原(Raw)類
對每個Generic類,用戶在使用時能夠不指定類型參數。例如,對於List<E>,用戶能夠以「List<String> list;」方式使用,也能夠以「List list;」方式使用。
「List<String>」被稱爲參數化的Generic類(類型參數被賦值),而「List」稱爲原類。原類 List的使用方式和效果與JDK1.5以前版本List的同樣;使用原類也就失去了Generic帶來的可讀性和健壯性的加強。
容許原類使用方式的存在顯然是爲了代碼的向前兼容:即JDK1.5以前的代碼在JDK1.5下仍然編譯經過且正常運行。
當你在JDK1.5中使用原類並向原類實例中添加對象時,編譯器會產生警告,由於它沒法保證待添加對象類型的正確性。編譯經過是爲了保證代碼向前兼容,產生警告是提醒潛在的風險。
public void test () {
List list = new ArrayList();
list.add("tt");//JDK1.5編譯器對此行產生警告
}
4 Generic類和子類
List<String> ls = new ArrayList<String>();
List<Object> lo = ls; //編譯錯誤:Type mismatch: cannot convert from List<Dummy> to List<Object>
以上第二行代碼致使的編譯錯誤「Type mismatch: cannot convert from List<Dummy> to List<Object>」是否是有點出人意料?
直觀上看,就像String是Object的子類,所以‘Object o = 「String」’合法同樣,存放String的List是存放Object的List的子類,所以第二行應該是合法的。反過來分析,若是第二行是合法 的,那麼以下會致使運行時異常的代碼也是合法的:
lo.add(new Object); //會致使在ls中添加了非String對象
String s = ls.get(0); //ls.get(0)返回的實際上只是一個Object實例,會致使ClassCastException
編譯器顯然不容許此種情形發生,所以不容許「List<Object> lo = ls」編譯經過。
所以,直觀上的「存放String的List是存放Object的List的子類」是錯誤的。更通常的說,設Foo是Bar的子類,G是Generic類型聲明,G<Foo>不是G<Bar>的子類。
5 參數化的Generic類和數組
咱們知道,若是T是S的子類,則T[]也是S[]的子類。所以,以下代碼編譯經過,只
在運行時於第三行代碼處拋ArrayStoreException。
String[] words = new String[10];
Object[] objects = words;
Objects[0] = new Object(); //編譯經過,但運行時會拋ArrayStoreException
再分析以下代碼:
List<String>[] wordLists = new ArrayList<String>[10];
ArrayList<Integer> integerList = new ArrayList<Integer>();
integerList.add(123);
Object[] objects = wordLists;
objects[0] = integerList;//運行時不出錯,由於運行時ArrayList<String>和ArrayList<Integer>都爲ArrayList
String s = wordlists[0].get(0); //編譯經過,運行時拋ClassCastException
就出現了「正確使用了Generic,但在運行時仍然出現ClassCastException」的情形。顯然Java編譯器不容許此種情形的發生。
事實上,以上代碼的第一行「List<String>[] wordLists = new ArrayList<String>[10];」就是編譯不經過的,也就不存在接下來的代碼。
更通常地說,不能建立參數化的Generic類的數組。
6 類型參數通配符?
由「Generic類和子類」節知,Collection<Object>不是存放其它類型對象的Collection(例如Collection<String>)的基類(抽象),那麼如何表示任一種參數化的Collection的呢?使用 Collection<?>。
?即表明任一類型參數值。例如,咱們能夠很容易寫出下面的通用函數printCollection():
public static void printCollection(Collection<?> c) {
//如此遍歷Collection的簡潔方式也是JDK1.5新引入的
for (Object o : c) {
System.out.println(o);
}
}
這樣,既能夠將Collection<String>的實例,也能夠將Collection<Integer>的實例做爲參數調用printCollection()方法。
然而,要注意一點,你不能往Collection<?>容器實例中加入任何非null元素,例如以下代碼的第三行編譯不經過:
public static void testAdd(Collection<?> c) {
c.add(null); //編譯經過
c.add(「test」); //編譯錯誤
}
很好理解:c中要存放的對象的具體類型不肯定,編譯器沒法驗證待添加對象類型的正確性,所以不能加入對象實例;而null能夠看做是任一個類的實例,於是容許加入。
另外,儘管c中要存放的對象的類型不肯定,但咱們知道任何類都是Object子類,所以從c中取出的對象都統一做爲Object實例。
更進一步,若是BaseClass表明某個可被繼承的類的類名,那麼Collection<? extends BaseClass>表明類型參數值爲BaseClass或BaseClass某個子類的任一參數化Collection。對於 Collection<? extends BaseClass>的實例c,由於c中要存放的對象具體類型不肯定,不能往其加入非null對象,但從c中取出的對象都統一做爲 BaseClass實例。事實上,你能夠把Collection<?>看做Collection<? extends Object>的簡潔形式。
另外一種情形:若是SubClass表明任一個類的類名,那麼Collection<? super SubClass>表明類型參數值爲SubClass或SubClass某個祖先類的任一參數化Collection。對於 Collection<? super SubClass>的實例c,你能夠將SubClass實例加入其中,但從中取出的對象都是Object實例。
7 Generic方法
咱們能夠定義Generic類,一樣能夠定義Generic方法,即將方法的一個或多個參數的類型參數化,如代碼:
public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); //合法。注意與Collection<?>的區別
}
}
咱們能夠以以下方式調用fromArrayToCollection():
Object[] oa = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(oa, co); //此時,T即爲Object
String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();
fromArrayToCollection(sa, cs); //此時,T即爲String
fromArrayToCollection(sa, co); //此時,T即爲Object
Integer[] ia = new Integer[100];
Float[] fa = new Float[100];
Number[] na = new Number[100];
Collection<Number> cn = new ArrayList<Number>();
fromArrayToCollection(ia, cn); //此時,T即爲Number
fromArrayToCollection(fa, cn); //此時,T即爲Number
fromArrayToCollection(na, cn); //此時,T即爲Number
fromArrayToCollection(na, co); //此時,T即爲Object
fromArrayToCollection(na, cs); //編譯錯誤
經過以上代碼能夠看出,咱們在調用fromArrayToCollection()時,無需明確指定T爲什麼種類型(與Generic類的使用方式不一樣), 而是像調用通常method同樣,直接提供參數值,編譯器會根據提供的參數值自動爲T賦類型值或提示編譯錯誤(參數值不當)。
考慮以下函數sum()
public static long sum(Collection<? extends Number> numbers) {
long sum = 0;
for (Number n : numbers) {
sum += n.longValue();
}
return sum;
}
咱們也能夠將其以Generic方法實現:
public static <T extends Number> long sum(Collection<T> numbers) {
long sum = 0;
for (Number n : numbers) {
sum += n.longValue();
}
return sum;
}
那麼對於一個方法,當要求參數類型可變時,是採用Generic方法,仍是採用類型參數通配符方式呢?通常而言,若是參數類型間或參數類型與返回值類型間存在某種依賴關係,則採起Generic方法,不然採起類型參數通配符方式。
這一原則在Collection類庫的源代碼中獲得了很好的體現,例如Collection接口的containsAll()、addAll()和toArray()方法:
interface Collection<E> {
public boolean containsAll(Collecion<?> c); //參數間類型以及參數與返回
//值間類型無依賴
<T> T[] toArray(T[] a); //參數a與返回值都是相同類的數組,有依賴
}
固然,根據須要,兩者也能夠結合使用,例如Collections中的copy()方法:
class Collections {
public static <T> void copy(List<T> dest, List<? extends T> src) {
…….
}
}
jdk1.6新特性
1.Desktop類和SystemTray類 2.使用JAXB2來實現對象與XML之間的映射 3.StAX
4.使用Compiler API 5.輕量級Http Server API 6.插入式註解處理API(Pluggable Annotation Processing API)
7.用Console開發控制檯程序 8.對腳本語言的支持 9.Common Annotations
1.Desktop類和SystemTray類
在JDK1.6中,AWT新增長了兩個類:Desktop和SystemTray.
前者能夠用來打開系統默認瀏覽器瀏覽指定的URL,打開系統默認郵件客戶端給指定的郵箱發郵件,用默認應用程序打開或編輯文件(好比,用記事本打開以txt爲後綴名的文件),用系統默認的打印機打印文檔;後者能夠用來在系統托盤區建立一個托盤程序.
2.使用JAXB2來實現對象與XML之間的映射
JAXB是Java Architecture for XML Binding的縮寫,能夠將一個Java對象轉變成爲XML格式,反之亦然.
我 們把對象與關係數據庫之間的映射稱爲ORM,其實也能夠把對象與XML之間的映射稱爲OXM(Object XML Mapping).原來JAXB是Java EE的一部分,在JDK1.6中,SUN將其放到了Java SE中,這也是SUN的一向作法.JDK1.6中自帶的這個JAXB版本是2.0,比起1.0(JSR 31)來,JAXB2(JSR 222)用JDK5的新特性Annotation來標識要做綁定的類和屬性等,這就極大簡化了開發的工做量.實際上,在Java EE 5.0中,EJB和Web Services也經過Annotation來簡化開發工做.另外,JAXB2在底層是用StAX(JSR 173)來處理XML文檔.除了JAXB以外,咱們還能夠經過XMLBeans和Castor等來實現一樣的功能.
3.StAX
StAX(JSR 173)是JDK1.6.0中除了DOM和SAX以外的又一種處理XML文檔的API.
StAX 的來歷:在JAXP1.3(JSR 206)有兩種處理XML文檔的方法:DOM(Document Object Model)和SAX(Simple API for XML).
JDK1.6.0 中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都會用到StAXSun決定把StAX加入到JAXP家族當中來,並將JAXP的版本升級到1.4(JAXP1.4是JAXP1.3的維護版 本).JDK1.6裏面JAXP的版本就是1.4.
StAX 是The Streaming API for XML的縮寫,一種利用拉模式解析(pull-parsing)XML文檔的API.StAX經過提供一種基於事件迭代器(Iterator)的API讓 程序員去控制xml文檔解析過程,程序遍歷這個事件迭代器去處理每個解析事件,解析事件能夠看作是程序拉出來的,也就是程序促使解析器產生一個解析事件 而後處理該事件,以後又促使解析器產生下一個解析事件,如此循環直到碰到文檔結束符;
SAX 也是基於事件處理xml文檔,但倒是用推模式解析,解析器解析完整個xml文檔後,才產生解析事件,而後推給程序去處理這些事件;DOM採 用的方式是將整個xml文檔映射到一顆內存樹,這樣就能夠很容易地獲得父節點和子結點以及兄弟節點的數據,但若是文檔很大,將會嚴重影響性能.
4.使用Compiler API
如今我 們能夠用JDK1.6 的Compiler API(JSR 199)去動態編譯Java源文件,Compiler API結合反射功能就能夠實現動態的產生Java代碼並編譯執行這些代碼,有點動態語言的特徵.
這 個特性對於某些須要用到動態編譯的應用程序至關有用,好比JSP Web Server,當咱們手動修改JSP後,是不但願須要重啓Web Server才能夠看到效果的,這時候咱們就能夠用Compiler API來實現動態編譯JSP文件,固然,如今的JSP Web Server也是支持JSP熱部署的,如今的JSP Web Server經過在運行期間經過Runtime.exec或ProcessBuilder來調用javac來編譯代碼,這種方式須要咱們產生另外一個進程去 作編譯工做,不夠優雅容易使代碼依賴與特定的操做系統;Compiler API經過一套易用的標準的API提供了更加豐富的方式去作動態編譯,是跨平臺的.
5.輕量級Http Server API
JDK1.6 提供了一個簡單的Http Server API,據此咱們能夠構建本身的嵌入式Http Server,它支持Http和Https協議,提供了HTTP1.1的部分實現,沒有被實現的那部分能夠經過擴展已有的Http Server API來實現,程序員本身實現HttpHandler接口,HttpServer會調用HttpHandler實現類的回調方法來處理客戶端請求,在這 裏,咱們把一個Http請求和它的響應稱爲一個交換,包裝成HttpExchange類,HttpServer負責將HttpExchange傳給 HttpHandler實現類的回調方法.
6.插入式註解處理API(Pluggable Annotation Processing API)
插入式註解處理API(JSR 269)提供一套標準API來處理Annotations(JSR 175)
實 際上JSR 269不只僅用來處理Annotation,我以爲更強大的功能是它創建了Java 語言自己的一個模型,它把method,package,constructor,type,variable, enum,annotation等Java語言元素映射爲Types和Elements(二者有什麼區別?),從而將Java語言的語義映射成爲對象,我 們能夠在javax.lang.model包下面能夠看到這些類. 咱們能夠利用JSR 269提供的API來構建一個功能豐富的元編程(metaprogramming)環境.
JSR 269用Annotation Processor在編譯期間而不是運行期間處理Annotation,Annotation Processor至關於編譯器的一個插件,稱爲插入式註解處理.若是Annotation Processor處理Annotation時(執行process方法)產生了新的Java代碼,編譯器會再調用一次Annotation Processor,若是第二次處理還有新代碼產生,就會接着調用Annotation Processor,直到沒有新代碼產生爲止.每執行一次process()方法被稱爲一個"round",這樣整個Annotation processing過程能夠看做是一個round的序列.
JSR 269主要被設計成爲針對Tools或者容器的API. 舉個例子,咱們想創建一套基於Annotation的單元測試框架(如TestNG),在測試類裏面用Annotation來標識測試期間須要執行的測試方法.
7.用Console開發控制檯程序
JDK1.6 中提供了java.io.Console 類專用來訪問基於字符的控制檯設備.你的程序若是要與Windows下的cmd或者Linux下的Terminal交互,就能夠用Console類代勞. 但咱們不老是能獲得可用的Console,一個JVM是否有可用的Console依賴於底層平臺和JVM如何被調用.若是JVM是在交互式命令行(好比 Windows的cmd)中啓動的,而且輸入輸出沒有重定向到另外的地方,那麼就能夠獲得一個可用的Console實例.
8.對腳本語言的支持
如: ruby,groovy,javascript
9.Common Annotations
Common annotations本來是Java EE 5.0(JSR 244)規範的一部分,如今SUN把它的一部分放到了Java SE 6.0中.
隨 着Annotation元數據功能(JSR 175)加入到Java SE 5.0裏面,不少Java 技術(好比EJB,Web Services)都會用Annotation部分代替XML文件來配置運行參數(或者說是支持聲明式編程,如EJB的聲明式事務),若是這些技術爲通用 目的都單獨定義了本身的Annotations,顯然有點重複建設,,爲其餘相關的Java技術定義一套公共的Annotation是有價值的,能夠避免 重複建設的同時,也保證Java SE和Java EE 各類技術的一致性.
下 面列舉出Common Annotations 1.0裏面的10個Annotations Common Annotations Annotation Retention Target Description Generated Source ANNOTATION_TYPE,CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE 用於標註生成的源代碼Resource Runtime TYPE,METHOD,FIELD用於標註所依賴的資源,容器據此注入外部資源依賴,有基於字段的注入和基於setter方法的注入兩種方式 Resources Runtime TYPE同時標註多個外部依賴,容器會把全部這些外部依賴注入PostConstruct Runtime METHOD標註當容器注入全部依賴以後運行的方法,用來進行依賴注入後的初始化工做,只有一個方法能夠標註爲PostConstruct PreDestroy Runtime METHOD當對象實例將要被從容器當中刪掉以前,要執行的回調方法要標註爲PreDestroy RunAs Runtime TYPE用於標註用什麼安全角色來執行被標註類的方法,這個安全角色和Container的Security角色一致的.RolesAllowed Runtime TYPE,METHOD用於標註容許執行被標註類或方法的安全角色,這個安全角色和Container的Security角色一致的PermitAll Runtime TYPE,METHOD容許全部角色執行被標註的類或方法DenyAll Runtime TYPE,METHOD不容許任何角色執行被標註的類或方法,代表該類或方法不能在Java EE容器裏面運行DeclareRoles Runtime TYPE用來定義能夠被應用程序檢驗的安全角色,一般用isUserInRole來檢驗安全角色.
jdk1.7新特性
1 對集合類的語言支持; 2 自動資源管理; 3 改進的通用實例建立類型推斷; 4 數字字面量下劃線支持;
5 switch中使用string; 6 二進制字面量; 7 簡化可變參數方法調用。
下面咱們來仔細看一下這7大新功能:
1 對集合類的語言支持
Java將包含對建立集合類的第一類語言支持。這意味着集合類的建立能夠像Ruby和Perl那樣了。
本來須要這樣:
List<String> list = new ArrayList<String>();
list.add("item");
String item = list.get(0);
Set<String> set = new HashSet<String>();
set.add("item");
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("key", 1);
int value = map.get("key");
如今你能夠這樣:
List<String> list = ["item"];
String item = list[0];
Set<String> set = {"item"};
Map<String, Integer> map = {"key" : 1};
int value = map["key"];
這些集合是不可變的。
2 自動資源管理
Java中某些資源是須要手動關閉的,如InputStream,Writes,Sockets,Sql classes等。這個新語言特性容許try語句自己申請更多資源,
這些資源做用於try代碼塊,並自動關閉。
這個:
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
br.close();
}
變成了這個:
try (BufferedReader br = new BufferedReader(new FileReader(path)) {
return br.readLine();
}
你能夠定義關閉多個資源:
try (
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest))
{
// code
}
爲了支持這個行爲,全部可關閉的類將被修改成能夠實現一個Closable(可關閉的)接口。
3 加強的對通用實例建立(diamond)的類型推斷
類型推斷是一個特殊的煩惱,下面的代碼:
Map<String, List<String>> anagrams = new HashMap<String, List<String>>();
經過類型推斷後變成:
Map<String, List<String>> anagrams = new HashMap<>();
這個<>被叫作diamond(鑽石)運算符,這個運算符從引用的聲明中推斷類型。
4 數字字面量下劃線支持
很長的數字可讀性很差,在Java 7中可使用下劃線分隔長int以及long了,如:
int one_million = 1_000_000;
運算時先去除下劃線,如:1_1 * 10 = 110,120 – 1_0 = 110
5 switch中使用string
之前你在switch中只能使用number或enum。如今你可使用string了:
String s = ...
switch(s) {
case "quux":
processQuux(s);
// fall-through
case "foo":
case "bar":
processFooOrBar(s); break;
case "baz":
processBaz(s);
// fall-through
default:
processDefault(s); break;
}
6 二進制字面量
因爲繼承C語言,Java代碼在傳統上迫使程序員只能使用十進制,八進制或十六進制來表示數(numbers)。
因爲不多的域是以bit導向的,這種限制可能致使錯誤。你如今可使用0b前綴建立二進制字面量:
int binary = 0b1001_1001;
如今,你可使用二進制字面量這種表示方式,而且使用很是簡短的代碼,可將二進制字符轉換爲數據類型,如在byte或short。
byte aByte = (byte)0b001;
short aShort = (short)0b010;
7 簡化的可變參數調用
當程序員試圖使用一個不可具體化的可變參數並調用一個*varargs* (可變)方法時,編輯器會生成一個「非安全操做」的警告。
JDK 7將警告從call轉移到了方法聲明(methord declaration)的過程當中。這樣API設計者就可使用vararg,由於警告的數量大大減小了。