【進階】Java8新特性的理解與應用

来源:https://www.cnblogs.com/Apluemxa/archive/2022/06/08/16354747.html
-Advertisement-
Play Games

Java 8是Java的一個重大版本,是目前企業中使用最廣泛的一個版本。 它支持函數式編程,新的Stream API 、新的日期 API等一系列新特性。 掌握Java8的新特性已經是java程式員的標配,掌握了它,就可以看懂公司里的代碼、高效率地處理大量集合數據以及消滅“嵌套地獄”等等。 ...


【進階】Java8新特性的理解與應用

前言

Java 8是Java的一個重大版本,是目前企業中使用最廣泛的一個版本。

它支持函數式編程,新的Stream API 、新的日期 API等一系列新特性。

掌握Java8的新特性已經是java程式員的標配,掌握了它,就可以看懂公司里的代碼、高效率地處理大量集合數據以及消滅“嵌套地獄”等等。

目錄

一、Lambda表達式

Lambda表達式是java8最重要的新特性之一,與Stream API一起成為JDK1.8最主要的更新內容。

Lambda是一個匿名函數(表達式),可以將Lambda表達式理解為是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。

這樣可以寫出更簡潔、更靈活的代碼。同時作為一種更緊湊的代碼風格,使java的語言表達能力得到了提升。

9.1基礎概念

  • 首先,lambda表達式需要函數式介面的支持,lambda表達式的實現是基於函數式介面的。

  • lambda表達式的底層思維還是執行方法(函數),但lambda表達式會使得代碼更簡潔,利於程式員編寫。

  • Java8中引入了一個新的操作符“->”,該操作符成為箭頭操作符或者lambda操作符。該操作符將lambda表達式分為了左側和右側兩部分。

  • 操作符左側:lambda表達式所需的參數列表,具體就是lambda表達式中介面抽象方法的參數列表;

  • 操作符右側:lambda表達式所需執行的功能,即lambda體,也就是介面中抽象方法具體要實現的功能。

9.2語法格式

9.2.1格式一:抽象方法無參數、無返回值
 /**
     *語法格式一:抽象方法無參數、無返回值
     * */
    @Test
    public void test_1(){
        //Runnable介面無參數、無返回值,無參數直接使用() 
        Runnable  r = () -> System.out.println("Hello,Lambda!");
        r.run();
    }
9.2.2格式二:抽象方法有1個參數,無返回值
  /**
     *語法格式二:抽象方法有1個參數,且無返回值
     * */
    @Test
    public void test_2(){
        //Consumer介面是JDK中一個有參、無返回的介面,作用是消費介面傳進來的泛型參數
        Consumer<String> con = (t) -> System.out.println(t);
        //accept()是該介面的抽象方法
        con.accept("Hello Lambda!");
    }

註:如該抽象方法的參數只有1個,則"->"的左側可以省略()不寫。

9.2.3格式三:抽象方法中有多個參數、有返回值,且lambda體中有多條語句
    /**
     * 語法格式三:抽象方法中有多個參數、有返回值,且lambda體中有多條語句
     * */
    @Test
    public void test_3(){
        //如果lambda體中有多條語句,則必須將語句寫在{};中
        Comparator<Integer> com = (x,y) -> {
            System.out.println("函數式介面");
            return Integer.compare(x,y);
        };
    }

註:如該lambda體中只有一條語句,則{};和return都可以省略不寫。

9.2.4lambda表達式中參數列表的數據類型可以省略
    /**
     *語法格式四:lambda表達式中參數列表的數據類型可以省略,JVM可以根絕上下文進行推斷,這個過程稱為”類型推斷“。
     *          本質上來說,由於lambda表達式基於函數式介面來實現,函數式介面中的抽象方法(T t)已經指定了泛型的數據類型。
     **/
    @Test
    public void test_4(){
        //參數列表中的Integer可以省略不寫
        Comparator<Integer> c = (Integer x,Integer y) -> Integer.compare(x,y);
    }

9.3lambda表達式的應用

9.3.1需求1
調用Collections.sort()方法,定製化排序比較兩個員工對象信息(第一比較年齡,年齡相同比較姓名),參數傳遞方式使用lambda表達式的形式。
    //定義員工信息list
    List<User> user_list = Arrays.asList(
            new User("張三",21,"[email protected]"),
            new User("李四",35,"[email protected]")
    );    
    @Test
    public void test_1(){
        //Collections介面,使用lambda表達式
        Collections.sort(user_list,(u1,u2) -> {
            //lambda體中有多條語句
            if (u1.getAge() == u2.getAge()){
                return u1.getName().compareTo(u2.getName());
            }else {
                return -Integer.compare(u1.getAge(), u2.getAge());
            }
        });
        for (User u : user_list){
            System.out.println(u);
        }
    }
9.3.2需求2
 a.聲明一個函數式介面,同時在該介面中聲明一個抽象方法 String getValue(String str);
 b.聲明一個類TestLambda_3,類中編寫成員方法test_2,使用a中定義的介面作為該方法的參數,將一個字元串"lambda"轉換為大寫,並作為方法的返回值;
 c.再將該字元串的第2和第4個索引位置的的字元進行字串截取。
 /**
     * 該成員方法的形參1表示的是需要被操作的字元串,形參2表示的是介面中的操作實現。
     * */
    public String transform(String str, MyPractice mp){
        return mp.getValue(str);
    }

    @Test
    public void test_2(){
        String s = transform("lambda",(str) -> str.toUpperCase());
        System.out.println(s);
        //subString()方法截取規則:從數組下標的方式進行計算,含頭不含尾。
        String ss = transform("lambda",(str -> str.substring(2,5)));
        System.out.println(ss);
    }
9.3.3需求3
a.聲明一個帶2個泛型的函數式介面,其中泛型類型為<T,R>且T為參數,R為返回值,同時在該介面中聲明對應的抽象方法;
b.在類TestLambda_3中聲明一個成員方法calculate()並使用a中的介面作為參數,輸出員工信息。
    /**
     * 該成員方法中形參1表示的是需要被操作的字元串,形參2表示的是介面中的操作實現。
     * */
    public  List<User> calculate(MyPractice_2<List<User>> mp){
        mp.calculateValues();
        return user_list;
    }

    @Test
    public List<User> test_3(){
        List<User> list = calculate(() -> {
            System.out.println(user_list);
            return null;
        });
        return list;
    }

二、函數式編程

在java中(尤其從java8開始),函數式介面的應用是函數式編程的一個典型實現。

基於以上對lambda表達式的認識,我們可以清楚地知道:lambda表達式的實現需要函數式介面的支持。

2.1函數式介面

函數式介面指的是:介面中只有一個抽象方法的介面,稱之為函數式介面。並且可以使用@FunctionnalInterface註解修飾,以此來判斷該介面是否是函數式介面。

在Java8以後,函數式介面中允許存在普通方法(即非抽象方法),使用default進行修飾。

2.2內置4大核心函數式介面

  1. Cosumer消費型介面;

    //抽象方法
    void accept(T t);
    
  2. Supploer< T> 供給型介面;

    //抽象方法
    T get();
    
  3. Function< T> 函數型介面;

    //抽象方法
    R apply(T t);
    
  4. Predicate< T>斷言式介面;

    //抽象方法
    boolean test(T t);
    

三、Stream流 API

Java8最為主要的更新內容是lambda表達式和Stream流API,關於lambda表達式在上面已經介紹過了,下麵就來看看今天的主角——Stream流 API(java.util.stream.*)。

3.1基本概念

  • Stream API是java8中處理集合的關鍵抽象概念,它可以對指定的集合進行操作,如執行非常複雜的查找、過濾和映射數據等操作;
  • 使用Stream API對集合數據進行操作,類似於使用SQL執行資料庫查詢的操作;
  • Stream API在對數據源(集合或數組)進行一系列流水線式的操作後,最終會產生一個新的流。

註意

  1. Stream本身不會存儲元素;
  2. Stream不會改變源對象,相反,Stream流執行完後會返回一個有結果的新Stream;
  3. Stream流的執行具有延遲性,只有當執行流的終止操作時(或者需要某些結果時),Stream才會執行。

簡而言之,Stream API提供了一種高效且易於使用的處理數據的方式。

3.2實現步驟

