Java 8 新特性
Java 8 (又稱為 jdk 1.8) 是 Java 語言開發的一個主要版本, Oracle 公司于 2014 年 3 月 18 日發布 Java 8 ,它支持函式式編程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等,

在本文中我們將圍繞以下五點學習(常用的)新特性,并用實際的例子說明在什么場景下適合使用,
文章目錄
- Java 8 新特性
- 友情鏈接
- 一.Lambda運算式
- 二.函式式(Funcation)介面
- 三.方法參考與構造器參考
- 方法參考
- 構造器參考
- 陣列參考
- 四.Stream API
- 五.Optional類
友情鏈接
菜鳥教程:點我跳轉Java8新特性菜鳥教程地址
JDK:點我跳轉Jdk8官網下載地址
一.Lambda運算式
Lambda 是一個
匿名函式,我們可以把Lambda運算式理解為是一段可以傳遞的代碼,將代碼像資料一樣進行傳遞,使用它可以寫出更簡潔,更靈活的代碼,作為一種更緊湊的代碼風格,使Java的語言表達能力得到了提升,
語法:
(o1,o2) -> Integer.compare(o1,o2);
->:lambda運算子,也叫箭頭運算子-> 左邊:lambda形參串列 (其實就是介面中的抽象方法的形參串列)-> 右邊:lambda體(其實就是重寫的抽象方法的方法體)
案例:
- 格式:無引數,無回傳值
@Test
public void test(){
//原寫法
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println("無參無回傳值原寫法");
}
};
runnable.run();
//Lambda運算式寫法
Runnable lrunnable =()->{System.out.println("無參無回傳值Lambda運算式寫法");};
lrunnable.run();
}

- 格式:Lambda 需要一個引數,但是沒有回傳值
@Test
public void test(){
//原寫法
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("需要一個引數沒有回傳值,原寫法");
//Lambda運算式寫法
Consumer<String> lconsumer =(String s)->{System.out.println(s);};
lconsumer.accept("需要一個引數沒有回傳值,Lambda運算式寫法");
}

- 格式:資料型別可以省略,因為可由編譯器推斷得出,稱為“型別推斷”
@Test
public void test(){
//原寫法
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("需要一個引數沒有回傳值,原寫法");
//Lambda運算式寫法
Consumer<String> lconsumer =(s)->{System.out.println(s);};
lconsumer.accept("需要一個引數沒有回傳值并且資料型別可以省略, Lambda運算式寫法");
}

- 格式:Lambda 若只需要一個引數時,引數的小括號可以省略
@Test
public void test(){
//原寫法
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("需要一個引數沒有回傳值,原寫法");
//Lambda運算式寫法
Consumer<String> lconsumer = s ->{System.out.println(s);};
lconsumer.accept("需要一個引數沒有回傳值,資料型別可以省略,小括號也可以省略 Lambda運算式寫法");
}

- 格式:Lambda 需要兩個或以上的引數,多條執行陳述句,并且可以有回傳值
@Test
public void test(){
//原寫法
Comparator<Integer> comparator=new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator.compare(1,2));
//Lambda運算式寫法
Comparator<Integer> lcomparator=(o1,o2)->{return o1.compareTo(o2);};
System.out.println(lcomparator.compare(1,2));
}

- 格式:當 Lambda 體只有一條陳述句時, return 與大括號都可以省略
@Test
public void test(){
//原寫法
Comparator<Integer> comparator=new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator.compare(1,2));
//Lambda運算式寫法
Comparator<Integer> lcomparator=(o1,o2)-> o1.compareTo(o2);
System.out.println(lcomparator.compare(1,2));
}

總結:
- lambda運算式的本質:作為函式式介面的實體
-> 左邊: 如果使用泛型,lambda形參串列的引數型別可以省略(型別推斷),如果lambda形參串列只有一個引數()也可以省略,-> 右邊:lambda體使用一對{}包裹,如果lambda體只有一條執行陳述句(可能是return陳述句),可以省略這一對{}和return關鍵字 (如果省略了return關鍵字{}也一定要去掉) ,
二.函式式(Funcation)介面
如果一個介面中,只宣告了一個抽象方法(可以有多個非抽象方法),則此介面就稱為函式式介面,函式式介面可以被隱式轉換為 lambda 運算式,
學習之前先來看一下JDK 1.8 之前已有的函式式介面 Runnable

我們常用的一些介面Callable、Runnable、Comparator等在JDK8中都添加了@FunctionalInterface注解,該注解不是必須的,如果一個介面符合"函式式介面"定義,那么加不加該注解都沒有影響,加上該注解能夠更好地讓編譯器進行檢查,如果撰寫的不是函式式介面,但是加上@FunctionInterface,那么編譯器會報錯,
自定義函式式介面:
/**
* @author mengzhichao
* @create 2021-06-06-18:25
*/
@FunctionalInterface
public interface MyInterface {
void method();
}
總結:在Java8中,Lambda運算式就是一個函式式介面的實體,這就是Lambda運算式和函式式介面的關系,也就是說,只要一個物件是函式式介面的實體,那么該物件就可以用Lambda運算式來表示,
所以以前用匿名實作類表示的現在都可以用Lambda運算式來寫,
JDK 1.8 新增加的函式介面:
| 介面 | 描述 |
|---|---|
| BiConsumer<T,U> | 代表了一個接受兩個輸入引數的操作,并且不回傳任何結果 |
| BiFunction<T,U,R> | 代表了一個接受兩個輸入引數的方法,并且回傳一個結果 |
| BinaryOperator< T > | 代表了一個作用于于兩個同型別運算子的操作,并且回傳了運算子同型別的結果 |
| BiPredicate<T,U> | 代表了一個兩個引數的boolean值方法 |
| BooleanSupplier | 代表了boolean值結果的提供方 |
| Consumer | 代表了接受一個輸入引數并且無回傳的操作 |
| DoubleBinaryOperator | 代表了作用于兩個double值運算子的操作,并且回傳了一個double值的結果, |
| DoubleConsumer | 代表一個接受double值引數的操作,并且不回傳結果, |
| DoubleFunction< R > | 代表接受一個double值引數的方法,并且回傳結果 |
| DoublePredicate | 代表一個擁有double值引數的boolean值方法 |
| DoubleSupplier | 代表一個double值結構的提供方 |
| DoubleToIntFunction | 接受一個double型別輸入,回傳一個int型別結果, |
| DoubleToLongFunction | 接受一個double型別輸入,回傳一個long型別結果 |
| DoubleUnaryOperator | 接受一個引數同為型別double,回傳值型別也為double , |
| Function<T,R> | 接受一個輸入引數,回傳一個結果, |
| IntBinaryOperator | 接受兩個引數同為型別int,回傳值型別也為int , |
| IntConsumer | 接受一個int型別的輸入引數,無回傳值 , |
| IntFunction< R > | 接受一個int型別輸入引數,回傳一個結果 , |
| IntPredicate | 接受一個int輸入引數,回傳一個布林值的結果, |
| IntSupplier | 無引數,回傳一個int型別結果, |
| IntToDoubleFunction | 接受一個int型別輸入,回傳一個double型別結果 , |
| IntToLongFunction | 接受一個int型別輸入,回傳一個long型別結果, |
| IntUnaryOperator | 接受一個引數同為型別int,回傳值型別也為int , |
| LongBinaryOperator | 接受兩個引數同為型別long,回傳值型別也為long, |
| LongConsumer | 接受一個long型別的輸入引數,無回傳值, |
| LongFunction< R > | 接受一個long型別輸入引數,回傳一個結果, |
| LongPredicate | R接受一個long輸入引數,回傳一個布林值型別結果, |
| LongSupplier | 無引數,回傳一個結果long型別的值, |
| LongToDoubleFunction | 接受一個long型別輸入,回傳一個double型別結果, |
| LongToIntFunction | 接受一個long型別輸入,回傳一個int型別結果, |
| LongUnaryOperator | 接受一個引數同為型別long,回傳值型別也為long, |
| ObjDoubleConsumer< T > | 接受一個object型別和一個double型別的輸入引數,無回傳值, |
| ObjIntConsumer< T > | 接受一個object型別和一個int型別的輸入引數,無回傳值, |
| ObjLongConsumer< T > | 接受一個object型別和一個long型別的輸入引數,無回傳值, |
| Predicate< T > | 接受一個輸入引數,回傳一個布林值結果, |
| Supplier< T > | 無引數,回傳一個結果, |
| ToDoubleBiFunction<T,U> | 接受兩個輸入引數,回傳一個double型別結果 |
| ToDoubleFunction< T > | 接受一個輸入引數,回傳一個double型別結果 |
| ToIntBiFunction<T,U> | 接受兩個輸入引數,回傳一個int型別結果, |
| ToIntFunction< T > | 接受一個輸入引數,回傳一個int型別結果, |
| ToLongBiFunction<T,U> | 接受兩個輸入引數,回傳一個long型別結果, |
| ToLongFunction< T > | 接受一個輸入引數,回傳一個long型別結果, |
| UnaryOperator< T > | 接受一個引數為型別T,回傳值型別也為T, |
案例(再此只演示極個別常用的,小伙伴們私底下可以自行練習):
- consumer即消費介面,傳入一個引數,并對其進行相應的操作
public class LambdaTest {
public void happyTime(double money,Consumer<Double> consumer){
consumer.accept(money);
}
@Test
public void test(){
//原寫法
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("原寫法,價格為:"+aDouble);
}
});
//Lambda運算式寫法
happyTime(500,money-> System.out.println("Lambda運算式寫法,價格為:"+money));
}
}

- supplier即供給介面,可以傳入資料,作為一個容器;
public class LambdaTest {
@Test
public void test(){
//原寫法
Supplier<String> supplier=new Supplier<String>() {
@Override
public String get() {
return "原寫法,供給型介面";
}
};
System.out.println(supplier.get());
//Lambda運算式寫法
Supplier<String> lSupplier=()->"lambda寫法,供給型介面";
System.out.println(lSupplier.get());
}
}

- function即方法介面,主要是用作資料型別之間的轉換;
public class LambdaTest {
@Test
public void test(){
//原寫法(給一個int 轉成字串并回傳)
Function<Integer,String> function=new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return String.valueOf(integer);
}
};
System.out.println(function.apply(10).getClass().getName());
//Lambda運算式寫法
Function<Integer,String> lFunction=(integer) -> String.valueOf(integer);
System.out.println(lFunction.apply(20).getClass().getName());
}
}

- predicate即判斷介面,傳入引數,而后回傳判斷的結果true/false;
public class LambdaTest {
//根據給定的規則,過濾集合中的字串,此規則由Predicate的方法決定
public List<String> filterString(List<String> list, Predicate<String> predicate){
List<String> filterList=new ArrayList<>();
for (String s:list){
if (predicate.test(s)){
filterList.add(s);
}
}
return filterList;
}
@Test
public void test(){
//原寫法
List<String> list=Arrays.asList(new String[]{"上海", "香港", "澳門", "曹縣"});
List<String> filterString = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return "曹縣".equals(s) ? true : false;
}
});
System.out.println(filterString);
//Lambda運算式寫法
List<String> lfilterString = filterString(list, s -> "上海".equals(s) ? true : false);
System.out.println(lfilterString);
}
}

三.方法參考與構造器參考
方法參考
當要傳遞給Lambda體的操作,已經有實作的方法了,可以使用方法參考 ! 方法參考可以看作是Lambda運算式深層次的表達,換句話說,
方法參考就是Lambda運算式,也就是函式式介面的一個實體,通過方法名字來指向一個方法,可以認為是Lambda運算式的一個語法糖,
語法:
使用運算子 “::” 將類(或物件)與方法名分割開來,
要求:實作介面的抽象方法的引數和回傳值型別,必須與方法參考的方法的引數串列和回傳值型別保持一致!
案例:
- 格式:物件 : : 非靜態方法
@Test
public void test(){
//Lambda運算式寫法
Consumer<String> consumer = str ->System.out.println(str);
consumer.accept("Lambda運算式");
//方法參考寫法
Consumer<String> fConsumer = System.out::println;
fConsumer.accept("方法參考");
}

- 格式:類 : : 靜態方法
public class LambdaTest {
@Test
public void test(){
//Lambda運算式寫法 比較兩個數大小
Comparator<Integer> comparator =(t1,t2) -> Integer.compare(t1,t2);
System.out.println(comparator.compare(10,20));
//方法參考寫法
Comparator<Integer> fComparator = Integer::compare;
System.out.println(fComparator.compare(20,10));
}
}

- 格式:類 : : 非靜態方法


public class LambdaTest {
@Test
public void test(){
//Lambda運算式寫法
Comparator<String> comparator=(s1,s2) -> s1.compareTo(s2);
System.out.println(comparator.compare("abc","abd"));
//方法參考寫法
Comparator<String> fComparator = String::compareTo;
System.out.println(fComparator.compare("www","www"));
}
}

正常來說
comparator.compare("abc","abd")有兩個引數,而s1.compareTo(s2)只有一個引數并不滿足上述所說的方法參考的要求,但是為什么能用呢?因為這種情況下,第一個引數作為方法的呼叫者出現((s1,s2) -> s1.compareTo(s2)),這種情況下也存在方法參考,只不過這時候我們不是拿具體的物件寫,而是拿它的類寫(Comparator<String> fComparator = String::compareTo;)
構造器參考
呼叫的構造器會根據實作的函式介面的抽象方法的引數串列來確定,如果抽象方法為無參的,則呼叫的構造器也是無參的,
語法:
ClassName :: new
案例
public class Student {
public Student() {
System.out.println("Student 無參構造已執行");
}
public Student(Integer id) {
System.out.println("Student 有參構造,一個引數.為:"+id);
}
public Student(Integer id, String name, char sex) {
this.id = id;
this.name = name;
this.sex = sex;
}
private Integer id;
private String name;
private char sex;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getsex() {
return sex;
}
public void setsex(char sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", sex=" + sex +
'}';
}
}
- 通過無參構造器參考(創建物件)
public class LambdaTest {
@Test
public void test(){
//原始寫法
Supplier<Student> supplier =new Supplier<Student>() {
@Override
public Student get() {
return new Student();
}
};
supplier.get();
//lambda運算式寫法
Supplier<Student> lSupplier =()-> new Student();
lSupplier.get();
//構造器參考寫法
Supplier<Student> gSupplier = Student::new;
gSupplier.get();
}
}

- 通過有參構造器參考(一個引數,創建物件)
@Test
public void test(){
//原始寫法
Function<Integer,Student> function =new Function<Integer, Student>() {
@Override
public Student apply(Integer integer) {
return new Student(integer);
}
};
function.apply(1);
//lambda運算式寫法
Function<Integer,Student> lFunction = id -> new Student(id);
lFunction.apply(2);
//構造器參考寫法
Function<Integer,Student> gFunction = Student::new;
gFunction.apply(3);
}

陣列參考
大家可以把陣列看做是一個特殊的類,寫法與構造器參考一致,
public class LambdaTest {
@Test
public void test(){
//原始寫法
Function<Integer,String[]> function =new Function<Integer, String[]>() {
@Override
public String[] apply(Integer integer) {
return new String[integer];
}
};
System.out.println(Arrays.toString(function.apply(10)));
//lambda運算式寫法
Function<Integer,String[]> lFunction= length -> new String[length];
System.out.println(Arrays.toString(lFunction.apply(20)));
//構造器參考寫法
Function<Integer,String[]> gFunction = String[] :: new;
System.out.println(Arrays.toString(gFunction.apply(30)));
}
}

