JDK各個版本新特性


 

jdk1.5的新特性:

1,泛型(它允許指定集合里元素的類型,這樣你可以得到強類型在編譯時刻進行類型檢查的好處)
   ArrayList list=new ArrayList()------>ArrayList<Integer>list=new ArrayList<Integer>();

2,自動裝箱/拆箱
  自動裝包:基本類型自動轉為包裝類.(int >> Integer)
  自動拆包:包裝類自動轉為基本類型.(Integer >> int)
  nt i=list.get(0).parseInt();-------->int i=list.get(0);原始類型與對應的包裝類不用顯式轉換

3,for-each(循環清晰許多,並且避免了強制類型轉換)

 i=0;i<a.length;i++------------>for(int i:a){......}

4,static import ( 靜態導入:要使用用靜態成員(方法和變量)我們必須給出提供這個方法的類。使用靜態導入可以使被導入類的所有靜態變量和靜態方法在當前類直接可見,使用這些靜態成員無需再給出他們的類名。)

  import static java.lang.Math.*;
  Math.sqrt();--------------->sqrt();

5 變長參數
  int sum(int ...intlist)有任意個參數,把他看作數組

6 枚舉(Enums)
  然后可以這樣來使用Color myColor = Color.Red.
  枚舉類型還提供了兩個有用的靜態方法values()和valueOf(). 我們可以很方便地使用它們

 

jdk1.6新特性

1,Desktop類和SystemTray類
  在JDK1.6中,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的一部分,在JDK1.6中,SUN將其放到了Java SE中,這也是SUN的一貫做法.JDK1.6中自帶的這個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)是JDK1.6.0中除了DOM和SAX之外的又一種處理XML文檔的API.
  StAX 的來歷:在JAXP1.3(JSR 206)有兩種處理XML文檔的方法:DOM(Document Object Model)和SAX(Simple API for XML).
  JDK1.6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都會用到StAXSun決定把StAX加入到JAXP家族當中來,並將JAXP的版本升級到1.4(JAXP1.4是JAXP1.3的維護版 本).JDK1.6里面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
  現在我 們可以用JDK1.6 的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
  JDK1.6 提供了一個簡單的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開發控制台程序
  JDK1.6中提供了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 ANNOTATION_TYPE,CONSTRUCTOR,FIELD,LOCAL_VARIABLE,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來檢驗安全角色.

 

jdk1.7新特性 

1,對集合類的語言支持
  Java將包含對創建集合類的第一類語言支持。這意味着集合類的創建可以像Ruby和Perl那樣了。
  原本需要這樣:

 List<String> list = new ArrayList<String>(); 
  list.add("item"); 
  String item = list.get(0); 
  Set<String> set = new HashSet<String>(); 
  set.add("item"); 
  Map<String, Integer> map = new HashMap<String, Integer>(); 
  map.put("key", 1); 
  int value = map.get("key"); 

  現在你可以這樣:

 List<String> list = ["item"]; 
  String item = list[0]; 
  Set<String> set = {"item"}; 
  Map<String, Integer> map = {"key" : 1}; 
  int value = map["key"]; 

  這些集合是不可變的。

2,自動資源管理
  Java中某些資源是需要手動關閉的,如InputStream,Writes,Sockets,Sql classes等。這個新的語言特性允許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(可關閉的)接口。

3,增強的對通用實例創建(diamond)的類型推斷
  類型推斷是一個特殊的煩惱,下面的代碼:

 Map<String, List<String>> anagrams = new HashMap<String, List<String>>(); 

  通過類型推斷后變成:

 Map<String, List<String>> anagrams = new HashMap<>(); 

  這個<>被叫做diamond(鑽石)運算符,這個運算符從引用的聲明中推斷類型。

4,數字字面量下划線支持
  很長的數字可讀性不好,在Java 7中可以使用下划線分隔長int以及long了,如:
  int one_million = 1_000_000;
  運算時先去除下划線,如:1_1 * 10 = 110,120 – 1_0 = 110

