想更詳細的熟悉JDK7新特性可以瀏覽官方介紹
JDK7新特性的目錄導航:
- 二進制字面值
- switch 語句支持 String
- try-with-resources
- catch 多個類型異常
- 字面值中使用下划線
- 類型推斷
- 改進泛型類型可變參數
- 其它
二進制字面值
在Java SE 7,整數類型(byte,short,int 和 long)也可以使用二進制數。要指定二進制,請添加前綴0b或0B編號。以下示例顯示了二進制:
// 一個 8-bit 'byte' 值: byte aByte = (byte)0b00100001; // 一個 16-bit 'short' 值: short aShort = (short)0b1010000101000101; // 一些 32-bit 'int' 值: int anInt1 = 0b10100001010001011010000101000101; int anInt2 = 0b101; int anInt3 = 0B101; // B可以是大寫 或 小寫. // 一個 64-bit 'long' 值。 注意 'L' 后綴: long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;
二進制可以使數據之間的關系比以十六進制或八進制更明顯。例如,以下數組中的每個連續數字都移動一位:
public static final int [] phases = { 0b00110001, 0b01100010, 0b11000100, 0b10001001, 0b00010011, 0b00100110, 0b01001100, 0b10011000 }
在十六進制中,數字之間的關系並不明顯:
public static final int[] phases = { 0x31, 0x62, 0xC4, 0x89, 0x13, 0x26, 0x4C, 0x98 }
您可以在代碼中使用二進制常量來驗證一個規范文檔,(例如假想的8位微處理器的模擬器)進行驗證:
public State decodeInstruction(int instruction, State state) { if ((instruction & 0b11100000) == 0b00000000) { final int register = instruction & 0b00001111; switch (instruction & 0b11110000) { case 0b00000000: return state.nop(); case 0b00010000: return state.copyAccumTo(register); case 0b00100000: return state.addToAccum(register); case 0b00110000: return state.subFromAccum(register); case 0b01000000: return state.multiplyAccumBy(register); case 0b01010000: return state.divideAccumBy(register); case 0b01100000: return state.setAccumFrom(register); case 0b01110000: return state.returnFromCall(); default: throw new IllegalArgumentException(); } } else { final int address = instruction & 0b00011111; switch (instruction & 0b11100000) { case 0b00000000: return state.jumpTo(address); case 0b00100000: return state.jumpIfAccumZeroTo(address); case 0b01000000: return state.jumpIfAccumNonzeroTo(address); case 0b01100000: return state.setAccumFromMemory(address); case 0b10100000: return state.writeAccumToMemory(address); case 0b11000000: return state.callTo(address); default: throw new IllegalArgumentException(); } } }
您可以使用二進制來使位圖更具可讀性:
public static final short[] HAPPY_FACE = { (short)0b0000011111100000, (short)0b0000100000010000, (short)0b0001000000001000, (short)0b0010000000000100, (short)0b0100000000000010, (short)0b1000011001100001, (short)0b1000011001100001, (short)0b1000000000000001, (short)0b1000000000000001, (short)0b1001000000001001, (short)0b1000100000010001, (short)0b0100011111100010, (short)0b0010000000000100, (short)0b0001000000001000, (short)0b0000100000010000, (short)0b0000011111100000 }
switch 語句支持 String
在JDK7中,可以swicth表達式中使用String對象:
public String getTypeOfDayWithSwitchStatement(String dayOfWeekArg) { String typeOfDay; switch (dayOfWeekArg) { case "Monday": typeOfDay = "Start of work week"; break; case "Tuesday": case "Wednesday": case "Thursday": typeOfDay = "Midweek"; break; case "Friday": typeOfDay = "End of work week"; break; case "Saturday": case "Sunday": typeOfDay = "Weekend"; break; default: throw new IllegalArgumentException("Invalid day of the week: " + dayOfWeekArg); } return typeOfDay; }
該switch語句將String其表達式中的對象與與每個case標簽關聯的表達式進行比較,就好像它使用該String.equals方法一樣;因此,代碼中String對象的比較switch區分大小寫。Java編譯器通過switch使用String對象的 if-then-else 語句比鏈式語句生成通常更高效的字節碼。
try-with-resources
try-with-resources可以自動關閉相關的資源(只要該資源實現了AutoCloseable接口,jdk7為絕大部分資源對象都實現了這個接口)
以下示例從文件讀取一行。它使用一個BufferedReader從文件中讀取數據的實例。BufferedReader是程序結束后必須關閉的資源:
static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }
在此示例中,在try-with-resources語句中聲明的資源是一個 BufferedReader。聲明語句出現在try關鍵字后面的括號內。該類BufferedReader在Java SE 7及更高版本中實現該接口java.lang.AutoCloseable。由於BufferedReader實例是在try-with-resource語句中聲明的,因此無論try語句是正常還是意外中斷(若BufferedReader.readLine拋出一個IOException),它都將被關閉。
public interface Closeable extends AutoCloseable public abstract class Reader implements Readable, Closeable public class BufferedReader extends Reader
如上是BufferedReader的繼承關系,他最終有實現AutoCloseable接口。
在JDK7之前,無論try語句是正常還是意外中斷,你都可以使用finally塊來確保資源已關閉。以下示例使用finally代替try-with-resources語句的塊:
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { if (br != null) br.close(); } }
然而,在這個例子中,如果方法readLine和close兩者都拋出異常,那么readFirstLineFromFileWithFinallyBlock方法拋出從finally塊拋出的異常;從try塊中拋出的異常被抑制。
相反,在本例中readFirstLineFromFile,如果try塊和try-with-resources語句都拋出異常,則此方法拋出該try塊拋出的異常;從try-with-resources塊拋出的異常被抑制。
您可以在try-with-resources語句中聲明一個或多個資源。以下示例:
public static void writeToFileZipFileContents(String zipFileName, String outputFileName) throws java.io.IOException { java.nio.charset.Charset charset = java.nio.charset.Charset.forName("US-ASCII"); java.nio.file.Path outputFilePath = java.nio.file.Paths.get(outputFileName); // Open zip file and create output file with try-with-resources statement try ( java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName); java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset) ) { // Enumerate each entry for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) { // Get the entry name and write it to the output file String newLine = System.getProperty("line.separator"); String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; writer.write(zipEntryName, 0, zipEntryName.length()); } } }
在本例中,try-with-resources語句有兩個聲明ZipFile 和 BufferedWriter使用分號分隔。當跟隨它的代碼塊正常結束或由於異常終止時,將按照BufferedWriter再ZipFile對象此順序自動調用close方法。請注意,close資源的方法按照其創建的相反順序進行調用。
以下示例使用try-with-resources語句自動關閉java.sql.Statement對象:
public static void viewTable(Connection con) throws SQLException { String query = "select COF_NAME, SUP_ID, PRICE, SALES, TOTAL from COFFEES"; try (Statement stmt = con.createStatement()) { ResultSet rs = stmt.executeQuery(query); while (rs.next()) { String coffeeName = rs.getString("COF_NAME"); int supplierID = rs.getInt("SUP_ID"); float price = rs.getFloat("PRICE"); int sales = rs.getInt("SALES"); int total = rs.getInt("TOTAL"); System.out.println(coffeeName + ", " + supplierID + ", " + price + ", " + sales + ", " + total); } } catch (SQLException e) { JDBCTutorialUtilities.printSQLException(e); } }
注意:try-with-resources語句可以像普通try語句一樣擁有catch和finally代碼塊。在try-with-resources語句中,任何catch或finally塊在聲明的資源關閉后才運行。
抑制異常
可以在try-with-resources語句的代碼塊中拋出異常。在該示例中writeToFileZipFileContents,可以從該try塊中引發異常,並且try在嘗試關閉ZipFile和BufferedWriter對象時最多可以從 try-with-resources 語句中拋出兩個異常。如果從try塊中拋出異常,並且從try-with-resources語句拋出一個或多個異常,那么從try-with-resources語句拋出的異常將被抑制,並且該塊引發的異常將被writeToFileZipFileContents方法拋出。您可以通過Throwable.getSuppressed從該try塊引發的異常中調用方法來檢索這些抑制的異常。
實現AutoCloseable或Closeable接口的類
請參閱Javadoc AutoCloseable和Closeable接口以獲取實現這些接口之一的類的列表。該Closeable接口繼承了AutoCloseable接口。接口Closeable的close方法拋出IOException異常,而接口AutoCloseable的close方法拋出Exception異常。因此,AutoCloseable接口的子類可以重寫此close方法的行為來拋出特殊的異常,例如IOException根本沒有異常。
catch 多個類型異常
這一章涵蓋兩個部分:Catch 多種類型異常 和 分析異常並重新拋出精准異常
catch 多種類型異常
在JDK7,單個catch塊可以處理多種類型的異常。此功能可以減少代碼重復並減少異常捕捉過度。
思考下面的例子,其中catch塊中的有重復代碼:
catch (IOException ex) { logger.log(ex); throw ex; catch (SQLException ex) { logger.log(ex); throw ex; }
在JDK7之前的版本中,很難創建一個通用方法來消除重復的代碼,因為該變量ex具有不同的類型。
以下示例在JDK7,可消除重復的代碼:
catch (IOException|SQLException ex) { logger.log(ex); throw ex; }
該catch塊指出可以處理的異常類型,每個異常類型用豎線(|)分隔。
注意:如果一個catch塊處理多個異常類型,則該catch參數是隱含的final。在這個例子中,catch參數ex是final,因此你不能在catch塊中為它分配任何值。
通過編譯處理多個異常類型的catch塊生成的字節碼將會更小(因此優於)編譯許多只處理一個異常類型的catch塊。處理多個異常類型的catch塊不會在編譯器生成的字節碼中創建重復;字節碼沒有重復的異常處理程序。
分析異常並重新拋出精准異常
與早期版本的JDK相比,JDK7 編譯器對重新產生的異常執行更精確的分析。這使您可以在throws方法聲明的子句中指定更具體的異常類型。
思考下面例子:
static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
這個例子的try塊可能會拋出FirstException或者SecondException。假設你想在rethrowException方法中throws這些異常類型。在JDK 7之前的版本中,您不能這樣做。由於catch子句的異常參數e是Exception類型,並且catch塊重新拋出異常參數e,所以只能在rethrowException方法拋出Exception異常類型。
在JDK7中的編譯器可以通過catch塊確定FirstException和SecondException異常。即使該catch子句的異常參數e類型為Exception,編譯器也可以確定它是FirstException實例或者是SecondException實例:
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } }
如果catch參數分配給catch塊中的另一個值,則此分析將失效。但是,如果catch參數分配給另一個值,則必須在方法聲明Exception的throws子句中指定異常類型。
詳細地說,在JDK7及更高版本中,當您在catch子句中聲明一個或多個異常類型並重新拋出由此catch塊處理的異常時,編譯器將驗證重新拋出異常的類型是否滿足以下條件:
- 該try塊可以throw它。
- 沒有前面其他catch可以處理它。
- 它是catch子句的異常參數之一的子類型或超類型。
JDK7編譯器允許在rethrowException方法throws拋出指定Exception類型FirstException和SecondException。因為您可以重新拋出一個throws聲明的任何類型的超類型。
在JDK7之前的版本中,您不能拋出該catch子句的異常參數之一的超類型。在JDK7之前的編譯器Exception會在語句中生成錯誤未報告的異常;必須捕獲或聲明throw,編譯器檢查拋出異常的類型是否可分配給rethrowException方法聲明的throws子句中聲明的任何類型。然而,捕捉參數的類型e是Exception,這是一種超類型,而不是一個子類型FirstException和SecondException。
字面值中使用下划線
在JDK7中,任意數量的下划線字符(_)可以出現在字面值的任意位置。這個特性使您能夠在字面值中分離數字組,這可以提高代碼的可讀性。
例如,如果您的代碼包含具有許多數字的數字,則可以使用下划線字符以三個一組來分隔數字,這與使用逗號或空格等標點符號作為分隔符類似。
以下示例顯示了可以在字面值中使用下划線的其他方法:
long creditCardNumber = 1234_5678_9012_3456L; long socialSecurityNumber = 999_99_9999L; float pi = 3.14_15F; long hexBytes = 0xFF_EC_DE_5E; long hexWords = 0xCAFE_BABE; long maxLong = 0x7fff_ffff_ffff_ffffL; byte nybbles = 0b0010_0101; long bytes = 0b11010010_01101001_10010100_10010010;
您只能在數字之間放置下划線; 你不能在下列地方放置下划線:
- 在數字的開頭或結尾
- 與浮點數字中的小數點相鄰
- 在F或L后綴之前
- 在期望一串數字的位置(In positions where a string of digits is expected)
下面的例子演示了字面值中有效和無效的下划線位置(突出顯示):
float pi1 = 3_.1415F; // 無效; 不能在小數點附近加下划線 float pi2 = 3._1415F; // 無效; 不能在小數點附近加下划線 long socialSecurityNumber1 = 999_99_9999_L; // 無效; 在L后綴之前不能加下划線 int x1 = _52; // 這是一個標識符,而不是字面值 int x2 = 5_2; // OK(十進制文字) int x3 = 52_; // 無效; 不能在文字的末尾加上下划線 int x4 = 5_______2; // OK(十進制文字) int x5 = 0_x52; // 無效; 不能在0x基數前綴中加下划線 int x6 = 0x_52; // 無效; 不能在數字的開頭加下划線 int x7 = 0x5_2; // OK(十六進制文字) int x8 = 0x52_; // 無效; 不能在數字的末尾加上下划線 int x9 = 0_52; // OK(八進制文字) int x10 = 05_2; // OK(八進制文字) int x11 = 052_; // 無效; 不能在數字的末尾加上下划線
類型推斷
只要編譯器可以從上下文中推斷出類型參數,你就可以用一組空的類型參數(<>)來代替調用泛型類的構造函數所需的類型參數。這一對尖括號被非正式地稱為鑽石。
例如,請考慮以下變量聲明:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
在JDK7中,可以使用一組空的類型參數(<>)替換構造函數的參數化類型:
Map<String, List<String>> myMap = new HashMap<>();
請注意,為了在泛型類實例化過程中利用自動類型推斷,您必須指定菱形。在以下示例中,編譯器會生成未經檢查的轉換警告,因為HashMap()構造函數引用的是HashMap原始類型,而不是Map<String, List<String>>類型:
Map<String, List<String>> myMap = new HashMap(); //未經檢查的轉換警告
JDK7支持泛型實例創建的類型推斷; 如果構造函數的參數化類型在上下文中顯而易見,則只能使用類型推斷。例如,以下示例沒有編譯:
List<String> list = new ArrayList<>(); list.add("A"); // 以下語句應該失敗,因為addAll需要 // Collection<? extends String> list.addAll(new ArrayList<>());
請注意,鑽石通常用於方法調用; 但是,建議您將菱形主要用於變量聲明。
相比之下,下面的例子會編譯:
// 以下語句會編譯: List<? extends String> list2 = new ArrayList<>(); list.addAll(list2);
泛型和非泛型類的類型推斷和構造函數
請注意,構造函數在泛型和非泛型類中都可以是通用的(換句話說,聲明它們自己的正式類型參數)。思考下面的例子:
class MyClass<X> { <T> MyClass(T t) { // ... } }
思考下面這個類的實例MyClass,它在JDK7和以前的版本中是有效的:
new MyClass<Integer>("")
該語句創建參數化類型的實例MyClass<Integer>;該語句明確指定了泛型類Integer的正式類型參數X的類型MyClass<X>請注意,此泛型類的構造函數包含一個正式的類型參數T。編譯器推斷這個泛型類的構造函數String的形式類型參數的類型T(因為這個構造函數的實際參數是一個String對象)
JDK7之前版本的編譯器能夠推斷泛型構造函數的實際類型參數,類似於泛型方法。但是,如果使用磚石(<>),JDK7中的編譯器可以推斷實例化的泛型類的實際類型參數<>。思考以下示例,該示例適用於JDK7及更高版本:
MyClass<Integer> myObject = new MyClass<>("");
在這個例子中,編譯器推斷泛型類Integer的形式類型參數X的類型MyClass<X>。它推斷這個泛型類的構造函數String的形式類型參數的類型T。
改進泛型類型可變參數
這一章涵蓋以下幾個部分:
- 堆污染
- 可變參數方法和非具體化形式參數
- 可變參數方法在傳遞非具體參數的缺點
- 抑制可變參數方法在傳遞不具體形式參數的警告
堆污染
大多數參數化類型(如ArrayList<Number>和List<String>)都是非具體化的類型。非具體化類型是一種在運行時不確定的類型。在編譯時,非具體化類型在此過程中經歷了一個稱為類型擦除的過程,擦除參數類型和類型參數相關的信息。這確保了在泛型之前創建的Java庫和應用程序的二進制兼容性。因為類型擦除在編譯時從參數類型中刪除信息,所以這些類型是非具體化的。
當參數化類型的變量引用不是該參數化類型的對象時,會發生堆污染。這種情況只有在程序執行一些操作時才會發生,從而在編譯時產生不受約束的警告。一個未經檢查的警告如果產生,無論是在編譯時(在編譯時類型檢查規則的限制范圍內)或運行時。一個涉及參數化類型(例如,一次轉換或方法調用)的操作的正確性是無法被驗證的。
思考下面的例子:
List l = new ArrayList<Number>(); List<String> ls = l; // 未經檢查的警告 l.add(0, new Integer(42)); // 另一個未經檢查的警告 String s = ls.get(0); // 拋出類型轉換異常 ClassCastException
在類型擦除時,ArrayList<Number>類型和List<String>類型分別變成ArrayList和List。
該ls變量具有參數化類型List<String>,當List引用l賦值給ls時,編譯器會生成未經檢查的警告;如果編譯器無法在編譯時確定,而且JVM也無法在運行時確定l它不是一個List<String>類型;因此,產生堆污染。
因此,在編譯時,編譯器會在add語句處生成另一個未經檢查的警告。編譯器無法確定變量l是List<String>類型還是List<Integer>類型(以及另一種發生堆污染的情況)。然而,編譯器不會在get語句中產生警告或錯誤。此聲明有效; 它調用List<String>的get方法來獲得一個String對象。相反,在運行時,get語句會拋出ClassCastException。
詳細地說,當List<Number>對象l被賦值給另一個List<String>對象ls時,就會出現堆污染情況,它有一個不同的靜態類型。然而,編譯器仍然允許這個賦值。它必須允許這個賦值來保證與不支持泛型的JDK版本兼容性。因為類型擦除,List<Number>和List<String>變成List。因此,編譯器允許對象l的賦值個體ls對象,因為ls是一個List類型。
另外,l.add調用該方法時會發生堆污染情況。該方法add第二參數應該是String,但實際參數是Integer。但是,編譯器仍然允許這種方法調用。因為類型擦除,add方法第二個參數(List<E>.add(int,E))變成Object。因此,編譯器允許這種方法調用。因為在類型擦除之后,該l.add方法可以添加任何類型,包括Integer類型對象,因為它Object的子類。
可變參數方法和非具體化形式參數
思考下面的ArrayBuilder.addToList方法。它將類型為T的elements可變參數,添加到List listArg參數中:
1 import java.util.*; 2 3 public class ArrayBuilder{ 4 5 public static <T> void addToList (List<T> listArg, T... elements) { 6 for (T x : elements) { 7 listArg.add(x); 8 } 9 } 10 11 public static void faultyMethod(List<String>... l) { 12 Object[] objectArray = l; // 有效 13 objectArray[0] = Arrays.asList(new Integer(42)); 14 String s = l[0].get(0); // 拋出ClassCastException異常 15 } 16 17 }
1 import java.util.*; 2 3 public class HeapPollutionExample { 4 5 public static void main(String[] args) { 6 7 List<String> stringListA = new ArrayList<String>(); 8 List<String> stringListB = new ArrayList<String>(); 9 10 ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine"); 11 ArrayBuilder.addToList(stringListA, "Ten", "Eleven", "Twelve"); 12 List<List<String>> listOfStringLists = new ArrayList<List<String>>(); 13 ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB); 14 15 ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!")); 16 } 17 }
JDK7編譯器為該方法的定義生成以下警告ArrayBuilder.addToList:
warning: [varargs] Possible heap pollution from parameterized vararg type T
當編譯器遇到可變參數方法時,它將可變參數形式參數轉換為數組。但是,Java編程語言不允許創建參數化類型的數組。在ArrayBuilder.addToList方法中,編譯器將可變參數形式參數T... elements轉換為形式參數T[] elements。即數組。但是,由於類型擦除,編譯器將可變參數形式參數轉換為Object[] 。因此,可能存在堆污染
下面是ArrayBuilder.addToList方法的反編譯結果:
public static <T> void addToList(List<T> listArg, T... elements) { Object[] var2 = elements; int var3 = elements.length; for(int var4 = 0; var4 < var3; ++var4) { T x = var2[var4]; listArg.add(x); } }
注意:JDK5和6編譯器在ArrayBuilder.addToList調用時會生成警告HeapPollutionExample。這些編譯器不會在聲明上生成警告。但是,JDK7會在聲明和調用上生成警告(除非這些警告被注解取代)。
可變參數方法在傳遞非具體參數的缺點
該方法ArrayBuilder.faultyMethod顯示了編譯器為什么會警告您這些類型的方法。該方法的第一條語句將可變參數形式參數l賦值給objectArgs的Object數組:
Object[] objectArray = l;
這里可能產生堆污染。可變參數l可以賦值給objectArray數組。但是,編譯器不會在此語句中生成未經檢查的警告。當編譯器編譯List<String>... l 為List[] l 時已經生成警告。List[]是Object[]類型的子類,所以該聲明是有效的。
因此,如果您將List任何類型的對象賦值給數組objectArray,則編譯器不會發出警告或錯誤,如此語句所示:
objectArray[0] = Arrays.asList(new Integer(42));
這個語句將List<Integer>賦值給objectArray數組。
如果你調用以下方法:
ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
調用以下方法,在運行時JVM會拋出ClassCastException異常。
String s = l[0].get(0); // 拋出ClassCastException
變量l存儲的是List<Integer>類型,但確期待是一個List<String>類型。
抑制可變參數方法在傳遞不具體形式參數的警告
如果聲明了一個具體參數化的可變參數方法。且方法的體不會拋出ClassCastException或其他類似的異常。您可以使用以下幾種方式來抑制編譯器為這些可變參數方法生成的警告:
- 將@SafeVarargs注解添加到靜態非構造函數方法聲明中:
- 將@SuppressWarnings({"unchecked", "varargs"})注解添加到方法聲明中:
- 使用編譯器選項-Xlint:varargs
例如,以下ArrayBuilder類的有兩個方法addToList2和addToList3:
1 import java.util.*; 2 3 public class ArrayBuilder { 4 5 public static <T> void addToList (List<T> listArg, T... elements) { 6 for (T x : elements) { 7 listArg.add(x); 8 } 9 } 10 11 @SuppressWarnings({"unchecked", "varargs"}) 12 public static <T> void addToList2 (List<T> listArg, T... elements) { 13 for (T x : elements) { 14 listArg.add(x); 15 } 16 } 17 18 @SafeVarargs 19 public static <T> void addToList3 (List<T> listArg, T... elements) { 20 for (T x : elements) { 21 listArg.add(x); 22 } 23 } 24 25 // ... 26 27 }
1 import java.util.*; 2 3 public class HeapPollutionExample { 4 5 // ... 6 7 public static void main(String[] args) { 8 9 // ... 10 11 ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB); 12 ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB); 13 ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB); 14 15 // ... 16 17 } 18 }
Java編譯器為此示例生成以下警告:
- addToList:
- 在該方法的聲明中:[unchecked] Possible heap pollution from parameterized vararg type T
- 當該方法被調用時:[unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
- addToList2:調用方法時(在方法聲明中不生成警告): [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
- addToList3:方法聲明或調用方法時不會生成警告。
其他
JDK7 還有許許多多的其他新特性,比如:Swing,IO and New IO,Networking 等等增強。這些可以去閱讀官方原文進行了解。下面是截圖:
前面只介紹了Java編程語言(Java Programming Language)部分,下面就簡單的介紹下增強swing,其余不多過介紹了。
增強 Swing
Swing 的增強主要包含如下幾點:
- JLayer 類: 是Swing組件的通用裝飾器,您能夠繪制組件並響應組件事件,而無需直接修改底層組件。
- Nimbus Look&Feel : Nimbus Look&Feel(L&F)已經從com.sun.java.swing
標准的API命名空間轉移 到了javax.swing。查看javax.swing.plaf.nimbus包有更詳細的介紹,雖然它不是默認的L&F,但您可以輕松使用它。
- 重量級和輕量級組件: 歷史上,在同一個容器中混合重量級(AWT)和輕量級(Swing)組件是有問題的。但是,混合重量級和輕量級組件很容易在Java SE 7中完成。
- 窗口的形狀和透明度: Java SE 7版本支持具有透明度和非矩形形狀的窗口。
- JColorChooser 類HSL選擇: JColorChooser該類已添加HSL選項卡,允許用戶使用色相飽和度亮度(HSL)顏色模型選擇顏色。
示例代碼:
自定義WallpaperLayerUI裝飾類繼承LayerUI重寫paint方法
1 import javax.swing.*; 2 import javax.swing.plaf.LayerUI; 3 import java.awt.*; 4 5 //繼承 LayerUI 實現自定義裝飾內容。 6 public class WallpaperLayerUI extends LayerUI<JComponent> { 7 8 @Override 9 public void paint(Graphics g, JComponent c) { 10 super.paint(g, c); 11 Graphics2D g2 = (Graphics2D) g.create(); 12 int w = c.getWidth(); 13 int h = c.getHeight(); 14 g2.setComposite(AlphaComposite.getInstance( 15 AlphaComposite.SRC_OVER, .5f)); 16 //透明顏色漸變 17 g2.setPaint(new GradientPaint(0, 0, Color.yellow, 0, h, Color.red)); 18 g2.fillRect(0, 0, w, h); 19 g2.dispose(); 20 } 21 }
測試代碼:
1 import javax.swing.*; 2 import javax.swing.plaf.LayerUI; 3 4 public class Test { 5 public static void main(String[] args) { 6 javax.swing.SwingUtilities.invokeLater(new Runnable() { 7 public void run() { 8 createUI(); 9 } 10 }); 11 } 12 13 public static void createUI() { 14 //創建JFrame 15 JFrame f = new JFrame("Wallpaper"); 16 JPanel panel = createPanel(); 17 18 // 這就是使用JDK7新特性:JLayer 類的裝飾功能。如果不使用裝飾 可以將 直接 f.add(panel) 19 LayerUI<JComponent> layerUI = new WallpaperLayerUI(); 20 JLayer<JComponent> jlayer = new JLayer<JComponent>(panel, layerUI); 21 f.add (jlayer); 22 // f.add(panel); 23 24 //設置 JFrame 寬高 默認關閉 相對位置 和 可見。 25 f.setSize(300, 200); 26 f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); 27 f.setLocationRelativeTo (null); 28 f.setVisible (true); 29 } 30 31 private static JPanel createPanel() { 32 //創建面板 33 JPanel p = new JPanel(); 34 35 //創建一個按鈕組,並添加 Beef、Chicken 和 Vegetable 三個單選按鈕 36 ButtonGroup entreeGroup = new ButtonGroup(); 37 JRadioButton radioButton; 38 p.add(radioButton = new JRadioButton("Beef", true)); 39 entreeGroup.add(radioButton); 40 p.add(radioButton = new JRadioButton("Chicken")); 41 entreeGroup.add(radioButton); 42 p.add(radioButton = new JRadioButton("Vegetable")); 43 entreeGroup.add(radioButton); 44 45 //添加 Ketchup 、 Mustard 和 Pickles 復選框 46 p.add(new JCheckBox("Ketchup")); 47 p.add(new JCheckBox("Mustard")); 48 p.add(new JCheckBox("Pickles")); 49 50 //添加 Special requests: 標簽 和 文本框 51 p.add(new JLabel("Special requests:")); 52 p.add(new JTextField(20)); 53 54 //添加 Place Order 按鈕 55 JButton orderButton = new JButton("Place Order"); 56 p.add(orderButton); 57 58 return p; 59 } 60 }
以上代碼結果展示:
我將裝飾效果去掉只需將,19-21行注釋,並放開22注釋執行結果如下: