JavaSE-新特性(JDK5-JDK12)持續維護

 

 

JDK5新特性

自動裝箱與拆箱
枚舉
靜態導入
可變參數(Varargs
內省(introspector
泛型(Generic
For-Each
循環

JDK6新特性

Desktop類和SystemTray
使用JAXB2來實現對象與XML之間的映射
理解STAX
使用Compiler API
輕量級 Http Server API
插入式註解處理 API
使用Console開發控制檯程序
對腳本語言的支持
Common Annotations
Java GUI
界面的顯示
嵌入式數據庫Derby
Web
服務元數據
Jtable
的排序和過濾
更簡單更強大的JAX-WS

JDK7新特性

switch中可以使用字符串了
泛型實例化類型自動推斷
自定義自動關閉類
新增一些讀取環境信息的工具方法
Boolean
類型反轉,空指針安全,參與位運算
兩個char之間的equals
安全的加減乘除|sd
對Java集合( Collections)的增強支持
數值可加下劃線
支持二進制數字
在try/catch異常撲捉中,一個catch可以寫多個異常類型用|隔開
可以不必要寫finally語句來關閉資源,只要你在try()的括號內部定義要使用的資源

JDK8新特性

接口的默認方法
Lambda表達式
函數式接口
方法與構造函數引用
擴展了集合類
新的Date API
Annotation多重註解
streams(流)
parallel streams(並行流)
Map數據結構改進

JDK9新特性

Jigsaw模塊化項目
簡化進程API
輕量級JSON API
錢和貨幣的API
改善鎖競爭機制
代碼分段緩存
智能java編譯
http2.0
客戶端
kulla
計劃

JDK10新特性
局部變量的類型推斷
GC改進和內存管理
線程本地握手
備用內存設備上的堆分配
其他Unicode語言 - 標記擴展
基於Java的實驗性JIT編譯器
開源根證書
根證書頒發認證(CA)
將JDK生態整合單個存儲庫
刪除工具javah

JDK11新特性
1
、基於嵌套的訪問控制
2
、動態類文件常量
3
、改進 Aarch64 內聯函數
4
、Epsilon:No-Op 垃圾收集器
5
、刪除 Java EE 和 CORBA 模塊
6
、HTTP 客戶端(標準)
7
、Lambda 參數的本地變量語法
8
、Curve25519 和 Curve448 密鑰協議
9
、Unicode 10
10
、運行記錄儀
11
、ChaCha20 和 Poly1305 密碼算法
12
、啓動單文件源代碼程序
13
、低開銷堆分析
14
、傳輸層安全性(TLS)1.3
15
、ZGC:可擴展的低延遲垃圾收集器
16
、棄用 Nashorn JavaScript 引擎
17
、棄用 Pack200 工具和 API

 

JEP(JDK Enhancement Proposals,JDK 增強提案)

JDK1.5新特性

1:自動裝箱與拆箱:

自動裝箱:每當需要一種類型的對象時,這種基本類型就自動地封裝到與它相同類型的包裝中。

自動拆箱:每當需要一個值時,被裝箱對象中的值就被自動地提取出來,沒必要再去調用intValue()和doubleValue()方法。

自動裝箱,只需將該值賦給一個類型包裝器引用,java會自動創建一個對象。

自動拆箱,只需將該對象值賦給一個基本類型即可。

java——類的包裝器

類型包裝器有:Double,Float,Long,Integer,Short,Character和Boolean

2:枚舉

把集合裏的對象元素一個一個提取出來。枚舉類型使代碼更具可讀性,理解清晰,易於維護。枚舉類型是強類型的,從而保證了系統安全性。而以類的靜態字段實現的類似替代模型,不具有枚舉的簡單性和類型安全性。

簡單用法:JavaEnum簡單的用法一般用於代表一組常用常量,可用來代表一類相同類型的常量值。

複雜用法:Java爲枚舉類型提供了一些內置的方法,同事枚舉常量還可以有自己的方法。可以很方便的遍歷枚舉對象。

3:靜態導入

通過使用 import static,就可以不用指定 Constants 類名而直接使用靜態成員,包括靜態方法。

import xxxx 和 import static xxxx的區別是前者一般導入的是類文件如import java.util.Scanner;後者一般是導入靜態的方法,import static java.lang.System.out。

4:可變參數(Varargs)

可變參數的簡單語法格式爲:

methodName([argumentList], dataType... argumentName);

5:內省(Introspector)

內省是Java語言對Bean類屬性、事件的一種缺省處理方法。例如類A中有屬性name,那可以通過getName,setName來得到其值或者設置新 的值。通過getName/setName來訪問name屬性,這就是默認的規則。Java中提供了一套API用來訪問某個屬性的getter /setter方法,通過這些API可以使你不需要了解這個規則(但你最好還是要搞清楚),這些API存放於包java.beans中。

一 般的做法是通過類Introspector來獲取某個對象的BeanInfo信息,然後通過BeanInfo來獲取屬性的描述器 (PropertyDescriptor),通過這個屬性描述器就可以獲取某個屬性對應的getter/setter方法,然後就可以通過反射機制來 調用這些方法。

6:泛型(Generic)

C++ 通過模板技術可以指定集合的元素類型,而Java在1.5之前一直沒有相對應的功能。一個集合可以放任何類型的對象,相應地從集合裏面拿對象的時候也不得不對他們進行強制得類型轉換。猛虎引入了泛型,它允許指定集合裏元素的類型,這樣你可以得到強類型在編譯時刻進行類型檢查的好處。

7:For-Each循環

For-Each循環得加入簡化了集合的遍歷。假設要遍歷一個集合對其中的元素進行一些處理。

 

JDK 1.6新特性

有關JDK1.6的新特性reamerit的博客文章已經說的很詳細了。

1:Desktop類和SystemTray類

在JDK6中 ,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的一部分,在JDK6中,SUN將其放到了Java SE中,這也是SUN的一貫做法。JDK6中自帶的這個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)是JDK6.0中除了DOM和SAX之外的又一種處理XML文檔的API。

StAX 的來歷 :在JAXP1.3(JSR 206)有兩種處理XML文檔的方法:DOM(Document Object Model)和SAX(Simple API for XML).

由 於JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都會用到StAX,所以Sun決定把StAX加入到JAXP家族當中來,並將JAXP的版本升級到1.4(JAXP1.4是JAXP1.3的維護版本). JDK6裏面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

現在可以用JDK6 的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

JDK6 提供了一個簡單的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開發控制檯程序

JDK6 中提供了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 ANNOTATIONTYPE, CONSTRUCTOR, FIELD, LOCALVARIABLE, 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來檢驗安全角色

注意:

1.RolesAllowed,PermitAll,DenyAll不能同時應用到一個類或方法上

2.標註在方法上的RolesAllowed,PermitAll,DenyAll會覆蓋標註在類上的RolesAllowed,PermitAll,DenyAll

3.RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles還沒有加到Java SE 6.0上來

4.處理以上Annotations的工作是由Java EE容器來做, Java SE 6.0只是包含了上面表格的前五種Annotations的定義類,並沒有包含處理這些Annotations的引擎,這個工作可以由Pluggable Annotation Processing API(JSR 269)來做

改動的地方最大的就是java GUI界面的顯示了,JDK6.0(也就是JDK1.6)支持最新的windows vista系統的Windows Aero視窗效果,而JDK1.5不支持!!!

你要在vista環境下編程的話最好裝jdk6.0,否則它總是換到windows basic視窗效果.

 

 

JDK 1.7 新特性

  1. 二進制字面量
  2. 數字字面量可以出現下劃線
  3. switch 語句可以用字符串
  4. 泛型簡化
  5. 異常的多個catch合併
  6. try-with-resources 語句

 

二進制字面量

  1. JDK7開始,終於可以用二進制來表示整數(byte,short,int和long)。使用二進制字面量的好處是,可以使代碼更容易被理解。語法非常簡單,只要在二進制數值前面加 0b或者0B
  2. 舉例:
    • int x = ob110110

 

數字字面量可以出現下劃線

  1. 爲了增強對數值的閱讀性,如經常把數據用逗號分隔一樣。JDK7提供了_對數據分隔。
  2. 舉例:
    • int x = 100_1000;
  3. 注意事項:
    • 不能出現在進制標識和數值之間
    • 不能出現在數值開頭和結尾
    • 不能出現在小數點旁邊

 

int a = 0b100_100;

int b = 0b_100_100;

int c = 0b100_100_;

float d = 12.34_56f;

float e = 12._34_56f;

 

switch 語句可以用字符串(底層是使用hashCode方法,然後再調用equals方法):String變量不能爲空,case子句中的字符串也不能爲null。字符串爲null會拋出空指針異常

 

泛型簡化:也就是鑽石操作符<>出現,泛型推斷

                   ArrayList<String> array = new ArrayList<>();

異常的多個catch合併:

Try{

}catch(Exception1 e | Exception2 e){

}

 

try-with-resources 語句:自動釋放(JDK1.9進一步優化)

  1. 格式:
    • try(必須是java.lang.AutoCloseable的子類對象){…}
    • 好處:
      • 資源自動釋放,不需要close()了
      • 把需要關閉資源的部分都定義在這裏就ok了
      • 主要是流體系的對象是這個接口的子類(看JDK7的API)

                   // 改進版的try和catch代碼

                   try (FileReader fr = new FileReader("a.txt");

                                     FileWriter fw = new FileWriter("b.txt");) {

                            int ch = 0;

                            while ((ch = fr.read()) != -1) {

                                     fw.write(ch);

                            }

                   } catch (IOException e) {

                            e.printStackTrace();

                   }

 

 

 

1:switch中可以使用字串

String s = "test";

switch (s) {

 case "test" :

   System.out.println("test");

case "test1" :

   System.out.println("test1");

break ;

default :

  System.out.println("break");

break ;

}

2:"<>"這個玩意兒的運用

ListtempList = new ArrayList<>(); 即泛型實例化類型自動推斷。

public class JDK7GenericTest {

 

   public static void main(String[] args) {

      // Pre-JDK 7

      List<String> lst1 = new ArrayList<String>();

 

      // JDK 7 supports limited type inference for generic instance creation

 

      List<String> lst2 = new ArrayList<>();

      lst1.add("Mon");

      lst1.add("Tue");

      lst2.add("Wed");

      lst2.add("Thu");

      for (String item: lst1) {

         System.out.println(item);

      }

 

      for (String item: lst2) {

         System.out.println(item);

      }

   }

}

3:自定義自動關閉類

以下是jdk7 api中的接口,(不過註釋太長,刪掉了close()方法的一部分註釋)

/**

 * A resource that must be closed when it is no longer needed.

 *

 * @author Josh Bloch

 * @since 1.7

 */

public interface AutoCloseable {

    /**

     * Closes this resource, relinquishing any underlying resources.

     * This method is invoked automatically on objects managed by the

     * {@code try}-with-resources statement.

     *

     */

    void close() throws Exception;

}

只要實現該接口,在該類對象銷燬時自動調用close方法,你可以在close方法關閉你想關閉的資源,例子如下

class TryClose implements AutoCloseable {

 

 @Override

 public void close() throw Exception {

  System.out.println(" Custom close method …

                                         close resources ");

 }

}

//請看jdk自帶類BufferedReader如何實現close方法(當然還有很多類似類型的類)

 

  public void close() throws IOException {

        synchronized (lock) {

            if (in == null)

                return;

            in.close();

            in = null;

            cb = null;

        }

    }

4:新增一些取環境信息的工具方法

File System.getJavaIoTempDir() // IO臨時文件夾

 

File System.getJavaHomeDir() // JRE的安裝目錄

 

File System.getUserHomeDir() // 當前用戶目錄

 

File System.getUserDir() // 啓動java進程時所在的目錄

 

.......

5:Boolean類型反轉,空指針安全,參與位運算

Boolean Booleans.negate(Boolean booleanObj)

 

True => False , False => True, Null => Null

 

boolean Booleans.and(boolean[] array)

 

boolean Booleans.or(boolean[] array)

 

boolean Booleans.xor(boolean[] array)

 

boolean Booleans.and(Boolean[] array)

 

boolean Booleans.or(Boolean[] array)

 

boolean Booleans.xor(Boolean[] array)

6: 兩個char間的equals

boolean Character.equalsIgnoreCase(char ch1, char ch2)

7:安全的加減乘除

int Math.safeToInt(long value)

 

int Math.safeNegate(int value)

 

long Math.safeSubtract(long value1, int value2)

 

long Math.safeSubtract(long value1, long value2)

 

int Math.safeMultiply(int value1, int value2)

 

long Math.safeMultiply(long value1, int value2)

 

long Math.safeMultiply(long value1, long value2)

 

long Math.safeNegate(long value)

 

int Math.safeAdd(int value1, int value2)

 

long Math.safeAdd(long value1, int value2)

 

long Math.safeAdd(long value1, long value2)

 

int Math.safeSubtract(int value1, int value2)

8:對Java集合(Collections)的增強支持

在JDK1.7之前的版本中,Java集合容器中存取元素的形式如下:

以List、Set、Map集合容器爲例:

    //創建List接口對象

    List<String> list=new ArrayList<String>();

    list.add("item"); //用add()方法獲取對象

    String Item=list.get(0); //用get()方法獲取對象

 

    //創建Set接口對象

    Set<String> set=new HashSet<String>();

    set.add("item"); //用add()方法添加對象

 

    //創建Map接口對象

    Map<String,Integer> map=new HashMap<String,Integer>();

    map.put("key",1); //用put()方法添加對象

    int value=map.get("key");

在JDK1.7中,摒棄了Java集合接口的實現類,如:ArrayList、HashSet和HashMap。而是直接採用[]、{}的形式存入對象,採用[]的形式按照索引、鍵值來獲取集合中的對象,如下:

List<String> list=["item"]; //向List集合中添加元素

      String item=list[0]; //從List集合中獲取元素

 

      Set<String> set={"item"}; //向Set集合對象中添加元素

      Map<String,Integer> map={"key":1}; //向Map集合中添加對象

      int value=map["key"]; //從Map集合中獲取對象

9:數值可加下劃線

例如:int one_million = 1_000_000;

10:支持二進制文字

例如:int binary = 0b1001_1001;

11:簡化了可變參數方法的調用

當程序員試圖使用一個不可具體化的可變參數並調用一個varargs (可變)方法時,編輯器會生成一個“非安全操作”的警告。

12:在try catch異常撲捉中,一個catch可以寫多個異常類型,用"|"隔開

jdk7之前:

try {

   ......

} catch(ClassNotFoundException ex) {

   ex.printStackTrace();

} catch(SQLException ex) {

   ex.printStackTrace();

}

jdk7例子如下

try {

   ......

} catch(ClassNotFoundException|SQLException ex) {

   ex.printStackTrace();

}

13:jdk7之前,你必須用try{}finally{}在try內使用資源,在finally中關閉資源,不管try中的代碼是否正常退出或者異常退出。jdk7之後,你可以不必要寫finally語句來關閉資源,只要你在try()的括號內部定義要使用的資源

jdk7之前:

import java.io.*;

// Copy from one file to another file character by character.

// Pre-JDK 7 requires you to close the resources using a finally block.

public class FileCopyPreJDK7 {

   public static void main(String[] args) {

      BufferedReader in = null;

      BufferedWriter out = null;

      try {

         in  = new BufferedReader(new FileReader("in.txt"));

         out = new BufferedWriter(new FileWriter("out.txt"));

         int charRead;

         while ((charRead = in.read()) != -1) {

            System.out.printf("%c ", (char)charRead);

            out.write(charRead);

         }

      } catch (IOException ex) {

         ex.printStackTrace();

      } finally {            // always close the streams

         try {

            if (in != null) in.close();

            if (out != null) out.close();

         } catch (IOException ex) {

            ex.printStackTrace();

         }

      }

 

      try {

         in.read();   // Trigger IOException: Stream closed

      } catch (IOException ex) {

         ex.printStackTrace();

      }

   }

}

jdk7之後

import java.io.*;

// Copy from one file to another file character by character.

// JDK 7 has a try-with-resources statement, which ensures that

// each resource opened in try() is closed at the end of the statement.

public class FileCopyJDK7 {

   public static void main(String[] args) {

      try (BufferedReader in  = new BufferedReader(new FileReader("in.txt"));

           BufferedWriter out = new BufferedWriter(new FileWriter("out.txt"))) {

         int charRead;

         while ((charRead = in.read()) != -1) {

            System.out.printf("%c ", (char)charRead);

            out.write(charRead);

         }

      } catch (IOException ex) {

         ex.printStackTrace();

      }

   }

}

 

 

 

 

 

 

 

 

JDK8特性

Java8新特性

1.Lambda 表達式

2.函數式接口

3.方法引用與構造器引用

4.Stream API

5.接口中的默認方法與靜態方法

6.新時間日期API

速度更快、代碼更少(增加了lambda表達式)、強大的Stream API。便於並行、最大haul減少空指針異常(Optional)

最核心的是Lambda和Stream API

 

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將在子類上可以直接使用。

代碼如下:

Formula formula = new Formula() {

    @Override

    public double calculate(int a) {

        return sqrt(a * 100);

    }

};

formula.calculate(100);     // 100.0

formula.sqrt(16);           // 4.0

文中的formula被實現爲一個匿名類的實例,該代碼非常容易理解,6行代碼實現了計算 sqrt(a * 100)。在下一節中,將會看到實現單方法接口的更簡單的做法。

譯者注: 在Java中只有單繼承,如果要讓一個類賦予新的特性,通常是使用接口來實現,在C++中支持多繼承,允許一個子類同時具有多個父類的接口與功能,在其他 語言中,讓一個類同時具有其他的可複用代碼的方法叫做mixin。新的Java 8 的這個特新在編譯器實現的角度上來說更加接近Scala的trait。 在C#中也有名爲擴展方法的概念,允許給已存在的類型擴展方法,和Java 8的這個在語義上有差別。

 

 

接口中的默認方法

Java 8中允許接口中包含具有具體實現的方法,該方法稱爲“默認方法”,默認方法使用 default 關鍵字修飾。

 

//知識點1:接口中定義的靜態方法,只能由接口自身調用

//知識點2:接口中定義的默認方法,可以被實現類的對象調用

//知識點3:如果父類和接口中定義了同名同參數的方法,對於子類來說,默認執行的是父類中的方法。---類優先原則

//知識點4:如果實現類實現了多個接口,而多個接口中定義了同名同參數的默認方法,則會出現接口衝突。

//要想解決衝突,實現類必須重寫這個同名同參數的方法。調用時,執行的是自己重寫的方法

 

public class MyClass {     

         public String getName(){

                   return "嘿嘿嘿";

         }

}

public interface MyFun {

         default String getName(){

                   return "哈哈哈";

         }

}

public interface MyInterface {

         default String getName(){

                   return "呵呵呵";

         }

         public static void show(){

                   System.out.println("接口中的靜態方法");

         }

}

如果類和接口擁有同一方法的話,默認是類。如果兩個接口,必須重寫方法,否則編譯爆錯

public class SubClass /*extends MyClass*/ implements MyFun, MyInterface{

         @Override

         public String getName() {

                   return MyInterface.super.getName();

         }

}

 

 

接口中的默認方法

接口默認方法的”類優先”原則

若一個接口中定義了一個默認方法,而另外一個父類或接口中又定義了一個同名的方法時

 選擇父類中的方法。如果一個父類提供了具體的實現,那麼接口中具有相同名稱和參數的默認方法會被忽略。

 接口衝突。如果一個父接口提供一個默認方法,而另一個接口也提供了一個具有相同名稱和參數列表的方法(不管方法是否是默認方法),那麼必須覆蓋該方法來解決衝突

 

接口默認方法的”類優先”原則

 

接口中的靜態方法

Java8 中,接口中允許添加靜態方法。

 

 

 

2:Lambda 表達式

首先看看在老版本的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));

ava編譯器可以自動推導出參數類型,所以你可以不用再寫一次類型。接下來看看lambda表達式還能作出什麼更方便的東西來

3:函數式接口

Lambda 表達式是如何在java的類型系統中表示的呢?每一個lambda表達式都對應一個類型,通常是接口類型。而“函數式接口”是指僅僅只包含一個抽象方法的 接口,每一個該類型的lambda表達式都會被匹配到這個抽象方法。因爲 默認方法 不算抽象方法,所以你也可以給你的函數式接口添加默認方法。

可以將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之前就有別的語言實現,比如Rhino JavaScript解釋器,如果一個函數參數接收一個單方法的接口而你傳遞的是一個function,Rhino 解釋器會自動做一個單接口的實例到function的適配器,典型的應用場景有 org.w3c.dom.events.EventTarget 的addEventListener 第二個參數 EventListener。

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方法的簽名來選擇合適的構造函數。

 

方法引用

當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!

(實現抽象方法的參數列表,必須與方法引用方法的參數列表保持一致!)

方法引用:使用操作符 “::” 將方法名和對象或類的名字分隔開來。

如下三種主要使用情況:

 對象::實例方法

 類::靜態方法

 類::實例方法

方法引用

注意:當需要引用方法的第一個參數是調用對象,並且第二個參數是需要引

用方法的第二個參數(或無參數)時:ClassName::methodName

 

 

構造器引用

格式: ClassName::new

與函數式接口相結合,自動與函數式接口中方法兼容

可以把構造器引用賦值給定義的方法,與構造器參數

列表要與接口中抽象方法的參數列表一致!

 

數組引用

格式: type[] :: new

 

 

 

 

5:Lambda 作用域

在lambda表達式中訪問外層作用域和老版本的匿名對象中的方式很相似。你可以直接訪問標記了final的外層局部變量,或者實例的字段以及靜態變量。

6:訪問局部變量

可以直接在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同樣是不允許的。

7:訪問對象字段與靜態變量

和本地變量不同的是,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);

        };

    }

}