5,switch中使用string
  以前你在switch中只能使用number或enum。現在你可以使用string了:

 String s = ... 
  switch(s) { 
    case "quux": 
      processQuux(s); 
      // fall-through 
    case "foo": 
    case "bar": 
      processFooOrBar(s); 
      break; 
    case "baz": 
      processBaz(s); 
      // fall-through 
    default: 
      processDefault(s); 
      break; 
  }

6,二進制字面量
  由於繼承C語言,Java代碼在傳統上迫使程序員只能使用十進制,八進制或十六進制來表示數(numbers)。
  由於很少的域是以bit導向的,這種限制可能導致錯誤。你現在可以使用0b前綴創建二進制字面量:

int binary = 0b1001_1001; 

  現在,你可以使用二進制字面量這種表示方式,並且使用非常簡短的代碼,可將二進制字符轉換為數據類型,如在byte或short。

byte aByte = (byte)0b001; 
short aShort = (short)0b010;

JDK1.8的新特性

1,hashMap等map集合的數據結構優化
在jdk1.8中對hashMap等map集合的數據結構優化。hashMap數據結構的優化
原來的hashMap采用的數據結構是哈希表(數組+鏈表),hashMap默認大小是16,一個0-15索引的數組,如何往里面存儲元素,首先調用元素的hashcode
方法,計算出哈希碼值,經過哈希算法算成數組的索引值,如果對應的索引處沒有元素,直接存放,如果有對象在,那么比較它們的equals方法比較內容
如果內容一樣,后一個value會將前一個value的值覆蓋,如果不一樣,在1.7的時候,后加的放在前面,形成一個鏈表,形成了碰撞,在某些情況下如果鏈表
無限下去,那么效率極低,碰撞是避免不了的
加載因子:0.75,數組擴容,達到總容量的75%,就進行擴容,但是無法避免碰撞的情況發生
在1.8之后,在數組+鏈表+紅黑樹來實現hashmap,當碰撞的元素個數大於8時 & 總容量大於64,會有紅黑樹的引入
除了添加之后,效率都比鏈表高,1.8之后鏈表新進元素加到末尾 ,

2,Lambda表達式
Lambda表達式本質上是一段匿名內部類,也可以是一段可以傳遞的代碼
Lambda表達式是jdk1.8里面的一個重要的更新,這意味着java也開始承認了函數式編程,並且嘗試引入其中。
首先,什么是函數式編程,引用廖雪峰先生的教程里面的解釋就是說:函數式編程就是一種抽象程度很高的編程范式,純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變量的程序設計語言,由於函數內部的變量狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
  簡單的來說就是,函數也是一等公民了,在java里面一等公民有變量,對象,那么函數式編程語言里面函數也可以跟變量,對象一樣使用了,也就是說函數既可以作為參數,也可以作為返回值了,看一下下面這個例子。

//這是常規的Collections的排序的寫法,需要對接口方法重寫
public void test1(){
  List<String> list =Arrays.asList("aaa","fsa","ser","eere");
  Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
      return o2.compareTo(o1);
    }
  });
  for (String string : list) {
    System.out.println(string);
  }
}
//這是帶參數類型的Lambda的寫法
public void testLamda1(){
  List<String> list =Arrays.asList("aaa","fsa","ser","eere");
  Collections.sort(list, (Comparator<? super String>) (String a,String b)->{
      return b.compareTo(a);
    }
  );
  for (String string : list) {
    System.out.println(string);
    }
}
//這是不帶參數的lambda的寫法
public void testLamda2(){
  List<String> list =Arrays.asList("aaa","fsa","ser","eere");
  Collections.sort(list, (a,b)->b.compareTo(a)
  );
  for (String string : list) {
    System.out.println(string);
  }
}

可以看到不帶參數的寫法一句話就搞定了排序的問題,所以引入lambda表達式的一個最直觀的作用就是大大的簡化了代碼的開發,像其他一些編程語言Scala,Python等都是支持函數式的寫法的。當然,不是所有的接口都可以通過這種方法來調用,只有函數式接口才行,jdk1.8里面定義了好多個函數式接口,我們也可以自己定義一個來調用,下面說一下什么是函數式接口。

3,函數式接口
定義:“函數式接口”是指僅僅只包含一個抽象方法的接口,每一個該類型的lambda表達式都會被匹配到這個抽象方法。jdk1.8提供了一個@FunctionalInterface注解來定義函數式接口,如果我們定義的接口不符合函數式的規范便會報錯。

@FunctionalInterface
public interface MyLamda {
  public void test1(String y);
  //這里如果繼續加一個抽象方法便會報錯
  // public void test1(); 
  //default方法可以任意定義
  default String test2(){
    return "123";
  }  
  default String test3(){
    return "123";
  }
//static方法也可以定義
  static void test4(){
    System.out.println("234");
  }
}

看一下這個接口的調用,符合lambda表達式的調用方法。

MyLamda m = y -> System.out.println("ss"+y);

4,方法引用和構造器調用
  jdk1.8提供了另外一種調用方式::,當你需要使用方法引用時 , 目標引用放在分隔符::前 ,方法的名稱放在后面 ,即ClassName :: methodName 。例如 ,Apple::getWeight就是引用了Apple類中定義的方法getWeight。請記住,不需要括號,因為你沒有實際調用這個方法。方法引用就是Lambda表達式(Apple a) -> a.getWeight()的快捷寫法,如下示例。

//先定義一個函數式接口
@FunctionalInterface
public interface TestConverT<T, F> {
  F convert(T t);
}

測試如下,可以以::形式調用。

public void test(){
  TestConverT<String, Integer> t = Integer::valueOf;
  Integer i = t.convert("111");
  System.out.println(i);
}

此外,對於構造方法也可以這么調用。

//實體類User和它的構造方法
public class User { 
  private String name;
  private String sex;
  public User(String name, String sex) {
    super();
    this.name = name;
    this.sex = sex;
  }
}
//User工廠
public interface UserFactory {
  User get(String name, String sex);
}
//測試類
UserFactory uf = User::new;
User u = uf.get("ww", "man");

這里的User::new就是調用了User的構造方法,Java編譯器會自動根據UserFactory.get方法的簽名來選擇合適的構造函數。

5,局部變量限制
  Lambda表達式也允許使用自由變量(不是參數,而是在外層作用域中定義的變量),就像匿名類一樣。 它們被稱作捕獲Lambda。 Lambda可以沒有限制地捕獲(也就是在其主體中引用)實例變量和靜態變量。但局部變量必須顯式聲明為final,或事實上是final。
  為什么局部變量有這些限制?
  (1)實例變量和局部變量背后的實現有一個關鍵不同。實例變量都存儲在堆中,而局部變量則保存在棧上。如果Lambda可以直接訪問局部變量,而且Lambda是在一個線程中使用的,則使用Lambda的線程,可能會在分配該變量的線程將這個變量收回之后,去訪問該變量。因此, Java在訪問自由局部變量時,實際上是在訪問它的副本,而不是訪問原始變量。如果局部變量僅僅賦值一次那就沒有什么區別了——因此就有了這個限制。
  (2)這一限制不鼓勵你使用改變外部變量的典型命令式編程模式。

final int num = 1;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2);

5,Stream API
定義:流是Java API的新成員,它允許我們以聲明性方式處理數據集合(通過查詢語句來表達,而不是臨時編寫一個實現)。就現在來說,我們可以把它們看成遍歷數據集的高級迭代器。此外,流還可以透明地並行處理,也就是說我們不用寫多線程代碼了。

Stream 不是集合元素,它不是數據結構並不保存數據,它是有關算法和計算的,它更像一個高級版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個一個遍歷元素並對其執行某些操作;高級版本的 Stream,用戶只要給出需要對其包含的元素執行什么操作,比如 “過濾掉長度大於 10 的字符串”、“獲取每個字符串的首字母”等,Stream 會隱式地在內部進行遍歷,做出相應的數據轉換。

Stream 就如同一個迭代器(Iterator),單向,不可往復,數據只能遍歷一次,遍歷過一次后即用盡了,就好比流水從面前流過,一去不復返。而和迭代器又不同的是,Stream 可以並行化操作,迭代器只能命令式地、串行化操作。顧名思義,當使用串行方式去遍歷時,每個 item 讀完后再讀下一個 item。而使用並行去遍歷時,數據會被分成多個段,其中每一個都在不同的線程中處理,然后將結果一起輸出。Stream 的並行操作依賴於 Java7 中引入的 Fork/Join 框架(JSR166y)來拆分任務和加速處理過程。

流的操作類型分為兩種:

Intermediate:一個流可以后面跟隨零個或多個 intermediate 操作。其目的主要是打開流,做出某種程度的數據映射/過濾,然后返回一個新的流,交給下一個操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調用到這類方法,並沒有真正開始流的遍歷。
Terminal:一個流只能有一個 terminal 操作,當這個操作執行后,流就被使用“光”了,無法再被操作。所以這必定是流的最后一個操作。Terminal 操作的執行,才會真正開始流的遍歷,並且會生成一個結果,或者一個 side effect。
  在對於一個 Stream 進行多次轉換操作 (Intermediate 操作),每次都對 Stream 的每個元素進行轉換,而且是執行多次,這樣時間復雜度就是 N(轉換次數)個 for 循環里把所有操作都做掉的總和嗎?其實不是這樣的,轉換操作都是 lazy 的,多個轉換操作只會在 Terminal 操作的時候融合起來,一次循環完成。我們可以這樣簡單的理解,Stream 里有個操作函數的集合,每次轉換操作就是把轉換函數放入這個集合中,在 Terminal 操作的時候循環 Stream 對應的集合,然后對每個元素執行所有的函數。

  構造流的幾種方式

// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();

6,新時間日期API
1.8之前JDK自帶的日期處理類非常不方便,我們處理的時候經常是使用的第三方工具包,比如commons-lang包等。不過1.8出現之后這個改觀了很多,比如日期時間的創建、比較、調整、格式化、時間間隔等。這些類都在java.time包下。比原來實用了很多。
  6.1 LocalDate/LocalTime/LocalDateTime
  LocalDate為日期處理類、LocalTime為時間處理類、LocalDateTime為日期時間處理類,方法都類似,具體可以看API文檔或源碼,選取幾個代表性的方法做下介紹。
  now相關的方法可以獲取當前日期或時間,of方法可以創建對應的日期或時間,parse方法可以解析日期或時間,get方法可以獲取日期或時間信息,with方法可以設置日期或時間信息,plus或minus方法可以增減日期或時間信息;
  6.2TemporalAdjusters
  這個類在日期調整時非常有用,比如得到當月的第一天、最后一天,當年的第一天、最后一天,下一周或前一周的某天等。
  6.3DateTimeFormatter
  以前日期格式化一般用SimpleDateFormat類,但是不怎么好用,現在1.8引入了DateTimeFormatter類,默認定義了很多常量格式(ISO打頭的),在使用的時候一般配合LocalDate/LocalTime/LocalDateTime使用,比如想把當前日期格式化成yyyy-MM-dd hh:mm:ss的形式:

LocalDateTime dt = LocalDateTime.now(); 
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); 
System.out.println(dtf.format(dt));

7,default關鍵字
 在java里面,我們通常都是認為接口里面是只能有抽象方法,不能有任何方法的實現的,那么在jdk1.8里面打破了這個規定,引入了新的關鍵字default,通過使用default修飾方法,可以讓我們在接口里面定義具體的方法實現,如下。,

public interface NewCharacter {
  public void test1();
  public default void test2(){
    System.out.println("我是新特性1");
  }
}

