2013年12月21日 星期六

Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods

許裕永 譯 (原文 內容取自 Oracle 官網)

Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods - The Java SE 7 complier generates a warning at the declaration site of a varargs method or constructor with a non-reifiable varargs formal parameter. Java SE 7 introduces the compiler option -Xlint:varargs and the annotations @SafeVarargs and @SuppressWarnings({"unchecked", "varargs"}) to supress these warnings.
增強編譯器的警告及錯誤, 當在 Varargs 的方法中使用非具體型式的參數時 - Java SE7 的編譯器會產生警告在 Varargs 方法宣告的位置或建構方法擁有非具體型式的參數時. Java SE7 提出一個編譯選項 - Xlint varargs 和便箋 @SafeVarargs 及 @SuppressWarnings({"unchecked", "varargs"}) 來抑制這些警告.

This page covers the following topics:
本頁包含下列主題:

Heap Pollution
Variable Arguments Methods and Non-Reifiable Formal Parameters
Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters
Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters

Heap Pollution
堆疊污染
Most parameterized types, such as ArrayList<Number> and List<String>, are non-reifiable types. A non-reifiable type is a type that is not completely available at runtime. At compile time, non-reifiable types undergo a process called type erasure during which the compiler removes information related to type parameters and type arguments. This ensures binary compatibility with Java libraries and applications that were created before generics. Because type erasure removes information from parameterized types at compile-time, these types are non-reifiable.
大部份可以設定限型的型別, 像是 ArrayList<Number> 及 List<String>, 是屬於非具體型式的型別. 一個非具體型式的型別是指在執行時期不一定合法的型別. 在編譯時期, 非具體型式的型別會進行一個所謂型別抺除的流程, 這個流程會刪除這個型別關於特徵及主旨的資訊. 這保證二進位的兼容性關於建立在限型(泛型)之前的 Java 函式庫及應用程式. 因為在編譯時期會刪除可以設定限型的型別的資訊, 這些型別是非具體型式.

Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type. This situation can only occur if the program performed some operation that would give rise to an unchecked warning at compile-time. An unchecked warning is generated if, either at compile-time (within the limits of the compile-time type checking rules) or at runtime, the correctness of an operation involving a parameterized type (for example, a cast or method call) cannot be verified.
當把一個有宣告限型型別的變數參照向一個不同限型型別的物件時會發生堆疊污染. 這種狀況只會在該程式呈現某些運算式時在編譯時期產生 unchecked warning. 如果產生 unchecked warning, 無論是在編譯時期(關於編譯時期型別檢查規則的限制)或在執行時期 , 則涉及限型型別的運算式的正確性將無法被證實.

Consider the following example:
了解下列範例:

List l = new ArrayList<Number>();
List<String> ls = l; // unchecked warning
l.add(0, new Integer(42)); // another unchecked warning
String s = ls.get(0); // ClassCastException is thrown

During type erasure, the types ArrayList<Number> and List<String> become ArrayList and List, respectively.
在型別抺除時, ArrayList<Number> 及 List<String> 在的型別會分別變成 ArrayList 及 List.
The variable ls has the parameterized type List<String>. When the List referenced by l is assigned to ls, the compiler generates an unchecked warning; the compiler is unable to determine at compile time, and moreover knows that the JVM will not be able to determine at runtime, if l refers to a List<String> type; it does not. Consequently, heap pollution occurs.
變數 ls 有設定限型型別為 List<String>. 當一個 List 的參照被指派給 ls, 則編譯器會產生一個 unchecked warning, 編譯器沒有能力在編譯時期確認, 而且除此之外也知道 JVM 在執行時期也無法確認, l 是否指向一個 List<String>. 如果不是的話, 必然會發生堆疊污染.

As a result, at compile time, the compiler generates another unchecked warning at the add statement. The compiler is unable to determine if the variable l refers to a List<String> type or a List<Integer> type (and another heap pollution situation occurs). However, the compiler does not generate a warning or error at the get statement. This statement is valid; it is calling the List<String>.get method to retrieve a String object. Instead, at runtime, the get statement throws a ClassCastException.
結果, 在編譯時期, 編譯器對於 add 敍述會產生另一個 unchecked warning. 編譯器無法確認變數 l 是否指向一個 List<String> 型別或是一個 List<Integer> 型別 ( 而且另一個堆疊污染情況發生了). 總之, 編譯器不會對 get 敍述提出警告或錯誤. 這個敍述是合法的, 它是呼叫 List<String> 的 get 的方法來取得一個 String 物件. 取代的是在執行時期丟出一個 classCastException.

In detail, a heap pollution situation occurs when the List object l, whose static type is List<Number>, is assigned to another List object, ls, that has a different static type, List<String>. However, the compiler still allows this assignment. It must allow this assignment to preserve backwards compatibility with versions of Java SE that do not support generics. Because of type erasure, List<Number> and List<String> both become List. Consequently, the compiler allows the assignment of the object l, which has a raw type of List, to the object ls.
詳細來說, 一個堆疊污染的情況是發生在當 List 型別的物件 l , 它的限型型別是 List<Number>, 它被指派給 List 型別物件 ls, 而 ls 的限型型別是 List<String>. 總之編譯器是允許這樣的指派的. 它必須允這樣的指派, 對於 Java SE 中不支援限型機制(泛型)的版本.來保留其向下的兼容性. 因為在型別袜除後, List<Number> 及 List<String> 都會變成 List. 因此編譯器允許原始型別為List 的物件 l 被指派給物件 ls.

Furthermore, a heap pollution situation occurs when the l.add method is called. The static type second formal parameter of the add method is String, but this method is called with an actual parameter of a different type,Integer. However, the compiler still allows this method call. Because of type erasure, the type of the second formal parameter of the add method (which is defined as List<E>.add(int,E)) becomes Object. Consequently, the compiler allows this method call because, after type erasure, the l.add method can add any object of type Object, including an object of Integer type, which is a subtype of Object.
此外, 當 l 呼叫 add 方法時也會產生堆疊污染. add 方法中的第二個參數的限型型別設定為 String, 但這個方法被呼叫時引用的參數的實際型別卻是不同的型別, Integer. 不過, 編譯器還是會允許這個方法的呼叫. 因為經過型別袜除後, add 方法中的第二個參數的型別 (那是很清楚的像 List<E> add<int, E) ) 會變成 Object. 編譯器會允許這個方法的呼叫是必然的, 因為在型別袜除後, l.add 方法可以新增任何 Object 型別的物件, 包含一個 Integer 型別的物件, 它是 Object 的下層型別.

Variable Arguments Methods and Non-Reifiable Formal Parameters
變動參數的方法與非具體型式的參數

Consider the method ArrayBuilder.addToList in the following example. It is a variable arguments (also known as varargs) method that adds the objects of type T contained in the elements varargs formal parameter to theList listArg:
了解下列範例中的 ArrayBuilder.addToList 方法. 它是一個變動參數 (也就是 varargs) 的方法, 可以把 T 型別的物件作為包含在變動參數中的元素, 新增到 List listArg 之中 :

import java.util.*;

public class ArrayBuilder {

 public static <T> void addToList (List<T> listArg, T... elements) {
  for (T x : elements) {
   listArg.add(x);
  }
 }

 public static void faultyMethod(List<String>... l) {
  Object[] objectArray = l; // Valid
  objectArray[0] = Arrays.asList(new Integer(42));
  String s = l[0].get(0); // ClassCastException thrown here
 }

}

import java.util.*;

public class HeapPollutionExample {

 public static void main(String[] args) {

  List<String> stringListA = new ArrayList<String>();
  List<String> stringListB = new ArrayList<String>();
 
  ArrayBuilder.addToList(stringListA, "Seven", "Eight", "Nine");
  ArrayBuilder.addToList(stringListA, "Ten", "Eleven", "Twelve");
  List<List<String>> listOfStringLists = new ArrayList<List<String>>();
  ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);

  ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
 }
}
 

The Java SE 7 compiler generates the following warning for the definition of the method ArrayBuilder.addToList:
Java SE7 編譯器會產生下列警告來解釋方法 ArrayBuilder.addToList:

warning: [varargs] Possible heap pollution from parameterized vararg type T
When the compiler encounters a varargs method, it translates the varargs formal parameter into an array. However, the Java programming language does not permit the creation of arrays of parameterized types. In the methodArrayBuilder.addToList, the compiler translates the varargs formal parameter T... elements to the formal parameter T[] elements, an array. However, because of type erasure, the compiler converts the varargs formal parameter to Object[] elements. Consequently, there is a possibility of heap pollution. See the next section, Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters, for more information.
當編譯器遇到了變動參數的方法, 它會把合格的變動參數轉換為陣列. 然而, Java 程式語言並不允許那些的陣列屬於限型型別. 在方法 ArrayBuilder.addToList 中, 編譯器會轉換合格的變數參數 T... 的元素為合格的 T[] 元素, 一個陣列. 然而, 由於型別袜除, 編譯器會把合格的變動參數轉換為 Object[] 元素. 因此, 這就有可能產生堆疊污染. 參閱下一個單元,  Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters, 了解更多訊息.

Note: The Java SE 5 and 6 compilers generate this warning when the ArrayBuilder.addToList is called; in this example, the warning is generated for the class HeapPollutionExample. These compilers do not generate the warning at the declaration site. However, the Java SE 7 generates the warning at both the declaration site and the call site (unless the warnings are preempted with annotations; see Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters for more information). The advantage of generating a warning when a compiler encounters a varargs method that has a non-reifiable varargs formal parameter at the declaration site as opposed to the call site is that there is only one declaration site; there are potentially many call sites.
註: Java SE 5 及 6 的編譯器會在 ArrayBuilder.addToList 方法呼叫的時候產生這個警告; 在這個範例中是在類別 HeapPollutionExample 中產生. 這些編譯器並不會在宣告的位置產生警告. 然而, Java SE 7 會在宣告的位置以及呼叫的位置都產生警告 (除非這些警告有先行取得註解; 參閱 Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters 了解更多訊息 ). 當編譯器遇到非具體型式的變動參數的宣告時產生警告, 而不只是在呼叫端產生警告的好處是: 只有一個宣告端;卻有許多呼叫端.

Potential Vulnerabilities of Varargs Methods with Non-Reifiable Formal Parameters
在變動參數中使用非具體型式參數時的潛在的容易造成的傷害.

The method ArrayBuilder.faultyMethod shows why the compiler warns you about these kinds of methods. The first statement of this method assigns the varargs formal parameter l to the Object array objectArgs:
方法 ArrayBuilder.faultyMethod 呈現出了為什麼編譯器會在這些方法的使用型態上對你提出警告. 方法中的第一行敍述指派合法的變動參數 l 給 Object 陣列 objectArgs:

Object[] objectArray = l;
This statement can potentially introduce heap pollution. A value that does match the parameterized type of the varargs formal parameter l can be assigned to the variable objectArray, and thus can be assigned to l. However, the compiler does not generate an unchecked warning at this statement. The compiler has already generated a warning when it translated the varargs formal parameter List<String>... l to the formal parameter List[] l. This statement is valid; the variable l has the type List[], which is a subtype of Object[].
這個敍述可能引出潛在的堆疊污染. 一個完全符合合法的變數參數 l 的值可以被指派給變數 objectArray, 而且也可以指派回給 l . 然而, 編譯器不會對這個敍述產生 unchecked 警告. 編譯器已經在轉換合格的變數參數 List<String>... l 的元素為合格的 List[] l 元素時產生警告. 這個敍述是合法的, 變數 l 擁有 List[] 型別, 它是 Object[] 的下層型別.

Consequently, the compiler does not issue a warning or error if you assign a List object of any type to any array component of the objectArray array as shown by this statement:
因此, 如果你以像下列一樣的敍述, 指派一個 設定任何限型型別的 List 物件為 objectArray 中的陣列元素時, 編譯器不會發佈警告或錯誤:

objectArray[0] = Arrays.asList(new Integer(42));
This statement assigns to the first array component of the objectArray array with a List object that contains one object of type Integer.
這個敍述把設定限型型別為 Integer 的 List 型別物件, 指派為 objectArray 中的第一個陣列元素.

Suppose you call the ArrayBuilder.makeArray method with the following statement:
假設你使用下列敍述呼叫方法 ArrayBuilder.makeArray :

ArrayBuilder.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!"));
At runtime, the JVM throws a ClassCastException at the following statement:
在執行時期, JVM 會針對下列敍述丟出一個 ClassCastException:

String s = l[0].get(0); // ClassCastException thrown here

The object stored in the first array component of the variable l has the type List<Integer>, but this statement is expecting an object of type List<String>.
儲存於變數 l 中的第一個陣列元素物件的型別是 List<Integer>, 但這個敍述期望的是型別為 List<String> 的物件.

Suppressing Warnings from Varargs Methods with Non-Reifiable Formal Parameters
避免產生使用非具體型式的變動參數的方法時的警告

If you declare a varargs method that has parameterized parameters, and you ensure that the body of the method does not throw a ClassCastException or other similar exception due to improper handling of the varargs formal parameter (as shown in the ArrayBuilder.faultyMethod method), you can suppress the warning that the compiler generates for these kinds of varargs methods by using one of the following options:
如果你宣告一個變動參數的方法它有可以設定限型的參數, 而且你確認方法中的程式碼不會丟出 ClassCaseException 或其他由於不適當的支援變動參數的類似例外 (例如示範中的 ArrayBuilder.faultyMethod 方法), 你可以使用下列選項之一的方式, 避免編譯器產生這些關於使用變動參數的方法的警告:

Add the following annotation to static and non-constructor method declarations:
在 static 及非建構方法的方法宣告時新增下列註解:

@SafeVarargsThe @SafeVarargs annotation is a documented part of the method's contract; this annotation asserts that the implementation of the method will not improperly handle the varargs formal parameter.
@SafeVarargs 註解是方法宣告合約中的一部份, 這個註解斷定這個方法的實作將不會, 不正確的使用變動參數.


Add the following annotation to the method declaration:
新增下例註解到方法的宣告:

@SuppressWarnings({"unchecked", "varargs"})Unlike the @SafeVarargs annotation, the @SuppressWarnings("varargs") does not suppress warnings generated from the method's call site.
不像 @SafeVarargs 註解, @SuppressWarnings("varargs") 不會在方法被呼叫時抑制警告產生.

Use the compiler option -Xlint:varargs.
使用編譯選項 -Xlint:varargs.

For example, the following version of the ArrayBuilder class has two additional methods, addToList2 and addToList3:
如同範例, 下列版本的 ArrayBuilder 類別有新增了兩個方法, addToList2 及 addToList3:

public class ArrayBuilder {

 public static <T> void addToList (List<T> listArg, T... elements) {
  for (T x : elements) {
   listArg.add(x);
  }
 }

@SuppressWarnings({"unchecked", "varargs"})
 public static <T> void addToList2 (List<T> listArg, T... elements) {
  for (T x : elements) {
   listArg.add(x);
  }
 }

@SafeVarargs
 public static <T> void addToList3 (List<T> listArg, T... elements) {
  for (T x : elements) {
   listArg.add(x);
  }
 }

// ...

}
public class HeapPollutionExample {

// ...

 public static void main(String[] args) {

// ...

  ArrayBuilder.addToList(listOfStringLists, stringListA, stringListB);
  ArrayBuilder.addToList2(listOfStringLists, stringListA, stringListB);
  ArrayBuilder.addToList3(listOfStringLists, stringListA, stringListB);

// ...

 }
}

The Java compiler generates the following warnings for this example:
這個範例 Java 編譯器會產生下列警告:

addToList:
At the method's declaration: [unchecked] Possible heap pollution from parameterized vararg type T
在這個方法的宣告: [unchecked] 變動參數 type T 可能會有堆疊污染.

When the method is called: [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
當這個方法被呼叫 : [unchecked] 不檢查限型陣列的建立, 當變動參數的型別是 List<String>[] 時.

addToList2:
When the method is called (no warning is generated at the method's declaration): [unchecked] unchecked generic array creation for varargs parameter of type List<String>[]
當這個方法被呼叫 (沒有警告產生在這個方法的宣告) : [unchecked] 不檢查限型陣列的建立, 當變動參數的型別是 List<String>[] 時.

addToList3:
No warnings are generated either at the method's declaration or when it is called.
沒有警告會產生無論是在方法宣告時或被呼叫時.

Note: In Java SE 5 and 6, it is the responsibility of the programmer who calls a varargs method that has a non-reifiable varargs formal parameter to determine whether heap pollution would occur. However, if this programmer did not write such a method, he or she cannot easily determine this. In Java SE 7, it is the responsibility of the programmer who writes these kinds of varargs methods to ensure that they properly handle the varargs formal parameter and ensure heap pollution does not occur.
注意 : 在 Java SE 5 及 6, 程式設計師有責任在呼叫非具體型式的變動參數時確認是否有堆疊污染會發生. 只是, 如果這個程式設計師不曾寫過這樣的程式, 他或她可能無法輕易的作出判定. 在 Java SE 7, 程式設計師有責任在開發非具體型式的變動參數的方法時, 擔保他們有正確的使用變動參數而且擔保不會發生堆疊污染.
Type Inference for Generic Instance Creation
許裕永 譯 (原文內容取自 Oracle 官網)

譯者註: Generic Class 一般翻譯為 泛型類別, 本文翻譯為 限型類別(可以限定單一型別的類別)

Type Inference for Generic Instance Creation - You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond.
建立限型類別的實體時的型別推斷 - 你可以用空的(<>) 符號, 取代呼限型類別的建構方法時, 必需指定限型類別的型別參數的設定, 只要編譯器可以從內容中推斷出參數的型別.

This pair of angle brackets is informally called the diamond.
這對尖括號非正式的稱呼為鑽石.

For example, consider the following variable declaration:
在範例中, 了解下列變數的宣告:

Map<String, List<String>> myMap = new HashMap<String, List<String>>();
In Java SE 7, you can substitute the parameterized type of the constructor with an empty set of type parameters (<>):
在 Java SE7 中, 你可以用設定為空的參數型別(<>)取代建構方法中的參數型別:

Map<String, List<String>> myMap = new HashMap<>();
Note that to take advantage of automatic type inference during generic class instantiation, you must specify the diamond. In the following example, the compiler generates an unchecked conversion warning because theHashMap() constructor refers to the HashMap raw type, not the Map<String, List<String>> type:
注意要取得限型類別實體化時能自動判斷參數型別的優點, 你必須宣告鑽石符號. 下列範例, 編譯器將產生 unchecked conversion warning 因為 HashMap() 建構時有提供宣告變數時的 限型型別, 但未提供建構方法的限型型別.

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning
Java SE 7 supports limited type inference for generic instance creation; you can only use type inference if the parameterized type of the constructor is obvious from the context. For example, the following example does not compile:
Java SE 7 支援有限度的型別推斷在 限型類別的實體建構時, 你只可以在建構方法要設定的限型型別的參數, 可以很顯然的從上下文中推斷出來時, 使用型別推斷. 如同範例, 下列範例無法編譯:

List<String> list = new ArrayList<>();
list.add("A");

// The following statement should fail since addAll expects
//下列敍述將失敗因為 addAll 的期待

// Collection<? extends String>
list.addAll(new ArrayList<>());

Note that the diamond often works in method calls; however, it is suggested that you use the diamond primarily for variable declarations.
注意鑽石符號時常在方法呼叫時使用; 然而建議你鑽石符號主要是使用在變數宣告時.

In comparison, the following example compiles:
對照之下, 下列範例可以編譯:

// The following statements compile:
//下列敍述可以編譯

List<? extends String> list2 = new ArrayList<>();
list.addAll(list2);

Type Inference and Generic Constructors of Generic and Non-Generic Classes
限型類別與非限型類別的限型建構方法的型別推斷

Note that constructors can be generic (in other words, declare their own formal type parameters) in both generic and non-generic classes. Consider the following example:
注意無論是限型類別或非限型類別的建構方法, 都可以宣告為限型的 (換句話說, 宣告它們自己的特定型別參數). 了解下列範例:

class MyClass<X> {
 <T> MyClass(T t) {
  // ...
 }
}
Consider the following instantiation of the class MyClass, which is valid in Java SE 7 and prior releases:
了解下列 MyClass 的實體過程, 它在 Java SE7 是合法而且優先發表的:

new MyClass<Integer>("");
This statement creates an instance of the parameterized type MyClass<Integer>; the statement explicitly specifies the type Integer for the formal type parameter, X, of the generic class MyClass<X>.
這個敍述建立了一個 MyClass<Integer> 型別的實體; 此敍述明確的宣告 Integer 型別為限型類別 MyClass<X> 的限型型別, X.

Note that the constructor for this generic class contains a formal type parameter, T. The compiler infers the type String for the formal type parameter, T, of the constructor of this generic class (because the actual parameter of this constructor is a Stringobject).
注意此限型類別的建構方法包含了一個特定型別參數, T. 編譯器把 String 型別推斷為這個限型類別的建構方法的特定型別, T
(因為這個建構方法的參數實際上就是一個 String 型別的物件).


Compilers from releases prior to Java SE 7 are able to infer the actual type parameters of generic constructors, similar to generic methods. However, the compiler in Java SE 7 can infer the actual type parameters of the generic class being instantiated if you use the diamond (<>). Consider the following examples, which are valid for Java SE 7 and later:
從Java SE 7之前版本的編譯器就能夠推斷出限型類別的建構方法的實際參數型別,類似 限型類別中的方法。但是,如果你使用的鑽石符號(<>), Java SE7編譯器可以在 限型類別被實例化時, 推斷出實際的參數的型別.

MyClass<Integer> myObject = new MyClass<>("");
In this example, the compiler infers the type Integer for the formal type parameter, X, of the generic class MyClass<X>. It infers the type String for the formal type parameter, T, of the constructor of this generic class.
在這個範例中, 編譯器推斷型別 Integer 為限型類別 MyClass<X> 的 參數 X 的正式型別. 它也推斷型別 String 為這個限型類別的建構方法的參數 T 的正式型別.

MyClass<Integer> myObject = new <String> MyClass<>("");
In this example, the compiler infers the type Integer for the formal type parameter, X, of the generic class MyClass<X>. The statement explicitly specifies the type String for the formal type parameter, T, of the constructor of this generic class.
在這個範例中, 編譯器推斷型別 Integer 為限型類別 MyClass<X> 的 參數 X 的正式型別. 但敍述中明確宣告型別 String 為這個限型類別的建構方法的參數 T 的正式型別.
 
The try-with-resources Statement
許裕永 譯 (原文內容取自 Oracle 官網)

The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements the new java.lang.AutoCloseable interface or the java.io.Closeable interface can be used as a resource. The classes java.io.InputStream, OutputStream, Reader, Writer, java.sql.Connection, Statement, and ResultSet have been retrofitted to implement the AutoCloseable interface and can all be used as resources in a try-with-resources statement.
try-with-resources 敘述 - 這個 try-with-resources 敘述是一個 try 敘述, 它宣告了一個或多個 resources. resource 是一個必須在程式結束時會一起關閉的物件. try-with-resources 敘述確保每一個 resource 會在敘述結束使用它之後被關閉. 任何物件只要實作了新的 java.lang.AutoCloseable 或 java.io.Closeable 界面, 就可以被當作是 resource 使用. 新樣式的類別 java.io.InputStream, OutputStream, Reader, Writer, java.sql.Connection, Statememt, 以及 ResultSet 都已經實作 AutoCloseable 界面, 而且已經可以被使用在 try-with-resources 敘述中.

The following example reads the first line from a file. It uses an instance of BufferedReader to read data from the file. BufferedReader is a resource that must be closed after the program is finished with it:
下列範例讀入一個檔案的第一行. 它使用 BufferedReader 的實體從檔案中讀入. BufferedReader 是一個 resource 它必須在程式結束使用它之後被關閉.

  static String readFirstLineFromFile(String path) throws IOException {
   try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
   }
  }

In this example, the resource declared in the try-with-resources statement is a BufferedReader. The declaration statement appears within parentheses immediately after the try keyword. The class BufferedReader, in Java SE 7 and later, implements the interface java.lang.AutoCloseable. Because the BufferedReader instance is declared in a try-with-resource statement, it will be closed regardless of whether the try statement completes normally or abruptly (as a result of the method BufferedReader.readLine throwing an IOException). Prior to Java SE 7, you can use a finally block to ensure that a resource is closed regardless of whether the whether the try statement completes normally or abruptly.
在這個範例, 這個 resource 是一個 BufferredReader, 它宣告在 try-with-resources 敘述之中. 宣告敘述位於緊接在 try 關鍵字之後的圓括號之後. 類別 BufferedReader 在 Java SE7 及以後版本中, 已實作了 java.lang.AutoCloseable 界面. 因為這個 BufferedReaqder 的實體是宣告在 try-with-reasources 敘述中, 它將會一定會關閉, 無論 try 敘述是正常的或意外的結束 (就像結果是由方法 BufferedReader.readLine 丟出 IOException) . 在 Java SE7 之前, 你可以使抈 finally 區塊來確保 resorece 一定會關閉, 無論 try 敘述是正常的或意外的結束.

The following example uses a finally block instead of a try-with-resources statement:
下列範例使用 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();
   }
  }

However, in this example, if the methods readLine and close both throw exceptions, then the method readFirstLineFromFileWithFinallyBlock throws the exception thrown from the finally block; the exception thrown from the try block is suppressed. In contrast, in the example readFirstLineFromFile, if exceptions are thrown from both the try block and the try-with-resources statement, then the method readFirstLineFromFile throws the exception thrown from the try block; the exception thrown from the try-with-resources block is suppressed. In Java SE 7 and later, you can retrieve suppressed exceptions; see the section Suppressed Exceptions for more information.
然而, 在這個範例中, readLine 及 close 這兩個方法都有可能丟出 exceptions, 由方法 readfirstLineFromFileWithFinallyBlock 宣告了來自 finally 區塊的 exception; 而 try 區塊中的 exception 是受到抑制的. 相對比較, 以範例 readFirstLineFromFile而言, 假設 exception 被從同時擁有 try 及 try-with-resources 的方法中丟出, 那麼從 readFirstLineFromFile 方法中丟出的 exception 會是來自於 try 區塊; 在 try-with-resources 中的 exception 是受到抑制的. 在 Java SE 7 以及以後的版本, 你可以追踪受抑制的 exception; 參閱單元  Suppressed Exceptions 取得更多資訊.

You may declare one or more resources in a try-with-resources statement. The following example retrieves the names of the files packaged in the zip file zipFileName and creates a text file that contains the names of these files:
你可能宣告一個或多個 resources 在 try-with-resources 敘述. 下列範例擷取了封裝在 zip 檔 ZipFileName 中的所有檔案的名稱, 並建立一個文字檔案儲存了這些檔案名稱:

  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());
    }
   }
  }


In this example, the try-with-resources statement contains two declarations that are separated by a semicolon: ZipFile and BufferedWriter. When the block of code that directly follows it terminates, either normally or because of an exception, the close methods of the BufferedWriter and ZipFile objects are automatically called in this order. Note that the close methods of resources are called in the opposite order of their creation.
在這個範例, try-with-resource 敘述包含了用冒號區隔的兩個宣告: ZipFile 及 BufferedWriter. 區塊中的程式碼無論是正常的或是由於產生 exception 而結束, BufferedWriter 及 ZipFile 物件的 close 方法會依特定順定自動被呼叫. 注意, close 方法的呼叫是依照物件被建立的相反順序.

The following example uses a try-with-resources statement to automatically close a java.sql.Statement object:
下列範例使用了一個 try-with-resource 敘述來自動關閉一個 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);
   }
  }

The resource java.sql.Statement used in this example is part of the JDBC 4.1 and later API.
這個範例中使用的  resource java.sql.Statement 是 JDBC 4.1 或之後版本的 API 的一部份.

Note: A try-with-resources statement can have catch and finally blocks just like an ordinary try statement. In a try-with-resources statement, any catch or finally block is run after the resources declared have been closed.
注意: try-with-resources 敘述可以有 catch 及 finally 區塊, 就像普通的 try 敍敘. 在 try-with-resources 敘述中, 任何 catch 或 finally 區塊會在宣告的 resources 關閉之後才執行.

Suppressed Exceptions
An exception can be thrown from the block of code associated with the try-with-resources statement. In the example writeToFileZipFileContents, an exception can be thrown from the try block, and up to two exceptions can be thrown from the try-with-resources statement when it tries to close the ZipFile and BufferedWriter objects. If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources statement, then those exceptions thrown from the try-with-resources statement are suppressed, and the exception thrown by the block is the one that is thrown by the writeToFileZipFileContents method. You can retrieve these suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try-with-resources.
受抑制的 Exceptions
try-with-resources 敘述的區塊中的相關程式碼可能產生 exception. 在範例中 writeToFileZipFileContents 可能丟出來自於 try 區塊的 exception, 以及最多兩個來自於 try-with-resources 敘述的 exceptions, 當它嘗試關閉 ZipFile 和 BufferedWriter 物件時. 如果丟出來的 exception 是來自於 try 區塊以及一個或多個 exceptions 來自於 try-with-resources 敘述, 那麼那些來自於 try-with-resources 敘述的 exception 是受到抑制的, 而來自於 try 區塊的 exception 才是那個會被 writeToFileZipFileContents 方法丟出的 exception. 你可以呼叫 Throwable.getSupperssed 方法來取得這些來自於 try-with-sources 的被抑制的 excepions.


Classes That Implement the AutoCloseable or Closeable Interface
See the Javadoc of the AutoCloseable and Closeable interfaces for a list of classes that implement either of these interfaces. The Closeable interface extends the AutoCloseable interface. The close method of the Closeable interface throws exceptions of type IOException while the close method of the AutoCloseable interface throws exceptions of type Exception. Consequently, subclasses of the AutoCloseable interface can override this behavior of the close method to throw specialized exceptions, such as IOException, or no exception at all.
實作 AutoCloseable 或 Closeable 界面的類別
查閱 Javadoc 關於 AutoCloseable 及 Closeable 界面的相關類別無論是否實作這些界面. Closeable 界面延伸自 AutoCloseable 界面. Closeable 界面中的 close 方法所 throws 的 exception 的型別是 IOException, 然而 AutoCloseable 界面的 close 方法所 throws 的 exception 的型別卻是 Exception. 所以, AutoCloseable 界面的下層類別可以用這種模式, 在 override close 這個方法時, throws 特定的 exception, 像是 IOException, 或完全沒有 exception.
Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking
許裕永 譯 (原文內容取自 Oracle 官網)
This page covers the following topics:
Handling More Than One Type of Exception
Rethrowing Exceptions with More Inclusive Type Checking
本頁包含下列主題:Handling More Than One Type of Exception
Rethrowing Exceptions with More Inclusive Type Checking
Handling More Than One Type of Exception
In Java SE 7 and later, a single catch block can handle more than one type of exception. This feature can reduce code duplication and lessen the temptation to catch an overly broad exception.
處理一個以上型別的 Exception
在 Java SE7 及以後版本, 單一一個 catch 區塊可以處理一個以上型別的 Exception. 這個特色可以減少重複的程式碼以及降低 catch 過度廣泛的 exception 的誘因.


Consider the following example, which contains duplicate code in each of the catch blocks:
了解下列範例, 它包含了重複的程式碼在每一個 catch 區塊之中:
 catch (IOException ex) {
  logger.log(ex);
  throw ex;
 catch (SQLException ex) {
  logger.log(ex);
  throw ex;
 }

In releases prior to Java SE 7, it is difficult to create a common method to eliminate the duplicated code because the variable ex has different types.
在 Java SE 7 之前的版本, 因為變數 ex 代表著不同的型別, 所以很難建立一個公用方法來排除重複的程式碼.

The following example, which is valid in Java SE 7 and later, eliminates the duplicated code:
下列範例, 它在 Java SE7 及以後版本中是合法的, 它排除了重複的程式碼:
 catch (IOException | SQLException ex) {
  logger.log(ex);
  throw ex;
 }

The catch clause specifies the types of exceptions that the block can handle, and each exception type is separated with a vertical bar (|).
在 catch 條文中宣告這個區塊可以處理的所有 exceptions 的型別, 而且每一個 exception 的型別用直條線 (|) 區隔.

Note: If a catch block handles more than one exception type, then the catch parameter is implicitly final. In this example, the catch parameter ex is final and therefore you cannot assign any values to it within the catch block.
注意: 如果一個 catch 區塊處理了一個以上的 exception 型別, 那麼該 catch 中的參數將會隱含宣告為 final. 在這個範例中, catch 中的參數 ex 就是 final, 所以在這個 catch 區塊中, 你不可以指派任何值給它.

Bytecode generated by compiling a catch block that handles multiple exception types will be smaller (and thus superior) than compiling many catch blocks that handle only one exception type each. A catch block that handles multiple exception types creates no duplication in the bytecode generated by the compiler; the bytecode has no replication of exception handlers.
一個 catch 區塊處理多個型別的 exception, 在編譯後所產生的 bytecode, 將會比用多個 catch 區塊各自處理一個 exception 所產生的 bytecode 更少而且更優越. 一個 catch 區塊處理多個型別的 exception,  編譯器在產生 bytecode 時不會發生重複的情形; bytecode 中不會有重複的 exception 處理者.

Rethrowing Exceptions with More Inclusive Type Checking
The Java SE 7 compiler performs more precise analysis of rethrown exceptions than earlier releases of Java SE. This enables you to specify more specific exception types in the throws clause of a method declaration.
Rethrowing Exceptions 時, 附加了對 exceptions 所包含的型別的檢查:

Consider the following example:
了解下列範例:

 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;
  }
 }

This examples's try block could throw either FirstException or SecondException. Suppose you want to specify these exception types in the throws clause of the rethrowException method declaration. In releases prior to Java SE 7, you cannot do so. Because the exception parameter of the catch clause, e, is type Exception, and the catch block rethrows the exception parameter e, you can only specify the exception type Exception in the throws clause of the rethrowException method declaration.
這個範例的 try 區塊可能丟出 FirstException 或 SecondException. 假設你想要在 rethrowException 方法宣告時在它的 throws 條文中宣告這些 exception 的型別. 在 Java SE 7 之前的版本中, 你是無法辦到的. 因為在 catch條文中的 exception 參數 e 的型別是 Exception, 而且在 catch 區塊中 rethrows exception 參數 e, 你只能夠在宣告 rethrowException 方法時在 throws 條文中指定 exception 型別為 Exception.

However, in Java SE 7, you can specify the exception types FirstException and SecondException in the throws clause in the rethrowException method declaration. The Java SE 7 compiler can determine that the exception thrown by the statement throw e must have come from the try block, and the only exceptions thrown by the try block can be FirstException and SecondException. Even though the exception parameter of the catch clause, e, is type Exception, the compiler can determine that it is an instance of either FirstException or SecondException:
然而, 在 Java SE 7, 你可以在宣告 rethrowException 方法時, 指定 exception 的型別 FirstException 及 SecondException 在 throws 條文之中. Java SE 7 編譯器可以判定 exception 的被丟出必定是來自於 try 區塊中的敘述  throw e, 而且從 try 區塊中丟出的 exceptions 一定只會是 FirstException 或 SecondException. 即使在 catch 條文中的 exception 參數 e 的型別是 Exception, 編譯器可以也判定它是 FirstException 或 SecondException 的實體.

 public void rethrowException (String exceptionName) throws FirstException, SecondException {
  try {
   // ...
  }
  catch (Exception e) {
   throw e;
  }
 }

This analysis is disabled if the catch parameter is assigned to another value in the catch block. However, if the catch parameter is assigned to another value, you must specify the exception type Exception in the throws clause of the method declaration.
如果在 catch 區塊中接收到的參數是被指派了其他型別的值, 那這個分析是不完整的. 總之, 如果有其他型別的參數會被指派,  你必須把該 exception 的型別宣告在方法宣告的 throw 條文之中.

In detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions:
就細部而言, 在 Java SE 7 及其以後版本, 當你在 catch 條文中宣告一個或一個以上的 exception 型別, 而且 rethrow 在這個 catch 區塊中要處理的 exception  , 編譯器會滿足下列條件來查核被宣告丟出的 exception 的型別:

  • The try block is able to throw it.
    這個 try 區塊有能力丟出它.
  • There are no other preceding catch blocks that can handle it.
    之前沒有其他的 catch 區塊可以處理它.
  • It is a subtype or supertype of one of the catch clause's exception parameters.
    它是 catch 條文中的  exception 參數中的其中一個參數的下層型別或上層型別.

The Java SE 7 compiler allows you to specify the exception types FirstException and SecondException in the throws clause in the rethrowException method declaration because you can rethrow an exception that is a supertype of any of the types declared in the throws.
Java SE 7 編譯器允許你可以指定 exception 型別 FirstException 及 SecondException 在 rethrowException 方法宣告的 throws 條文中, 因為你可能 rethrow 的 exception 的型別, 會是在 throws 宣告的型別中的任何一個型別的上層型別.

In releases prior to Java SE 7, you cannot throw an exception that is a supertype of one of the catch clause's exception parameters. A compiler from a release prior to Java SE 7 generates the error, "unreported exception Exception; must be caught or declared to be thrown" at the statement throw e. The compiler checks if the type of the exception thrown is assignable to any of the types declared in the throws clause of the rethrowException method declaration. However, the type of the catch parameter e is Exception, which is a supertype, not a subtype, of FirstException and SecondException.
在 Java SE  7 之前釋出的版出, 你不可以丟出 throws 條文中宣告的 exception 參數的上層型別的 exception. Java SE 7 之前版本的編譯器會針對 throw e 這行敘述, 產生一個 error: "unreported exception Exception; must be caught or declared to be thrown". 之前版本的編譯器會依照宣告在 rethrowException 方法的 throws 條文中的型別來查核被丟出的物件是否是可指派的. 然而在 catch 區塊衵的參數 e 的型別是 Exception, 它是 FirstException 及 SecondException 的上層型別不是下層型別.

Underscores in Numeric Literals
許裕永 譯 (原文內容取自 Oracle 官網)
Underscores in Numeric Literals - Any number of underscore characters (_) can appear anywhere between digits in a numerical literal. This feature enables you, for example, to separate groups of digits in numeric literals, which can improve the readability of your code.
數值字面值中的下底線 - 任意數量的下底線字元( _ )可以出現在任何數值字面值的任何位置. 這個特色可以讓你如同範例一般, 把數值字面值中的數字分割成數個群組, 這樣可以增加你的程式碼的可讀性.

For instance, if your code contains numbers with many digits, you can use an underscore character to separate digits in groups of three, similar to how you would use a punctuation mark like a comma, or a space, as a separator.
在實務上, 如果你的程式碼中包含有許多位數的數值, 你可以使用下底線字元將數字切割成三個部份, 如同你使用像是逗點或空格之類的標點符號來區分數字.

The following example shows other ways you can use the underscore in numeric literals:
下列範例秀出了其他你可以使用下底線在數值字面值之中的方式.

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;

You can place underscores only between digits; you cannot place underscores in the following places:
你只可以在數字之中置放下底線; 你不可以在下列地方置放下底線:
  • At the beginning or end of a number
    數字的起頭或結尾Adjacent to a decimal point in a floating point literal
    緊臨浮點數字面值中的小數點
  • Prior to an F or L suffix
    在 F 或 L 的符號之前
  • In positions where a string of digits is expected
    在形勢上被預期為數字的 String.
The following examples demonstrate valid and invalid underscore placements (which are highlighted) in numeric literals:
下列範例示範了在數值字面值中合法及不合法的下底線置放位置(這些為最突出的):

float pi1 = 3_.1415F; // Invalid; cannot put underscores adjacent to a decimal point
float pi2 = 3._1415F; // Invalid; cannot put underscores adjacent to a decimal point
long socialSecurityNumber1
= 999_99_9999_L; // Invalid; cannot put underscores prior to an L suffix

int x1 = _52; // This is an identifier, not a numeric literal
int x2 = 5_2; // OK (decimal literal)
int x3 = 52_; // Invalid; cannot put underscores at the end of a literal
int x4 = 5_______2; // OK (decimal literal)

int x5 = 0_x52; // Invalid; cannot put underscores in the 0x radix prefix
int x6 = 0x_52; // Invalid; cannot put underscores at the beginning of a number
int x7 = 0x5_2; // OK (hexadecimal literal)
int x8 = 0x52_; // Invalid; cannot put underscores at the end of a number

int x9 = 0_52; // OK (octal literal)
int x10 = 05_2; // OK (octal literal)
int x11 = 052_; // Invalid; cannot put underscores at the end of a number
Strings in switch Statements
許裕永 譯 (原文內容取自 Oracle 官網)
Strings in switch Statements - You can use the String class in the expression of a switch statement.
Strings 在 switch 敍述中 - 你可以在 switch 敍述中使用 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;
}


The switch statement compares the String object in its expression with the expressions associated with each case label as if it were using the String.equals method; consequently, the comparison of String objects in switch statements is case sensitive. The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.
switch 敍述在與其相關的每一個 case 標籤進行比對 String 物件的運算時, 就如同使用 String.equals 方法; 所以在 switch 敍述中對於 String 物件的比對是有區分大小寫的. 使用 switch 敍述比對 String 物件, Java 編譯器通常會產生比使用 if-then-else 鏈結敍述比對 String 物件更加有效率的 bytecode.
 
Binary Literals
許裕永 譯 (原文內容取自 Oracle 官網)
Binary Literals - In Java SE 7, the integral types (byte, short, int, and long) can also be expressed using the binary number system. To specify a binary literal, add the prefix 0b or 0B to the number.
 二進位字面值 - 在 Java SE 7 , 整數型別 (byte, short, int, and long) 可以使用二進位數字系統表示. 要撰寫二進位的字面值, 以 0b 或 0B 為數字的字首.

The following examples show binary literals:
下列範例示範二進位字面值的表示方式:

// An 8-bit 'byte' value: byte aByte = (byte)0b00100001;
// A 16-bit 'short' value: short aShort = (short)0b1010000101000101;
// Some 32-bit 'int' values: int anInt1 = 0b10100001010001011010000101000101;
int anInt2 = 0b101;
 int anInt3 = 0B101;
 // The B can be upper or lower case.
 // A 64-bit 'long' value. Note the "L" suffix:
 long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;


Binary literals can make relationships among data more apparent than they would be in hexadecimal or octal. For example, each successive number in the following array is rotated by one bit:
二進位字面值可以讓資料之間的關係比使用16進位或8進位更加顯而易見. 範例中, 各個連續的數字於陣列中以一個 bit 往前進位:

public static final int[] phases = { 0b00110001, 0b01100010, 0b11000100, 0b10001001, 0b00010011, 0b00100110, 0b01001100, 0b10011000 }
In hexadecimal, the relationship among the numbers is not readily apparent:
在16進位中, 數字之間的關係便不是很容易顯示出來:

public static final int[] phases = { 0x31, 0x62, 0xC4, 0x89, 0x13, 0x26, 0x4C, 0x98 } You can use binary integral constants in code that you can verify against a specifications document, such as a simulator for a hypothetical 8-bit microprocessor:
你可以把二進位數字包含在程式碼中, 讓你可以與規格文件進行比對驗證, 如同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 0b00100000:
    return state.jumpTo(address);
   case 0b01000000:
    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();
  }
 }
}


 You can use binary literals to make a bitmap more readable:
你可以使用二進位字面值讓一個圖檔更加容易讀取.

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;