JDK是Java開發者的靈魂,每一個版本的迭代都代表著Java開發的朝向。我們按照時間軸順序來總結JDK的更新版本和相關特性,以此來加深JDK版本功能特性記憶。由於篇幅有限,本文旨在介紹版本功能特性和差異性,不對每一個新功能做原理性詳情介紹。
JDK7(2011.07.18-2016.10.18)
功能特性
1、二進位制字面量
2、在數字字面量使用下劃線
3、switch可以使用string
4、例項建立的型別推斷
5、使用Varargs方法使用不可維護的形式引數時改進了編譯器警告和錯誤
6、try-with-resources 資源的自動管理
7、捕捉多個異常型別和對重新丟擲異常的高階型別檢查
功能介紹和使用
二進位制字面量
整數型別例如(byte,short,int,long)能夠用二進位制來表示了。透過在數字前面加入0b或者0B來標示一個二進位制的字面量:
// 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;
在數字字面量使用下劃線
你的程式碼包含一些數字有很多的位數,你能夠用下劃線字元把位數分為三組,類似於你用一個像逗號或者一個空格作為分隔符。
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;
下劃線只能出現在數字之間,下面的情形不能出現下劃線:
A、數字的開頭和結尾
B、在浮點數中與小數點相鄰
C、F或者L字尾之前
D、在預期數字串的位置
switch可以使用string
在使用switch中可以使用String型別的表示式來分支處理。
泛型例項建立的型別推斷
可以用空的型別引數集(<>)替換呼叫泛型類的建構函式所需的型別引數,只要編譯器可以從上下文中推斷型別引數即可。這對尖括號被非正式地稱為菱形。
Map<String, List<String>> myMap = new HashMap<>();
try-with-resources語句
try-with-resources 宣告是try 一個或多個資源的宣告。一個資源作為一個物件在程式結束之後必須關閉它。try-with-resources宣告保證每一個資源都會被關閉在宣告結束的時候。任何實現了java.lang.AutoCloseable介面或者實現了java.io.Closeable,可以作為一個資源。
這意味著JDK以後的try塊程式碼塊將變的簡潔,安全。
static String readFirstLineFromFile(String path) throws IOException {try (BufferedReader br = new BufferedReader(new FileReader(path))) {return br.readLine();}}
捕捉多個異常型別和對重新丟擲異常的高階型別檢查
在Java SE 7和更高版本可以消除catch中異常的重複程式碼,可以指定了該塊可以處理的異常型別,並且每種異常型別都用豎線(|)分隔。
try { System.out.println(10 / 0); System.out.println(arr[10]); // 陣列長度為3 } catch (ArithmeticException | ArrayIndexOutOfBoundsException e) { e.printStackTrace(); }
總結
從功能特性可見JDK7更新的是比較基礎的語法規則,在語法規則,異常機制做了最佳化。
JDK8(穩定版2014.03.18~2016.10.18)
功能特性
1、Lambda表示式
2、函式式介面
3、方法引用和構造器呼叫
4、Stream API
5、介面中的預設方法和靜態方法
6、新時間日期API
功能介紹和使用
Lambda表示式
lambda表示式本質上是一段匿名內部類,也可以是一段可以傳遞的程式碼,它簡潔直觀,很容易上手使用。
//匿名內部類 Comparator<Integer> cpt = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; TreeSet<Integer> set = new TreeSet<>(cpt);//改造成Lambda形式Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y); TreeSet<Integer> set2 = new TreeSet<>(cpt2);
lambda表示式語法總結:
前置書寫和語法要求
無引數無返回值
() -> System.out.println(“Hello WOrld”)
有一個引數無返回值
(x) -> System.out.println(x)
有且只有一個引數無返回值
x -> System.out.println(x)
有多個引數,有返回值,有多條lambda體語句
(x,y) -> {System.out.println(“xxx”);return xxxx;};
有多個引數,有返回值,只有一條lambda體語句
(x,y) -> xxxx
函式式介面
函式式介面的提出是為了給Lambda表示式的使用提供更好的支援。什麼是函式式介面?簡單來說就是隻定義了一個抽象方法的介面(Object類的public方法除外),就是函式式介面,並且還提供了註解:@FunctionalInterface
A、消費型介面,有參無返回值
@Test public void test(){ changeStr("hello",(str) -> System.out.println(str)); } /** * Consumer<T> 消費型介面 * @param str * @param con */ public void changeStr(String str, Consumer<String> con){ con.accept(str); }
B、供給型介面,無參有返回值
@Test public void test2(){ String value = getValue(() -> "hello"); System.out.println(value); } /** * Supplier<T> 供給型介面 * @param sup * @return */ public String getValue(Supplier<String> sup){ return sup.get(); }
C、函式式介面,有參有返回值
@Test public void test3(){ Long result = changeNum(100L, (x) -> x + 200L); System.out.println(result); } /** * Function<T,R> 函式式介面 * @param num * @param fun * @return */ public Long changeNum(Long num, Function<Long, Long> fun){ return fun.apply(num);}
D、斷言型介面,有參有返回值,返回值是boolean類
public void test4(){ boolean result = changeBoolean("hello", (str) -> str.length() > 5); System.out.println(result); } /** * Predicate<T> 斷言型介面 * @param str * @param pre* @return */ public boolean changeBoolean(String str, Predicate<String> pre){ return pre.test(str); }
方法引用
1、方法引用
2、構造器引用
3、陣列引用
方法引用
物件:例項方法名類:靜態方法名類:例項方法名 (lambda引數列表中第一個引數是例項方法的呼叫 者,第二個引數是例項方法的引數時可用)
Stream API
Stream操作的三個步驟
A、建立stream
B、中間操作(過濾、map)
C、終止操作
並行流和序列流
在jdk1.8新的stream包中針對集合的操作也提供了並行操作流和序列操作流。並行流就是把內容切割成多個數據塊,並且使用多個執行緒分別處理每個資料塊的內容。Stream api中宣告可以透過parallel()與sequential()方法在並行流和序列流之間進行切換。jdk1.8並行流使用的是fork/join框架進行並行操作。
ForkJoin框架
Fork/Join 框架:就是在必要的情況下,將一個大任務,進行拆分(fork)成若干個小任務(拆到不可再拆時),再將一個個的小任務運算的結果進行 join 彙總。關鍵字:遞迴分合、分而治之。採用 “工作竊取”模式(work-stealing):當執行新的任務時它可以將其拆分分成更小的任務執行,並將小任務加到執行緒佇列中,然後再從一個隨機執行緒的佇列中偷一個並把它放在自己的佇列中相對於一般的執行緒池實現,fork/join框架的優勢體現在對其中包含的任務的處理方式上.在一般的執行緒池中,如果一個執行緒正在執行的任務由於某些原因無法繼續執行,那麼該執行緒會處於等待狀態.而在fork/join框架實現中,如果某個子問題由於等待另外一個子問題的完成而無法繼續執行.那麼處理該子問題的執行緒會主動尋找其他尚未執行的子問題來執行.這種方式減少了執行緒的等待時間,提高了效能.。
/** * java8 並行流 parallel() */ @Test public void test2() { //開始時間 Instant start = Instant.now(); // 並行流計算 累加求和 LongStream.rangeClosed(0, 10000000000L).parallel() .reduce(0, Long :: sum); //結束時間 Instant end = Instant.now(); System.out.println(Duration.between(start, end).getSeconds());}
Optional容器
使用Optional容器可以快速的定位NPE,並且在一定程度上可以減少對引數非空檢驗的程式碼量。
/** *Optional.of(T t); // 建立一個Optional例項 * Optional.empty(); // 建立一個空的Optional例項 * Optional.ofNullable(T t); // 若T不為null,建立一個Optional例項,否則建立一個空例項 * isPresent(); // 判斷是夠包含值 * orElse(T t); //如果呼叫物件包含值,返回該值,否則返回T * orElseGet(Supplier s); // 如果呼叫物件包含值,返回該值,否則返回s中獲取的值 * map(Function f): // 如果有值對其處理,並返回處理後的Optional,否則返回Optional.empty(); * flatMap(Function mapper);// 與map類似。返回值是Optional * * 總結:Optional.of(null) 會直接報NPE */Optional<Employee> op = Optional.of(new Employee("zhansan", 11, 12.32, Employee.Status.BUSY)); System.out.println(op.get()); // NPEOptional<Employee> op2 = Optional.of(null);System.out.println(op2); @Test public void test2(){ Optional<Object> op = Optional.empty(); System.out.println(op); // No value present System.out.println(op.get());}
介面中可以定義預設實現方法和靜態方法
在介面中可以使用default和static關鍵字來修飾介面中定義的普通方法
在JDK1.8中很多介面會新增方法,為了保證1.8向下相容,1.7版本中的介面實現類不用每個都重新實現新新增的介面方法,引入了default預設實現,static的用法是直接用介面名去調方法即可。當一個類繼承父類又實現介面時,若後兩者方法名相同,則優先繼承父類中的同名方法,即“類優先”,如果實現兩個同名方法的介面,則要求實現類必須手動宣告預設實現哪個介面中的方法。
新的日期API LocalDate | LocalTime | LocalDateTime
新的日期API都是不可變的,更使用於多執行緒的使用環境中。
之前使用的java.util.Date月份從0開始,我們一般會+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum * java.util.Date和SimpleDateFormat都不是執行緒安全的,而LocalDate和LocalTime和最基本的String一樣,是不變型別,不但執行緒安全,而且不能修改。 * java.util.Date是一個“萬能介面”,它包含日期、時間,還有毫秒數,更加明確需求取捨 * 新介面更好用的原因是考慮到了日期時間的操作,經常發生往前推或往後推幾天的情況。用java.util.Date配合Calendar要寫好多程式碼,而且一般的開發人員還不一定能寫對。
/** * 日期轉換常用,第一天或者最後一天... */public static void localDateTransferTest(){ //2018-05-04 LocalDate today = LocalDate.now(); // 取本月第1天: 2018-05-01 LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 取本月第2天:2018-05-02 LocalDate secondDayOfThisMonth = today.withDayOfMonth(2); // 取本月最後一天,再也不用計算是28,29,30還是31: 2018-05-31 LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth()); // 取下一天:2018-06-01 LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1); // 取2018年10月第一個週三 LocalDate thirdMondayOf2018 = LocalDate.parse("2018-10-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY)); }
總結
1、Java 8允許我們給介面新增一個非抽象的方法實現,只需要使用 default關鍵字即可;
2、新增lambda表示式;
3、提供函式式介面;
4、Java 8 允許你使用關鍵字來傳遞方法或者建構函式引用;
5、我們可以直接在lambda表示式中訪問外層的區域性變數。JDK8作為穩定版長期被使用。