【讀書筆記】《寫給大忙人看的Java SE 8》——Java8新特性總結

接口中的默認方法和靜態方法

先考慮一個問題,如何向Java中的集合庫中增長方法?例如在Java 8中向Collection接口中添加了一個forEach方法。html

若是在Java 8以前,對於接口來講,其中的方法必須都爲抽象方法,也就是說接口中不容許有接口的實現,那麼就須要對每一個實現Collection接口的類都須要實現一個forEach方法。java

但這就會形成在給接口添加新方法的同時影響了已有的實現,因此Java設計人員引入了接口默認方法,其目的是爲了解決接口的修改與已有的實現不兼容的問題,接口默認方法能夠做爲庫、框架向前兼容的一種手段。算法

默認方法就像一個普通Java方法,只是方法用default關鍵字修飾。
sql

下面來舉一個簡單的例子express

public interface Person {
    //默認方法
    default String getName(String name) {
        return name;
    }
}
///////////////////////////////////////////////////////////////////////
public class Student implements Person {

}
//////////////////////////////////////////////////////////////////////
public class Test {
    public static void main(String[] args) {
        Person p = new Student();
        String name = p.getName("小李");
        System.out.println(name);
    }
}

咱們定義了一個Person接口,其中getName是一個默認方法。接着編寫一個實現類,能夠從結果中看到,雖然Student是空的,可是仍然能夠實現getName方法。編程

顯然默認接口的出現打破了以前的一些基本規則,使用時要注意幾個問題。api

考慮若是接口中定義了一個默認方法,而另一個父類或者接口中又定義了一個同名的方法,該選擇哪一個?數組

1. 選擇父類中的接口。若是一個父類提供了具體的實現方法,那麼接口中具備相同名稱和參數的默認方法會被忽略。多線程

2. 接口衝突。若是一個父接口提供了一個默認方法,而另外一個接口也提供了具備相同名稱和參數類型的方法(無論該方法是不是默認方法),那麼必須經過覆蓋方法來解決。併發

記住一個原則,就是「類優先」,即當類和接口都有一個同名方法時,只有父類中的方法會起做用。

「類優先」原則能夠保證與Java 7的兼容性。若是你再接口中添加了一個默認方法,它對Java 8之前編寫的代碼不會產生任何影響。

下面來講說靜態方法

靜態方法就像一個普通Java靜態方法,但方法的權限修飾只能是public或者不寫。

默認方法和靜態方法使Java的功能更加豐富。

在Java 8中Collection接口中就添加了四個默認方法,stream()、parallelStream()、forEach()和removeIf()。Comparator接口也增長了許多默認方法和靜態方法。

函數式接口和Lambda表達式

函數式接口(Functional Interface)是隻包含一個方法的抽象接口。

好比Java標準庫中的java.lang.Runnable,java.util.concurrent.Callable就是典型的函數式接口。

在Java 8中經過@FunctionalInterface註解,將一個接口標註爲函數式接口,該接口只能包含一個抽象方法。

@FunctionalInterface註解不是必須的,只要接口只包含一個抽象方法,虛擬機會自動判斷該接口爲函數式接口。

通常建議在接口上使用@FunctionalInterface註解進行聲明,以避免他人錯誤地往接口中添加新方法,若是在你的接口中定義了第二個抽象方法的話,編譯器會報錯。

函數式接口是爲Java 8中的lambda而設計的,lambda表達式的方法體其實就是函數接口的實現。

爲何要使用lambda表達式?

「lambda表達式」是一段能夠傳遞的代碼,由於他能夠被執行一次或屢次。咱們先回顧一下以前在Java中一直使用的類似的代碼塊。

當咱們在一個線程中執行一些邏輯時,一般會將代碼放在一個實現Runnable接口的類的run方法中,以下所示:

new Thread(new Runnable(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++)
                    System.out.println("Without Lambda Expression");
            }}).start();

而後經過建立實例來啓動一個新的線程。run方法內包含了一個新線程中須要執行的代碼。

再來看另外一個例子,若是想利用字符串長度排序而不是默認的字典順序排序,就須要自定義一個實現Comparator接口的類,而後將對象傳遞給sort方法。

class LengthComparator implements Comparator<String> {
    @Override
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
}
Arrays.sort(strings, new LengthComparator());

按鈕回調是另外一個例子。將回調操做放在了一個實現了監聽器接口的類的一個方法中。

