Java8特性及常用API使用

這篇文章主要介紹Java 8的Lambda表達式和Steam流的使用,以及其他特性。

目錄:

<a name="簡介"></a>

簡介

Java 8帶來的最大的改變的是添加了Lambda(λ)表達式,提供了新的API(稱為"流",Stream)其思路在和使用數(shù)據(jù)庫查詢語言中的思路類似-用更高級的方式表達想要的東西,其他的還有新的日期的API、GC的改進、并發(fā)改進等。
<a name="函數(shù)式編程"></a>

函數(shù)式編程

函數(shù)式編程是一種編程范式,它的主要思想是把函數(shù)過程盡量寫成一系列嵌套的函數(shù)調(diào)用。具有以下幾個特點

  • 函數(shù)是一等公民:函數(shù)和其他數(shù)據(jù)類型一樣,可以賦值給其他變量,也可以作為參數(shù),傳遞給另外一個函數(shù),或者作為返回值返回。

  • 沒有副作用,不修改狀態(tài)。

  • 強調(diào)將計算過程分解成可復用的函數(shù)。
    <a name="Lambda表達式"></a>

Lambda表達式

Lambda表達式?jīng)]有名字,但它有參數(shù)列表、函數(shù)主體、返回類型,可能還有一個可以拋出的異常列表。

使用案例 Lambda示例
布爾表達式 (List<String> list) -> list.isEmpty();
創(chuàng)建對象 () -> new User();
消費一個對象 (User user) -> {System.out.println(user.getName());}
從一個對象中選擇/抽取 (String s) -> s.length();
組合兩個值 (int a, int b) -> a * b;
比較兩個對象 (User user1, User user2) -> user1.getAge().compareTo(user2.getAge());

<a name="什么是函數(shù)式接口"></a>

什么是函數(shù)式接口

Lambda的設計者們?yōu)榱俗尙F(xiàn)有的功能與Lambda表達式良好兼容,于是產(chǎn)生了函數(shù)接口這個概念。函數(shù)式接口僅僅聲明了一個抽象方法的接口,這樣的接口可以隱式轉換為Lambda表達式,不過函數(shù)式接口可以包含默認方法和靜態(tài)方法。如下:

@FunctionalInterface
public interface FunctionInterface {

    void test();

    static void say() {
        System.out.println("hello");
    }

    default void play() {
        System.out.println("play");
    }
}

使用:

FunctionInterface.say();
FunctionInterface functionInterface = () -> System.out.println("這是一個函數(shù)式接口");
functionInterface.test();
functionInterface.play();

輸出:

hello
這是一個函數(shù)式接口
play

<a name="Java8中重要的函數(shù)式接口"></a>

Java8中重要的函數(shù)式接口

下面是Java 8中一些常用的函數(shù)式接口。

接口名 參數(shù) 返回類型 示例
Predicate<T> <T> boolean 判斷是否
Consumer<T> <T> void 輸出一個值
Function<T> <T> R 獲得一個對象的名字
Supplier<T> None T 工廠方法
UnaryOperator<T> T T 邏輯非(!)
BinaryOperator<T> (T, T) T 求兩個數(shù)的乘積(*)

簡單的使用例子:

Predicate<Integer> predicate = x -> x == 1;
Consumer<Integer> consumer = x -> System.out.println(x);
Function<Integer, Integer> function = x -> x = x + 1;
Supplier<Integer> supplier = () -> 1;
UnaryOperator<Integer> unaryOperator = x -> x + 1;
BinaryOperator<Integer> binaryOperator = (x, y) -> x * y;

方法引用

方法引用是Lambda的一種快捷寫法,在某種情況下比Lambda更易讀,在使用方法引用時引用對象方法在分隔符::的前面,方法的名稱在后面。如下

(User user) -> user.getAge() == User::getAge
(str, i) ->  str.substring(i) == String::substring
(String s) -> System.out.println(s) == System.out::println

構建方法引用的方式有三種:

  • 指向靜態(tài)方法的方法引用, 例如:Objects::nonNull
  • 指向任意類型實例方法的方法引用, 例如:String::length
  • 指向現(xiàn)有對象的實例方法的方法引用 , 例如:user::getName

<a name="Stream流"></a>

Stream流

Stream是一個借口繼承了BaseStream接口,BaseStream接口繼承了AutoCloseable接口。它只能被消費一次,如果想要繼續(xù)使用,需要重新創(chuàng)建一個流。如下會拋出異常。

