Java:Java8新特性

2020-08-11 16:37:27

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqtue7xA-1597134806757)(F:\JianShu_material\Java\图片\Java8新特性\000.png)]

1. Lambda表達式

1.1 Lambda表達式的好處

Lambda 是一個匿名函數,我們可以把 Lambda 表達式理解爲是一段可以傳遞的程式碼(將程式碼像數據一樣進行傳遞)

使用它可以寫出更簡潔、更靈活的程式碼。作爲一種更緊湊的程式碼風格,使Java的語言表達能力得到了提升

Lambda表達式的本質是:作爲函數式介面的範例

1.2 Lambda表達式舉例

//匯入的包有;import org.junit.Test;import java.util.Comparator;

public class LambdaTest1 {
    @Test
    public void test1(){
        Runnable r1 = new Runnable(){
            @Override
            public void run() {
                System.out.println("我愛北京天安門");
            }
        };
        r1.run();

        //Lambad表達式寫法
        Runnable r2 = () -> System.out.println("我愛北京天安門");
        r2.run();
    }
    @Test
    public void test2(){
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        int compare1 = com1.compare(12,21);
        System.out.println(compare1);

        //Lambda表達式
        Comparator<Integer> com2 = (o1,o2) ->Integer.compare(o1,o2);
        int compare2 = com2.compare(12,21);
        System.out.println(compare2);

        //方法參照
        Comparator<Integer> com3 = Integer :: compare;
        int compare3 = com3.compare(12,21);
        System.out.println(compare3);
    }
}

1.3 Lambda表達式的使用

舉例:(o1,o2) -> Integer.compare(o1,o2)

格式

  • ->:Lambda操作符或箭頭操作符
  • ->左邊:Lambda形參列表(其實就是介面中的抽象方法的形參列表)
  • ->右邊:Lambda體(其實就是重寫的抽象方法的方法體)

語法格式

  1. 語法格式一:無參,無返回值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cOZ1sSqJ-1597134806760)(F:\JianShu_material\Java\图片\Java8新特性\001.png)]

  2. 語法格式二:Lambda需要一個參數,但是沒有返回值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zncMEWx0-1597134806773)(F:\JianShu_material\Java\图片\Java8新特性\002.png)]

  3. 語法格式三:數據型別可以省略,因爲可由編譯器推斷得出,稱爲「型別推斷」

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RtKJAheV-1597134806776)(F:\JianShu_material\Java\图片\Java8新特性\003.png)]

  4. 語法格式四:Lambda若只需要一個參數時,參數的小括號可以省略

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2nu1seMM-1597134806778)(F:\JianShu_material\Java\图片\Java8新特性\004.png)]

  5. 語法格式五:Lambda需要兩個或以上的參數,多條執行語句,並且可以有返回值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KSmiFUmA-1597134806780)(F:\JianShu_material\Java\图片\Java8新特性\005.png)]

  6. 語法格式六:當Lambda體只有一條語句時,return與大括號若有,都可以省略

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FhJNTnjw-1597134806783)(F:\JianShu_material\Java\图片\Java8新特性\006.png)]

總的來說,語法格式爲

  • ->左邊:lambda形參列表的參數型別可以省略(型別推斷),如果形參列表只有一個參數,其一對()也可省略
  • ->右邊:lambda體應該使用一對{}包裹,如果lambda體只有一條執行語句(可能是return語句),省略這一對{}和return關鍵字
//匯入的包有:import org.junit.Test;import java.util.Comparator;import java.util.function.Consumer;