四.Stream API
Java8中有兩大最為重要的改變,第一個是
Lambda運算式;另外一個則是Stream API,
Stream API(java.util.Stream) 把真正的函式式編程風格引入到Java中,這是目前為止對Java類別庫最好的補充,因為Stream API 可以極大提供Java程式員的生產力,讓程式員寫出高效率,干凈,簡潔的代碼,(Stream 使用一種類似用 SQL 陳述句從資料庫查詢資料的直觀方式來提供一種對 Java 集合運算和表達的高階抽象,)
Strema到底是什么呢?
是資料渠道,用于操作資料源(集合,陣列等) 所生成的元素序列,
“集合將的是資料,Stream講的是計算!”
語法
集合或陣列.stream().過濾().映射().終止操作
Stream 操作的三個步驟
創建 Stream
一個資料源(集合,陣列),獲取一個流中間操作
一個中間操作鏈,對資料源的資料進行處理終止操作(終端操作)
一旦執行終止操作,就執行中間操作鏈,并產生結果,之后,不會再被使用
注意:
- Stream 自已不會存盤元素
- Stream 不會改變源物件,相反,他們會回傳一個持有結果的新Stream
- Stream 操作是延遲執行的,這意味著他們會等到需要結果的時候才執行,
案例(圍繞以上三點進行操作)
首先我們先來了解兩個概念
并行流(parallelStream):多個執行緒同時運行
順序流(stream):使用主執行緒,單執行緒
- Stream 實體化
創建 Stream 方式一:通過集合創建
public class LambdaTest {
@Test
public void test(){
List<Student> students= StudentData.getStudent();
//回傳一個順序流
Stream<Student> stream = students.stream();
//回傳一個并行流
Stream<Student> parallelStream = students.parallelStream();
}
}
創建 Stream 方式二:通過陣列創建(Java8中的Arrays的靜態方法
Stream()可以獲取陣列流)
public class LambdaTest {
@Test
public void test(){
int[] arr =new int[]{1,2,3,4,5};
//通過泛型識明你的型別 放進去的是一個int型別的陣列,回傳的也是int型別的流
IntStream stream = Arrays.stream(arr);
}
}
創建 Stream 方式三:通過Stream的
of()
public class LambdaTest {
@Test
public void test(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
}
}
創建 Stream方式四:創建無限流 (了解)
public class LambdaTest {
@Test
public void test(){
/**
* 例子:遍歷前10個偶數并列印出來
* iterate(): 迭代
* seed:種子
* UnaryOperator:函式式介面
* 注意:無限流不加對應的中間操作會無限的進行迭代 limit(10):前10個
*/
Stream.iterate(0, t -> t+2).limit(10).forEach(System.out::println);
}
}
- Stream 中間操作
多個中間操作可以連接起來形成一個 “流水線” ,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理!而在終止操作時一次全部處理,成為 “惰性求值”,
篩選與切片方法 | 描述 |
|---|---|
| filter(Predicate p) | 接收Lambda,從流中排除某些元素 |
| distinct() | 篩選,通過流所產生元素的 hashCode() 和 equals() 去除重復元素 |
| limit(long maxSize) | 截斷流,使其元素不超過給定數量 |
| skip(long n) | 跳過元素,回傳一個扔掉了前 n 個元素的流,若流中元素不足 n 個,則回傳一個空流,與 limit(n) 互補 |
映射方法 | 描述 |
| map(Funcation f) | 接收一個函式作為引數,該函式會被應用到每個元素上,并將其映射成一個新的元素 |
| mapToDouble(ToDoubleFunction f) | 接收一個函式作為引數,該函式會被應用到每個元素上,產生一個新的 DoubleStream |
| mapToInt(TolintFunction f) | 接收一個函式作為引數,該函式會被應用到每個元素上,產生一個新的IntStream |
| mapToLong(ToLongFunction f) | 接收一個函式作為引數,該函式會被應用到每個元素上,產生一個新的LongStream |
| flatMap(Function f) | 接收一個函式作為引數,將流中的每個值都換成另一個流,然后把所有流連接成一個流 |
排序方法 | 描述 |
| sorted() | 產生一個新流,其中按自然順序排序 |
| sorted(Comparator com) | 產生一個新流,其中按比較器順序排序 |
filter 用法
public class LambdaTest {
@Test
public void test(){
List<Student> students = StudentData.getStudent();
//篩選出性別為男的同學資訊
students.stream().filter(p-> '男' == p.getsex()).forEach(System.out::println);
}
}