List<String> strs = Arrays.asList("A", "B", "C");
Stream<String> s = strs.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);

內(nèi)部迭代實現(xiàn)機制

外部迭代:首先調(diào)用Iterator方法,產(chǎn)生一個新的Iterator對象,進行控制迭代過程。

User jack = User.of("Jack", 21, "杭州");
User rose = User.of("Rose", 18, "杭州");
List<User> users = Arrays.asList(jack, rose);
for(User user : users) {
    System.out.println(user.getName());
}

內(nèi)部迭代:首先調(diào)用stream方法,它的作用和iterator()方法一樣,不過它返回的是內(nèi)部迭代中相應的接口Stream。這樣做的好處就是在迭代時要進行多次操作時可以不用多次循環(huán),只需要迭代一次就好了。
像filter這種不產(chǎn)生新集合的方法叫做惰性求值方法,像count這樣最終會從Stream產(chǎn)生值的方法叫作及早求值方法。

例子:

User jack = User.of("Jack", 21, "杭州");
User rose = User.of("Rose", 18, "杭州");
List<User> users = Arrays.asList(jack, rose);
users.stream()
     .filter(user -> {
         System.out.println(user.getName());
         return user.getAge() > 20;
     });
System.out.println("------------");
users.stream()
     .filter(user -> {
         System.out.println(user.getName());
         return user.getAge() > 20;
     }).count();

輸出:

------------
Jack 
Rose

結論:只有在需要的時候進行計算可以更好的提示效率
<a name="collect方法"></a>

常用的方法

collect方法

將Stream流轉換為一個集合

#輸入
Stream<String> stream = Stream.of("a", "b", "c");
List<String> strs = stream.collect(Collectors.toList());
strs.forEach(System.out::println);
#輸出
a
b
c

<a name="filter方法"></a>

filter方法

過濾掉流中不符合條件的元素

#輸入
Stream<Integer> nums = Stream.of(18, 19 ,20);
nums.filter(num -> num > 18).forEach(System.out::println);

#輸出
c

<a name="map方法"></a>

map方法

將流中的元素轉換為另外一個元素。

#輸入
Stream<Integer> nums = Stream.of(18, 19 ,20);
map(num -> num + 100).forEach(System.out::println);

#輸出
118
119
120

<a name="flatMap方法"></a>

flatMap方法

將底層的元素全部抽出來放到一起,如下將List中的元素全部抽取出來,流中只包含Integer元素。

List<Integer> nums1 = Arrays.asList(1, 2, 3);
List<Integer> nums2 = Arrays.asList(4, 6);
Stream.of(nums1, nums2).flatMap(num -> num.stream()).forEach(System.out::println);

<a name="reduce"></a>

reduce方法

將Stream流中的數(shù)據(jù)聚合成一個數(shù)據(jù)


圖片來源
#輸入
int ages = Stream.of(1, 2, 34).reduce(0, (a, b) -> a + b);
System.out.println(ages);

#輸出
37

<a name="創(chuàng)建無限流"></a>

創(chuàng)建無限流

Stream的API提供了兩個靜態(tài)方法創(chuàng)建無限流:iterate和generate。由兩個靜態(tài)方法創(chuàng)建的流會根據(jù)給定的函數(shù)按需創(chuàng)建流一般會使用limit限制流的大小。iterate方法會對每個新生成的值都調(diào)用函數(shù),generate方法不會對每個新生成的值應用函數(shù).

// 從0開始,生成10個偶數(shù)。
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
// 生成10個隨機數(shù)。
Stream.generate(Math::random). limit(10).forEach(System.out::println);

<a name="其他方法"></a>

其他方法

  • skip:跳過指定個數(shù)的流。
  • limit:返回不超過給定長度的流。
  • max:找到最大的元素
  • min:找到最小的元素
  • findFirst:找到第一個匹配元素
  • findAny:獲取任意一個元素
  • anyMatch:是否存在一個匹配元素
  • noneMatch:是否全部不匹配
  • allMatch:是否全部匹配
  • sorted:排序
  • distinct:去重

<a name="高效Java8編程"></a>

其他

Optional

Optional是為了減少NullPointException,增加代碼的可讀性。Optional是一個final類型的類

