Java8知識梳理

来源:https://www.cnblogs.com/Enid/archive/2023/08/29/17666044.html
-Advertisement-
Play Games

### Java 8 的改進 - 速度更快 - 代碼更少(**Lambda表達式**) - 引入強大的**Stream API** - 便於並行 - 最大化減少空指針異常(**Optional**) - **Nashorn**引擎,允許在JVM上運行**js**應用 - **並行流**就是把一個內容 ...


Java 8 的改進

  • 速度更快
  • 代碼更少(Lambda表達式
  • 引入強大的Stream API
  • 便於並行
  • 最大化減少空指針異常(Optional
  • Nashorn引擎,允許在JVM上運行js應用
  • 並行流就是把一個內容分成多個數據塊,並用不同的線程分別處理每個數據塊的流。相比較串列的流,並行的流可以很大程度上提高程式的執行效率。

一、Lambda 表達式

  • Lambda表達式的重要特征
  • 可選用類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。
  • 可選用的參數圓括弧:一個參數無需定義圓括弧,但多個參數需要定義圓括弧。
  • 可選的大括弧:如果主體包含了一個語句,就不需要使用大括弧。
  • 可選的返回關鍵字:如果主體只有一個表達式,返回值則編譯器會自動返回值,大括弧需要指定表達式返回了一個數值。(即return…)
  • Lambda表達式是對象,而不是函數。
  • Lambda表達式基本語法
  1. 舉例:

    (o1,o2) -> Integer.compare(o1,o2);
    
  2. 格式:

  • -> :lambda 操作符 或 箭頭操作符
  • -> 左邊:lambda 形參列表 (其實就是介面中的抽象方法的形參列表)
  • -> 右邊:lambda 體(其實就是重寫的抽象方法的方法體)
  • 舉例
public class Java8Tester {
   public static void main(String args[]){
      Java8Tester tester = new Java8Tester();
        
      // 類型聲明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用類型聲明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括弧中的返回語句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 沒有大括弧及返回語句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));//10 + 5 = 15
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));//10 - 5 = 5
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));//10 x 5 = 50
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));//10 / 5 = 2
        
      // 不用括弧
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括弧
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");//Hello Runoob
      greetService2.sayMessage("Google");//Hello Google
   }
    
   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }
}

  • 變數作用域
  1. lambda 表達式只能引用標記了 final 的外層局部變數,即不能在 lambda 內部修改定義在域外的局部變數,否則會編譯錯誤。
public class Java8Tester {
    public static void main(String args[]) {
        final int num = 1;
        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));//訪問外層的局部變數num
        s.convert(2);  // 輸出結果為 3
    }
 
    public interface Converter<T1, T2> {
        void convert(int i);
    }
}
  1. lambda 表達式的局部變數可以不用聲明為 final,但是必須不可被後面的代碼修改(即隱性的具有 final 的語義
int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//報錯信息:Local variable num defined in an enclosing scope must be final or effectively final
  1. 在 Lambda 表達式當中不允許聲明一個與局部變數同名的參數或者局部變數

    String first = "";  
    Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //編譯會出錯 
    
  • Lambda表達式的本質:作為函數式介面的實例(關於函數式介面的概念在下一點)

二、函數式介面

  • 什麼是函數式介面

​ 如果一個介面中,只聲明瞭一個抽象方法,則此介面就稱為函數式介面。在介面中加上@FunctionalInterface註解可以用於檢驗該介面是否為一個函數式介面。

  • 四大核心函數式介面

image-20200507110931093

public class LambdaTest3 {
    //消費型介面 Consumer<T>     void accept(T t)
    @Test
    public void test1() {
        //未使用Lambda表達式
        Learn("java", new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println("學習什麼? " + s);
            }
        });
        System.out.println("====================");
        //使用Lambda表達
        Learn("html", s -> System.out.println("學習什麼? " + s));

    }

    private void Learn(String s, Consumer<String> stringConsumer) {
        stringConsumer.accept(s);
    }

    //供給型介面 Supplier<T>     T get()
    @Test
    public void test2() {
        //未使用Lambda表達式
        Supplier<String> sp = new Supplier<String>() {
            @Override
            public String get() {
                return new String("我能提供東西");
            }
        };
        System.out.println(sp.get());
        System.out.println("====================");
        //使用Lambda表達
        Supplier<String> sp1 = () -> new String("我能通過lambda提供東西");
        System.out.println(sp1.get());
    }

    //函數型介面 Function<T,R>   R apply(T t)
    @Test
    public void test3() {
        //使用Lambda表達式
        Employee employee = new Employee(1001, "Tom", 45, 10000);

        Function<Employee, String> func1 =e->e.getName();
        System.out.println(func1.apply(employee));
        System.out.println("====================");

        //使用方法引用
        Function<Employee,String> func2 = Employee::getName;
        System.out.println(func2.apply(employee));

    }

    //斷定型介面 Predicate<T>    boolean test(T t)
    @Test
    public void test4() {
        //使用匿名內部類
        Function<Double, Long> func = new Function<Double, Long>() {
            @Override
            public Long apply(Double aDouble) {
                return Math.round(aDouble);
            }
        };
        System.out.println(func.apply(10.5));
        System.out.println("====================");

        //使用Lambda表達式
        Function<Double, Long> func1 = d -> Math.round(d);
        System.out.println(func1.apply(12.3));
        System.out.println("====================");

        //使用方法引用
        Function<Double,Long>func2 = Math::round;
        System.out.println(func2.apply(12.6));

    }
}
  • 其他函數式介面