distinct 用法 (去重復資料是通過流所生成元素的
hashCode()和equals()所以物體類中需要生成對應的方法)
public class LambdaTest {
@Test
public void test(){
List<Student> students = StudentData.getStudent();
//去掉重復的資料
students.stream().distinct().forEach(System.out::println);
}
}

limt 用法
public class LambdaTest {
@Test
public void test(){
List<Student> students = StudentData.getStudent();
//展示前三個同學的資訊
students.stream().limit(3).forEach(System.out::println);
}
}

skip 用法
public class LambdaTest {
@Test
public void test(){
List<Student> students = StudentData.getStudent();
//顯示除了前三個之外的學生資訊
students.stream().skip(3).forEach(System.out::println);
}
}

map 用法
public class LambdaTest {
@Test
public void test(){
List<Student> students = StudentData.getStudent();
//獲取到男學生的姓名映射到新的集合中并列印
students.stream().filter(student -> '男'==student.getsex()).map(Student::getName).forEach(System.out::println);
}
}

- Stream 終止操作
終止操作 (終端操作) 從流水線生成結果,其結果可以是任何不是流的值,例如:List,Integer,甚至是 void,
流進行了終止操作后,不能再使用!
匹配與查找方法 | 描述 |
|---|---|
| allMatch(Predicate p) | 檢查是否匹配所有元素 |
| anyMatch(Predicate p) | 檢查是否至少匹配一個元素 |
| noneMatch(Predicate p) | 檢查是否沒有匹配所有元素 |
| findFirst() | 回傳第一個元素 |
| findAny() | 回傳當前流中的任意元素 |
| count() | 回傳流中元素的總個數 |
| max(Comparator c) | 回傳流中最大值 |
| min(Comparator c) | 回傳流中最小值 |
| forEach(Consumer c) | 內部迭代 |
規約 方法 | 描述 |
| reduce(T iden,BlnaryOperator b) | 可以將流中元素反復結合起來,得到一個值,回傳 T |
| reduce(BlnaryOperator b) | 可以將流中元素反復結合起來,得到一個值,回傳 Optional < T > |
收集 方法 | 描述 |
| collect(Collector c) | 將流轉換為其他形式,接收一個 Collector介面的實作,用于給Stream中元素做匯總的方法 |
allMatch 方法 (allMatch方法需要所有匹配才會回傳true)
public class LambdaTest {
@Test
public void test(){
List<Student> students = StudentData.getStudent();
//所有學生是不是都是男生
boolean rst = students.stream().allMatch(student -> '男' == student.getsex());
System.out.println(rst);
}
}

anyMatch 方法 (anyMatch方法只需要一個元素匹配就可以回傳true)
public class LambdaTest {
@Test
public void test(){
List<Student> students = StudentData.getStudent();
//是否存在人妖
boolean rst = students.stream().anyMatch(student -> '妖' == student.getsex());
System.out.println(rst);
}
}

reduce 方法
public class LambdaTest {
@Test
public void test(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//計算 1-10 的自然數的和
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
}
}

public class LambdaTest {
@Test
public void test(){
List<Student> students=StudentData.getStudent();
//學生id的總和
Optional<Integer> idSum = students.stream().map(Student::getId).reduce(Integer::sum);
System.out.println(idSum);
}
}

collect 方法 (Collector 物件也有很多可選方法)
public class LambdaTest {
@Test
public void test(){
List<Student> students=StudentData.getStudent();
//查找性別為女的學生并回傳一個 list集合
List<Student> list = students.stream().filter(student -> '女' == student.getsex()).collect(Collectors.toList());
System.out.println(list);
}
}

在此只演示個別的方法,小伙伴們私下可自行練習 (^ - ^) !
五.Optional類
Optional<T>類可以優雅的處理空指標例外,它可以保存T的值,代表這個值存在,或者僅僅保存null,表示這個值不存在,原來用null表示一個值不存在,現在Optional可以更好的表達這個概念,并且可以避免空指標例外,
Optional提供了很多方法,這樣我們就不用顯式進行空值檢測,
【創建實體物件】
創建Optional類物件方法 | 描述 |
|---|---|
| of(T t) | 創建一個Optional實體 t 必須非空 |
| empty() | 創建一個空的 Optional 實體 |
| ofNullable(T t) | t 可以為 null |
通過 of() 方法創建物件,school 不能為空,為空則拋出控制值例外
public class LambdaTest {
@Test
public void test() {
School school=new School();
Optional<School> optionalSchool = Optional.of(school);
}
}

public class LambdaTest {
@Test
public void test() {
School school=new School();
school=null;
Optional<School> optionalSchool = Optional.of(school);
}
}

通過 ofNullable(school) 創建物件,school 可以為空
public class LambdaTest {
@Test
public void test() {
School school=new School();
school=null;
Optional<School> optionalSchool = Optional.ofNullable(school);
System.out.println(optionalSchool);
}
}

【對容器中物件進行操作】
判斷Optional容器中是否包含物件方法 | 描述 |
|---|---|
| boolean isPresent() | 判斷是否包含物件 |
| boolean ifPresent(Consumer<? super T> consumer) | 如果有值,就執行Consumer介面的實作代碼,并且該值會作為引數傳給它 |
Optional物件的 isPresent() 方法使用
public class LambdaTest {
@Test
public void test() {
School school=new School();
school=null;
Optional<School> optionalSchool = Optional.ofNullable(school);
System.out.println(optionalSchool.isPresent());
}
}

【獲取容器的物件】
獲取Optional容器的物件方法 | 描述 |
|---|---|
| T get() | 如果呼叫物件包含值,回傳該值,否則拋例外 |
| T orElse(T other) | 如果有值則將其回傳,否則回傳指定的other物件 |
| T orElseGet(Supplier<? extends T> other) | 如果有值將其回傳,否則回傳由Supplier介面實作提供的物件 |
| T orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果有值將其回傳,否則回傳由Supplier介面實作提供的例外 |
orElse 方法
public class OptionalTest {
//內部類
public class Student{
public Student() {
}
public Student(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private Integer id;
private String name;
}
@Test
public void test(){
String name = getStudentName(new Student(1, "小五"));
System.out.println(name);
}
private String getStudentName(Student student){
//ofNullable()可以接收student為null
Optional<Student> optional = Optional.ofNullable(student);
//如果當前的optional內部封裝的student不為空,則回傳內部的studetn如果為空,則回傳.orElse(new Student(1, "張三"))方法中的引數
return optional.orElse(new Student(1, "張三")).getName();
}
}

public class OptionalTest {
//內部類
public class Student{
public Student() {
}
public Student(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private Integer id;
private String name;
}
@Test
public void test(){
String name = getStudentName(null);
System.out.println(name);
}
private String getStudentName(Student student){
//ofNullable()可以接收student為null
Optional<Student> optional = Optional.ofNullable(student);
//如果當前的optional內部封裝的student不為空,則回傳內部的studetn如果為空,則回傳.orElse(new Student(1, "張三"))方法中的引數
return optional.orElse(new Student(1, "張三")).getName();
}
}

如果明確知道資料就是非空可以搭配使用:
of()+get(),
如果要避免空指標的話可以搭配使用:ofNullable()+orElse(),
總體來說Java8大部分都是語法相關,部分方法沒有演示,看完之后一定要多練習,熟能生巧,
最后別忘了一件三連加關注!😊 
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292487.html
標籤:java