8:訪問接口的默認方法

還記得第一節中的formula例子麼,接口Formula定義了一個默認方法sqrt可以直接被formula的實例包括匿名對象訪問到,但是在lambda表達式中這個是不行的。 Lambda表達式中是無法訪問到默認方法的,以下代碼將無法編譯:

Formula formula = (a) -> sqrt( a * 100);

Built-in Functional Interfaces

JDK 1.8 API包含了很多內建的函數式接口,在老Java中常用到的比如Comparator或者Runnable接口,這些接口都增加了@FunctionalInterface註解以便能用在lambda上。 Java 8 API同樣還提供了很多全新的函數式接口來讓工作更加方便,有一些接口是來自Google Guava庫裏的,即便你對這些很熟悉了,還是有必要看看這些是如何擴展到lambda上使用的。 Predicate接口

Predicate 接口只有一個參數,返回boolean類型。該接口包含多種默認方法來將Predicate組合成其他複雜的邏輯(比如:與,或,非):

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true

predicate.negate().test("foo");     // false

 

Predicate<Boolean> nonNull = Objects::nonNull;

Predicate<Boolean> isNull = Objects::isNull;

 

Predicate<String> isEmpty = String::isEmpty;

Predicate<String> isNotEmpty = isEmpty.negate();

Function 接口

Function 接口有一個參數並且返回一個結果,並附帶了一些可以和其他函數組合的默認方法(compose, andThen):

Function<String, Integer> toInteger = Integer::valueOf;

Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

Supplier 接口Supplier 接口返回一個任意範型的值,和Function接口不同的是該接口沒有任何參數

Supplier<Person> personSupplier = Person::new;

personSupplier.get();   // new Person

Consumer 接口

Consumer 接口表示執行在單個參數上的操作。

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);

greeter.accept(new Person("Luke", "Skywalker"));

Comparator 接口

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

Optional 接口

Optional 不是函數是接口,這是個用來防止NullPointerException異常的輔助類型,這是下一屆中將要用到的重要概念,現在先簡單的看看這個接口能幹什麼:

Optional 被定義爲一個簡單的容器,其值可能是null或者不是null。在Java 8之前一般某個函數應該返回非空對象但是偶爾卻可能返回了null,而在Java 8中,不推薦你返回null而是返回Optional。

Optional<String> optional = Optional.of("bam");

optional.isPresent();           // true

optional.get();                 // "bam"

optional.orElse("fallback");    // "bam"

 

optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"

Stream 接口

java.util.Stream 表示能應用在一組元素上一次執行的操作序列。Stream 操作分爲中間操作或者最終操作兩種,最終操作返回一特定類型的計算結果,而中間操作返回Stream本身,這樣你就可以將多個操作依次串起來。 Stream 的創建需要指定一個數據源,比如 java.util.Collection的子類,List或者Set, Map不支持。Stream的操作可以串行執行或者並行執行。

首先看看Stream是怎麼用,首先創建實例代碼的用到的數據List:

List<String> stringCollection = new ArrayList<>();

stringCollection.add("ddd2");

stringCollection.add("aaa2");

stringCollection.add("bbb1");

stringCollection.add("aaa1");

stringCollection.add("bbb3");

stringCollection.add("ccc");

stringCollection.add("bbb2");

stringCollection.add("ddd1");

Java 8擴展了集合類,可以通過 Collection.stream() 或者 Collection.parallelStream() 來創建一個Stream。下面幾節將詳細解釋常用的Stream操作:

Filter 過濾

過濾通過一個predicate接口來過濾並只保留符合條件的元素,該操作屬於中間操作,所以可以在過濾後的結果來應用其他Stream操作 (比如forEach)。forEach需要一個函數來對過濾後的元素依次執行。forEach是一個最終操作,所以不能在forEach之後來執行 其他Stream操作。