微信圖片_20230729162030


三、方法引用

  • 方法引用就是Lambda表達式,也就是函數式介面的一個實例,通過方法的名字來指向一個方法。(Lambda表達式深層次的表達)

  • 當要傳遞給Lambda體的操作,已經有實現的方法了,就可以使用方法引用。

  • 4種不同方法的引用

現有一個Car類如下:

@FunctionalInterface
public interface Supplier<T> {
 T get();
}

class Car {
 //Supplier是jdk1.8的介面,這裡和lamda一起使用了
 public static Car create(final Supplier<Car> supplier) {  //靜態方法
     return supplier.get();
 }

 public static void collide(final Car car) {  //靜態方法
     System.out.println("Collided " + car.toString());
 }

 public void follow(final Car another) {  //非靜態方法
     System.out.println("Following the " + another.toString());
 }

 public void repair() {  //非靜態方法
     System.out.println("Repaired " + this.toString());
 }
}
  1. 構造器引用Class::new

    final Car car = Car.create( Car::new );
    final List< Car > cars = Arrays.asList( car );
    
  2. 靜態方法引用Class::static_method

    cars.forEach( Car::collide );
    
  3. 特定類的任意對象的方法引用Class::method

    cars.forEach( Car::repair );
    
  4. 特定對象的方法引用instance::method

    final Car police = Car.create( Car::new );
    cars.forEach( police::follow );
    
  • 數組引用

    public void test4() {
        Function<Integer, String[]> func1 = length -> new String[length];
        String[] arr1 = func1.apply(5);
        System.out.println(Arrays.toString(arr1));
    
        System.out.println("====================");
    
        //使用方法引用
        Function<Integer,String[]> func2 = String[]::new;  //數組引用
        String[] arr2 = func2.apply(10);
        System.out.println(Arrays.toString(arr2));
    }
    

三、Stream API

  • Java8中有兩大最為重要的改變。第一個是Lambda 表達式;另外一個則是Stream API。

  • Stream API ( java.util.stream)把真正的函數式編程風格引入到Java中。這是目前為止對Java類庫最好的補充,因為Stream API可以極大提供Java程式員的生產力,讓程式員寫出高效率、乾凈、簡潔的代碼。

  • Stream 是Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查找、過濾和映射數據等操作。使用Stream API 對集合數據進行操作,就類似於使用SQL 執行的資料庫查詢。也可以使用Stream API 來並行執行操作。簡言之,Stream API 提供了一種高效且易於使用的處理數據的方式。

  • 為什麼要使用Stream API:實際開發中,項目中多數數據源都來自於Mysql,Oracle等。但現在數據源可以更多了,有MongDB,Radis等,而這些NoSQL的數據就需要Java層面去處理。

  • Stream 和Collection 集合的區別:Collection 是一種靜態的記憶體數據結構,而Stream 是有關計算的。前者是主要面向記憶體,存儲在記憶體中,後者主要是面向CPU,通過CPU 實現計算

  • 註意

    1. Stream自己不會存儲元素
    2. Stream不會改變源對象。相反,他們會返回一個持有結果的新Stream
    3. Stream操作是延遲執行的。
  • Stream的操作三個步驟

  1. Stream的實例化:一個數據源(如集合、數組),獲取一個流

    //創建Stream的方式一:通過集合
    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方式二:通過數組
    public void test2(){
        int[] arr = new int[]{1, 2, 3, 4, 5, 6};
        //調用Arrays類的static<T> Stream<T> stream(T[] array):返回一個流
        IntStream stream = Arrays.stream(arr);//int數組
    
        Employee e1 = new Employee(1001, "Tom");
        Employee e2 = new Employee(1002, "Jerry");
        Employee[] arr1 = new Employee[]{e1, e2};
        Stream<Employee> stream2 = Arrays.stream(arr1);//Employee類數組
    }
    
    //創建Stream方式三:通過Stream的of()函數
    public void test3(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);//這裡的1~6不是int類型數據,而是一個包裝類 
    }
    
    //創建Stream方式四:創建無限流
    public void test4(){
        //①迭代
        //public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        //遍歷前10個偶數
        Stream.iterate(0, t->t+2).limit(10).forEach(System.out::println);
    
        //②生成
        //public static<T> Stream<T> generate(Supplier<T> s)
        //輸出10個隨機數
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }
    
    • 順序流與並行流的區別:順序流的集合元素時按存入順序儲存的,取出時按順序取出;並行流的集合元素是並行存儲的,取出時是隨機的。

  1. 一系列的中間操作:一個中間操作鏈,對數據源的數據進行處理。多個中間操作可以連接起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理!而在終止操作時一次性全部處理,稱為“惰性求值”。

    2.1 篩選與切片

    2_1

    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Employee> employeeStream = employees.stream();
    
        //filter(Predicate p)——接收 Lambda , 從流中排除某些元素。
        employeeStream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);//查詢員工表中薪資大於7000的員工信息
        System.out.println();
    
        //limit(n)——截斷流,使其元素不超過給定數量。
        employees.stream().limit(3).forEach(System.out::println);//因為上一條代碼中的Stream已經執行了終止操作,所以不能使用【employeeStream.limit(3).forEach(System.out::println)】,而是要重新創建一個Stream
        System.out.println();
    
        //skip(n) —— 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補
        employees.stream().skip(3).forEach(System.out::println);
        System.out.println();
    
        //distinct()——篩選,通過流所生成元素的 hashCode() 和 equals() 去除重覆元素
        employees.add(new Employee(1010,"劉慶東",56,8000));
        employees.add(new Employee(1010,"劉慶東",56,8000));
        employees.add(new Employee(1010,"劉慶東",56,8000));
        employees.add(new Employee(1010,"劉慶東",56,8000));
    
        employees.stream().distinct().forEach(System.out::println);
    }
    

    2.2 映射

    image-20200507115738168

    public void test2(){
        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        //map(Function f)——接收一個函數作為參數,將元素轉換成其他形式或提取信息,該函數會被應用到每個元素上,並將其映射成一個新的元素。
        list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
        //練習1:獲取員工姓名長度大於3的員工的姓名。
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<String> nameStream = employees.stream().map(Employee::getName);
        nameStream.filter(name -> name.length() >3).forEach(System.out::println);
        System.out.println();
        //練習2:使用map()中間操作實現flatMap()中間操作方法
        Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
        streamStream.forEach(s ->{
            s.forEach(System.out::println);
        });
        System.out.println();
    
        //flatMap(Function f)——接收一個函數作為參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流。
        Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::fromStringToStream);
        characterStream.forEach(System.out::println);
    
        //
    
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 使用 defineStore 創建一個 store, 每個 store 要設置一個唯一 id; ```ts import { defineStore } from 'pinia' import { ref } from 'vue' // useStore 可以是 useUser、useCart ...
  • 通過這個示例,你將更深入地瞭解如何在實際業務中應用Flutter,以及如何運用不同的解決方案和技術來構建高效、穩定的應用。 ...
  • # el-autocomplete核心參數 可以實現非同步的數據拉取,從非同步返回的數據中,選擇需要的結果,並回顯到文本框中。 ## fetch-suggestions 回調列表,非同步的方式獲取數據列表,顯示在列表框中 ## @select 當選中某一項時,會觸發這個方法,將數據獲取到,這時,我們可以將 ...
  • # 核心原理 長鏈接轉為短鏈接的核心原理是: 將短鏈接與原始長鏈接做一個映射,訪問短鏈接的時候,通過重定向的方式轉到長鏈接。 # 應用場景 比如分享功能,查看分享信息的原始鏈接通常是很長的,直接發給用戶,體驗不是很好,這時候就可以將其映射為一個短鏈接再發給用戶。 又比如我們熟知的百度網盤分享文件,雖 ...
  • ##一、定義 **講一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種創建型模式。** ##二、描述 **包含以下四個角色:** ![](https://img2023.cnblogs.com/blog/1780813/202305/1780813-202305 ...
  • 你想成為一名架構師,對嗎?別對我撒謊,我知道你想成為架構師。即使你不想,你還是想成為一名更好的開發者。否則,你就不會花時間閱讀這篇文章。 這種態度值得贊賞。畢竟,我們都希望在自己所從事的領域變得更好,即使不能稱為最好。我在這裡就是為了幫助你實現這一目標。 那麼,你如何成為一名架構師呢?當然是通過學習 ...
  • #### 本文為[李你幹嘛](https://www.cnblogs.com/liniganma)原創,轉載請註明出處:[Pybind11綁定C++抽象類(DLL介面)](https://www.cnblogs.com/liniganma/p/17666063.html) # 摘要 假設我們將DLL ...
  • 數據類型是編程中的重要概念。數據類型指定了變數值的大小和類型。 Go是靜態類型的,這意味著一旦變數類型被定義,它只能存儲該類型的數據。 Go有三種基本數據類型: - bool:表示布爾值,要麼是true,要麼是false。 - 數值型:表示整數類型、浮點數值和複數類型。 - string:表示字元串 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...