那這么定義一個方法的作用是什么呢?為什么不在接口的實現類里面再去實現方法呢?
其實這么定義一個方法的主要意義是定義一個默認方法,也就是說這個接口的實現類實現了這個接口之后,不用管這個default修飾的方法,也可以直接調用,如下。

public class NewCharacterImpl implements NewCharacter{
  @Override
  public void test1() {}
  public static void main(String[] args) {
    NewCharacter nca = new NewCharacterImpl();
    nca.test2();
  }
}

所以說這個default方法是所有的實現類都不需要去實現的就可以直接調用,那么比如說jdk的集合List里面增加了一個sort方法,那么如果定義為一個抽象方法,其所有的實現類如arrayList,LinkedList等都需要對其添加實現,那么現在用default定義一個默認的方法之后,其實現類可以直接使用這個方法了,這樣不管是開發還是維護項目,都會大大簡化代碼量。

8,Objects方法新特性

//比較兩個對象是否相等(首先比較內存地址,然后比較a.equals(b),只要符合其中之一返回true)
public static boolean equals(Object a, Object b);

//深度比較兩個對象是否相等(首先比較內存地址,相同返回true;如果傳入的是數組,則比較數組內的對應下標值是否相同)
public static boolean deepEquals(Object a, Object b);

//返回對象的hashCode,若傳入的為null,返回0
public static int hashCode(Object o);

//返回傳入可變參數的所有值的hashCode的總和(這里說總和有點牽強,具體參考Arrays.hashCode()方法)
public static int hash(Object... values);

//返回對象的String表示,若傳入null,返回null字符串
public static String toString(Object o)

//返回對象的String表示,若傳入null,返回默認值nullDefault
public static String toString(Object o, String nullDefault)

//使用指定的比較器c 比較參數a和參數b的大小(相等返回0,a大於b返回整數,a小於b返回負數)
public static <T> int compare(T a, T b, Comparator<? super T> c)

//如果傳入的obj為null拋出NullPointerException,否者返回obj
public static <T> T requireNonNull(T obj)

//如果傳入的obj為null拋出NullPointerException並可以指定錯誤信息message,否者返回obj
public static <T> T requireNonNull(T obj, String message)

-----------------------------以下是jdk8新增方法---------------------------

//判斷傳入的obj是否為null,是返回true,否者返回false
public static boolean isNull(Object obj)

//判斷傳入的obj是否不為null,不為空返回true,為空返回false (和isNull()方法相反)
public static boolean nonNull(Object obj)

//如果傳入的obj為null拋出NullPointerException並且使用參數messageSupplier指定錯誤信息,否者返回obj
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier)

 

 JDK1.9 新特性

1,集合加強
jdk9為所有集合(List/Set/Map)都增加了of和copyOf方法,用來創建不可變集合,即一旦創建就無法再執行添加、刪除、替換、排序等操作,否則將報java.lang.UnsupportedOperationException異常。一般在特定場景下用還是可以的,不過如果引用了guava庫的話推薦還是使用guava把hhhh,例子如下:

    List strs = List.of("Hello", "World");
    List strsCopy = List. copyOf(strs);
    Set strs = Set.of("Hello", "World");
    Map maps = Map.of("Hello", 1, "World", 2);

2,私有接口方法
jdk8提供了接口的默認方法(default)和靜態方法,打破了之前接口只能定義方法而不能存在行為。jdk9則是允許接口定義私有方法,私有方法可以作為通用方法放在默認方法中調用,不過實際中並無多大用處,至少對我來說。

3,垃圾收集機制
jdk9把G1作為默認的垃圾收集器實現,替換了jdk7和jdk8的默認垃圾收集器實現:Parallel Scavenge(新生代)+Parallel Old(老年代)。

4,I/O流加強
java.io.InputStream 中增加了新的方法來讀取和復制 InputStream 中包含的數據:
readAllBytes:讀取 InputStream 中的所有剩余字節
readNBytes: 從 InputStream 中讀取指定數量的字節到數組中
transferTo:讀取 InputStream 中的全部字節並寫入到指定的 OutputStream 中 