stringCollection

    .stream()

    .filter((s) -> s.startsWith("a"))

    .forEach(System.out::println);

// "aaa2", "aaa1"

Sort 排序

排序是一箇中間操作,返回的是排序好後的Stream。如果你不指定一個自定義的Comparator則會使用默認排序。

stringCollection

    .stream()

    .sorted()

    .filter((s) -> s.startsWith("a"))

    .forEach(System.out::println);

// "aaa1", "aaa2"

需要注意的是,排序只創建了一個排列好後的Stream,而不會影響原有的數據源,排序之後原數據stringCollection是不會被修改的:

System.out.println(stringCollection);

// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

Map 映射

中間操作map會將元素根據指定的Function接口來依次將元素轉成另外的對象,下面的示例展示了將字符串轉換爲大寫字符串。你也可以通過map來講對象轉換成其他類型,map返回的Stream類型是根據你map傳遞進去的函數的返回值決定的。

stringCollection

    .stream()

    .map(String::toUpperCase)

    .sorted((a, b) -> b.compareTo(a))

    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

Match 匹配

Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。所有的匹配操作都是最終操作,並返回一個boolean類型的值。

boolean anyStartsWithA =

    stringCollection

        .stream()

        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

 

boolean allStartsWithA =

    stringCollection

        .stream()

        .allMatch((s) -> s.startsWith("a"));

 

System.out.println(allStartsWithA);      // false

 

boolean noneStartsWithZ =

    stringCollection

        .stream()

        .noneMatch((s) -> s.startsWith("z"));

 

System.out.println(noneStartsWithZ);      // true

Count 計數

計數是一個最終操作,返回Stream中元素的個數,返回值類型是long。

long startsWithB =

    stringCollection

        .stream()

        .filter((s) -> s.startsWith("b"))

        .count();

System.out.println(startsWithB);    // 3

Reduce 規約

這是一個最終操作,允許通過指定的函數來講stream中的多個元素規約爲一個元素,規越後的結果是通過Optional接口表示的:

Optional<String> reduced =

    stringCollection

        .stream()

        .sorted()

        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);

// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

並行Streams

前面提到過Stream有串行和並行兩種,串行Stream上的操作是在一個線程中依次完成,而並行Stream則是在多個線程上同時執行。

下面的例子展示了是如何通過並行Stream來提升性能:

首先創建一個沒有重複元素的大表:

int max = 1000000;

List<String> values = new ArrayList<>(max);

for (int i = 0; i < max; i++) {

    UUID uuid = UUID.randomUUID();

    values.add(uuid.toString());

}

然後計算一下排序這個Stream要耗時多久,

串行排序:

long t0 = System.nanoTime();

long count = values.stream().sorted().count();

System.out.println(count);

 

long t1 = System.nanoTime();

 

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);

System.out.println(String.format("sequential sort took: %d ms", millis));

// 串行耗時: 899 ms

並行排序:

long t0 = System.nanoTime();

long count = values.parallelStream().sorted().count();

System.out.println(count);

 

long t1 = System.nanoTime();

 

long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);

System.out.println(String.format("parallel sort took: %d ms", millis));

 

// 並行排序耗時: 472 ms

上面兩個代碼幾乎是一樣的,但是並行版的快了50%之多,唯一需要做的改動就是將stream()改爲parallelStream()。

Map

前面提到過,Map類型不支持stream,不過Map提供了一些新的有用的方法來處理一些日常任務。

Map<Integer, String> map = new HashMap<>();

for (int i = 0; i < 10; i++) {

    map.putIfAbsent(i, "val" + i);

}

map.forEach((id, val) -> System.out.println(val));

以上代碼很容易理解, putIfAbsent 不需要做額外的存在性檢查,而forEach則接收一個Consumer接口來對map裏的每一個鍵值對進行操作。

下面的例子展示了map上的其他有用的函數:

map.computeIfPresent(3, (num, val) -> val + num);

map.get(3);             // val33

map.computeIfPresent(9, (num, val) -> null);

map.containsKey(9);     // false

 

map.computeIfAbsent(23, num -> "val" + num);

map.containsKey(23);    // true

 

map.computeIfAbsent(3, num -> "bam");

map.get(3);             // val33

接下來展示如何在Map裏刪除一個鍵值全都匹配的項:

map.remove(3, "val3");

map.get(3);             // val33

map.remove(3, "val33");

map.get(3);             // null

另外一個有用的方法:

map.getOrDefault(42, "not found");  // not found

對Map的元素做合併也變得很容易了:

map.merge(9, "val9", (value, newValue) -> value.concat(newValue));

map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));

map.get(9);             // val9concat

Merge做的事情是如果鍵名不存在則插入,否則則對原鍵對應的值做合併操作並重新插入到map中。

9:Date API

 

日期時間:

JDK1.8以前:

Calendar:實現日期和時間之間的轉換,它的屬性可變,線程不安全

DataFormat:格式化和分析日期字符串

Data:承載日期和時間信息,屬性可變,線程不安全。

 

JDK1.8新增java.time包:包含了處理日期、時間、日期/時間、時區、時刻(Instnts)、時鐘(Clock)等的操作

 

常用:

System.currentTimeMillis()

 

 

 

 

Java 8 在包java.time下包含了一組全新的時間日期API。新的日期API和開源的Joda-Time庫差不多,但又不完全一樣,下面的例子展示了這組新API裏最重要的一些部分: Clock 時鐘

Clock類提供了訪問當前日期和時間的方法,Clock是時區敏感的,可以用來取代 System.currentTimeMillis() 來獲取當前的微秒數。某一個特定的時間點也可以使用Instant類來表示,Instant類也可以用來創建老的java.util.Date對象。

Clock clock = Clock.systemDefaultZone();

long millis = clock.millis();

Instant instant = clock.instant();

Date legacyDate = Date.from(instant);   // legacy java.util.Date

Timezones 時區

在新API中時區使用ZoneId來表示。時區可以很方便的使用靜態方法of來獲取到。 時區定義了到UTS時間的時間差,在Instant時間點對象到本地日期對象之間轉換的時候是極其重要的。

System.out.println(ZoneId.getAvailableZoneIds());

// prints all available timezone ids

ZoneId zone1 = ZoneId.of("Europe/Berlin");

ZoneId zone2 = ZoneId.of("Brazil/East");

System.out.println(zone1.getRules());

System.out.println(zone2.getRules());

// ZoneRules[currentStandardOffset=+01:00]

// ZoneRules[currentStandardOffset=-03:00]

LocalTime 本地時間

LocalTime 定義了一個沒有時區信息的時間,例如 晚上10點,或者 17:30:15。下面的例子使用前面代碼創建的時區創建了兩個本地時間。之後比較時間並以小時和分鐘爲單位計算兩個時間的時間差:

LocalTime now1 = LocalTime.now(zone1);

LocalTime now2 = LocalTime.now(zone2);

System.out.println(now1.isBefore(now2));  // false

 

long hoursBetween = ChronoUnit.HOURS.between(now1, now2);

long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

 

System.out.println(hoursBetween);       // -3

System.out.println(minutesBetween);     // -239

LocalTime 提供了多種工廠方法來簡化對象的創建,包括解析時間字符串。

LocalTime late = LocalTime.of(23, 59, 59);

System.out.println(late);       // 23:59:59

DateTimeFormatter germanFormatter =

    DateTimeFormatter

        .ofLocalizedTime(FormatStyle.SHORT)

        .withLocale(Locale.GERMAN);

 

LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);

System.out.println(leetTime);   // 13:37

LocalDate 本地日期

LocalDate 表示了一個確切的日期,比如 2014-03-11。該對象值是不可變的,用起來和LocalTime基本一致。下面的例子展示瞭如何給Date對象加減天/月/年。另外要注意的是這些對象是不可變的,操作返回的總是一個新實例。

LocalDate today = LocalDate.now();

LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);

LocalDate yesterday = tomorrow.minusDays(2);

LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);

DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();

 

System.out.println(dayOfWeek);    // FRIDAY

從字符串解析一個LocalDate類型和解析LocalTime一樣簡單:

DateTimeFormatter germanFormatter =

    DateTimeFormatter

        .ofLocalizedDate(FormatStyle.MEDIUM)

        .withLocale(Locale.GERMAN);

LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);

System.out.println(xmas);   // 2014-12-24

LocalDateTime 本地日期時間

LocalDateTime 同時表示了時間和日期,相當於前兩節內容合併到一個對象上了。LocalDateTime和LocalTime還有LocalDate一樣,都是不可變的。LocalDateTime提供了一些能訪問具體字段的方法。

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

DayOfWeek dayOfWeek = sylvester.getDayOfWeek();

System.out.println(dayOfWeek);      // WEDNESDAY

 

Month month = sylvester.getMonth();

System.out.println(month);          // DECEMBER

 

long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);

System.out.println(minuteOfDay);    // 1439

只要附加上時區信息,就可以將其轉換爲一個時間點Instant對象,Instant時間點對象可以很容易的轉換爲老式的java.util.Date。

Instant instant = sylvester

        .atZone(ZoneId.systemDefault())

        .toInstant();

Date legacyDate = Date.from(instant);

System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014

格式化LocalDateTime和格式化時間和日期一樣的,除了使用預定義好的格式外,也可以自己定義格式:

DateTimeFormatter formatter =

    DateTimeFormatter

        .ofPattern("MMM dd, yyyy - HH:mm");

LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);

String string = formatter.format(parsed);

System.out.println(string);     // Nov 03, 2014 - 07:13

和java.text.NumberFormat不一樣的是新版的DateTimeFormatter是不可變的,所以它是線程安全的。關於時間日期格式的詳細信息:http://download.java.net/jdk8/docs/api/java/time/format/DateTimeFormatter.html

 

 

新時間日期API

使用LocalDate、LocalTime、LocalDateTime

 LocalDate、LocalTime、LocalDateTime 類的實例是不可變的對象,分別表示使用ISO-8601日曆系統的日期、時間、日期和時間。它們提供了簡單的日期或時間,並不包含當前的時間信息。也不包含與時區相關的信息。

 

背景:

因爲原來的日期API不是很好用。

最開始的Date JDK1.0、如構造器中,傳入的時間要通過計算後才準確,

Calander JDK1.1 每週第一天默認星期天

TimeZone:使用麻煩

。。以上的都線程不安全

SimpleDateFormat類,居然是在java.text下

新的API在java.time下

 

傳統API的多線程不安全問題:

//     SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
//
//    Callable<Date> task1 = new Callable<Date>() {
//       @Override
//       public Date call() throws Exception {
//          return sdf.parse("20180115");
//       }
//    };
//    //
使用多線程去訪問。結果因爲SimpleDateFormat線程不安全,報異常
//    ExecutorService pool = Executors.newFixedThreadPool(10);
//
//    List<Future<Date>> results = new ArrayList<>();
//
//    for (int i = 0; i < 10; i++) {
//       results.add(pool.submit(task1));
//    }
//
//    for (Future<Date> future : results) {
//       System.out.println(future.get());
//    }
//    pool.shutdown();

將發生異常。

傳統解決方案:

public class DateFormatThreadLocal {
   
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
       
protected DateFormat initialValue(){
           
return new SimpleDateFormat("yyyyMMdd");
       
}
    }
;
    public static final
Date convert(String source) throws ParseException {
       
return df.get().parse(source);
   
}
}

測試:成功通過

Callable<Date> task = new Callable<Date>() {
  
@Override
  
public Date call() throws Exception {
     
return DateFormatThreadLocal.convert("20180115");
  
}
}
;

ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
for
(int i = 0; i < 10; i++) {
   results.add(pool.submit(task))
;
}
for (Future<Date> future : results) {
   System.
out.println(future.get());
}
pool.shutdown()
;

JDK8的方式:

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
//DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

Callable<LocalDate> task2 = new Callable<LocalDate>() {
   
@Override
   
public LocalDate call() throws Exception {
        LocalDate ld = LocalDate.parse(
"20180115", dtf);
        return
ld;
   
}
}
;

ExecutorService pool2 = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> results2 = new ArrayList<>();
for
(int i = 0; i < 10; i++) {
    results2.add(pool2.submit(task2))
;
}
for (Future<LocalDate> future : results2) {
    System.
out.println(future.get());
}
pool2.shutdown()
;

 

 

 

Instant 時間戳:用於“時間戳”的運算。它是以Unix元年(傳統的設定爲UTC時區1970年1月1日午夜時分)開始所經歷的描述進行運算

 

Duration 和Period

Duration:用於計算兩個“時間”間隔

Period:用於計算兩個“日期”間隔

 

日期的操縱

 TemporalAdjuster : 時間校正器。有時可能需要獲取例如:將日期調整到“下個週日”等操作。

 TemporalAdjusters : 該類通過靜態方法提供了大量的常用TemporalAdjuster 的實現。

例如獲取下個週日:

 

 

解析與格式化

java.time.format.DateTimeFormatter 類:該類提供了三種

格式化方法:

 預定義的標準格式

 語言環境相關的格式

 自定義的格式

 

時區的處理

Java8 中加入了對時區的支持,帶時區的時間爲分別爲:

ZonedDate、ZonedTime、ZonedDateTime

其中每個時區都對應着ID,地區ID都爲“{區域}/{城市}”的格式

例如:Asia/Shanghai 等

ZoneId:該類中包含了所有的時區信息

getAvailableZoneIds() : 可以獲取所有時區時區信息

of(id) : 用指定的時區信息獲取ZoneId 對象

 

 

與傳統日期處理的轉換

 

    //1. LocalDateLocalTimeLocalDateTime:三者使用方式一樣,以一個爲例
   
@Test
   
public void test1(){
       
// 獲取當前系統時間
       
LocalDateTime ldt = LocalDateTime.now();
       
PrintUtil.print(ldt);

       
LocalDateTime ld2 = LocalDateTime.of(2018, 01, 15, 01, 15, 10);
       
PrintUtil.print(ld2);
       
// 增加20
       
LocalDateTime ldt3 = ld2.plusYears(20);
       
PrintUtil.print(ldt3);
       
// 2個月
       
LocalDateTime ldt4 = ld2.minusMonths(2);
       
PrintUtil.print(ldt4);

       
PrintUtil.print(ldt.getYear());
       
PrintUtil.print(ldt.getMonthValue());
       
PrintUtil.print(ldt.getDayOfMonth());
       
PrintUtil.print(ldt.getHour());
       
PrintUtil.print(ldt.getMinute());
       
PrintUtil.print(ldt.getSecond());
   
}
   
// JDK1.8
    //2. Instant :
時間戳。 (使用 Unix 元年  197011 00:00:00 所經歷的毫秒值)
   
@Test
   
public void test2(){
        Instant ins = Instant.now()
//默認使用 UTC 時區(不是系統時間)
       
PrintUtil.print(ins);
       
PrintUtil.print(ins.toEpochMilli());
       
// 偏移8小時時差,就是中國的時間
       
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
       
PrintUtil.print(odt);
       
PrintUtil.print(ins.getNano());
       
// 加上5s
       
Instant ins2 = Instant.ofEpochSecond(5);
       
PrintUtil.print(ins2);
   
}
   
//3.
    //Duration :
用於計算兩個時間間隔
   
//Period : 用於計算兩個日期間隔
   
@Test
   
public void test3(){
        Instant ins1 = Instant.now()
;
        try
{
            Thread.sleep(
1000);
       
} catch (InterruptedException e) {
        }
        Instant ins2 = Instant.now()
;
       
PrintUtil.print("所耗費時間爲:" + Duration.between(ins1, ins2));
       
PrintUtil.print("所耗費時間爲:" + Duration.between(ins1, ins2).toMillis());
       
PrintUtil.print("----------------------------------");
       
LocalTime localTime1 = LocalTime.now();
        try
{
            Thread.sleep(
1000);
       
} catch (InterruptedException e) {
        }
        LocalTime localTime2 = LocalTime.now()
;
       
PrintUtil.print("所耗費時間爲:" + Duration.between(localTime1,localTime2).toMillis());
       
PrintUtil.print("----------------------------------");
       
// 日期間隔
       
LocalDate ld1 = LocalDate.now();
       
LocalDate ld2 = LocalDate.of(2011, 1, 1);
       
Period pe = Period.between(ld2, ld1);
       
PrintUtil.print(pe.getYears());
       
PrintUtil.print(pe.getMonths());
       
PrintUtil.print(pe.getDays());
   
}
   
//4. TemporalAdjuster : 時間校正器
   
@Test
   
public void test4(){
        LocalDateTime ldt = LocalDateTime.now()
;
       
PrintUtil.print(ldt);
       
// 指定Day10
       
LocalDateTime ldt2 = ldt.withDayOfMonth(10);
       
PrintUtil.print(ldt2);
       
// 下週日
       
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
       
PrintUtil.print(ldt3);
       
//自定義:下一個工作日
       
LocalDateTime ldt5 = ldt.with((ll) -> {
            LocalDateTime ldt4 = (LocalDateTime) ll
;
           
DayOfWeek dow = ldt4.getDayOfWeek();
            if
(dow.equals(DayOfWeek.FRIDAY)){
               
return ldt4.plusDays(3);
           
}else if(dow.equals(DayOfWeek.SATURDAY)){
               
return ldt4.plusDays(2);
           
}else{
               
return ldt4.plusDays(1);
           
}
        })
;
       
PrintUtil.print(ldt5);
   
}
   
//5. DateTimeFormatter : 解析和格式化日期或時間
   
@Test
   
public void test5(){
//    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
       
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss E");
       
LocalDateTime ldt = LocalDateTime.now();
       
String strDate = ldt.format(dtf);
       
System.out.println(strDate);
       
LocalDateTime newLdt = ldt.parse(strDate, dtf);
       
System.out.println(newLdt);
   
}
   
@Test
   
public void test6(){
       
// 查看支持的時區
       
Set<String> set = ZoneId.getAvailableZoneIds();
       
set.forEach(System.out::println);
   
}
   
//6.ZonedDateZonedTimeZonedDateTime : 帶時區的時間或日期
   
@Test
   
public void test7(){
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of(
"Asia/Shanghai"));
       
System.out.println(ldt);
       
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
       
System.out.println(zdt);
   
}

 

 

 

 

10:Annotation 註解

在Java 8中支持多重註解了,先看個例子來理解一下是什麼意思。 首先定義一個包裝類Hints註解用來放置一組具體的Hint註解:

@interface Hints {

    Hint[] value();

}

@Repeatable(Hints.class)

@interface Hint {

    String value();

}

Java 8允許把同一個類型的註解使用多次,只需要給該註解標註一下@Repeatable即可。

例 1: 使用包裝類當容器來存多個註解(老方法)

@Hints({@Hint("hint1"), @Hint("hint2")})

class Person {}

例 2:使用多重註解(新方法)

@Hint("hint1")

@Hint("hint2")

class Person {}

第二個例子裏java編譯器會隱性的幫你定義好@Hints註解,瞭解這一點有助於你用反射來獲取這些信息:

Hint hint = Person.class.getAnnotation(Hint.class);

System.out.println(hint);                   // null

Hints hints1 = Person.class.getAnnotation(Hints.class);

System.out.println(hints1.value().length);  // 2

Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);

System.out.println(hints2.length);          // 2

即便沒有在Person類上定義@Hints註解,還是可以通過 getAnnotation(Hints.class) 來獲取 @Hints註解,更加方便的方法是使用 getAnnotationsByType 可以直接獲取到所有的@Hint註解。 另外Java 8的註解還增加到兩種新的target上了:

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})

@interface MyAnnotation {}

 

 

增加JS引擎:使得java可以調用js代碼:javax.script

ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine javaScriptEngine = scriptEngineManager.getEngineByName("JavaScript");
PrintUtil.print(javaScriptEngine.getClass().getName());
PrintUtil.print(javaScriptEngine.eval("function f(){return 'Hello';};f()+'World'"));

 

加入Base64

java.util.Base64

String str = "HelloWorld";
String encodStr = Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
PrintUtil.print(encodStr);
String decodStr = new String(Base64.getDecoder().decode(encodStr),StandardCharsets.UTF_8);
PrintUtil.print(decodStr);

SGVsbG9Xb3JsZA==

HelloWorld

 

 

增強泛型推斷:

Class List<E>{

 Static <Z> List <Z> nul(){   …}

}

JDK1.7:

List<Integer> I = List.<Integer>nil();

JDK1.8

List<Integer> I = List.nil();

 

內部數據結構的優化:

HashMap:

如果不用hashcode的話,要和每一個進行equals,效率極低。

使用哈希表(數組,默認16個),先調用哈希算法。算出數組的索引值,索引值沒有Object的話,直接插入,如果有的話,碰撞。先equals,如果內容一樣,後者覆蓋前者,如果不一樣,形成鏈表。

JDK1.8優化:鏈表過長後,將導致效率降低。如果hash後都在一個地方,就退化成鏈表。JDK1.8優化後,變成當鏈表長度到達一定值(鏈表總大小大於64.長度大於8)的時候,鏈表升級爲紅黑樹(Hash算法再好也避免不了碰撞)【紅黑樹,除了添加外,其他都比鏈表快】

擴容:當到達75%的時候,就進行擴容。然後每個值都需要重新運算。然後重新排

 

ConcurrentHahMap:

JDK1.7採用鎖中斷機制,隔離級別太大,

JDK1.8改變,採用CAS算法,無鎖算法。

 

棧、堆、方法區的改變:略

 

重複註解與類型註解

Java 8對註解處理提供了兩點改進:可重複的註解及可用於類型的註解。

 

 

//@MyAnnotations({@MyAnnotation(value="hello"),@MyAnnotation(value="atguigu")})

 

 

 

 

 

11.其他

肯定還有更多的特性等待發掘。JDK 1.8裏還有很多很有用的東西,比如Arrays.parallelSort, StampedLock和CompletableFuture等等。

 

Java9

JDK9新特性:參考  Open JDK :http://openjdk.java.net/projects/jdk9/

102: Process API Updates
110: HTTP 2 Client
143: Improve Contended Locking
158: Unified JVM Logging
165: Compiler Control
193: Variable Handles
197: Segmented Code Cache
199: Smart Java Compilation, Phase Two
200: The Modular JDK
201: Modular Source Code
211: Elide Deprecation Warnings on Import Statements
212: Resolve Lint and Doclint Warnings
213: Milling Project Coin
214: Remove GC Combinations Deprecated in JDK 8
215: Tiered Attribution for javac
216: Process Import Statements Correctly
217: Annotations Pipeline 2.0
219: Datagram Transport Layer Security (DTLS)
220: Modular Run-Time Images
221: Simplified Doclet API
222: jshell: The Java Shell (Read-Eval-Print Loop)
223: New Version-String Scheme
224: HTML5 Javadoc
225: Javadoc Search
226: UTF-8 Property Files
227: Unicode 7.0
228: Add More Diagnostic Commands
229: Create PKCS12 Keystores by Default
231: Remove Launch-Time JRE Version Selection
232: Improve Secure Application Performance
233: Generate Run-Time Compiler Tests Automatically
235: Test Class-File Attributes Generated by javac
236: Parser API for Nashorn
237: Linux/AArch64 Port
238: Multi-Release JAR Files
240: Remove the JVM TI hprof Agent
241: Remove the jhat Tool
243: Java-Level JVM Compiler Interface
244: TLS Application-Layer Protocol Negotiation Extension
245: Validate JVM Command-Line Flag Arguments
246: Leverage CPU Instructions for GHASH and RSA
247: Compile for Older Platform Versions
248: Make G1 the Default Garbage Collector
249: OCSP Stapling for TLS
250: Store Interned Strings in CDS Archives
251: Multi-Resolution Images
252: Use CLDR Locale Data by Default
253: Prepare JavaFX UI Controls & CSS APIs for Modularization
254: Compact Strings
255: Merge Selected Xerces 2.11.0 Updates into JAXP
256: BeanInfo Annotations
257: Update JavaFX/Media to Newer Version of GStreamer
258: HarfBuzz Font-Layout Engine
259: Stack-Walking API
260: Encapsulate Most Internal APIs
261: Module System
262: TIFF Image I/O
263: HiDPI Graphics on Windows and Linux
264: Platform Logging API and Service
265: Marlin Graphics Renderer
266: More Concurrency Updates
267: Unicode 8.0
268: XML Catalogs
269: Convenience Factory Methods for Collections
270: Reserved Stack Areas for Critical Sections
271: Unified GC Logging
272: Platform-Specific Desktop Features
273: DRBG-Based SecureRandom Implementations
274: Enhanced Method Handles
275: Modular Java Application Packaging
276: Dynamic Linking of Language-Defined Object Models
277: Enhanced Deprecation
278: Additional Tests for Humongous Objects in G1
279: Improve Test-Failure Troubleshooting
280: Indify String Concatenation
281: HotSpot C++ Unit-Test Framework
282: jlink: The Java Linker
283: Enable GTK 3 on Linux
284: New HotSpot Build System
285: Spin-Wait Hints
287: SHA-3 Hash Algorithms
288: Disable SHA-1 Certificates
289: Deprecate the Applet API
290: Filter Incoming Serialization Data
291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector
292: Implement Selected ECMAScript 6 Features in Nashorn
294: Linux/s390x Port
295: Ahead-of-Time Compilation
297: Unified arm32/arm64 Port
298: Remove Demos and Samples
299: Reorganize Documentation

 

特性1:目錄結構的變化

JDK8目錄結構:

 

bin  目錄

包含命令行開發和調試工具,如 javac, jar 和 javadoc

include 目錄

包含在編譯本地代碼時使用的 C/C++頭文件

lib  目錄

 

包含 JDK 工具的幾個 JAR 和其他類型的文件。  它有

一個 tools.jar 文件,其中包含 javac 編譯器的 Java 類

 

jre/bin  目錄

 

包含基本命令,如 java 命令。  在 Windows 平臺上,

它包含系統的運行時動態鏈接庫(DLL)。

jre/lib  目錄

 

包含用戶可編輯的配置文件,如.properties 和.policy

文件。包含幾個 JAR。  rt.jar 文件包含運行時的 Java

類和資源文件。

 

JDK9目錄結構:

沒有名爲 jre 的子目錄

bin  目錄

包含所有命令。  在 Windows 平臺上,它繼續包含系

統的運行時動態鏈接庫。

 

conf  目錄

包含用戶可編輯的配置文件,例如以前位於 jre\lib 目

錄中的.properties 和.policy 文件

include  目錄

 

包含要在以前編譯本地代碼時使用的 C/C++頭文件。

它只存在於 JDK 中

 

jmods  目錄

 

包含 JMOD 格式的平臺模塊。  創建自定義運行時映像

時需要它。  它只存在於 JDK 中

 

legal  目錄

包含法律聲明

lib  目錄

包含非 Windows 平臺上的動態鏈接本地庫。  其子目

錄和文件不應由開發人員直接編輯或使用

 

 

附:JDK和JRE的關係

JDK : Java Development Kit (Java 開發工具包)

JRE : Java Runtime Environment (Java 運行環境)

JDK = JRE +  開發工具集(例如 Javac 編譯工具等)

JRE = JVM + Java SE 標準類庫

 

 

 

特性2:模塊化Modularity

背景:Java越來越豐富的同時,帶來了臃腫(包含爲了兼容低版本,不同版本類庫交叉。。java開發和運行效率降低)

Java運行環境的膨脹和臃腫。JVM每啓動時,至少有30~60MB的內存加載,因爲JVM需要加載rt.jar,不管其中的類是否被classloader加載,整個jar都會被JVM加載到內存當中去 (而模塊化可以根據模塊的需要加載程序運行需要的class)

 

 

很難真正地對代碼進行封裝,  而系統並沒有對不同部分(即JAR文件)之間的依賴關係有個明確的概念。每一個公共類都可以被類路徑之下任何其它的公共類所訪問到, 會導致無意中使用了並不想被公開訪問的API。

類路徑本身也存在問題:  需要的JAR是否都已經有了,  是否存在重複的項?

 

JSR 376 Java  平臺模塊化系統(JPMS, Java Platform Module System)作爲  Jigsaw  項目的核心,  其主體部分被分解成 6 個 JEP(JDK Enhancement Proposals)。

 

 

本質上,模塊(module)的概念,就是package外再裹一層,用模塊來管理各個package,通過聲明某個package暴露,不聲明默認就是隱藏。因此,模塊化使得代碼組織上更安全,因爲它可以指定哪些部分可以暴露,哪些部分隱藏。

 

 

實現目標

主要目的在於減少內存的開銷

只須必要模塊,而非全部jdk模塊,可簡化各種類庫和大型應用的開發和維護

改進 Java SE 平臺,使其可以適應不同大小的計算設備

改進其安全性,可維護性,提高性能

 

模塊將由通常的類和新的模塊聲明文件(module-info.java)組成。

該文件是位於 java 代碼結構的頂層, 該模塊描述符明確地定義了的模塊需要什麼依賴關係,以及哪些模塊被外部使用。在 exports 子句中未提及的所有包默認情況下將封裝在模塊中,不能在外部使用

 

右鍵添加module-info.java

導出

module Java9Entity {
   
exports com.jinwen.www.entity;
}

導入

module Java9Test {

    requires Java9Entity;

    requires java.logging;

}

 

使用

public class ModuleTest {

    private static final Logger LOGGER = Logger.getLogger("jinwen");

    public static void main(String[] args) {

        User user = new User("jinern",2);

        LOGGER.info("Hello World");

    }

}

requires:指明對其它模塊的依賴。

exports:控制着哪些包可以被其它模塊訪問到。所有不被導出的包默認都被封裝在模塊裏面。

 

 

特性3:多版本兼容jar包

多版本兼容jar功能能讓你創建僅在特定版本的Java環境中運行庫程序選擇使用的class版本。

舉例1:

jar root

  - A.class

  - B.class

  - C.class

  - D.class

  - META-INF

     - versions

        - 9

           - A.class

           - B.class

說明:

在上述場景中,root.jar 可以在 Java 9 中使用,不過A或B類使用的不是頂層的root.A或root.B 這兩個class,而是處在“META-INF/versions/9”下面的這兩個。這是特別爲 Java9 準備的 class 版本,可以運用 Java 9 所提供的特性和庫。同時,在早期的 Java 諸版本中使用這個JAR 也是能運行的,因爲較老版本的 Java只會看到頂層的A類或 B 類。

 

舉例2:

jar root

  - A.class

  - B.class

  - C.class

  - D.class

  - META-INF

     - versions

        - 9

           -  A.class

           - B.class

        - 10

           -

  A.class

官方說明:

By this scheme, it is possiblefor versions of a class designed for a later Java platform release to overridethe version of that same class designed for an earlier Java platform release.

 

 

public class Generator {

    public Set<String> createStrings() {

        Set<String> strings = new HashSet<String>();

        strings.add("Java");

        strings.add("8");

        return strings;

    }

}

 

public class Application {

   public static void testMultiJar(){

      Generator gen = new Generator();

      System.out.println("Generated strings: " + gen.createStrings());

   }

}

Java9文件夾下

public class Generator {

    public Set<String> createStrings() {

        return Set.of("Java", "9");

    }

}

打包

指令如下:

javac -d build --release 8  src/main/java/com/atguigu/*.java

javac -d build9 --release 9  src/main/java-9/com/atguigu/*.java

jar --create --main-class=Application --file  multijar.jar -C build . --release 9 -C build9 .

 

特性3:接口再次增強

Java 8中規定接口中的方法除了抽象方法之外,還可以定義靜態方法和默認的方法。一定程度上,擴展了接口的功能,此時的接口更像是一個抽象類。

 

在Java 9中,接口更加的靈活和強大,連方法的訪問權限修飾符都可以聲明爲private的了,此時方法將不會成爲你對外暴露的API的一部分。

 

/**

 * 接口的增強

 * 抽象類和接口的異同:

 * ①二者的定義: a.聲明的方式   b.內部的結構(jdk 7 ;jdk 8 ; jdk 9)

 * ②共同點:不能實例化;以多態的方式使用

 * ③不同點:單繼承 ; 多實現.

 */

interface MyInterface{

    /**

     * jdk 7 : 只能聲明全局常量(public static final)和抽象方法(public abstract)

     * */

    void method1();

    /**

     * jdk 8 : 聲明靜態方法 和 默認方法

     * */

    public static void method2(){

        System.out.println("method2");

    }

    default void method3(){

        System.out.println("method3");

        method4();

    }

    /**

     * jdk 9 : 支持聲明私有方法

     * */

    private void method4(){

        System.out.println("私有方法");

    }

}

class MyInterfaceImpl implements MyInterface{

    @Override

    public void method1() {

        System.out.println("實現接口中的抽象方法method1()");

    }

}

public class InterfaceDemo {

    public static void main(String[] args) {

        MyInterface impl = new MyInterfaceImpl();

        impl.method3();

        impl.method1();

        //不可以調用

        //impl.method2();

        //impl.method4();

    }

}

 

 

特性:語法改進:鑽石操作符(Diamond Operator)使用升級

將能夠與匿名實現類共同使用鑽石操作符(diamond operator)

 

在java8中如下的操作是會報錯的:

編譯報錯信息:'<>' cannot be used with anonymous classes

 

public void diamondOperator(){

    //創建一個繼承於HashSet的匿名子類的對象

    Set<String> set = new HashSet<>(){};//編譯通過,JDK9以前只能new HashSet<>();

    set.add("MM");

    set.add("JJ");

    set.add("GG");

    set.add("DD");

    for(String s : set){

        System.out.println(s);

    }

}

 

 

特性:try語句增強

在java8 之前,習慣於這樣處理資源的關閉:

InputStreamReader reader = null;

try {

    reader = new InputStreamReader(System.in);

    reader.read();

} catch (IOException e) {

    e.printStackTrace();

}finally {

    //資源關閉操作

    if (reader!=null){

        try {

            reader.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

java 8 中,可以實現資源的自動關閉,但是要求執行後必須關閉的所有資源必須在try子句中初始化,否則編譯不通過

//舉例2:在舉例1基礎上,升級的寫法。不用顯式的處理資源的關閉.會自動調用close

//java 8 中:要求資源對象的實例化,必須放在try的一對()內完成。

//java 9 中:可以在try()中調用已經實例化的資源對象
try(InputStreamReader reader = new InputStreamReader(System.in)){

    //讀取數據的過程:略

    reader.read();

}catch(IOException e){

    e.printStackTrace();

}

 

Java 9 中,用資源語句編寫try將更容易,可以在try子句中使用已經初始化過的資源,此時的資源是final的:

//如下的操作不可以在jdk 8 及之前的版本中使用
        InputStreamReader reader = new InputStreamReader(System.in);

        OutputStreamWriter writer = new OutputStreamWriter(System.out);

        try(reader;writer){

            //此時的readerfinal的,不可再被賦值

//            reader = null;

            //讀取數據的過程:略

            reader.read();

        }catch(IOException e){

            e.printStackTrace();

        }

 

 

 

特性:語法改進:UnderScore(下劃線)使用的限制

在java 8 中,標識符可以獨立使用“_”來命名:

但是,在java 9 中規定“_”不再可以單獨命名標識符了,如果使用,會報錯:

 

 

特性:String存儲結構變更【用戶體驗不到string的改變】

產生背景

Motivation

The current implementation of the String classstores characters in a char array,using two bytes (sixteen bits) for each character. Data gathered from manydifferent applications indicates that strings are a major component of heapusage and, moreover, that most String objectscontain only Latin-1 characters. Such characters require only one byte ofstorage, hence half of the space in the internal char arraysof such String objects is going unused.

以前底層是char型數組。佔用2個字節。大部分是單字符。省空間,用byte數組。使用flag標識字符集,實現中文類用多個字節存儲等的作用。UTF-16不管什麼,都2個字節。UTF-8.可能1個,可能3個等等。都有可能

 

 

使用說明

Description

We propose to change the internal representation of the Stringclass from a UTF-16 char array to a byte array plus an encoding-flag field. The new String class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character), or as UTF-16 (two bytes per character), based upon the contents of the string. The encoding flag will indicate which encoding is used.

 

�結論:String 再也不用 char[] 來存儲啦,改成了 byte[] 加上編碼標記,節約了一些空間。

public final class String

    implements java.io.Serializable, Comparable<String>, CharSequence {

    /**

     * The value is used for character storage.

     *

     * @implNote This field is trusted by the VM, and is a subject to

     * constant folding if String instance is constant. Overwriting this

     * field after construction will cause problems.

     *

     * Additionally, it is marked with {@link Stable} to trust the contents

     * of the array. No other facility in JDK provides this functionality (yet).

     * {@link Stable} is safe here, because value is never null.

     */

    @Stable

    private final byte[] value;

拓展:StringBuffer 與StringBuilder

那StringBuffer 和 StringBuilder 是否仍無動於衷呢?

�String-related classes such as AbstractStringBuilder, StringBuilder, and StringBuffer will

be updated to use the same representation, as will the HotSpot VM's intrinsic string operations.

/**

 * String內部優化

 * Stringjdk 8 及之前:底層使用char[]存儲;jdk 9 : 底層使用byte[] (encoding flag)

 * StringBuffer:jdk 8 及之前:底層使用char[]存儲;jdk 9 : 底層使用byte[]

 * StringBuilder:jdk 8 及之前:底層使用char[]存儲;jdk 9 : 底層使用byte[]

 *

 * String:不可變的字符序列;

 * StringBuffer:可變的字符序列;線程安全的,效率低;

 * StringBuilder:可變的字符序列;線程不安全的,效率高(jdk 5.0)

 */

 

 

 

特性:集合工廠方法:快速創建只讀集合

產生背景

要創建一個只讀、不可改變的集合,必須構造和分配它,然後添加元素,最後包裝成一個不可修改的集合。

        List<String> list = new ArrayList<>();

        list.add("Tom");

        list.add("Jerry");

        list.add("Lilei");

        list.add("HanMeimei");

        //調用Collections中的方法,將list變爲只讀的

        List<String> newList = Collections.unmodifiableList(list);

//        newList.add("Tim");//不能執行,否則報異常

        //遍歷:jdk 8

        newList.forEach(System.out::println);

缺點:一下寫了五行。即:它不能表達爲單個表達式。

當然,也可以稍微簡單點處理:

使用說明

//jdk 8 以及之前:創建一個只讀特點的集合
        //List:

        List<Integer> list = Collections.unmodifiableList(Arrays.asList(1, 2, 3));

//        list.add(4);

        //Set:

        Set<Integer> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(1, 2, 3)));

//        set.add(4);

//        set.forEach(System.out::println);

        //Map:

        Map<Object, Object> map = Collections.unmodifiableMap(new HashMap<>() {

            {

                put("Tom", 78);

                put("Jerry", 88);

                put("Tim", 68);

            }

        });

        map.forEach((k,v) -> System.out.println(k + ":" + v));

 

Java9因此引入了方便的方法,這使得類似的事情更容易表達。

List firsnamesList= List.of(“Joe”,”Bob”,”Bill”);

 

調用集合中靜態方法of(),可以將不同數量的參數傳輸到此工廠方法中。此功能可用於Set和List,也可用於Map的類似形式。此時得到的集合,是不可變的:在創建後,繼續添加元素到這些集合會導致 “UnsupportedOperationException” 。

 

由於Java 8中接口方法的實現,可以直接在List,Set和Map的接口內定義這些方法,便於調用。

    //jdk 9 中:創建一個只讀特點的集合

        //List:

        List<Integer> list = List.of(1, 2, 3);

//        list.add(4);

        list.forEach(System.out::println);

        //Set:

        Set<Integer> set = Set.of(2, 3, 4);

//        set.add(6);

        //Map:

        //創建只讀集合的方式一:

        Map<String, Integer> map = Map.of("Tom", 23, "Jerry", 22, "Lilei", 12, "HanMeimei", 18);

//        map.put("Tim",33);

        //創建只讀集合的方式二

        Map<String, Integer> map1 = Map.ofEntries(Map.entry("Tom", 23), Map.entry("Jerry", 21));

//        map1.put("Tim",33);

        System.out.println(map1);

繼續添加元素到這些集合會導致 “UnsupportedOperationException”

 

特性:全新的HTTP 客戶端API

使用說明

 

HTTP,用於傳輸網頁的協議,早在1997年就被採用在目前的1.1版本中。直到2015年,HTTP2才成爲標準。

HTTP/1.1和HTTP/2的主要區別是如何在客戶端和服務器之間構建和傳輸數據。HTTP/1.1依賴於請求/響應週期。 HTTP/2允許服務器“push”數據:它可以發送比客戶端請求更多的數據。 這使得它可以優先處理併發送對於首先加載網頁至關重要的數據。

 

Java 9中有新的方式來處理HTTP調用。它提供了一個新的HTTP客戶端(HttpClient),它將替代僅適用於blocking模式的HttpURLConnection (HttpURLConnection是在HTTP1.0的時代創建的,並使用了協議無關的方法),並提供對WebSocket和 HTTP/2的支持。

 

此外,HTTP客戶端還提供API來處理HTTP/2的特性,比如流和服務器推送等功能。

 

全新的HTTP客戶端API可以從jdk.incubator.httpclient模塊中獲取。因爲在默認情況下,這個模塊是不能根據classpath獲取的,需要使用add

 

modules命令選項配置這個模塊,將這個模塊添加到classpath中。

module Java9Demo {

    requires Java9Module;

    requires java.logging;

    requires junit;

    requires jdk.incubator.httpclient;

}

 

//jdk 9 中 使用 HttpClient替換原有的HttpURLConnection

try {

    HttpClient client = HttpClient.newHttpClient();

    HttpRequest req = HttpRequest.newBuilder(URI.create("http://www.atguigu.com")).GET().build();

    HttpResponse<String> response = null;

    response = client.send(req, HttpResponse.BodyHandler.asString());

    System.out.println(response.statusCode());

    System.out.println(response.version().name());

    System.out.println(response.body());

} catch (IOException e) {

    e.printStackTrace();

} catch (InterruptedException e) {

    e.printStackTrace();

}

 

 

特性:多分辨率圖像 API

產生背景

在Mac上,JDK已經支持視網膜顯示,但在Linux和Windows上,它並沒有。在那裏,Java程序在當前的高分辨率屏幕上可能看起來很小,不能使用它們。這是因爲像素用於這些系統的大小計算(無論像素實際有多大)。畢竟,高分辨率顯示器的有效部分是像素非常小。

JEP 263以這樣的方式擴展了JDK,即Windows和Linux也考慮到像素的大小。爲此,使用比現在更多的現代API:Direct2D for

Windows和GTK +,而不是Xlib for Linux。圖形,窗口和文本由此自動縮放。

JEP 251還提供處理多分辨率圖像的能力,即包含不同分辨率的相同圖像的文件。根據相應屏幕的DPI度量,然後以適當的分辨率使用圖像。

3.使用說明

新的API定義在java.awt.image包下

將不同分辨率的圖像封裝到一張(多分辨率的)圖像中,作爲它的變體

獲取這個圖像的所有變體

獲取特定分辨率的圖像變體-表示一張已知分辨率單位爲DPI的特定尺寸大小的邏輯圖像,並且這張圖像是最佳的變體。

基於當前屏幕分辨率大小和運用的圖像轉換算法,java.awt.Graphics類可以從接口MultiResolutionImage獲取所需的變體。

MultiResolutionImage的基礎實現是java.awt.image.BaseMultiResolutionImage。

 

 

 

Deprecated的相關API

Java 9 廢棄或者移除了幾個不常用的功能。其中最主要的是 Applet API,現在是標記爲廢棄的。隨着對安全要求的提高,主流瀏覽器已經取消對 Java 瀏覽器插件的支持。HTML5 的出現也進一步加速了它的消亡。開發者現在可以使用像 Java Web Start 這樣的技術來代替 Applet,它可以實現從瀏覽器啓動應用程序或者安裝應用程序。

 

同時,appletviewer 工具也被標記爲廢棄。

 

 

 

特性:智能Java編譯工具

智能java編譯工具( sjavac)的第一個階段始於JEP139這個項目,用於在多核處理器情況下提升JDK的編譯速度。如今,這個項目已經進入第二階段,即JEP199,其目的是改進Java編譯工具,並取代目前JDK編譯工具javac,繼而成爲Java環境默認的通用的智能編譯工具。

 

JDK9 還更新了javac編譯器以便能夠將 java 9 代碼編譯運行在低版本Java 中。

 

 

特性:統一的JVM日誌系統

日誌是解決問題的唯一有效途徑:曾經很難知道導致JVM性能問題和導致JVM崩潰的根本原因。不同的JVM日誌的碎片化和日誌選項(例如:JVM組件對於日誌使用的是不同的機制和規則),這使得JVM難以進行調試。

解決該問題最佳方法:對所有的JVM組件引入一個單一的系統,這些JVM組件支持細粒度的和易配置的JVM日誌。

 

 

javadoc的HTML 5 支持

jdk 8 :生成的java幫助文檔是在HTML 4 中,而HTML 4 已經是很久的標準了。

jdk 9 :javadoc的輸出,現在符合兼容HTML 5 標準。

 

Javascript引擎升級:Nashorn

Nashorn 項目在 JDK 9 中得到改進,它爲 Java 提供輕量級的 Javascript 運行時。Nashorn 項目跟隨 Netscape 的 Rhino 項目,目的是爲了在 Java 中實現一個高性能但輕量級的 Javascript 運行時。Nashorn 項目使得 Java 應用能夠嵌入 Javascript。它在 JDK 8 中爲 Java 提供一個 Javascript 引擎。

 

JDK 9 包含一個用來解析Nashorn 的ECMAScript 語法樹的API。這個 API 使得 IDE 和服務端框架不需要依賴 Nashorn 項目的內部實現類,就能夠分析 ECMAScript 代碼。

 

 

java的動態編譯器

Oracle 一直在努力提高 Java 啓動和運行時性能,希望其能夠在更廣泛的場景達到或接近本地語言的性能。但是,直到今天,談到 Java,很多 C/C++ 開發者還是會不屑地評價爲啓動慢,吃內存。

簡單說,這主要是因爲 Java 編譯產生的類文件是 Java 虛擬機可以理解的二進制代碼,而不是真正的可執行的本地代碼,需要 Java 虛擬機進行解釋和編譯,這帶來了額外的開銷。

使用說明:

JITJust-in-time)編譯器可以在運行時將熱點編譯成本地代碼,速度很快。但是 Java 項目現在變得很大很複雜,因此 JIT 編譯器需要花費較長時間才能熱身完,而且有些 Java 方法還沒法編譯,性能方面也會下降。AoT 編譯就是爲了解決這些問題而生的。

在 JDK 9 中,AOT(JEP 295: Ahead-of-Time Compilation)作爲實驗特性被引入進來,開發者可以利用新的 jaotc 工具將重點代碼轉換成類似類庫一樣的文件。雖然仍處於試驗階段,但這個功能使得 Java 應用在被虛擬機啓動之前能夠先將 Java 類編譯爲原生代碼。此功能旨在改進小型和大型應用程序的啓動時間,同時對峯值性能的影響很小。

但是 Java 技術供應商 Excelsior 的營銷總監 Dmitry Leskov 擔心AoT 編譯技術不夠成熟,希望 Oracle 能夠等到 Java10 時有個更穩定版本才發佈。

另外 JVMCI (JEP 243: Java-Level JVM Compiler Interface)等特性,對於整個編程語言的發展,可能都具有非常重要的意義,雖然未必引起了廣泛關注。目前 Graal Core API 已經被集成進入 Java 9,雖然還只是初始一小步,但是完全用 Java 語言來實現的可靠的、高性能的動態編譯器,似乎不再是遙不可及,這是 Java 虛擬機開發工程師的福音。

與此同時,隨着 Truffle 框架和 Substrate VM 的發展,已經讓個別信心滿滿的工程師高呼“One VM to Rule Them All!”, 也許就在不遠的將來 Ploygot 以一種另類的方式成爲現實。

 

總結

在 java 9  中看不到什麼?

1.1 一個標準化和輕量級的JSON API

 

一個標準化和輕量級的JSON API被許多java開發人員所青睞。但是由於資金問題無法在Java 9中見到,但並不會削減掉。Java平臺首席架構師Mark Reinhold在JDK 9郵件列中說:“這個JEP將是平臺上的一個有用的補充,但是在計劃中,它並不像Oracle資助的其他功能那麼重要,可能會重新考慮JDK 10或更高版本中實現。 ”

 

1.2 新的貨幣API

對許多應用而言貨幣價值都是一個關鍵的特性,但JDK對此卻幾乎沒有任何支持。嚴格來講,現有的java.util.Currency類只是代表了當前ISO 4217貨幣的一個數據結構,但並沒有關聯的值或者自定義貨幣。JDK對貨幣的運算及轉換也沒有內建的支持,更別說有一個能夠代表貨幣值的標準類型了。

 

此前,Oracle 公佈的JSR 354定義了一套新的Java貨幣API:JavaMoney,計劃會在Java 9中正式引入。但是目前沒有出現在JDK 9 中。

 

不過,如果你用的是Maven的話,可以做如下的添加,即可使用相關的API處理貨幣:

<dependency>

   <groupId>org.javamoney</groupId>

   <artifactId>moneta</artifactId>

   <version>0.9</version>

</dependency>

隨着雲計算和 AI 等技術浪潮,當前的計算模式和場景正在發生翻天覆地的變化,不僅對 Java 的發展速度提出了更高要求,也深刻影響着 Java技術的發展方向。傳統的大型企業或互聯網應用,正在被雲端、容器化應用、模塊化的微服務甚至是函數(FaaS, Function-as-a-Service所替代。

Java雖然標榜面向對象編程,卻毫不顧忌的加入面向接口編程思想,又扯出匿名對象之概念,每增加一個新的東西,對Java的根本所在的面向對象思想的一次衝擊。反觀Python,抓住面向對象的本質,又能在函數編程思想方面遊刃有餘。Java對標C/C++,以拋掉內存管理爲賣點,卻又陷入了JVM優化的噩夢。選擇比努力更重要,選擇Java的人更需要對它有更清晰的認識。

Java 需要在新的計算場景下,改進開發效率。這話說的有點籠統,我談一些自己的體會,Java 代碼雖然進行了一些類型推斷等改進,更易用的集合 API 等,但仍然給開發者留下了過於刻板、形式主義的印象,這是一個長期的改進方向。

 

特性3:jShell命令:交互式編程環境

http://upload-images.jianshu.io/upload_images/9654439-3d3b8bff6a576b3a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/438

 

http://upload-images.jianshu.io/upload_images/9654439-1c4319dce00b127b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/346

查看包含的包

http://upload-images.jianshu.io/upload_images/9654439-f1007349ae4bc156.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/296

只需按下 Tab 鍵,就能自動補全代碼

 

列出當前 session 裏所有有效的代碼片段

http://upload-images.jianshu.io/upload_images/9654439-a8ac006cba06c501.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/318

查看當前 session 下所有創建過的變量

http://upload-images.jianshu.io/upload_images/9654439-38ccaec12218b619.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/205

查看當前 session 下所有創建過的方法

http://upload-images.jianshu.io/upload_images/9654439-bd621dae2e8fd976.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/197

Tips:還可以重新定義相同方法名和參數列表的方法,即爲對現有方法的修改(或覆蓋)。

使用外部代碼編輯器來編寫 Java 代碼

http://upload-images.jianshu.io/upload_images/9654439-b05fe1adb4a50629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/283

使用/open命令調用:

http://upload-images.jianshu.io/upload_images/9654439-b7adfa9cabac61b7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/415

沒有受檢異常(編譯時異常)

http://upload-images.jianshu.io/upload_images/9654439-18f4cdc4eeee55bd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/430

退出jShell

http://upload-images.jianshu.io/upload_images/9654439-7ac36dcf406c0bb0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/149

 

 

JDK10新特性(12個)

這次發佈的 Java 10,新帶來的特性並不多。

根據官網公開資料,共有 12 個 JEP(JDK Enhancement Proposal 特性加強提議),帶來以下加強功能:

 

 

1.JEP286: Local-Variable Type Inference(var 局部變量類型推斷)

 

 

2.JEP296: Consolidate the JDK Forest into a Single Repository()

將原來用 Mercurial 管理的衆多 JDK 倉庫代碼,合併到一個倉庫中,簡化開發和管理過程。

 

3.JEP304: Garbage-Collector Interface(統一的垃圾回收接口)

 

 

4.JEP307: Parallel Full GC for G1()

G1 垃圾回收器的並行完整垃圾回收,實現並行性來改善最壞情況下的延遲。

5.JEP310: Application Class-Data Sharing()

應用程序類數據 (AppCDS) 共享,通過跨進程共享通用類元數據來減少內存佔用空間,和減少啓動時間。

 

6.JEP312: Thread-Local Handshakes()

ThreadLocal 握手交互。在不進入到全局 JVM 安全點 (Safepoint) 的情況下,對線程執行回調。優化可以只停止單個線程,而不是停全部線程或一個都不停

7.JEP313: Remove the Native-Header Generation Tool (javah)()

移除 JDK 中附帶的 javah 工具。可以使用 javac -h 代替

8.JEP314: Additional Unicode Language-Tag Extensions)()

使用附加的 Unicode 語言標記擴展

9.JEP316: Heap Allocation on Alternative Memory Devices)()

能將堆內存佔用分配給用戶指定的備用內存設備

10.JEP317: Experimental Java-Based JIT Compiler)()

用 Graal 基於 Java 的編譯器,可以預先把 Java 代碼編譯成本地代碼來提升效能。

11.JEP319: Root Certificates)()

在 OpenJDK 中提供一組默認的根證書頒發機構證書。開源目前 Oracle 提供的的 Java SE 的根證書,這樣 OpenJDK 對開發人員使用起來更方便。

12.JEP322: Time-Based Release Versioning)()

基於時間定義的發佈版本,即上述提到的發佈週期。版本號爲\$FEATURE.\$INTERIM.\$UPDATE.\$PATCH,分別是大版本,中間版本,升級包和補丁版本。

 

 

 

部分新特性解析

1,迄今爲止,在官方放出了Java 10少數新特性裏面,局部變量類型推斷(local-variable type inference) 絕對是備受萬衆矚目的。它將常常在JS裏面使用的var 變量引入到語言特性中,把從那些冗長的變量聲明中解放出來。

這個語言功能在其他一些語言 (C#、JavaScript) 和基於 JRE 的一些語言 (Scala 和 Kotlin) 中,早已被加入。

在 Java 語言很早就在考慮,早在 2016 年正式提交了 JEP286 提議。後來舉行了一次公開的開發者調查,獲得最多建議的是採用類似 Scala 的方案,“同時使用 val 和 var”,約佔一半;第二多的是“只使用 var”,約佔四分之一。後來 Oracle 公司經過慎重考慮,採用了只使用 var 關鍵字的方案。

有了這個功能,開發者在寫這樣的代碼時:

List<String> myList = new ArrayList<String>()

可以省去前面的類型聲明,而只需要

var list = new ArrayList<String>()

編譯器會自動推斷出 list 變量的類型。對於鏈式表達式來說,也會很方便:

var stream = blocks.stream();

使用範圍:局部變量。更加確切的說法是:具有初始化器的局部類型變量聲明。

2,應用程序類數據共享(AppCDS)

CDS 特性在原來的 bootstrap 類基礎之上,擴展加入了應用類的 CDS(Application Class-Data Sharing) 支持。

其原理爲:在啓動時記錄加載類的過程,寫入到文本文件中,再次啓動時直接讀取此啓動文本並加載。設想如果應用環境沒有大的變化,啓動速度就會得到提升。

可以想像爲類似於操作系統的休眠過程,合上電腦時把當前應用環境寫入磁盤,再次使用時就可以快速恢復環境。

我在自己 PC 電腦上做以下應用啓動實驗。

首先部署 wildfly 12 應用服務器,採用 JDK10 預覽版作爲 Java 環境。另外需要用到一個工具 cl4cds[1],作用是把加載類的日誌記錄,轉換爲 AppCDS 可以識別的格式。

A、安裝好 wildfly 並部署一個應用,具有 Angularjs, rest, jpa 完整應用技術棧,預熱後啓動三次,並記錄完成部署時間

分別爲 6716ms, 6702ms, 6613ms,平均時間爲 6677ms。

B、加入環境變量並啓動,導出啓動類日誌

export PREPEND_JAVA_OPTS="-Xlog:class+load=debug:file=/tmp/wildfly.classtrace"

C、使用 cl4cds 工具,生成 AppCDS 可以識別的 cls 格式

/jdk-10/bin/java -cp src/classes/ io.simonis.cl4cds /tmp/wildfly.classtrace /tmp/wildfly.cls

打開文件可以看到內容爲:

java/lang/Object id: 0x0000000100000eb0

java/io/Serializable id: 0x0000000100001090

java/lang/Comparable id: 0x0000000100001268

java/lang/CharSequence id: 0x0000000100001440

......

org/hibernate/type/AssociationType id: 0x0000000100c61208 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

org/hibernate/type/AbstractType id: 0x0000000100c613e0 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

org/hibernate/type/AnyType id: 0x0000000100c61820 super: 0x0000000100c613e0 interfaces: 0x0000000100c61030 0x0000000100c61208 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

....

這個文件用於標記類的加載信息。

D、使用環境變量啓動 wildfly,模擬啓動過程並導出 jsa 文件,就是記錄了啓動時類的信息。

export PREPEND_JAVA_OPTS="-Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=/tmp/wildfly.cls -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"

查看產生的文件信息,jsa 文件有較大的體積。

/opt/work/cl4cds$ ls -l /tmp/wildfly.*

-rw-rw-r-- 1 shihang shihang  8413843 Mar 20 11:07 /tmp/wildfly.classtrace

-rw-rw-r-- 1 shihang shihang  4132654 Mar 20 11:11 /tmp/wildfly.cls

-r--r--r-- 1 shihang shihang 177659904 Mar 20 11:13 /tmp/wildfly.jsa

E、使用 jsa 文件啓動應用服務器

export PREPEND_JAVA_OPTS="-Xshare:on -XX:+UseAppCDS -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"

啓動完畢後記錄時長,三次分別是 5535ms, 5333ms, 5225ms,平均爲 5364ms,相比之前的 6677ms 可以算出啓動時間提升了 20% 左右。

這個效率提升,對於雲端應用部署很有價值。

 

 

JDK11 新特性(17個新特性)

 

 

JEP 181: Nest-Based Access Control(基於嵌套的訪問控制)

 

 

JEP 309: Dynamic Class-File Constants(動態類文件常量)

類文件新添的一種結構

Java的類型文件格式將被拓展,支持一種新的常量池格式:CONSTANT_Dynamic,加載CONSTANT_Dynamic會將創建委託給bootstrap方法。

目標

其目標是降低開發新形式的可實現類文件約束帶來的成本和干擾。

 

 

JEP 315: Improve Aarch64 Intrinsics(改進 Aarch64 函數)

 

 

JEP 318: Epsilon: A No-Op Garbage Collector(Epsilon一個無操作的垃圾收集器)

新的Epsilon垃圾收集器。

A NoOp Garbage Collector(不會用來回收的GC,什麼都不做)

JDK上對這個特性的描述是: 開發一個處理內存分配但不實現任何實際內存回收機制的GC, 一旦可用堆內存用完, JVM就會退出.

如果有System.gc()調用, 實際上什麼也不會發生(這種場景下和-XX:+DisableExplicitGC效果一樣), 因爲沒有內存回收, 這個實現可能會警告用戶嘗試強制GC是徒勞.

 

用法 : -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC

 

class Garbage {

         int n = (int)(Math.random() * 100);

         @Override

         public void finalize() {

                   System.out.println(this + " : " + n + " is dying");

         }

}

public class EpsilonTest {

        

         public static void main(String[] args) {

                   boolean flag = true;

                   List<Garbage> list = new ArrayList<>();

                   long count = 0;

                   while (flag) {

                            list.add(new Garbage());

                            if (list.size() == 1000000 && count == 0) {

                                     list.clear();

                                     count++;

                            }

                   }

                   System.out.println("程序結束");

         }

}

如果使用選項-XX:+UseEpsilonGC, 程序很快就因爲堆空間不足而退出

 

使用這個選項的原因 :

提供完全被動的GC實現, 具有有限的分配限制和儘可能低的延遲開銷,但代價是內存佔用和內存吞吐量.

衆所周知, java實現可廣泛選擇高度可配置的GC實現. 各種可用的收集器最終滿足不同的需求, 即使它們的可配置性使它們的功能相交. 有時更容易維護單獨的實現, 而不是在現有GC實現上堆積另一個配置選項.

 

主要用途如下 :

         性能測試(它可以幫助過濾掉GC引起的性能假象)

         內存壓力測試(例如,知道測試用例 應該分配不超過1GB的內存, 我們可以使用-Xmx1g –XX:+UseEpsilonGC, 如果程序有問題, 則程序會崩潰)

         非常短的JOB任務(對象這種任務, 接受GC清理堆那都是浪費空間)

         VM接口測試

         Last-drop 延遲&吞吐改進

 

 

JEP 320: Remove the Java EE and CORBA Modules(刪除 Java EE 和 CORBA 模塊)

在java11中移除了不太使用的JavaEE模塊和CORBA技術

CORBA來自於二十世紀九十年代,Oracle說,現在用CORBA開發現代Java應用程序已經沒有意義了,維護CORBA的成本已經超過了保留它帶來的好處。

 

但是刪除CORBA將使得那些依賴於JDK提供部分CORBA API的CORBA實現無法運行。目前還沒有第三方CORBA版本,也不確定是否會有第三方願意接手CORBA API的維護工作。

 

在java11中將java9標記廢棄的Java EE及CORBA模塊移除掉,具體如下:

  1. xml相關的,

java.xml.ws,

java.xml.bind,

java.xml.ws,

java.xml.ws.annotation,

jdk.xml.bind,

jdk.xml.ws被移除,

只剩下java.xml,java.xml.crypto,jdk.xml.dom這幾個模塊;

  1. java.corba,

java.se.ee,

java.activation,

java.transaction被移除,

但是java11新增一個java.transaction.xa模塊

 

 

JEP 321: HTTP Client (Standard)(標準HTTP客戶端)

HTTP客戶端進一步升級

 

JDK 9 中就已對 HTTP Client API 進行標準化,然後通過JEP 110,在 JDK 10 中進行了更新。在本次的Java 11的更新列表中,由以JEP 321進行進一步升級。該API通過CompleteableFutures提供非阻塞請求和響應語義,可以聯合使用以觸發相應的動作。 JDK 11完全重寫了該功能。現在,在用戶層請求發佈者和響應發佈者與底層套接字之間追蹤數據流更容易了,這降低了複雜性,並最大程度上提高了HTTP / 1和HTTP / 2之間的重用的可能性。

 

標準Java異步HTTP客戶端。

這是 Java 9 開始引入的一個處理 HTTP 請求的的 HTTP Client API,該 API 支持同步和異步,而在 Java 11 中已經爲正式可用狀態,你可以在 java.net 包中找到這個 API。

 

來看一下 HTTP Client 的用法:

 

var request = HttpRequest.newBuilder()

.uri(URI.create("https://javastack.cn"))

.GET()

.build();

var client = HttpClient.newHttpClient();

// 同步

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

System.out.println(response.body());

// 異步

client.sendAsync(request, HttpResponse.BodyHandlers.ofString())

.thenApply(HttpResponse::body)

.thenAccept(System.out::println);

上面的 .GET() 可以省略,默認請求方式爲 Get!

 

更多使用示例可以看這個 API,後續有機會再做演示。

 

現在 Java 自帶了這個 HTTP Client API,我們以後還有必要用 Apache 的 HttpClient 工具包嗎?

         @Test

         public void testName2() throws Exception {

                   HttpClient client = HttpClient.newHttpClient();

                   HttpRequest request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();

                   BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();

                   CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, responseBodyHandler);

                   sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println);

                   //HttpResponse<String> response = sendAsync.get();

                   //String body = response.body();

                   //System.out.println(body);

                  

         }

        

         @Test

         public void testName() throws Exception {

                   HttpClient client = HttpClient.newHttpClient();

                   HttpRequest request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();

                   BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();

                   HttpResponse<String> response = client.send(request, responseBodyHandler);

                   String body = response.body();

                   System.out.println(body);

         }

 

 

 

JEP 323: Local-Variable Syntax for Lambda Parameters(用於 Lambda 參數的局部變量語法)

增強var用法

 

Java 10中增加了本地變量類型推斷的特性,可以使用var來定義局部變量。儘管這一特性被很多人詬病,但是並不影響Java繼續增強他的用法,在Java 11中,var可以用來作爲Lambda表達式的局部變量聲明。

 

 

JEP 324: Key Agreement with Curve25519 and Curve448(Curve25519 和 Curve448 算法的密鑰協議)

 

JEP 327: Unicode 10

Unicode 10 增加了8518個字符, 總計達到了136690個字符. 並且增加了4個腳本.同時還有56個新的emoji表情符號.

 

 

JEP 328: Flight Recorder(飛行記錄器)

Flight Recorder源自飛機的黑盒子

 

Flight Recorder以前是商業版的特性,在java11當中開源出來,它可以導出事件到文件中,之後可以用Java Mission Control來分析。可以在應用啓動時配置java -XX:StartFlightRecording,或者在應用啓動之後,使用jcmd來錄製,比如

$ jcmd <pid> JFR.start

$ jcmd <pid> JFR.dump filename=recording.jfr

$ jcmd <pid> JFR.stop

 

是 Oracle 剛剛開源的強大特性。我們知道在生產系統進行不同角度的 Profiling,有各種工具、框架,但是能力範圍、可靠性、開銷等,大都差強人意,要麼能力不全面,要麼開銷太大,甚至不可靠可能導致 Java 應用進程宕機。

而 JFR 是一套集成進入 JDK、JVM 內部的事件機制框架,通過良好架構和設計的框架,硬件層面的極致優化,生產環境的廣泛驗證,它可以做到極致的可靠和低開銷。在 SPECjbb2015 等基準測試中,JFR 的性能開銷最大不超過 1%,所以,工程師可以基本沒有心理負擔地在大規模分佈式的生產系統使用,這意味着,我們既可以隨時主動開啓 JFR 進行特定診斷,也可以讓系統長期運行 JFR,用以在複雜環境中進行“After-the-fact”分析。還需要苦惱重現隨機問題嗎?JFR 讓問題簡化了很多。

在保證低開銷的基礎上,JFR 提供的能力也令人眼前一亮,例如:我們無需 BCI 就可以進行 Object Allocation Profiling,終於不用擔心 BTrace 之類把進程搞掛了。對鎖競爭、阻塞、延遲,JVM GC、SafePoint 等領域,進行非常細粒度分析。甚至深入 JIT Compiler 內部,全面把握熱點方法、內聯、逆優化等等。JFR 提供了標準的 Java、C++ 等擴展 API,可以與各種層面的應用進行定製、集成,爲複雜的企業應用棧或者複雜的分佈式應用,提供 All-in-One 解決方案。而這一切都是內建在 JDK 和 JVM 內部的,並不需要額外的依賴,開箱即用。

https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=423998638,3946386819&fm=173&app=25&f=JPEG?w=414&h=290&s=14C649B63A0024CC5CAF0EA80300702A

JFR 是一套集成進入 JDK、JVM 內部的事件機制框架,通過良好架構和設計的框架,硬件層面的極致優化,生產環境的廣泛驗證,它可以做到極致的可靠和低開銷。在 SPECjbb2015 等基準測試中,JFR 的性能開銷最大不超過 1%,所以,工程師可以基本沒有心理負擔地在大規模分佈式的生產系統使用,這意味着,我們既可以隨時主動開啓 JFR 進行特定診斷,也可以讓系統長期運行 JFR,用以在複雜環境中進行“After-the-fact”分析。

在保證低開銷的基礎上,JFR 提供的能力可以應用在對鎖競爭、阻塞、延遲,JVM GC、SafePoint 等領域,進行非常細粒度分析。甚至深入 JIT Compiler 內部,全面把握熱點方法、內聯、逆優化等等。JFR 提供了標準的 Java、C++ 等擴展 API,可以與各種層面的應用進行定製、集成,爲複雜的企業應用棧或者複雜的分佈式應用,提供 All-in-One 解決方案。

而這一切都是內建在 JDK 和 JVM 內部的,並不需要額外的依賴,開箱即用。

 

 

JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms(ChaCha20 和 Poly1305 加密算法)

實現RFC7539中指定的ChaCha20和Poly1305兩種加密算法, 代替RC4

實現 RFC 7539的ChaCha20 and ChaCha20-Poly1305加密算法

 

RFC7748定義的祕鑰協商方案更高效, 更安全. JDK增加兩個新的接口

XECPublicKey 和 XECPrivateKey

KeyPairGenerator kpg = KeyPairGenerator.getInstance(“XDH”);

NamedParameterSpec paramSpec = new NamedParameterSpec(“X25519”);

kpg.initialize(paramSpec);

KeyPair kp = kgp.generateKeyPair();

 

KeyFactory kf = KeyFactory.getInstance(“XDH”);

BigInteger u = new BigInteger(“xxx”);

XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);

PublicKey pubKey = kf.generatePublic(pubSpec);

 

KeyAgreement ka = KeyAgreement.getInstance(“XDH”);

ka.init(kp.getPrivate());

ka.doPhase(pubKey, true);

byte[] secret = ka.generateSecret();

 

 

JEP 330: Launch Single-File Source-Code Programs(啓動單一文件的源代碼程序)

 

JEP 331: Low-Overhead Heap Profiling(低開銷的 Heap Profiling)

免費的低耗能飛行記錄儀和堆分析儀

通過JVMTI的SampledObjectAlloc回調提供了一個開銷低的heap分析方式

 

提供一個低開銷的, 爲了排錯java應用問題, 以及JVM問題的數據收集框架, 希望達到的目標如下 :

         提供用於生產和消費數據作爲事件的API

         提供緩存機制和二進制數據格式

         允許事件配置和事件過濾

         提供OS,JVM和JDK庫的事件

它來源於 Google 等業界前沿廠商的一線實踐,通過獲取對象分配細節,爲 JDK 補足了對象分配診斷方面的一些短板,工程師可以通過 JVMTI 使用這個能力增強自身的工具。

從 Java 類庫發展的角度來看,JDK 11 最大的進步也是兩個方面:

第一, HTTP/2 Client API,新的 HTTP API 提供了對 HTTP/2 等業界前沿標準的支持,精簡而又友好的 API 接口,與主流開源 API(如,Apache HttpClient, Jetty, OkHttp 等)對等甚至更高的性能。與此同時它是 JDK 在 Reactive-Stream 方面的第一個生產實踐,廣泛使用了 Java Flow API 等,終於讓 Java 標準 HTTP 類庫在擴展能力等方面,滿足了現代互聯網的需求。

第二,就是安全類庫、標準等方面的大範圍升級,其中特別是 JEP 332: Transport Layer Security (TLS) 1.3,除了在安全領域的重要價值,它還是中國安全專家範學雷所領導的 JDK 項目,完全不同於以往的修修補補,是個非常大規模的工程。

 

JEP 332: Transport Layer Security (TLS) 1.3(支持 TLS 1.3)

最新的HTTPS安全協議TLS 1.3。

實現TLS協議1.3版本, TLS允許客戶端和服務器端通過互聯網以一種防止竊聽, 篡改以及消息僞造的方式進行通信.

JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental) (可伸縮低延遲垃圾收集器)

這應該是JDK11最爲矚目的特性, 沒有之一. 但是後面帶了Experimental, 說明這還不建議用到生產環境.

ZGC, A Scalable Low-Latency Garbage Collector(Experimental)  可伸縮,低延遲的GC

ZGC, 這應該是JDK11最爲矚目的特性, 沒有之一. 但是後面帶了Experimental, 說明這還不建議用到生產環境.

         GC暫停時間不會超過10ms

         既能處理幾百兆的小堆, 也能處理幾個T的大堆(OMG)

         和G1相比, 應用吞吐能力不會下降超過15%

         爲未來的GC功能和利用colord指針以及Load barriers優化奠定基礎

         初始只支持64位系統

 

ZGC的設計目標是:支持TB級內存容量,暫停時間低(<10ms),對整個程序吞吐量的影響小於15%。 將來還可以擴展實現機制,以支持不少令人興奮的功能,例如多層堆(即熱對象置於DRAM和冷對象置於NVMe閃存),或壓縮堆。

 

GC是java主要優勢之一. 然而, 當GC停頓太長, 就會開始影響應用的響應時間.消除或者減少GC停頓時長, java將對更廣泛的應用場景是一個更有吸引力的平臺. 此外, 現代系統中可用內存不斷增長,用戶和程序員希望JVM能夠以高效的方式充分利用這些內存, 並且無需長時間的GC暫停時間.

 

STW – stop the world

 

ZGC是一個併發, 基於region, 壓縮型的垃圾收集器, 只有root掃描階段會STW, 因此GC停頓時間不會隨着堆的增長和存活對象的增長而變長.

 

 

ZGC : avg 1.091ms   max:1.681

G1    : avg 156.806  max:543.846

 

用法 : -XX:+UnlockExperimentalVMOptions –XX:+UseZGC, 因爲ZGC還處於實驗階段, 所以需要通過JVM參數來解鎖這個特性

 

 

 

JEP 335: Deprecate the Nashorn JavaScript Engine(棄用 Nashorn JavaScript 引擎)

廢除Nashorn javascript引擎,在後續版本準備移除掉,有需要的可以考慮使用GraalVM

它進一步明確了 Graal 很有可能將成爲 JVM 向前演進的核心選擇,Java-on-Java 正在一步步的成爲現實。

JEP 336: Deprecate the Pack200 Tools and API (棄用 Pack200 工具和 API)

Java5中帶了一個壓縮工具:Pack200,這個工具能對普通的jar文件進行高效壓縮。其  實現原理是根據Java類特有的結構,合併常數  池,去掉無用信息等來實現對java類的高效壓縮。由於是專門對Java類進行壓縮的,所以對普通文件的壓縮和普通壓縮軟件沒有什麼兩樣,但是對於Jar  文件卻能輕易達到10-40%的壓縮率。這在Java應用部署中很有用,尤其對於移動Java計算,能夠大大減小代碼下載量。

Java5中還提供了這一技術的API接口,你可以將其嵌入到你的程序中使用。使用的方法很簡單,下面的短短几行代碼即可以實現jar的壓縮和解壓:

壓縮

Packer packer=Pack200.newPacker();

OutputStream output=new BufferedOutputStream(new  FileOutputStream(outfile));

packer.pack(new JarFile(jarFile), output);

output.close();

解壓

Unpacker unpacker=Pack200.newUnpacker();

output=new JarOutputStream(new FileOutputStream(jarFile));

unpacker.unpack(pack200File, output);

output.close();

 

Pack200的壓縮和解壓縮速度是比較快的,而且壓縮率也是很驚人的,在我是使用  的包4.46MB壓縮後成了1.44MB(0.322%),而且隨着包的越大壓縮率會根據明顯,據說如果jar包都是class類可以壓縮到1/9的大  小。其實JavaWebStart還有很多功能,例如可以按不同的jar包進行lazy下載和 單獨更新,設置可以根據jar中的類變動進行class粒度的下載。

 

但是在java11中廢除了pack200以及unpack200工具以及java.util.jar中的Pack200 API。因爲Pack200主要是用來壓縮jar包的工具,由於網絡下載速度的提升以及java9引入模塊化系統之後不再依賴Pack200,因此這個版本將其移除掉。

 

 

 

JDK12新特性:(持續更新)

189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)

230: Microbenchmark Suite

325: Switch Expressions (Preview)

334: JVM Constants API

340: One AArch64 Port, Not Two

341: Default CDS Archives

344: Abortable Mixed Collections for G1

346: Promptly Return Unused Committed Memory from G1

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章