Java版本更新歷史(ing)

歷史版本特性

JDK Version 1.0

開發代號爲Oak(橡樹),於1996-01-23發行.javascript

 

JDK Version 1.1

於1997-02-19發行.html

引入的新特性包括:java

引入JDBC(Java DataBase Connectivity);git

支持內部類;正則表達式

引入Java Bean;c#

引入RMI(Remote Method Invocation)api

引入反射(僅用於內省)數組

 

J2SE Version 1.2

開發代號爲Playground(操場),於1998-12-08發行.安全

引入的新特性包括:服務器

引入集合框架;

對字符串常量作內存映射;

引入JIT(Just In Time)編譯器

引入對打包的Java文件進行數字簽名;

引入控制受權訪問系統資源的策略工具;

引入JFC(Java Foundation Classes), 包括Swing 1.0, 拖放和Java2D類庫;

引入Java插件;

在JDBC中引入可滾動結果集,BLOB,CLOB,批量更新和用戶自定義類型;

在Applet中添加聲音支持.

 

J2SE Version 1.3

開發代號爲Kestrel(紅隼),於2000-05-08發行.

引入的新特性包括:

引入Java Sound API;

jar文件索引;

對Java的各個方面都作了大量優化和加強.

 

J2SE Version 1.4

開發代號Merlin(隼),於2004-02-06(首次在JCP下發行).

引入的新特性包括:

XML處理;

Java打印服務;

引入Logging API;

引入Java Web Start;

引入JDBC 3.0 API;

引入斷言;

引入Preferences API;

引入鏈式異常處理;

支持IPV6;

支持正則表達式;

引入Imgae I/O API.

 

Java Version SE 5.0

開發代號爲Tiger(老虎),於2004-09-30發行.

引入的新特性包括:

引入泛型;

加強循環,可使用迭代方式;

自動裝箱與自動拆箱;

類型安全的枚舉;

可變參數;

靜態引入;

元數據(註解);

引入Instrumentation

 

Java Version SE 6

開發代號爲Mustang(野馬),於2006-12-11發行.

引入的新特性包括:

支持腳本語言

引入JDBC 4.0API;

引入Java Compiler API;

可插拔註解;

增長對Native PKI(Public Key Infrastructure), Java GSS(Generic Security Service),Kerberos和LDAP(Lightweight Directory Access Protocol)支持

繼承Web Services;

 

Java Version SE 7

開發代號是Dolphin(海豚),於2011-07-28發行.

引入的新特性包括:

switch語句塊中容許以字符串做爲分支條件;

在建立泛型對象時應用類型推斷;

在一個語句塊中捕獲多種異常;

支持動態語言;

支持try-with-resources(在一個語句塊中捕獲多種異常)();

<參考: http://blog.csdn.net/hengyunabc/article/details/18459463>

引入Java NIO.2開發包;

數值類型能夠用二進制字符串表示,而且能夠在字符串表示中添加下劃線;

鑽石型語法(在建立泛型對象時應用類型推斷);

null值得自動處理.

 

Java Version SE 8

引入Lambda 表達式;

管道和流;

新的日期和時間 API;

默認的方法;

類型註解;

Nashorn javascript引擎;

並行累加器;

並行操做

內存錯誤移除

TLS SNI 服務器名稱標識(Server Name Identification)

  • Lambda Expressions
  • Pipelines and Streams
  • Date and Time API
  • Default Methods
  • Type Annotations
  • Nashhorn JavaScript Engine
  • Concurrent Accumulators
  • Parallel operations
  • PermGen Error Removed
  • TLS SNI

 

特性詳細內容

Java 5

1,泛型(Generics):

  引入泛型以後,容許指定集合裏元素的類型,免去了強制類型轉換,而且能在編譯時刻進行類型檢查的好處.

Parameterized Type做爲範圍和返回值,Generic是vararg,annotation,enumeration,collection的基石.

  A, 類型安全

    拋棄List,Map,使用List<T>,Map<K,V> 給他們添加元素或者使用iterator<T> 遍歷時, 編譯期間就能夠給你檢查出類型錯誤.

  B, 方法參數和返回值加上了Type

    拋棄List,Map,使用List<T>,Map<K,V>

  C, 不須要類型轉換

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

    String str = list.get(1);

  D, 類型通配符"?"

    假設一個打印List<T>中元素的方法printList,咱們但願任何類型T的List<T>均可以被打印;