5,JShell工具
jdk9引入了jshell這個交互性工具,讓Java也可以像腳本語言一樣來運行,可以從控制台啟動 jshell ,在 jshell 中直接輸入表達式並查看其執行結果。當需要測試一個方法的運行效果,或是快速的對表達式進行求值時,jshell 都非常實用。舉個例子:

JDK10新特性

1,局部變量類型推斷
局部變量類型推斷可以說是jdk10中最值得注意的特性,這是Java語言開發人員為了簡化Java應用程序的編寫而采取的又一步,舉個例子:
原先我們需要這么定義一個list
List<String> list = new ArrayList<>();
使用局部類型推斷var關鍵詞定義
var list = new ArrayList<String>();
不過局部變量類型推斷僅僅適用在:
有初始化值的局部變量
增強 for 循環中的索引
傳統 for 循環中聲明的局部變量
Oracle 的 Java 團隊申明,以下不支持局部變量類型推斷:
方法參數
構造函數參數
方法返回類型
字段
catch 代碼塊(或任何其他類型的變量聲明)

2,線程本地握手
jdk10將引入一種在線程上執行回調的新方法,因此這將會很方便能停止單個線程而不是停止全部線程或者一個都不停。說實話並不是很懂是什么意思...

3,GC改進和內存管理
jdk10中有2個JEP專門用於改進當前的垃圾收集元素。
第一個垃圾收集器接口是(JEP 304),它將引入一個純凈的垃圾收集器接口,以幫助改進不同垃圾收集器的源代碼隔離。
預定用於Java 10的第二個JEP是針對G1的並行完全GC(JEP 307),其重點在於通過完全GC並行來改善G1最壞情況的等待時間。G1是Java 9中的默認GC,並且此JEP的目標是使G1平行。

 
JDK11新特性

1,字符串加強

// 判斷字符串是否為空白
" ".isBlank(); // true
// 去除首尾空格
" Javastack ".strip(); // "Javastack"
// 去除尾部空格 
" Javastack ".stripTrailing(); 
// 去除首部空格 
" Javastack ".stripLeading(); // "Javastack "
// 復制字符串
"Java".repeat(3); // "JavaJavaJava"
// 行數統計
"A\nB\nC".lines().count(); // 3

 

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

3,用於 Lambda 參數的局部變量語法
用於 Lambda 參數的局部變量語法簡單來說就是支持類型推導:

var x = new A();
for (var x : xs) { ... }
try (var x = ...) { ... } catch ...

 

4,ZGC
從JDK 9開始,JDK使用G1作為默認的垃圾回收器。G1可以說是GC的一個里程碑,G1之前的GC回收,還是基於固定的內存區域,而G1采用了一種“細粒度”的內存管理策略,不在固定的區分內存區域屬於surviors、eden、old,而我們不需要再去對於年輕代使用一種回收策略,老年代使用一種回收策略,取而代之的是一種整體的內存回收策略。這種回收策略在我們當下cpu、內存、服務規模都越來越大的情況下提供了更好的表現,而這一代ZGC更是有了突破性的進步。
從原理上來理解,ZGC可以看做是G1之上更細粒度的內存管理策略。由於內存的不斷分配回收會產生大量的內存碎片空間,因此需要整理策略防止內存空間碎片化,在整理期間需要將對於內存引用的線程邏輯暫停,這個過程被稱為"Stop the world"。只有當整理完成后,線程邏輯才可以繼續運行,一般而言,主要有如下幾種方式優化"Stop the world":
使用多個線程同時回收(並行回收)
回收過程分為多次停頓(增量回收)
在程序運行期間回收,不需要停頓或只停頓很短時間(並發回收)
只回收內存而不整理內存
ZGC主要采用的是並發回收的策略,相較於G1 ZGC最主要的提升是使用Load Barrier技術實現,引用R大對於ZGC的評價:
與標記對象的傳統算法相比,ZGC在指針上做標記,在訪問指針時加入Load Barrier(讀屏障),比如當對象正被GC移動,指針上的顏色就會不對,這個屏障就會先把指針更新為有效地址再返回,也就是,永遠只有單個對象讀取時有概率被減速,而不存在為了保持應用與GC一致而粗暴整體的Stop The World。