JButton button = new JButton("click");

button.addActionListener(new ActionListener() {    
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Without Lambda Expression");
    }
});

這三個例子中,出現了相同方式,一段代碼被傳遞給其餘調用者——一個新線程、是一個排序方法或者是一個按鈕。這段代碼會在稍後被調用。

在Java中傳遞代碼並非很容易,不可能將代碼塊處處傳遞。你不得不構建一個類的對象,由他的某個方法來包含所需的代碼。

而lambda表達式實際上就是代碼塊的傳遞的實現。其語法結構以下:

(parameters) -> expression 或者 (parameters) -> {statements;}

括號裏的參數能夠省略其類型,編譯器會根據上下文來推導參數的類型,你也能夠顯式地指定參數類型,若是沒有參數,括號內能夠爲空。

方法體,若是有多行功能語句用大括號括起來,若是隻有一行功能語句則能夠省略大括號。

new Thread(() -> {
            for (int i = 0; i < 100; i++)
                System.out.println("Lambda Expression");
        }).start();
Comparator<String> c = (s1, s2) -> Integer.compare(s1.length(), s2.length());
button.addActionListener(e -> System.out.println("Lambda Expression"));

能夠看到lambda表達式使代碼變得簡單,代替了匿名內部類。

下面來講一下方法引用,方法引用是lambda表達式的一種簡寫形式。 若是lambda表達式只是調用一個特定的已經存在的方法,則可使用方法引用。

使用「::」操做符將方法名和對象或類的名字分隔開來。如下是四種使用狀況:

  • 對象::實例方法
  • 類::靜態方法
  • 類::實例方法
  • 類::new
Arrays.sort(strings, String::compareToIgnoreCase);
// 等價於
Arrays.sort(strings, (s1, s2) -> s1.compareToIgnoreCase(s2));

上面的代碼就是第三種狀況,對lambda表達式又一次進行了簡化。

Stream API

當處理集合時,一般會迭代全部元素並對其中的每個進行處理。例如,咱們但願統計一個字符串類型數組中,全部長度大於3的元素。

String[] strArr = { "Java8", "new", "feature", "Stream", "API" };
        int count = 0;
        for (String s : strArr) {
            if (s.length() > 3)
                count++;
        }

一般咱們都會使用這段代碼來統計,並無什麼錯誤,只是它很難被並行計算。這也是Java8引入大量操做符的緣由,在Java8中,實現相同功能的操做符以下所示:

long count = Stream.of(strArr).filter(w -> w.length() > 3).count();

stream方法會爲字符串列表生成一個Streamfilter方法會返回只包含字符串長度大於3的一個Stream,而後經過count方法計數。

一個Stream表面上與一個集合很相似,容許你改變和獲取數據,但實際上卻有很大區別:

  1. Stream本身不會存儲元素。元素可能被存儲在底層的集合中,或者根據須要產生出來。
  2. Stream操做符不會改變源對象。相反,他們返回一個持有新結果的Stream。
  3. Stream操做符多是延遲執行的。意思是它們會等到須要結果的時候才執行。

Stream相對於循環操做有更好的可讀性。而且能夠並行計算:

long count = Arrays.asList(strArr).parallelStream().filter(w -> w.length() > 3).count();

只須要把stream方法改爲parallelStream,就可讓Stream去並行執行過濾和統計操做。

Stream遵循「作什麼,而不是怎麼去作」的原則。只須要描述須要作什麼,而不用考慮程序是怎樣實現的。

Stream很像Iterator,單向,只能遍歷一遍。可是Stream能夠只經過一行代碼就實現多線程的並行計算。

當使用Stream時,會有三個階段:

  1. 建立一個Stream。
  2. 在一個或多個步驟中,將初始Stream轉化到另外一個Stream的中間操做
  3. 使用一個終止操做來產生一個結果。該操做會強制他以前的延遲操做當即執行。在這以後,該Stream就不會在被使用了。

從着三個階段來看,對應着三種類型的方法,首先是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();

中間操做包括:map (mapToInt, flatMap 等)、 filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered。

終止操做包括:forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator。

關於Stream的每一個方法如何使用就不展開了,更詳盡的介紹看這篇文章:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

新的日期和時間 API

Java8 引入了一個新的日期和時間API,位於java.time包下。

新的日期和時間API借鑑了Joda Time庫,其做者也爲同一人,但它們並非徹底同樣的,作了不少改進。

下面來講一下幾個經常使用的類。首先是Instant,一個Instant對象表示時間軸上的一個點。

Instant.now()會返回當前的瞬時點(格林威治時間)。Instant.MIN和Instant.MAX分別爲十億年前和十億年後。

以下代碼能夠計算某算法的運行時間:

Instant start = Instant.now();
runAlgorithm();
Instant end = Instant.now();
Duration timeElapsed = Duration.between(start, end);
long millis = timeElapsed.toMillis();

Duration對象表示兩個瞬時點間的時間量。能夠經過不一樣的方法,換算成各類時間單位。

上面所說的絕對時間並不能應用到生活中去,因此新的Java API中提供了兩種人類時間,本地日期/時間帶時區的時間

LocalDate是一個帶有年份、月份和天數的日期。建立他可使用靜態方法now或者of。

LocalDate today = LocalDate.now();
LocalDate myBirthday = LocalDate.of(1994, 03, 15);
// use Enum
myBirthday = LocalDate.of(1994, Month.MARCH, 15);

System.out.println(today); // 2017-10-23
System.out.println(myBirthday); // 1994-03-15

下面是LocalDate中的一些經常使用方法:

LocalTime表示一天中的某個時間,一樣可使用now或者of來建立實例。

LocalTime rightNow = LocalTime.now();
LocalTime bedTime = LocalTime.of(2, 0);
System.out.println(rightNow); // 01:26:17.139
System.out.println(bedTime); // 02:00

LocalDateTime表示一個日期和時間,用法和上面相似。

上面幾種日期時間類都屬於本地時間,下面來講一下帶時區的時間。

ZonedDateTime經過設置時區的id來建立一個帶時區的時間。

ZonedDateTime beijingOlympicOpenning = ZonedDateTime.of(2008, 8, 8, 20, 0, 0, 0, ZoneId.of("Asia/Shanghai"));
System.out.println(beijingOlympicOpenning); // 2008-08-08T20:00+08:00[Asia/Shanghai]

更新後的API一樣加入了新的格式化類DateTimeFormatter。DateTimeFormatter提供了三種格式化方法來打印日期/時間:

  • 預約義的標準格式
String formattered = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(beijingOlympicOpenning);
System.out.println(formattered); // 2008-08-08T20:00:00

DateTimeFormatter類提供了多種預約義的標準格式可供使用。

  • 語言環境相關的格式

標準格式主要用於機器可讀的時間戳。爲了讓人可以讀懂日期和時間,你須要使用語言環境相關的格式。

Java8提供了4種風格,SHORT、MEDIUM、LONG、FULL。

String formattered = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).format(beijingOlympicOpenning);
System.out.println(formattered);    //2008年8月8日 星期五 下午08時00分00秒 CST
  • 自定義的格式

或者你也能夠自定義日期和時間的格式。

String formattered = DateTimeFormatter.ofPattern("E yyyy-MM-dd HH:mm").format(beijingOlympicOpenning);
System.out.println(formattered); // 星期五 2008-08-08 20:00

  下圖中爲一些經常使用的模式元素。

新的API提供了從字符串解析出日期/時間的parse靜態方法和與遺留類(java.util.Date、java.sql.Time和java.txt.DateFormat等)互相轉換的方法。

雜項改進

 Java8在String類中只添加了一個新方法,就是join,該方法實現了字符串的拼接,能夠把它看做split方法的逆操做。

String joined = String.join(".", "www", "cnblogs", "com");
System.out.println(joined); // www.cnblogs.com

數字包裝類提供了BYTES靜態方法,以byte爲單位返回長度。

全部八種包裝類都提供了靜態的hashCode方法。

Short、Integer、Long、Float和Double這5種類型分別提供了了sum、max和min,用來在流操做中做爲聚合函數使用。

集合類和接口中添加的方法:

Java8爲使用流讀取文件行及訪問目錄項提供了一些簡便的方法(Files.linesFiles.list)。同時也提供了進行Base64編碼/解碼的方法。

Java8對GUI編程(JavaFX)、併發等方面也作了改進,本文沒有一一列出。


轉載請註明原文連接:http://www.cnblogs.com/justcooooode/p/7701260.html

參考資料

《寫給大忙人看的Java SE 8》

http://study.163.com/course/introduction/1003856028.htm

https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/

相關文章
相關標籤/搜索