代碼:

1 public void printList(List<?> list,PrintStream out)throws IOException{
2 for(Iterator<?> i=list.iterator();i.hasNext();){
3 System.out.println(i.next.toString());
4 }
5 
6 }

若是通配符?讓咱們的參數類型過於普遍,咱們能夠把List<?>、Iterator<?> 修改成

      List<? Extends Number>、Iterator<? Extends Number>限制一下它。

 

2, 枚舉類型 Enumeration:

3, 自動裝箱拆箱( 自動類型包裝和解包 ) autoboxing & unboxing:

  簡單地說是類型自動轉換.

  自動裝包: 基本類型自動轉爲包裝類(int ---Integer)

  自動拆包: 包裝類自動轉爲基本類型(Integer ---int)

4, 可變參數varargs(varargs number of arguments)

  參數類型相同時, 把重載函數合併到一塊兒了.

  如: public void test(object... objs){

    for(Object obj:objs){

      System.out.println(obj);

    }

  }

5, Annotations 它是Java中的metadata(註釋)

  A, tiger(代號)終於定義的三種標準annotation

    a, Override

    指出某個method覆蓋了superclass的method當你要覆蓋的方法名拼寫錯時編譯不經過

    b, Deprecated

    指出某個method或element類型的使用是被阻止的,子類將不能覆蓋該方法

    c, SupressWarnings

    關閉class, method, field, variable初始化的編譯器警告, 好比: List沒有使用Generic, 則@SuppressWarnings("unchecked")去掉編譯期警告.

  B, 自定義annotation

    public @interface Marked{}

  C, meta-annotation

    或者說annotation的annotation

    四種標準的meta-annotation所有定義在java.lang.annotation包中:

    a, Target

    指定所定義的annotation能夠用在哪些程序單元上

    若是Target沒有指定, 則表示該annotation可使用在任意程序單元上

代碼:

 

 1  @Retention(RetentionPolicy.RUNTIME)  
 2  @Target({ ElementType.FIELD, ElementType.METHOD })  
 3  public @interface RejectEmpty {  
 4     /** hint title used in error message */  
 5   String value() default "";  
 6  }  
 7   
 8  @Retention(RetentionPolicy.RUNTIME)  
 9  @Target( { ElementType.FIELD, ElementType.METHOD })  
10  public @interface AcceptInt {  
11   int min() default Integer.MIN_VALUE;  
12  int max() default Integer.MAX_VALUE;  
13    String hint() default "";  
14  }

 

    b, Retention

    指出Java編譯期如何對待annotation

    annotation能夠被編譯期丟掉,或者在編譯過的class文件中

    在annotation被保留時,他也指定是否會在JVM加載class時讀取該annotation

代碼:

1 @Retention(RetentionPolicy.SOURCE)  // Annotation會被編譯期丟棄    
2 public @interface TODO1 {}    
3  @Retention(RetentionPolicy.CLASS)   // Annotation保留在class文件中,但會被JVM忽略    
4  public @interface TODO2 {}    
5 @Retention(RetentionPolicy.RUNTIME) // Annotation保留在class文件中且會被JVM讀取    
6  public @interface TODO3 {}

    c,Documented

    指出被定義的annotation被視爲所熟悉的程序單元的公開API之一

    被@Documented標註的annotation會在javadoc中顯示,這在annotation對它標註的元素被客戶端使用時有影響時起做用

    d,Inherited

    該meta-annotation應用於目標爲class的annotation類型上,被此annotattion標註的class會自動繼承父類的annotation

 

 

  D, Annotation的反射

    咱們發現java.lang.Class有許多與Annotation的反射相關的方法,如getAnnotations, isAnnotationpresent 咱們能夠利用Annotation反射來作許多事情, 好比自定義Annotation來作Model對象驗證

代碼:

 

 1      @Retention(RetentionPolicy.RUNTIME)    
 2      @Target({ ElementType.FIELD, ElementType.METHOD })    
 3     public @interface RejectEmpty {    
 4        /** hint title used in error message */    
 5       String value() default "";    
 6      }    
 7          
 8      @Retention(RetentionPolicy.RUNTIME)    
 9     @Target( { ElementType.FIELD, ElementType.METHOD })    
10      public @interface AcceptInt {    
11          int min() default Integer.MIN_VALUE;    
12         int max() default Integer.MAX_VALUE;    
13      String hint() default "";    
14     }    

 

使用@RejectEmpty和@AcceptInt標註咱們的Model的field, 而後利用反射來作Model驗證

 

6,新的迭代語句 (for(int n:numbers))

7, 靜態導入( import satic )

8, 新的格式化方法( java.util.Formatter )

formatter.format('"Remaining account balance:$%.2f",balance');

9,新的線程模型和併發庫Thread Framework

 HashMap的替代者ConcurrentHashMap和ArrayList的替代者CopyOnWriteArrayList 在大併發量讀取時採用java.util.concurrent包裏的一些類會讓你們滿意BlockingQueue、Callable、Executor、Semaphore...

 

Java 6

一、引入了一個支持腳本引擎的新框架

二、UI的加強

三、對WebService支持的加強(JAX-WS2.0和JAXB2.0)

四、一系列新的安全相關的加強

五、JDBC4.0

六、Compiler API

七、通用的Annotations支持

 

Java 7

 1,switch中可使用字串了

 

 1     String s = "test";   
 2     switch (s) {   
 3     case "test" :   
 4     System.out.println("test");   
 5     case "test1" :   
 6     System.out.println("test1");   
 7     break ;   
 8     default :   
 9     System.out.println("break");   
10     break ;   
11     }  

 

2.運用List<String> tempList = new ArrayList<>(); 即泛型實例化類型自動推斷

3.語法上支持集合,而不必定是數組

final List<Integer> piDigits = [1,2.3,4,5];

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

1 File System.getJavaIoTempDir() // IO臨時文件夾
2 File System.getJavaHomeDir() // JRE的安裝目錄
3 File System.getUserHomeDie() // 當前用戶目錄
4 File System.getUserDie() // 啓動java進程時所在的目錄5

 

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

1     Boolean Booleans.negate(Boolean booleanObj)  
2     True => False , False => True, Null => Null  
3     boolean Booleans.and(boolean[] array)  
4     boolean Booleans.or(boolean[] array)  
5     boolean Booleans.xor(boolean[] array)  
6     boolean Booleans.and(Boolean[] array)  
7     boolean Booleans.or(Boolean[] array)  
8     boolean Booleans.xor(Boolean[] array)  

6, 兩個char間的equals

1 boolean Character.equalsIgnoreCase(char ch1, char ch2)

7,安全的加減乘除

 1 int Math.safeToInt(long value)  
 2 int Math.safeNegate(int value)  
 3 long Math.safeSubtract(long value1, int value2)  
 4 long Math.safeSubtract(long value1, long value2)  
 5 int Math.safeMultiply(int value1, int value2)  
 6 long Math.safeMultiply(long value1, int value2)  
 7 long Math.safeMultiply(long value1, long value2)  
 8 long Math.safeNegate(long value)  
 9 int Math.safeAdd(int value1, int value2)  
10 long Math.safeAdd(long value1, int value2)  
11 long Math.safeAdd(long value1, long value2)  
12 int Math.safeSubtract(int value1, int value2) 

 

8,map集合支持併發請求, 且能夠寫成Map map = {name:"xxx",age:18};

 

 

Java 8

java8的新特性逐一列出, 並將使用簡單的代碼示例來知道你如何使用默認接口方法, lambda表達式, 方法引用以及多重Annotation, 以後你將會學到最新的API上的改進,好比流, 函數式接口 Map以及全新的日期API

一, 接口的默認方法

Java 8容許咱們給接口添加一個非抽象的方法實現, 只須要使用default關鍵字便可, 這個特徵又叫作擴展方法, 示例以下:

1 interface Formula{
2  double calculate(int a);
3  default double sqrt(int a){
4   return Math.sqrt(a);
5 }      
6 }

Formula接口在擁有calculate方法以外同時還定義了sqrt方法,實現了Formula接口的子類只須要實現一個calculate方法,默認方法sqrt將在子類上能夠直接使用

1 Formula formula = new Formula() {  
2     @Override  
3     public double calculate(int a) {  
4         return sqrt(a * 100);  
5     }  
6 };    
7  formula.calculate(100);     // 100.0  
8 formula.sqrt(16);           // 4.0 

文中的formula被實現爲一個匿名類的實例,該代碼很是容易理解,6行代碼實現了計算 sqrt(a * 100)。在下一節中,咱們將會看到實現單方法接口的更簡單的作法。譯者注: 在Java中只有單繼承,若是要讓一個類賦予新的特性,一般是使用接口來實現,在C++中支持多繼承,容許一個子類同時具備多個父類的接口與功能,在其餘 語言中,讓一個類同時具備其餘的可複用代碼的方法叫作mixin。新的Java 8 的這個特新在編譯器實現的角度上來講更加接近Scala的trait。 在C#中也有名爲擴展方法的概念,容許給已存在的類型擴展方法,和Java 8的這個在語義上有差異。

 

二, Lambda表達式

首先看看在老版本的Java中是如何排列字符串的:

1 List<String> names = Arrays.adList("peter","anna","mike","xenia");
2     Collections.sort(names, new Comparator<String>(){
3     @Override
4     public int compare(String a, String b){
5         return b.compareTo(a);
6     }
7 });

只須要給靜態方法 Collections.sort 傳入一個List對象以及一個比較器來按指定順序排列。一般作法都是建立一個匿名的比較器對象而後將其傳遞給sort方法。在Java 8 中你就不必使用這種傳統的匿名對象的方式了,Java 8提供了更簡潔的語法,lambda表達式:

1 Collections.sort(names, (String a, String b) -> {
2     reuturn b.compareTo(a);
3 });

對於函數體只有一行代碼的,你能夠去掉大括號{}以及return關鍵字, 還能夠更短

1 Collections.sort(names, (String a, String b) -> b.compareTo(a)); 

更短

1 Collections.sort(names, (a, b) -> b.compareTo(a));

Java編譯器能夠自動推導出參數類型,因此你能夠不用再寫一次類型。接下來咱們看看lambda表達式還能做出什麼更方便的東西來:

 

 

三, 函數式接口

Lambda表達式是如何在java的類型系統中表示的呢?每個lambda表達式都對應一個類型,一般是接口類型。而「函數式接口」是指僅僅只 包含一個抽象方法的接口,每個該類型的lambda表達式都會被匹配到這個抽象方法。由於 默認方法 不算抽象方法,因此你也能夠給你的函數式接口添加默認方法。

咱們能夠將lambda表達式看成任意只包含一個抽象方法的接口類型,確保你的接口必定達到這個要求,你只須要給你的接口添加 @FunctionalInterface 註解,編譯器若是發現你標註了這個註解的接口有多於一個抽象方法的時候會報錯的

1 @FunctionalInterface  
2 interface Converter<F, T> {  
3     T convert(F from);  
4 }  
5 Converter<String, Integer> converter = (from) -> Integer.valueOf(from);  
6 Integer converted = converter.convert("123");  
7 System.out.println(converted);    // 123

須要注意若是@FunctionalInterface若是沒有指定,上面的代碼也是對的。

譯者注 將lambda表達式映射到一個單方法的接口上,這種作法在Java 8以前就有別的語言實現,好比Rhino JavaScript解 釋器,若是一個函數參數接收一個單方法的接口而你傳遞的是一個function,Rhino 解釋器會自動作一個單接口的實例到function的適配器,典型的應用場景有 org.w3c.dom.events.EventTarget 的addEventListener 第二個參數 EventListener。

 

四, 方法與構造函數引用

前一節中的代碼還能夠經過靜態方法引用來表示:

    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" 

 

接下來看看構造函數是如何使用::關鍵字來引用的,首先咱們定義一個包含多個構造函數的簡單類:

1     class Person {  
2         String firstName;  
3         String lastName;    
4          Person() {}   
5          Person(String firstName, String lastName) {  
6             this.firstName = firstName;  
7             this.lastName = lastName;  
8         }  
9     }   

接下來咱們指定一個用來建立Person對象的對象工廠接口:

1     interface PersonFactory<P extends Person> {  
2         P create(String firstName, String lastName);  
3     }  

這裏咱們使用構造函數引用來將他們關聯起來,而不是實現一個完整的工廠:

1     PersonFactory<Person> personFactory = Person::new;  
2     Person person = personFactory.create("Peter", "Parker");  

咱們只須要使用 Person::new 來獲取Person類構造函數的引用,Java編譯器會自動根據PersonFactory.create方法的簽名來選擇合適的構造函數。

五, Lambda做用域

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

六, 訪問局部變量

咱們能夠直接在lambda表達式中訪問外層的局部變量:

1 final int num =1;
2 Coverter<Integer , String> stringConverter = (x) -> String.valueOf(x+num);
3 stringConverter.conver(2);

可是和匿名對象不一樣的是,這裏的變量num能夠不用聲明爲final,該代碼一樣正確:

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

不過這裏的num必須不可被後面的代碼修改(即隱性的具備final的語義),例以下面的就沒法編譯:

錯誤代碼:

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

在lambda表達式中試圖修改num一樣是不容許的。

 

七, 訪問對象字段與靜態變量

和本地變量不一樣的是,lambda內部對於實例的字段以及靜態變量是便可讀又可寫。該行爲和匿名對象是一致的:

 1     class Lambda4 {  
 2         static int outerStaticNum;  
 3         int outerNum;    
 4          void testScopes() {  
 5             Converter<Integer, String> stringConverter1 = (from) -> {  
 6                 outerNum = 23;  
 7                 return String.valueOf(from);  
 8             };   
 9              Converter<Integer, String> stringConverter2 = (from) -> {  
10                 outerStaticNum = 72;  
11                 return String.valueOf(from);  
12             };  
13         }  
14     }   

8、訪問接口的默認方法


Lambda表達式中是沒法訪問到default修飾的接口方法(java新特性的接口方法)的,如下代碼將沒法編譯:

錯誤代碼:

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

==

點撥:   lambda的使用是創建在函數式接口的基礎上, 函數式接口就是隻須要實現一個方法的接口,其餘的方法可使用default,或者靜態方法實現具體實現

爲何用lambda, 由於書寫方便,簡潔, 不須要寫接口的實現類,而後建立對象,也不須要用匿名類寫一堆代碼.

==

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

Predicate接口

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

 

1 Predicate<String> predicate = (s) -> s.length() > 0;    
2  predicate.test("foo");              // true  
3 predicate.negate().test("foo");     // false   
4  Predicate<Boolean> nonNull = Objects::nonNull;  
5 Predicate<Boolean> isNull = Objects::isNull;   
6  Predicate<String> isEmpty = String::isEmpty;  
7 Predicate<String> isNotEmpty = isEmpty.negate(); 

 

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

 

1 Function 接口
2 Function 接口有一個參數而且返回一個結果,並附帶了一些能夠和其餘函數組合的默認方法(compose, andThen): 

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

1 Supplier<Person> personSupplier = Person::new;  
2 personSupplier.get();   // new Person

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

1     Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);  
2     greeter.accept(new Person("Luke", "Skywalker"));  

Comparator 接口

Comparator 是老Java中的經典接口, Java 8在此之上添加了多種默認方法:

1 Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);    
2  Person p1 = new Person("John", "Doe");  
3 Person p2 = new Person("Alice", "Wonderland");   
4  comparator.compare(p1, p2);             // > 0  
5 comparator.reversed().compare(p1, p2);  // < 0 

Optional 接口

 

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

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

1     Optional<String> optional = Optional.of("bam");    
2      optional.isPresent();           // true  
3     optional.get();                 // "bam"  
4     optional.orElse("fallback");    // "bam"   
5      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:

1 List<String> stringCollection = new ArrayList<>();  
2 stringCollection.add("ddd2");  
3 stringCollection.add("aaa2");  
4 stringCollection.add("bbb1");  
5 stringCollection.add("aaa1");  
6 stringCollection.add("bbb3");  
7 stringCollection.add("ccc");  
8 stringCollection.add("bbb2");  
9 stringCollection.add("ddd1"); 

Java 8擴展了集合類,能夠經過 Collection.stream() 或者 Collection.parallelStream() 來建立一個Stream。下面幾節將詳細解釋經常使用的Stream操做:

Filter 過濾
過濾經過一個predicate接口來過濾並只保留符合條件的元素,該操做屬於中間操做,因此咱們能夠在過濾後的結果來應用其餘Stream操做(好比 forEach)。forEach須要一個函數來對過濾後的元素依次執行。forEach是一個最終操做,因此咱們不能在forEach以後來執行其餘 Stream操做。

1 stringCollection  
2     .stream()  
3     .filter((s) -> s.startsWith("a"))  
4     .forEach(System.out::println);    
5  // "aaa2", "aaa1"

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

1 stringCollection  
2     .stream()  
3     .sorted()  
4     .filter((s) -> s.startsWith("a"))  
5     .forEach(System.out::println);    
6  // "aaa1", "aaa2"  

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

1 System.out.println(stringCollection);  
2 // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

Map 映射
中間操做map會將元素根據指定的Function接口來依次將元素轉成另外的對象,下面的示例展現了將字符串轉換爲大寫字符串。你也能夠經過map來說對象轉換成其餘類型,map返回的Stream類型是根據你map傳遞進去的函數的返回值決定的。

1 stringCollection  
2     .stream()  
3     .map(String::toUpperCase)  
4     .sorted((a, b) -> b.compareTo(a))  
5     .forEach(System.out::println);    
6  // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"  

Match 匹配
Stream提供了多種匹配操做,容許檢測指定的Predicate是否匹配整個Stream。全部的匹配操做都是最終操做,並返回一個boolean類型的值。

 1 boolean anyStartsWithA =   
 2     stringCollection  
 3         .stream()  
 4         .anyMatch((s) -> s.startsWith("a"));    
 5  System.out.println(anyStartsWithA);      // true   
 6  boolean allStartsWithA =   
 7     stringCollection  
 8         .stream()  
 9         .allMatch((s) -> s.startsWith("a"));   
10  System.out.println(allStartsWithA);      // false   
11  boolean noneStartsWithZ =   
12     stringCollection  
13         .stream()  
14         .noneMatch((s) -> s.startsWith("z"));   
15  System.out.println(noneStartsWithZ);      // true 

Count 計數

 

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

1     long startsWithB =   
2         stringCollection  
3             .stream()  
4             .filter((s) -> s.startsWith("b"))  
5             .count();    
6      System.out.println(startsWithB);    // 3   

Reduce 規約
這是一個最終操做,容許經過指定的函數來說stream中的多個元素規約爲一個元素,規越後的結果是經過Optional接口表示的:

1 Optional<String> reduced =  
2     stringCollection  
3         .stream()  
4         .sorted()  
5         .reduce((s1, s2) -> s1 + "#" + s2);    
6  reduced.ifPresent(System.out::println);  
7 // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2" 

並行Streams

 

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

 

下面的例子展現了是如何經過並行Stream來提高性能:

首先咱們建立一個沒有重複元素的大表:

1     int max = 1000000;  
2     List<String> values = new ArrayList<>(max);  
3     for (int i = 0; i < max; i++) {  
4         UUID uuid = UUID.randomUUID();  
5         values.add(uuid.toString());  
6     }  

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

 

串行排序:

1 long t0 = System.nanoTime();    
2  long count = values.stream().sorted().count();  
3 System.out.println(count);   
4  long t1 = System.nanoTime();   
5  long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
6 System.out.println(String.format("sequential sort took: %d ms", millis)); 

// 串行耗時: 899 ms
並行排序:

1 long t0 = System.nanoTime();    
2  long count = values.parallelStream().sorted().count();  
3 System.out.println(count);   
4  long t1 = System.nanoTime();   
5  long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
6 System.out.println(String.format("parallel sort took: %d ms", millis)); 

// 並行排序耗時: 472 ms
上面兩個代碼幾乎是同樣的,可是並行版的快了50%之多,惟一須要作的改動就是將stream()改成parallelStream()。
Map
前面提到過,Map類型不支持stream,不過Map提供了一些新的有用的方法來處理一些平常任務。

1     Map<Integer, String> map = new HashMap<>();    
2      for (int i = 0; i < 10; i++) {  
3         map.putIfAbsent(i, "val" + i);  
4     }   

map.forEach((id, val) -> System.out.println(val));
以上代碼很容易理解, putIfAbsent 不須要咱們作額外的存在性檢查,而forEach則接收一個Consumer接口來對map裏的每個鍵值對進行操做。

下面的例子展現了map上的其餘有用的函數:

1     map.computeIfPresent(3, (num, val) -> val + num);  
2     map.get(3);             // val33    
3      map.computeIfPresent(9, (num, val) -> null);  
4     map.containsKey(9);     // false   
5      map.computeIfAbsent(23, num -> "val" + num);  
6     map.containsKey(23);    // true   
7      map.computeIfAbsent(3, num -> "bam");  
8     map.get(3);             // val33   

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

1 map.remove(3, "val3");  
2 map.get(3);             // val33    
3  map.remove(3, "val33");  
4 map.get(3);             // null 

另一個有用的方法:

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

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

1     map.merge(9, "val9", (value, newValue) -> value.concat(newValue));  
2     map.get(9);             // val9    
3      map.merge(9, "concat", (value, newValue) -> value.concat(newValue));  
4     map.get(9);             // val9concat   

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

 

9、Date API

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

Clock 時鐘

 

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

1     Clock clock = Clock.systemDefaultZone();  
2     long millis = clock.millis();    
3      Instant instant = clock.instant();  
4     Date legacyDate = Date.from(instant);   // legacy java.util.Date   

 

Timezones 時區

 

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

1 System.out.println(ZoneId.getAvailableZoneIds());  
2 // prints all available timezone ids    
3  ZoneId zone1 = ZoneId.of("Europe/Berlin");  
4 ZoneId zone2 = ZoneId.of("Brazil/East");  
5 System.out.println(zone1.getRules());  
6 System.out.println(zone2.getRules());   
7  // ZoneRules[currentStandardOffset=+01:00]  
8 // ZoneRules[currentStandardOffset=-03:00] 

LocalTime 本地時間

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

1 LocalTime now1 = LocalTime.now(zone1);  
2 LocalTime now2 = LocalTime.now(zone2);    
3  System.out.println(now1.isBefore(now2));  // false   
4  long hoursBetween = ChronoUnit.HOURS.between(now1, now2);  
5 long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);   
6  System.out.println(hoursBetween);       // -3  
7 System.out.println(minutesBetween);     // -239 

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

1     LocalTime late = LocalTime.of(23, 59, 59);  
2     System.out.println(late);       // 23:59:59    
3      DateTimeFormatter germanFormatter =  
4         DateTimeFormatter  
5             .ofLocalizedTime(FormatStyle.SHORT)  
6             .withLocale(Locale.GERMAN);   
7      LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);  
8     System.out.println(leetTime);   // 13:37   

LocalDate 本地日期

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

1     LocalDate today = LocalDate.now();  
2     LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);  
3     LocalDate yesterday = tomorrow.minusDays(2);    
4      LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);  
5     DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();   

System.out.println(dayOfWeek);    // FRIDAY
從字符串解析一個LocalDate類型和解析LocalTime同樣簡單:

1 DateTimeFormatter germanFormatter =  
2     DateTimeFormatter  
3         .ofLocalizedDate(FormatStyle.MEDIUM)  
4         .withLocale(Locale.GERMAN);    
5  LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);  
6 System.out.println(xmas);   // 2014-12-24 

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

1     LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);    
2      DayOfWeek dayOfWeek = sylvester.getDayOfWeek();  
3     System.out.println(dayOfWeek);      // WEDNESDAY   
4      Month month = sylvester.getMonth();  
5     System.out.println(month);          // DECEMBER   
6      long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);  
7     System.out.println(minuteOfDay);    // 1439   

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

1 Instant instant = sylvester  
2         .atZone(ZoneId.systemDefault())  
3         .toInstant();    
4  Date legacyDate = Date.from(instant);  
5 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

 

10、Annotation 註解

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

1     @interface Hints {  
2         Hint[] value();  
3     }    
4      @Repeatable(Hints.class)  
5     @interface Hint {  
6         String value();  
7     }   

Java 8容許咱們把同一個類型的註解使用屢次,只須要給該註解標註一下@Repeatable便可。例 1: 使用包裝類當容器來存多個註解(老方法)

1     @Hints({@Hint("hint1"), @Hint("hint2")})  
2     class Person {}  

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

1     @Hint("hint1")  
2     @Hint("hint2")  
3     class Person {}  

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

1 Hint hint = Person.class.getAnnotation(Hint.class);  
2 System.out.println(hint);                   // null    
3  Hints hints1 = Person.class.getAnnotation(Hints.class);  
4 System.out.println(hints1.value().length);  // 2   
5  Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);  
6 System.out.println(hints2.length);          // 2 

即使咱們沒有在Person類上定義@Hints註解,咱們仍是能夠經過 getAnnotation(Hints.class) 來獲取 @Hints註解,更加方便的方法是使用 getAnnotationsByType 能夠直接獲取到全部的@Hint註解。
另外Java 8的註解還增長到兩種新的target上了:

1 @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})  
2 @interface MyAnnotation {} 

 十一, lambda與集合類批處理操做

原來:

1 for(Object o: list) { // 外部迭代
2         System.out.println(o);
3     }

如今:

1 list.forEach(o -> {System.out.println(o);}); //forEach函數實現內部迭代

或者

list.forEach(o -> System.out.println(o)); //forEach函數實現內部迭代

 

(內容收集於網絡)

相關文章
相關標籤/搜索