JDK12新特性

1,Switch Expressions
這是一個為開發者准備的特性,我們可以利用具體代碼快速了解一下,下面是傳統 statement 形式的 switch 語法:

switch (day) {
  case MONDAY:
  case FRIDAY:
  case SUNDAY:
    System.out.println(6);
    break;
  case TUESDAY:
    System.out.println(7);
    break;
  case THURSDAY:
  case SATURDAY:
    System.out.println(8);
    break;
  case WEDNESDAY:
    System.out.println(9);
    break;
}

如果有編碼經驗,你一定知道,switch 語句如果漏寫了一個 break,那么邏輯往往就跑偏了,這種方式既繁瑣,又容易出錯。如果換成 switch 表達式,Pattern Matching 機制能夠自然地保證只有單一路徑會被執行,請看下面的代碼示例:

switch (day) {
  case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
  case TUESDAY -> System.out.println(7);
  case THURSDAY, SATURDAY -> System.out.println(8);
  case WEDNESDAY -> System.out.println(9);
}

 

更進一步,下面的表達式,為我們提供了優雅地表達特定場合計算邏輯的方式

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};

Switch Expressions 或者說起相關的 Pattern Matching 特性,為我們提供了勾勒出了 Java 語法進化的一個趨勢,將開發者從復雜繁瑣的低層次抽象中逐漸解放出來,以更高層次更優雅的抽象,既降低代碼量,又避免意外編程錯誤的出現,進而提高代碼質量和開發效率。

2,Shenandoah GC
新增了一個名為 Shenandoah 的 GC 算法,通過與正在運行的 Java 線程同時進行 evacuation 工作來減少 GC 暫停時間。使用 Shenandoah 的暫停時間與堆大小無關,這意味着無論堆是 200 MB 還是 200 GB,都將具有相同的暫停時間。

JDK13新特性
JDK13於9月17日正式發布。目前該版本包含的特性已經全部固定,主要包含以下五個:
下面來逐一介紹下這五個重要的特性。
Dynamic CDS Archives
這一特性是在JEP310:Application Class-Data Sharing基礎上擴展而來的,Dynamic CDS Archives中的CDS指的就是Class-Data Sharing。
那么,這個JEP310是個啥東西呢?
我們知道在同一個物理機/虛擬機上啟動多個JVM時,如果每個虛擬機都單獨裝載自己需要的所有類,啟動成本和內存占用是比較高的。所以Java團隊引入了CDS的概念,通過把一些核心類在每個JVM間共享,每個JVM只需要裝載自己的應用類,啟動時間減少了,另外核心類是共享的,所以JVM的內存占用也減少了。
CDS 只能作用於 Boot Class Loader 加載的類,不能作用於 App Class Loader 或者自定義的 Class Loader 加載的類。
在 Java 10 中,則將 CDS 擴展為 AppCDS,顧名思義,AppCDS 不止能夠作用於 Boot Class Loader了,App Class Loader 和自定義的 Class Loader 也都能夠起作用,大大加大了 CDS 的適用范圍。也就說開發自定義的類也可以裝載給多個JVM共享了。
Java 10中包含的JEP310的通過跨不同Java進程共享公共類元數據來減少了內存占用和改進了啟動時間。
但是,JEP310中,使用AppCDS的過程還是比較復雜的,需要有三個步驟:
這一次的JDK 13中的JEP 350 ,在JEP310的基礎上,又做了一些擴展。允許在Java應用程序執行結束時動態歸檔類,歸檔類將包括默認的基礎層 CDS(class data-sharing)存檔中不存在的所有已加載的應用程序類和庫類。
也就是說,在Java 13中再使用AppCDS的時候,就不在需要這么復雜了。
ZGC: Uncommit Unused Memory
在討論這個問題之前,想先問一個問題,JVM的GC釋放的內存會還給操作系統嗎?
GC后的內存如何處置,其實是取決於不同的垃圾回收器的。因為把內存還給OS,意味着要調整JVM的堆大小,這個過程是比較耗費資源的。
在JDK 11中,Java引入了ZGC,這是一款可伸縮的低延遲垃圾收集器,但是當時只是實驗性的。並且,ZGC釋放的內存是不會還給操作系統的。
而在Java 13中,JEP 351再次對ZGC做了增強,本次 ZGC 可以將未使用的堆內存返回給操作系統。之所以引入這個特性,是因為如今有很多場景中內存是比較昂貴的資源,在以下情況中,將內存還給操作系統還是很有必要的:
1、那些需要根據使用量付費的容器
2、應用程序可能長時間處於空閑狀態並與許多其他應用程序共享或競爭資源的環境。
3、應用程序在執行期間可能有非常不同的堆空間需求。例如,啟動期間所需的堆可能大於稍后在穩定狀態執行期間所需的堆。