Stream流的操作可分為3個步驟:創建Stream、中間操作以及終止操作(結果)。

3.2.1步驟一:創建Stream
 //可以通過Collection系列集合提供的stream(),或者parallelStream()
         List<String> list_1 = new ArrayList<>();
         Stream<String> stream_1 = list_1.stream();
//可以通過of()創建Stream的
         Stream<String> stream_2 = Stream.of("aa","bb","cc");
//創建無限流
         Stream<Integer> stream_3 = Stream.iterate(3,(x) -> x+2);
         stream_3.limit(10).forEach(System.out::println);
3.2.2步驟二:中間操作

中間操作主要包括:篩選與切片、映射,查找和排序等。

  1. 篩選與切片、映射
 /**
     * 篩選與切片
     * filter:接收Lambda,從流中排除某些元素;
     * map:接收Lambda,將元素轉換為其它形式或者提取數據源的具體信息;(接收一個函數作為參數,該函數會應用到每個元素上,並將其映射成一個新的元素)
     * limit(n):截斷流,使其元素不超過指定數量,即只取前n個元素;
     * skip(n):跳過元素,返回n個後的元素;
     * distinct:通過流生成元素的hashCode()和equals()去除重覆元素。
     * */
    @Test
    public void test_1(){
         List<Integer> a = Arrays.asList(1,2,3,4,5,6,7,8,9,9,9);
                //創建Stream
                a.stream()
                //中間操作
                .filter(i -> i%2 ==0 || i%3 ==0)
                .limit(7)
                .map(i -> i * i)
                .distinct()
                //終止操作
                .forEach(System.out::println);
    }

打開IDEA的調試模式,並點擊橫排圖標最右側的“Trace Current Stream Chain”就可追蹤到Stream流的一些中間操作,具體如圖3-1所示:

圖3-1
  1. 查找
 /**
     * 查找
     * findFirst:查找流中的第一個元素
     * findAny:查找流中的任意一個元素
     * count:返迴流中元素的總個數
     * */
    @Test
    public void testMatch(){


        //比較過後獲取流中第一個元素,並放入Optional容器中
        Optional<User> op = user_list.stream()
                .sorted((e1,e2) -> -Double.compare(e1.getAge(), e2.getAge()))
                .findFirst();
        System.out.println(op.get());

        //從流中取出任意一個符合條件的元素,並放入Optional容器中
        Optional<User> op_2 = user_list.parallelStream()
                .filter((e) -> e.getStatus().equals(User.Status.STATUS_0))
                .findAny();
        System.out.println(op_2.get());

        //獲取流中元素的個數
        Long count = user_list.stream()
                //filter加以條件限制
                .filter((e) -> e.getAge() > 30)
                .count();
        System.out.println(count);
        
        //獲取最小的年齡(而不是最小年齡員工的信息)
        Optional<Integer> op_3 = user_list.stream()
                .map(User::getAge)
                .min(Integer::compare);
        System.out.println(op_3.get());
    }

對於獲取源數據(如集合)中的具體某個元素,可以使用map()將所需信息提取出來,然後再進行比較操作。過程分析如下圖3-2所示:

圖3-2
  1. 排序
  /**
     * 排序
     * 1、sorted:自然排序(Comparable);
     * 2、sorted(Comparator com):定製排序。
     * */
    @Test
    public void test_1(){
        List<Integer> list = Arrays.asList(564,457,54,43,1,4,65);
        list.stream()
                .sorted()
                .forEach(System.out::println);

        user_list.stream()
                 .sorted((e1,e2) -> {
                     //年齡是否相同
                     if (e1.getAge().equals(e2.getAge())){
                         //年齡相同按照姓名比
                         return e1.getName().compareTo(e2.getName());
                     }else
                         return e1.getAge().compareTo(e2.getAge());
                 })
                 .forEach(System.out::println);
    }

四、時間日期 API

Java8中更新了時間日期相關的API,主要包括時間日期轉換、時間校正器等。

4.1時間日期轉換

在實際開發中的時間日期轉換主要包括Date類型與String的互相轉換、Long類型時間轉換為String、Long類型時間轉換為Date。

4.1.1Date與String的互轉
  • DateTimeFormatter類
 /**
     * 時間格式轉化:
     * 1、獲取當前Date類型時間;
     * 2、將該時間轉化為String類型;
     * 3、返回(輸出)String類型的時間。
     * */
    @Test
    public void test_1(){
        //獲取當前時間
        LocalDateTime ldt = LocalDateTime.now();
        //指定需要轉化的格式
        DateTimeFormatter simpleDateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //將當前時間按照指定格式,轉化為String類型輸出
        String time = simpleDateFormat.format(ldt);
        System.out.println(time);
    }
  • @JsonFormat註解
  /**
     * 該註解將String類型的數據按照指定格式返回,但其數據類型仍然還是String
     * */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private String activityStartTime;
4.1.2Long轉換為String(Date)
  • getTime()方法、Calendar類、SimpleDateFormat類
 /**
     * 時間格式轉化:
     * 1、獲取long類型的時間數(毫秒數);
     * 2、將該時間轉化為String類型;
     * 3、返回(輸出)String類型的時間。
     * */
    @Test
    public void test_2(){
        DateTest dateTest = new DateTest();
        //getTime()方法獲取long類型時間數
        Long ssTime = dateTest.getTestTime().getTime();
        //聲明一個Calendar類的通用對象
        Calendar calendar = Calendar.getInstance();
        //將long類型時間Set為Date類型
        calendar.setTimeInMillis(ssTime);
        System.out.println(calendar);
        //指定需要轉化的格式
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String time = simpleDateFormat.format(calendar.getTime());
        System.out.println(time);
    }

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • vector是一個常被使用的容器,本文對如何使用它進行初步的介紹。 ...
  • mkcert(Windows環境) 1.下載地址:https://github.com/FiloSottile/mkcert/releases 2.選擇版本 3.以管理員身份運行`命令提示符 1) cd C:/ #進入工具存放的目錄下 2) mkcert-v1.4.4-windows-amd64.e ...
  • 在NumPy中,矩陣是 ndarray 的子類,與數學概念中的矩陣一樣,NumPy中的矩陣也是二維的,可以使用 mat 、 matrix 以及 bmat 函數來創建矩陣。 一、創建矩陣 mat 函數創建矩陣時,若輸入已為 matrix 或 ndarray 對象,則不會為它們創建副本。 因此,調用 m ...
  • 一個工作了6年的粉絲和我說, 最近面試感覺越來越難的,基本上都會問技術底層原理,甚至有些還會問到操作系統層面的知識。 我說,現在各個一線大廠有很多優秀的程式員畢業了,再加上市場大環境不好對程式員的需求量也在減少。 如果技術底子不好,確實找工作會很困難。 今天分享的問題是:”new String(“a ...
  • #所有流程 !!!不要修改為搶票,遵守法律法規是每一個中國公民應盡的責任,盜用並造成不良後果自負!!! ''' #可以實現買學生票,兒童票,殘疾票,成人票和所有能選的座位類別。 2022-05-22目前為止還是完全正常運行的,之後網頁改動就不一定了哦!!!!!!12306官網模擬買票:1.輸入賬號密 ...
  • 2 Sentinel 限流熔斷降級 Sentinel 可以簡單的分為 Sentinel 核心庫和 Dashboard。核心庫不依賴 Dashboard,但是結合 Dashboard 可以取得最好的效果。我們先來學習Sentinel 核心庫的使用,後面再學習Dashboard使用。 在我們項目中,用戶 ...
  • 題目描述 密碼要求: 1.長度超過8位 2.包括大小寫字母.數字.其它符號,以上四種至少三種 3.不能有長度大於2的包含公共元素的子串重覆 (註:其他符號不含空格或換行) 數據範圍:輸入的字元串長度滿足 1≤n≤100 輸入描述 一組字元串 輸入描述 如果符合要求輸出:OK,否則輸出NG 代碼和解題 ...
  • 前言 以下僅做相關知識的簡述,更深入的瞭解和學習,請自行查閱資料或留言。 一、Python簡介 Python請查看官網自行瞭解。 Python是一種編程語言,可以讓您更快地工作,並更有效地集成您的系統。 Python is a programming language that lets you w ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...