public class LambdaTest2 {
    @Test
    public void test1(){
        //語法格式一:無參,無返回值
        Runnable r1 = new Runnable(){
            @Override
            public void run() {
                System.out.println("我愛北京天安門");
            }
        };
        r1.run();

        System.out.println("***************************");

        //Lambad表達式寫法
        Runnable r2 = () -> {
            System.out.println("我愛北京天安門");
        };
        r2.run();
    }
    @Test
    public void test2(){
        //語法格式二:Lambda需要一個參數,但是沒有返回值
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("今天是個好日子");

        System.out.println("***************************");

        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("今天是個好日子");
    }
    @Test
    public void test3(){
        //語法格式三:數據型別可以省略,因爲可由編譯器推斷得出,稱爲「型別推斷」
        Consumer<String> con1 = (String s) -> {
            System.out.println(s);
        };
        con1.accept("今天是個好日子");

        System.out.println("***************************");

        Consumer<String> con2 = (s) -> {
            System.out.println(s);
        };
        con2.accept("今天是個好日子");
    }
    @Test
    public void test4(){
        //語法格式四:Lambda若只需要一個參數時,參數的小括號可以省略
        Consumer<String> con1 = (s) -> {
            System.out.println(s);
        };
        con1.accept("今天是個好日子");

        System.out.println("***************************");

        Consumer<String> con2 = s -> {
            System.out.println(s);
        };
        con2.accept("今天是個好日子");
    }
    @Test
    public void test5(){
        //語法格式五:Lambda需要兩個或以上的參數,多條執行語句,並且可以有返回值
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            }
        };

        System.out.println("*******************");

        Comparator<Integer> com2 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
    }
    @Test
    public void test6(){
        //語法格式六:當Lambda體只有一條語句時,return與大括號若有,都可以省略
        Comparator<Integer> com1 = (o1,o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };

        System.out.println("*******************");

        Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
    }
}

2. 函數式(Functional)介面

  • 只包含一個抽象方法的介面,稱爲函數式介面
  • 你可以通過 Lambda 表達式來建立該介面的物件
  • 我們可以在一個介面上使用@FunctionalInterface註解,這樣做可以檢查它是否是一個函數式介面
  • 以前用匿名實現類表示的現在都可以用Lambda表達式來寫
  • 在java.util.function包下定義了Java 8 的豐富的函數式介面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wnk1DVfl-1597134806786)(F:\JianShu_material\Java\图片\Java8新特性\007.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rwBc3NA3-1597134806788)(F:\JianShu_material\Java\图片\Java8新特性\008.png)]

3. 方法參照與構造器參照

3.1 方法參照

  1. 使用情景:當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法參照

  2. 方法參照本質:其本質爲Lambda表達式,而Lambda表達式是爲函數式介面的範例,所以方法參照,也是函數式介面的範例

  3. 使用格式:類(物件) :: 方法名

  4. 具體使用情況

    • 物件 :: 非靜態方法(實體方法)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4wdYYa4r-1597134806790)(F:\JianShu_material\Java\图片\Java8新特性\009.png)]

    • 類 :: 靜態方法

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhL8dvp6-1597134806791)(F:\JianShu_material\Java\图片\Java8新特性\010.png)]

    • 類 :: 非靜態方法

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hUQoGgX7-1597134806792)(F:\JianShu_material\Java\图片\Java8新特性\011.png)]

  5. 使用要求:要求介面中抽象方法的形參列表和返回值型別,與方法參照的方法的形參列表和返回值型別相同,但當類呼叫非靜態方法時,可以不相同

3.2 構造器參照與陣列參照

構造器參照

和方法參照類似,函數式介面的抽象方法的形參列表和構造器的形參列表一致。抽象方法的返回值型別即爲構造器所屬類的型別

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LqeKLapv-1597134806793)(F:\JianShu_material\Java\图片\Java8新特性\012.png)]

陣列參照

可以把陣列看做是一個特殊的類,則寫法與構造器參照一致

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fqhUSBZS-1597134806795)(F:\JianShu_material\Java\图片\Java8新特性\013.png)]

4. 強大的Stream API

4.1 Stream API概述

什麼是Stream API

  • Stream API ( java.util.stream) 把真正的函數語言程式設計風格引入到Java中
  • Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查詢、過濾和對映數據等操作
  • 使用Stream API 對集合數據進行操作,就類似於使用 SQL 執行的數據庫查詢
  • Stream 和 Collection 集合的區別:Collection 是一種靜態的記憶體數據結構,而 Stream 是有關計算的。前者是主要面向記憶體,儲存在記憶體中,後者主要是面向 CPU,通過 CPU 實現計算
  • 集合講的是數據,Stream講的是計算

Stream的操作步驟

  1. 建立Stream:一個數據源(如:集合、陣列),獲取一個流
  2. 中間操作:一箇中間操作鏈,對數據源的數據進行處理
  3. 終止操作(終端操作) :一旦執行終止操作,就執行中間操作鏈,併產生結果。之後,不會再被使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppHWK8GE-1597134806796)(F:\JianShu_material\Java\图片\Java8新特性\014.png)]

注意事項

  • Stream 自己不會儲存元素
  • Stream 不會改變源物件。相反,他們會返回一個持有結果的新Stream
  • Stream 操作是延遲執行的。這意味着他們會等到需要結果的時候才執行

4.2 Stream的範例化

Stream的範例化有四種方式

  1. 建立Stream方式一:通過集合
  2. 建立Stream方式二:通過陣列
  3. 建立Stream方式三:通過Stream的of()
  4. 建立Stream方式四:建立無限流(不常使用)
//匯入的包有:import org.junit.Test;import java.util.Arrays;import java.util.List;import java.util.stream.IntStream;import java.util.stream.Stream;

public class StreamTest {
    //建立Stream方式一:通過集合
    @Test
    public void test1(){
        //先建立一個集合
        List<Employee> employees = EmployeeData.getEmployees();

        //default Stream<E> stream() : 返回一個順序流
        Stream<Employee> stream = employees.stream();

        //default Stream<E> parallelStream() : 返回一個並行流
        Stream<Employee> parallelStream = employees.parallelStream();
    }

    //建立Stream方式二:通過陣列
    @Test
    public void test2(){
        int[] arr = new int[]{1,2,3,4,5,6};
        //呼叫Array類的static <T> Stream<T> stream(T[] array): 返回一個流
        IntStream stream = Arrays.stream(arr);

        Employee e1 = new Employee(1001,"Tom");
        Employee e2 = new Employee(1002,"Jerry");
        Employee[] arr1 = new Employee[]{e1,e2};
        //常見的陣列可以,自定義的陣列arr1也行
        Stream<Employee> stream1 = Arrays.stream(arr1);
    }

    //建立Stream方式三:通過Stream的of()
    @Test
    public void test3(){
        Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
    }
}

4.3 Stream的中間操作

  1. 篩選與切片

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XgbIIcFb-1597134806798)(F:\JianShu_material\Java\图片\Java8新特性\015.png)]

    //篩選與切片
        @Test
        public void test1(){
            List<Employee> list = EmployeeData.getEmployees();
            //filter(Predicate p):接收Lambda,從流中排除某些元素
            Stream<Employee> stream = list.stream();
            //查詢員工表中薪資大於7000的員工資訊
            stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
    
            //limit(n):截斷流,使其元素不超過給定數量
            list.stream().limit(3).forEach(System.out::println);
    
            //skip(n):跳過元素,返回一個扔掉了前n個元素的流,若流中元素不足n個,則返回一個空流
            list.stream().skip(3).forEach(System.out::println);
    
            //distinct():篩選,通過流所生成元素的hashCode()和equals()去除重複元素
            list.stream().distinct().forEach(System.out::println);
        }
    
  2. 對映

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3QPyGHL-1597134806799)(F:\JianShu_material\Java\图片\Java8新特性\016.png)]

    //對映
        @Test
        public void test2(){
            //接收一個函數作爲參數,該函數會被應用到每個元素上,並將其對映成一個新的元素
            List<String> list = Arrays.asList("aa","bb","cc","dd");
            list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
        }
    
  3. 排序

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uWl5zxvN-1597134806801)(F:\JianShu_material\Java\图片\Java8新特性\017.png)]

    //排序
        @Test
        public void test3(){
            //sorted():自然排序
            List<Integer> list = Arrays.asList(12,45,-8,2,56,34);
            list.stream().sorted().forEach(System.out::println);
            //使用自然排序的條件是:所指定泛型要實現Comparable介面
    
            //sorted(Comparator com):定製排序
            List<Employee> employees = EmployeeData.getEmployees();
            employees.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge()))
                    .forEach(System.out::println);
        }
    

4.4 Stream的終止操作

  1. 匹配與查詢

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZZurfU8-1597134806802)(F:\JianShu_material\Java\图片\Java8新特性\018.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfdPAYIK-1597134806804)(F:\JianShu_material\Java\图片\Java8新特性\021.png)]

    //匹配與查詢
        @Test
        public void test1(){
            List<Employee> employees = EmployeeData.getEmployees();
            //allMatch(Predicate p):檢查是否匹配所有元素:所有員工年齡是否大於18
            boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
            System.out.println(allMatch);
    
            //anyMatch(Predicate p):檢查是否至少匹配一個元素:是否存在員工工資大於1000
            boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 1000);
            System.out.println(anyMatch);
    
            //noneMatch(Predicate p):檢查是否沒有匹配所有元素:是否存在員工姓「雷」
            boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
            System.out.println(noneMatch);
    
            //findFirst():返回第一個元素
            Optional<Employee> employee = employees.stream().findFirst();
            System.out.println(employee);
    
            //findAny():返回當前流中的任意元素
            Optional<Employee> employee1 = employees.stream().findAny();
            System.out.println(employee1);
    
            //count():返迴流中元素總個數
            long count = employees.stream().count();
            System.out.println(count);
    
            //max(Comparator c):返迴流中最大值:返回最高的工資
            Optional<Double> maxSalary = employees.stream().map(e -> e.getSalary()).max(Double::compare);
            System.out.println(maxSalary);
    
            //min(Comparator c):返迴流中最小值:返回最低的工資
            Optional<Employee> minSalary = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
            System.out.println(minSalary);
    
            //forEach(Consumer c):內部迭代
            // 使用 Collection 介面需要使用者去做迭代,稱爲外部迭代
            // 相反,Stream API 使用內部迭代——它幫你把迭代做了
            employees.stream().forEach(System.out::println);
        }
    
  2. 歸約

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NyQtW551-1597134806805)(F:\JianShu_material\Java\图片\Java8新特性\019.png)]

    //規約
        @Test
        public void test2(){
            //reduce(T iden, BinaryOperator b) 可以將流中元素反覆 反復結合起來,得到一個值。返回 T
            //練習:計算1-10的自然數的和
            List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
            Integer sum = list.stream().reduce(0, Integer::sum);
            System.out.println(sum);
    
            //reduce(BinaryOperator b) 可以將流中元素反覆 反復結合起來,得到一個值。返回 Optional<T>
            //練習:計算公司所有員工工資的總和
            List<Employee> employees = EmployeeData.getEmployees();
            Stream<Double> salaryStream = employees.stream().map(Employee::getSalary);
            Optional<Double> sumMoney = salaryStream.reduce((d1, d2) -> d1 + d2);
            System.out.println(sumMoney);
        }
    
  3. 收集

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4CNUPvIh-1597134806806)(F:\JianShu_material\Java\图片\Java8新特性\020.png)]

    //收集
        @Test
     public void test3(){
            //collect(Collector c)將流轉換爲其他形式。接收一個 Collector介面的實現,用於給Stream中元素做彙總的方法
            //練習:查詢工資大於6000的員工,結果返回爲一個List或Set
    
            //結果返回爲一個List
            List<Employee> employees = EmployeeData.getEmployees();
            List<Employee> list = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
            list.forEach(System.out::println);
    
            //結果返回爲一個Set
            Set<Employee> set = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
            list.forEach(System.out::println);
        }
    

5. Optional類

Optional< T > 類(java.util.Optional) 是一個容器類,它可以儲存型別T的值,代表這個值存在。或者僅僅儲存null,表示這個值不存在。

原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。並且可以避免空指針異常

Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測

  1. 建立Optional類物件的方法
    • Optional.of(T t):建立一個 Optional 範例,t必須非空
    • Optional.empty():建立一個空的 Optional 範例
    • Optional.ofNullable(T t):t可以爲null
  2. 判斷Optional容器中是否包含物件
    • boolean isPresent():判斷是否包含物件
  3. 獲取Optional容器的物件
    • T get(): 如果呼叫物件包含值,返回該值,否則拋異常
    • T orElse(T other) **:**如果有值則將其返回,否則返回指定的other物件

舉例說明Optional類

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlRDHwQU-1597134806809)(F:\JianShu_material\Java\图片\Java8新特性\022.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-scjcRtsu-1597134806810)(F:\JianShu_material\Java\图片\Java8新特性\023.png)]