Reimplement the Legacy Socket API
使用易於維護和調試的更簡單、更現代的實現替換 java.net.Socket 和 java.net.ServerSocket API。
java.net.Socket和java.net.ServerSocket的實現非常古老,這個JEP為它們引入了一個現代的實現。現代實現是Java 13中的默認實現,但是舊的實現還沒有刪除,可以通過設置系統屬性jdk.net.usePlainSocketImpl來使用它們。
運行一個實例化Socket和ServerSocket的類將顯示這個調試輸出。這是默認的(新的).
上面輸出的sun.nio.ch.NioSocketImpl就是新提供的實現。
如果使用舊的實現也是可以的(指定參數jdk.net.usePlainSocketImpl):
上面的結果中,舊的實現java.net.PlainSocketImpl被用到了。
Switch Expressions (Preview)
在JDK 12中引入了Switch表達式作為預覽特性。JEP 354修改了這個特性,它引入了yield語句,用於返回值。這意味着,switch表達式(返回值)應該使用yield, switch語句(不返回值)應該使用break。
在以前,我們想要在switch中返回內容,還是比較麻煩的,一般語法如下:
在JDK13中使用以下語法:
或者
在這之后,switch中就多了一個關鍵字用於跳出switch塊了,那就是yield,他用於返回一個值。和return的區別在於:return會直接跳出當前循環或者方法,而yield只會跳出當前switch塊。
Text Blocks (Preview)
在JDK 12中引入了Raw String Literals特性,但在發布之前就放棄了。這個JEP在引入多行字符串文字(text block)在意義上是類似的。
text block,文本塊,是一個多行字符串文字,它避免了對大多數轉義序列的需要,以可預測的方式自動格式化字符串,並在需要時讓開發人員控制格式。
我們以前從外部copy一段文本串到Java中,會被自動轉義,如有一段以下字符串:
將其復制到Java的字符串中,會展示成以下內容:
使用“”“作為文本塊的開始符合結束符,在其中就可以放置多行的字符串,不需要進行任何轉義。看起來就十分清爽了。
如常見的SQL語句:
看起來就比較直觀,清爽了。

JDK13中包含的5個特性,能夠改變開發者的編碼風格的主要有Text Blocks和Switch Expressions兩個新特性,但是這兩個特性還處於預覽階段。
而且,JDK13並不是LTS(長期支持)版本,如果你正在使用Java 8(LTS)或者Java 11(LTS),暫時可以不必升級到Java 13.

 

原地址:https://blog.csdn.net/papima/article/details/78219001

原地址:https://blog.csdn.net/wlanye/article/details/51954855

原地址:https://www.cnblogs.com/junrong624/p/11596191.html

原地址:https://blog.csdn.net/jlq_diligence/article/details/102775215


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM