0.6.Java 8 . 7 新特性
Java 7
1.1. Switch語句支援string型別
1.2. 泛型例項的建立可以通過型別推斷來簡化 可以去掉後面new部分的泛型型別,只用<>就可以了。 //使用泛型前 List strList = new ArrayList(); List<String> strList4 = new ArrayList<String>(); List<Map<String, List<String>>> strList5 = new ArrayList<Map<String, List<String>>>(); //編譯器使用尖括號 (<>) 推斷型別 List<String> strList0 = new ArrayList<String>(); List<Map<String, List<String>>> strList1 = new ArrayList<Map<String, List<String>>>(); List<String> strList2 = new ArrayList<>(); List<Map<String, List<String>>> strList3 = new ArrayList<>(); List<String> list = new ArrayList<>(); list.add("A"); // The following statement should fail since addAll expects // Collection<? extends String> //list.addAll(new ArrayList<>())
1.3.對集合類(collections)的語言支援
原本需要這樣:
List list = new ArrayList();
list.add(“item”);
String item = list.get(0);
Set set = new HashSet();
set.add(“item”);
Map map = new HashMap();
map.put(“key” , 1);
int value = map.get(“key”);
現在你可以這樣:
List list = [“item”];
String item = list[0];
Set set = {“item”};
Map map = {“key”: 1};
int value = map[“key”];
1.4.自動資源管理
Java中的有些資源需要通過如InputStream、Writers、Sockets、Sql類等方式手動關閉。這個新的語言特性可以讓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(可關閉的)介面
1.5.數值的下劃線
很長的數字串很難閱讀。現在你可以在int以及long中使用下劃線將其分開。
int
one_million = 1_000_000;
1.6.二進位制文字
由於繼承C語言,Java程式碼在傳統上迫使程式設計師只能使用十進位制,八進位制或十六進位制來表示數(numbers)。
由於很少的域是以bit導向的,這種限制可能導致錯誤。你現在可以使用0b字首建立二進位制文字:
int binary = 0b1001_1001;
JAVA8
2.1.介面的預設方法
Java 8允許我們給介面新增一個非抽象的方法實現,只需要使用 default關鍵字即可,這個特徵又叫做擴充套件方法,示例如下:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
Formula介面在擁有calculate方法之外同時還定義了sqrt方法,實現了Formula介面的子類只需要實現一個calculate方法,預設方法sqrt將在子類上可以直接使用。
2.2.Lambda 表示式(參考2.3.函式式介面)
首先看看在老版本的Java中是如何排列字串的:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
只需要給靜態方法 Collections.sort 傳入一個List物件以及一個比較器來按指定順序排列。通常做法都是建立一個匿名的比較器物件然後將其傳遞給sort方法。
在Java 8 中你就沒必要使用這種傳統的匿名物件的方式了,Java 8提了更簡潔,lambda表示式:
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
程式碼變得更段且更具有可讀性,但是實際上還可以寫得更短:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
對於函式體只有一行程式碼的,你可以去掉大括號{}以及return關鍵字,還可以寫得更短點:
Collections.sort(names, (a, b) -> b.compareTo(a));
Java編譯器可以自動推匯出引數型別,所以你可以不用再寫一次型別。
2.3.函式式介面(是指僅僅只包含一個抽象方法的介面)
將lambda表示式當作任意只包含一個抽象方法的介面型別,確保你的介面一定達到這個要求,你只需要給你的介面新增 @FunctionalInterface 註解,編譯器如果發現你標註了這個註解的介面有多於一個抽象方法的時候會報錯的。
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
需要注意如果@FunctionalInterface如果沒有指定,上面的程式碼也是對的。
將lambda表示式對映到一個單方法的介面上,這種做法在Java 8之前就有別的語言實現,比如JavaScript直譯器。
2.4.方法與建構函式引用
前一節中的程式碼還可以通過靜態方法引用來表示:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123
Java 8 允許你使用 :: 關鍵字來傳遞方法或者建構函式引用,上面的程式碼展示瞭如何引用一個靜態方法,我們也可以引用一個物件的方法:
converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted); // "J"
接下來看看建構函式是如何使用::關鍵字來引用的,首先我們定義一個包含多個建構函式的簡單類:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
接下來指定一個用來建立Person物件的物件工廠介面:
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
這裡我們使用建構函式引用來將他們關聯起來,而不是實現一個完整的工廠:
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
我們只需要使用 Person::new 來獲取Person類建構函式的引用,Java編譯器會自動根據PersonFactory.create方法的簽名來選擇合適的建構函式。
2.5訪問區域性變數
在lambda表示式中訪問外層作用域和老版本的匿名物件中的方式很相似。你可以直接訪問標記了final的外層區域性變數,或者例項的欄位以及靜態變數
我們可以直接在lambda表示式中訪問外層的區域性變數:
final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
但是和匿名物件不同的是,這裡的變數num可以不用宣告為final,該程式碼同樣正確:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2); // 3
不過這裡的num必須不可被後面的程式碼修改(即隱性的具有final的語義),例如下面的就無法編譯:
int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
num = 3;
在lambda表示式中試圖修改num同樣是不允許的。
2.6.訪問物件欄位與靜態變數
和本地變數不同的是,lambda內部對於例項的欄位以及靜態變數是即可讀又可寫。該行為和匿名物件是一致的:
class Lambda4 {
static int outerStaticNum;
int outerNum;
void testScopes() {
Converter<Integer, String> stringConverter1 = (from) -> {
outerNum = 23;
return String.valueOf(from);
};
Converter<Integer, String> stringConverter2 = (from) -> {
outerStaticNum = 72;
return String.valueOf(from);
};
}
}
2.7.訪問介面的預設方法
還記得第一節中的formula例子麼,介面Formula定義了一個預設方法sqrt可以直接被formula的例項包括匿名物件訪問到,但是在lambda表示式中這個是不行的。
Lambda表示式中是無法訪問到預設方法的,以下程式碼將無法編譯:
Formula formula = (a) -> sqrt( a * 100);
JDK 1.8 API包含了很多內建的函式式介面,在老Java中常用到的比如Comparator或者Runnable介面,這些介面都增加了@FunctionalInterface註解以便能用在lambda上。
Java 8 API同樣還提供了很多全新的函式式介面來讓工作更加方便。
Comparator 是老Java中的經典介面, Java 8在此之上添加了多種預設方法:
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");
comparator.compare(p1, p2); // > 0
comparator.reversed().compare(p1, p2); // < 0
10.13.JDK8對併發新支援(StampedLock)