Optional<User> optUser = Optional.of(user);
  • empty:返回一個空的Optional實例。
  • filter:如果滿足提交返回Optional對象,否則返回一個空的Optional對象。
  • flatMap:如果值存在,就使用mapping函數(shù)調(diào)用,返回一個Optional對象,否則返回一個空對象。
  • get:如果值存在返回用Optional封裝返回,否則拋出NoSuchElementException異常
  • ifPresent:如果值存在,就返回該方法的調(diào)用,否則什么也不做。
  • isPresent:如果值存在,就返回true,否則返回false。
  • map:如果值存在,就執(zhí)行提供的mapping函數(shù)調(diào)用。
  • of:如果值不存在就拋出異常,否則返回一個Optional封裝的對象。
  • ofNullable:如果值為空就返回一個空的Optional對象,否則調(diào)用of方法。
  • orElse:如果值存在就返回該值,否則返回默認值。
  • orElseGet:如果值存在就返回該值,否則返回一個函數(shù)接口生成的值。
  • orElseThrow:如果值存在就返回該值,否則拋出一個接口生成的異常。

DateTime

在Java 8 以前需要使用Date和SimpdateFormatter操作時間,而且都不是線程安全的,Date不僅包含日期還包含時間和毫秒數(shù),使用起來非常的困難,而Java 8把日期分成了LocalDate和LocalTime,還有LocalDateTime。

使用LocalDate操作日期

// 獲取當前日期
LocalDate now = LocalDate.now();

// 用靜態(tài)方法創(chuàng)建日期
LocalDate date = LocalDate.of(2017, 5, 1);

// 將String類型轉換為日期類型.注:02不能寫成2,否則會拋出DateTimeParseException
LocalDate endOfFeb = LocalDate.parse("2017-02-28");

// 獲取這個月的第一天的日期
now.with(TemporalAdjusters.firstDayOfMonth())

// 獲取這個月的最后一天
now.with(TemporalAdjusters.lastDayOfMonth())

使用LocalTime操作時間

// 獲取當前時間
LocalTime now = LocalTime.now();

// 舍棄納秒
now.withNano(0);

/*
 * 獲取特定的時間
 * Localtime.MIN 00:00
 * Localtime.MIDNIGHT 00:00
 * Localtime.NOON 12:00
 * LocalTime.MAX 23:59:59.999999999
 */
now.with(LocalTime.MIN);
now.with(LocalTime.MIDNIGHT);
now.with(LocalTime.NOON);
now.with(LocalTime.MAX);

// of可以傳入四個參數(shù)
LocalTime zero = LocalTime.of(23, 59, 59, 999_999_999);

// parse方法可以轉換的格式有 HH:mm:ss.nnnnnnnnn HH:mm:ss HH:mm
LocalTime last = LocalTime.parse("23:59:59.999999999");

// 也可以自定義轉換格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
LocalTime start = LocalTime.parse("00:00:00", formatter);

使用LocalDateTime操作時間

// 獲取當前時間
LocalDateTime now = LocalDateTime.now();
// 獲取當前日期所在的月份、星期等
now.getDayOfMonth();
now.getDayOfWeek()
// 自定義
LocalDateTime last = LocalDateTime.parse("2017-05-09T23:25:58.700");

JDBC映射和新類型關聯(lián)

date -> LocalDate
time -> LocalTime
timestamp -> LocalDateTime

代碼地址

https://github.com/huangbuhuan/java8

<a name="參考鏈接"></a>

參考鏈接

  1. 函數(shù)式編程入門教程
  2. Java 8開發(fā)的4大頂級技巧
  3. Java 8 Optional: How to Use it
  4. Java 8 時間日期庫的20個使用示例
  5. 如何在Java 8中愉快地處理日期和時間
  6. Java 8學習資料匯總
  7. Java 8中Stream詳解
  8. Java 8 API在線版地址
  9. Java 8新特性終極指南
  10. 《Java8實戰(zhàn)》
  11. 《Java8函數(shù)式編程》
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內(nèi)容

  • Java8 in action 沒有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說的函數(shù)式...
    鐵牛很鐵閱讀 1,251評論 1 2
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 第一章 為什么要關心Java 8 使用Stream庫來選擇最佳低級執(zhí)行機制可以避免使用Synchronized(同...
    謝隨安閱讀 1,505評論 0 4
  • Java 8自Java 5(發(fā)行于2004)以來最具革命性的版本。Java 8 為Java語言、編譯器、類庫、開發(fā)...
    誰在烽煙彼岸閱讀 904評論 0 4
  • 你要知道的Java8 匿名內(nèi)部類、函數(shù)式接口、lambda表達式與Stream API都在這里 轉載請注明出處 h...
    WWWWDotPNG閱讀 4,327評論 1 14