站長資訊網
        最全最豐富的資訊網站

        Java總結分享之反射、枚舉、Lambda表達式

        本篇文章給大家帶來了關于java的相關知識,其中主要介紹了關于反射、枚舉、lambda表達式的相關內容,包括了反射的概述、使用以及優缺點、自定義構造枚舉對象等等內容,下面一起來看一下,希望對大家有幫助。

        Java總結分享之反射、枚舉、Lambda表達式

        程序員必備接口測試調試工具:立即使用
        Apipost = Postman + Swagger + Mock + Jmeter
        Api設計、調試、文檔、自動化測試工具
        后端、前端、測試,同時在線協作,內容實時同步

        推薦學習:《java視頻教程》

        一. 反射

        1. 反射的概述

        • 什么是反射

        Java的反射(reflection)機制是在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意方法和屬性,既然能拿到那么,我們就可以修改部分類型信息;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射(reflection)機制。

        • 反射的基本信息

        Java程序中許多對象在運行時會出現兩種類型:運行時類型(RTTI)和編譯時類型,例如Person p = new Student();這句代碼中p在編譯時類型為Person,運行時類型為Student。程序需要在運行時發現對象和類的真實 信心。而通過使用反射程序就能判斷出該對象和類屬于哪些類。

        • 反射的用途
        1. 在日常的第三方應用開發過程中,經常會遇到某個類的某個成員變量、方法或是屬性是私有的或是只對系統應用開放,這時候就可以利用Java的反射機制通過反射來獲取所需的私有成員或是方法 。
        2. 反射最重要的用途就是開發各種通用框架,比如在spring中,我們將所有的類Bean交給spring容器管理,無論是XML配置Bean還是注解配置,當我們從容器中獲取Bean來依賴注入時,容器會讀取配置,而配置中給的就是類的信息,spring根據這些信息,需要創建那些Bean,spring就動態的創建這些類。

        2. 反射的使用

        2.1 反射常用的類

        類名 用途
        Class類 代表類的實體,在運行的Java應用程序中表示類和接口
        Field類 代表類的成員變量/類的屬性
        Method類 代表類的方法
        Constructor類 代表類的構造方法

        Class代表類的實體,在運行的Java應用程序中表示類和接口 ,Java文件被編譯后,生成了.class文件,JVM此時就要去加載.class文件 ,被編譯后的Java文件,也就是.class文件會被JVM解析為一個對象,這個對象就是 java.lang.Class 。這樣當程序在運行時,每個java文件就最終變成了Class類的一個實例。我們通過Java的反射機制應用到這個實例,就可以去獲得甚至去添加改變Class對象所對應類的屬性和動作, 使得這個類成 為一個動態的類 .

        2.2 通過反射獲取Class對象

        反射獲取對象一共有三種方式:

        • 通過Class類中的通過forName方法。
        • 通過類名.class獲取。
        • 通過使用實例對象調用getclass方法獲取。

        下面我們演示使用三種方式得到的對象是否是同一個對象,我們來獲取相關Student類的類信息對象。

        Student類定義:

        class Student{     //私有屬性name     private String name = "rong";     //公有屬性age     public int age = 18;     //不帶參數的構造方法     public Student(){         System.out.println("Student()");     }     //帶兩個參數的構造方法     private Student(String name,int age) {         this.name = name;         this.age = age;         System.out.println("Student(String,name)");     }      private void eat(){         System.out.println("i am eating");     }      public void sleep(){         System.out.println("i am sleeping");     }      private void function(String str) {         System.out.println("私有方法function被調用:"+str);     }      @Override     public String toString() {         return "Student{" +                 "name='" + name + ''' +                 ", age=" + age +                 '}';     }}
        登錄后復制

        獲取對應類的Class對象:

        public static void main(String[] args) {     //有3種方式可以獲取Class對象     //1.通過對象的getClass()方法     Student student1 = new Student();     Class<?> c1 = student1.getClass();     //2、通過類名.class獲取     Class<?> c2 = Student.class;     //3. forName(“路徑”)     Class<?> c3 = null;     try {         c3 = Class.forName("Student");     } catch (ClassNotFoundException e) {         throw new RuntimeException(e);     }     System.out.println(c1.equals(c2));     System.out.println(c1.equals(c3));     System.out.println(c2.equals(c3));}
        登錄后復制

        執行結果:

        通過結果發現, 三種方式獲取到的對象是同一個.

        Java總結分享之反射、枚舉、Lambda表達式

        2.3 獲得Class類相關的方法

        方法 用途
        getClassLoader() 獲得類的加載器
        getDeclaredClasses() 返回一個數組,數組中包含該類中所有類和接口類的對象(包括私有的)
        forName(String className) 根據類名返回類的對象
        newInstance() 創建類的實例
        getName() 獲得類的完整路徑名字

        2.4 使用反射創建實例對象

        首先獲取到Class對象,然后通過Class對象中的newInstance()方法創建實例對象 .

        需要注意的是newInstance()方法的返回值的是一個泛型,在編譯階段會被擦除為Object,所以我們在接收的時候需要強制類型轉換 .

        public static void main(String[] args) {     //獲取相關類的Class對象     Class<?> c = Student.class;     //使用newInstance方法創建實例     try {         //需要進行強轉         Student student = (Student) c.newInstance();         System.out.println(student);     } catch (InstantiationException e) {         e.printStackTrace();     } catch (IllegalAccessException e) {         e.printStackTrace();     }}
        登錄后復制

        執行結果:

        通過反射成功創建了Student類的實例。

        Java總結分享之反射、枚舉、Lambda表達式

        2.5 使用反射獲取實例對象中的構造方法

        方法 用途
        getConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型匹配的公有構造方法
        getConstructors() 獲得該類的所有公有構造方法
        getDeclaredConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型匹配的構造方法
        getDeclaredConstructors() 獲得該類所有構造方法

        使用反射獲取實例對象中構造方法然后創建實例對象:

        • 獲取Class對象。

        • 通過上述的方法獲取構造器。

        • 如果獲取的是私有的構造方法,則需要記得通過構造器的setAccessible方法將訪問權限開啟。

        • 調用構造器中的newInstance方法獲取對象。

        public static void main(String[] args) throws ClassNotFoundException {     //1.獲取Clas對象     Class<?> c = Class.forName("Student");     //2.獲取指定參數列表的構造器,演示獲取Student中的一個私有構造器,參數傳形參列表類型     try {         Constructor<?> constructor = c.getDeclaredConstructor(String.class, int.class);         //獲取的私有構造方法,需要打開訪問權限,默認關閉         constructor.setAccessible(true);         //3.根據獲取到的構造器獲取實例對象,使用newInstance方法,需要傳入構造器需要的參數         Student student = (Student) constructor.newInstance("張三", 20);         System.out.println(student);     } catch (NoSuchMethodException e) {         e.printStackTrace();     } catch (InvocationTargetException e) {         e.printStackTrace();     } catch (InstantiationException e) {         e.printStackTrace();     } catch (IllegalAccessException e) {         e.printStackTrace();     }}
        登錄后復制

        運行結果:

        獲取到了私有的構造器,按照所傳參數創建實例對象。

        Java總結分享之反射、枚舉、Lambda表達式

        2.6 通過反射獲取實例對象的屬性

        方法 用途
        getField(String name) 獲得某個公有的屬性對象
        getFields() 獲得所有公有的屬性對象
        getDeclaredField(String name) 獲得某個屬性對象
        getDeclaredFields() 獲得所有屬性對象

        通過如下過程修改一個對象的私有屬性:

        • 獲取Class對象。

        • 創建或通過反射實例化一個需要修改其私有字段的類。

        • 通過屬性名,調用上述getDeclaredField方法獲取對應的屬性對象。

        • 通過setAccessible方法設置為訪問私有屬性開權限。

        • 通過Field對象的set方法,修改傳入對象中的對應屬性。

        public static void main(String[] args) {     //1.獲取Class對象     Class<?> c = Student.class;     try {         //2.通過反射創建實例對象         Student student = (Student) c.newInstance();         //3.獲取私有屬性name         Field field =  c.getDeclaredField("name");         //4.給該私有屬性開權限         field.setAccessible(true);         //5.修改該私有屬性         field.set(student, "被反射修改的私有屬性");         System.out.println(student);     } catch (NoSuchFieldException e) {         e.printStackTrace();     } catch (InstantiationException e) {         e.printStackTrace();     } catch (IllegalAccessException e) {         e.printStackTrace();     }}
        登錄后復制

        運行結果:

        實例對象里面的私有屬性name被修改了。

        Java總結分享之反射、枚舉、Lambda表達式

        2.7 通過反射獲取實例對象的方法

        方法 用途
        getMethod(String name, Class…<?> parameterTypes) 獲得該類某個公有的方法
        getMethods() 獲得該類所有公有的方法
        getDeclaredMethod(String name, Class…<?> parameterTypes) 獲得該類某個方法
        getDeclaredMethods() 獲得該類所有方法

        通過如下過程獲取Student對象中的私有方法function:

        • 獲取相關Student類的Class對象。

        • 創建或通過反射實例化一個Student。

        • 通過class對象獲取到實例對象中的方法對象,參數為方法名,形參類型列表。

        • 為獲取的私有方法開訪問權限。

        • 通過invork方法調用方法。

        public static void main(String[] args) {     try {         //1.獲取Class對象         Class<?> c = Class.forName("Student");         //2.獲取Student的一個實例對象         Student student = (Student) c.newInstance();         //3.通過class對象獲取實例的方法對象,參數為方法名,以及形參列表         Method method =  c.getDeclaredMethod("function", String.class);         //4.為私有方法開訪問權限         method.setAccessible(true);         //5.通過invork方法調用方法         method.invoke(student, "傳入私有方法參數");     } catch (ClassNotFoundException e) {         e.printStackTrace();     } catch (NoSuchMethodException e) {         e.printStackTrace();     } catch (InstantiationException e) {         e.printStackTrace();     } catch (IllegalAccessException e) {         e.printStackTrace();     } catch (InvocationTargetException e) {         e.printStackTrace();     }}
        登錄后復制

        運行結果:

        通過反射可以獲取到實例對象的私有方法并進行調用。

        Java總結分享之反射、枚舉、Lambda表達式

        2.8 獲得類中注解相關的方法

        方法 用途
        getAnnotation(Class annotationClass) 返回該類中與參數類型匹配的公有注解對象
        getAnnotations() 返回該類所有的公有注解對象
        getDeclaredAnnotation(Class annotationClass) 返回該類中與參數類型匹配的所有注解對象
        getDeclaredAnnotations() 返回該類所有的注解對象

        3. 反射的優缺點

        優點

        • 對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意一個方法

        • 增加程序的靈活性和擴展性,降低耦合性,提高自適應能力

        • 反射已經運用在了很多流行框架如:Struts、Hibernate、Spring 等等。

        缺點

        • 使用反射會有效率問題。會導致程序效率降低。

        • 反射技術繞過了源代碼的技術,因而會帶來維護問題。反射代碼比相應的直接代碼更復雜 。

        二. 枚舉

        1. 枚舉的概述

        枚舉是在JDK1.5以后引入的; 關鍵字enum可以將一組具名的值的有限集合創建為一種新的類型,而這些具名的值可以作為常規的程序組件使用,這個新的類型就是枚舉

        主要用途是:將一組常量組織起來,在這之前表示一組常量通常使用定義常量的方式:

        public static int final RED = 1;public static int final GREEN = 2;public static int final BLACK = 3;
        登錄后復制

        但是常量舉例有不好的地方,例如:可能碰巧有個數字1,但是他有可能誤會為是RED,現在我們可以直接用枚舉來進行組織,這樣一來,就擁有了類型,枚舉類型。而不是普通的整形1.

        下面是創建一個Color枚舉類型 :

        public enum Color {     RED,BLUE,GREEN,YELLOW,BLACK;}
        登錄后復制

        優點:將常量組織起來統一進行管理

        場景:錯誤狀態碼,消息類型,顏色的劃分,狀態機等等…

        本質是 java.lang.Enum 的子類,也就是說,自己寫的枚舉類,就算沒有顯示的繼承 Enum ,但是其默認繼承了這個類

        2. 枚舉的使用

        2.1 switch語句中使用枚舉

        switch語句中可以使用枚舉來提高代碼的可讀性。

        其實enum關鍵字組織的是一個特殊的類,里面包含一個或多個的枚舉對象,下面定義的Color,其實里面包含了3個枚舉對象,每個對象都是Color類型。

        enum Color {     BLACK,YELLOW,GREEN;}public class Test {     public static void main(String[] args) {         Color color = Color.YELLOW;         switch (color) {             case BLACK:                 System.out.println("BLACK");                 break;             case YELLOW:                 System.out.println("YELLOW");                 break;             case GREEN:                 System.out.println("GREEN");                 break;             default:                 break;         }     }}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        2.2 枚舉enum中的常用方法

        枚舉中常用的方法如下:

        方法名稱 描述
        values() 以數組形式返回枚舉類型的所有成員
        ordinal() 獲取枚舉成員的索引位置
        valueOf() 將普通字符串轉換為枚舉實例
        compareTo() 比較兩個枚舉成員在定義時的順序

        關于Enum類源碼中找不到values()方法的解釋:

        values方法,在編譯前無法找到,這是因為enum聲明實際上定義了一個類,我們可以通過定義的enum調用一些方法,Java編譯器會自動在enum類型中插入一些方法,其中就包括values(),valueOf(),所以我們的程序在沒編譯的時候,就沒辦法查看到values()方法以及源碼,這也是枚舉的特殊性。

        • 使用values()得到一個含有所有枚舉對象的一個數組

        public enum Color {     BLACK,YELLOW,GREEN;      public static void main(String[] args) {         Color[] colors = Color.values();         for (Color c : colors) {             System.out.println(c);         }     }}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        • 使用valueOf()通過一個字符串獲取同名枚舉:

        public enum Color {     BLACK,YELLOW,GREEN;     public static void main(String[] args) {         Color color = Color.valueOf("BLACK");         System.out.println(color);     }}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        • 使用ordinal()獲取枚舉在枚舉類中的位置次序,也就是索引:

        public enum Color {     BLACK,YELLOW,GREEN;     public static void main(String[] args) {         Color[] colors = Color.values();         for (Color c : colors) {             System.out.println(c + "的索引:" + c.ordinal());         }     }}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        • 使用compareTo() 比較兩個枚舉成員在定義時的順序:

        public enum Color {     BLACK,YELLOW,GREEN;     public static void main(String[] args) {         System.out.println(Color.GREEN.compareTo(Color.YELLOW));         System.out.println(Color.BLACK.compareTo(Color.YELLOW));     }}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        3. 自定義構造枚舉對象

        上面的例子中enum本質上其實是一個特殊的類,默認繼承了抽象類java.lang.Enum,里面包含了一個或多個枚舉對象,并且這些枚舉對象默認情況下都是通過無參數的構造方法構造的,

        其實我們可以在枚舉類中自定義屬性方法以及構造方法,實現自定義枚舉對象.

        看下面的寫法, 和上面的例子是一樣的 , 只不過上面的寫法是無參構造省略了 ( )

        Java總結分享之反射、枚舉、Lambda表達式

        我們可以自己在枚舉類中定義一些屬性, 然后去寫含有含有參數的構造方法, 實現自定義枚舉;

        注意 : 枚舉中的構造方法必須(默認)是私有的, 且當我們寫了含有參數的構造方法時, 編譯器不會再提提供無參的構造方法 , 所以此時需要按照我們自己寫的構造方法傳入參數;

        public enum Color {     BLACK("BLACK", 11, 1),     YELLOW("YELLOW", 12, 2),     GREEN("GREEN", 13, 3);     public String colorName;     public int colorId;     public int ordonal;      Color(String colorName, int colorId, int ordonal) {         this.colorName = colorName;         this.colorId = colorId;         this.ordonal = ordonal;     }      @Override     public String toString() {         return "Color{" +                 "colorName='" + colorName + ''' +                 ", colorId=" + colorId +                 ", ordonal=" + ordonal +                 '}';     }      public static void main(String[] args) {         Color[] colors = Color.values();         for (Color c : colors) {             System.out.println(c);         }     }}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        4. 枚舉的安全性

        首先看下面的代碼, 我們想要從外部通過反射獲取到枚舉類:

        public class Test {     public static void main(String[] args) {         //嘗試獲取枚舉對象         Class<?> c = Color.class;         try {             //獲取構造方法對象             Constructor<?> constructor = c.getDeclaredConstructor(String.class, int.class, int.class);             //開權限             constructor.setAccessible(true);             //通過構造方法構造對象             Color color = (Color) constructor.newInstance("藍色", 88, 2);             System.out.println(color);         } catch (NoSuchMethodException e) {             e.printStackTrace();         } catch (InvocationTargetException e) {             e.printStackTrace();         } catch (InstantiationException e) {             e.printStackTrace();         } catch (IllegalAccessException e) {             e.printStackTrace();         }     }}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        結果中拋出一個java.lang.NoSuchMethodException: Color.<init>(java.lang.String, int, int)異常,表示沒有找到我們給定參數列表的構造方法,但是我們枚舉類中是定義過這個構造方法的,那么這里報錯的原因是什么呢?

        上面說過枚舉類是默認繼承抽象類java.lang.Enum的,所以要構造enum需要先幫助父類完成構造,但是枚舉類與一般的類相比比較特殊,它不是使用super關鍵字進行顯示地幫助父類構造,而是在編譯后會多插入兩個參數來幫助父類構造,也就是說,我們傳參時要在原本所定義的構造方法參數列表基礎上前面再添加String和int類型的兩個參數

        所以實際情況下,我們需要在反射獲取構造器時,多寫兩個參數

        Constructor<?> constructor = c.getDeclaredConstructor(String.class, int.class, String.class, int.class, int.class);
        登錄后復制

        再次運行程序結果如下:

        Java總結分享之反射、枚舉、Lambda表達式

        可以發現結果還是會拋出異常,但是此時拋的不是構造方法找不到的異常,而是枚舉無法進行反射異常Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects;

        所以枚舉對象是無法通過反射得到的, 這也就保證了枚舉的安全性;

        其實枚舉無法通過反射獲取到枚舉對象是因為在**newInstance****()**中獲取枚舉對象時,會過濾掉枚舉類型,如果遇到的是枚舉類型就會拋出異常。

        Java總結分享之反射、枚舉、Lambda表達式

        5. 總結

        • 枚舉本身就是一個類,其構造方法默認為私有的,且都是默認繼承與 java.lang.Enum

        • 枚舉可以避免反射和序列化問題

        • 枚舉實現單例模式是安全的

        枚舉的優點:

        • 枚舉常量更簡單安全
        • 枚舉具有內置方法 ,代碼更優雅

        枚舉的缺點:

        • 不可繼承,無法擴展

        三. Lambda表達式

        1. 函數式接口

        要了解Lambda表達式,首先需要了解什么是函數式接口,函數式接口定義:一個接口有且只有一個抽象方法 。

        注意:

        • 如果一個接口只有一個抽象方法,那么該接口就是一個函數式接口

        • 如果我們在某個接口上聲明了 @FunctionalInterface注解,那么編譯器就會按照函數式接口的定義來要求該接 口,這樣如果有兩個抽象方法,程序編譯就會報錯的。所以,從某種意義上來說,只要你保證你的接口中只有一個抽象方法,你可以不加這個注解。加上就會自動進行檢測的。

        定義方式

        @FunctionalInterfaceinterface NoParameterNoReturn { 	//注意:只能有一個方法 	void test();}
        登錄后復制

        基于jdk1.8, 還以有如下定義:

        @FunctionalInterfaceinterface NoParameterNoReturn { 	void test(); 	default void test2() { 		System.out.println("JDK1.8新特性,default默認方法可以有具體的實現"); 	}}
        登錄后復制

        2. 什么是Lambda表達式?

        Lambda表達式是Java SE 8中一個重要的新特性。lambda表達式允許你通過表達式來代替功能接口。 lambda表達 式就和方法一樣,它提供了一個正常的參數列表和一個使用這些參數的主體(body,可以是一個表達式或一個代碼 塊)。 Lambda 表達式(Lambda expression),基于數學中的λ演算得名,也可稱為閉包(Closure) 。

        Lambda表達式的語法:

         (parameters) -> expression 或 (parameters) ->{ statements; }
        登錄后復制

        Lambda表達式由三部分組成

        • paramaters:類似方法中的形參列表,這里的參數是函數式接口里的參數。這里的參數類型可以明確的聲明也可不聲明而由JVM隱含的推斷。另外當只有一個推斷類型時可以省略掉圓括號。

        • ->:可理解為“被用于”的意思

        • 方法體:可以是表達式也可以代碼塊,是函數式接口里方法的實現。代碼塊可返回一個值或者什么都不反回,這里的代碼塊塊等同于方法的方法體。如果是表達式,也可以返回一個值或者什么都不反回。

        常用的lambda表達式格式:

        // 1. 不需要參數,返回值為 2() -> 2     // 2. 接收一個參數(數字類型),返回其2倍的值x -> 2 * x     // 3. 接受2個參數(數字),并返回他們的和(x, y) -> x + y     // 4. 接收2個int型整數,返回他們的乘積(int x, int y) -> x * y     // 5. 接受一個 string 對象,并在控制臺打印,不返回任何值(看起來像是返回void)(String s) -> System.out.print(s)
        登錄后復制

        3. Lambda表達式的基本使用

        • 參數類型可以省略,如果需要省略,每個參數的類型都要省略。

        • 參數的小括號里面只有一個參數,那么小括號可以省略

        • 如果方法體當中只有一句代碼,那么大括號可以省略

        • 如果方法體中只有一條語句,其是return語句,那么大括號可以省略,且去掉return關鍵字

        以下面這些接口為例:

        //無返回值無參數@FunctionalInterfaceinterface NoParameterNoReturn {     void test();}//無返回值一個參數@FunctionalInterfaceinterface OneParameterNoReturn {     void test(int a);}//無返回值多個參數@FunctionalInterfaceinterface MoreParameterNoReturn {     void test(int a,int b);}//有返回值無參數@FunctionalInterfaceinterface NoParameterReturn {     int test();}//有返回值一個參數@FunctionalInterfaceinterface OneParameterReturn {     int test(int a);}//有返回值多參數@FunctionalInterfaceinterface MoreParameterReturn {     int test(int a,int b);}
        登錄后復制

        實現接口最原始的方式就是定義一個類去重寫對應的方法,其次更簡便的方式就是使用匿名內部類去實現接口;

        public class TestDemo {     public static void main(String[] args) {         NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){             @Override             public void test() {                 System.out.println("hello");             }         };         noParameterNoReturn.test();     }}
        登錄后復制

        那么這里使用lambda表達式, 可以進一步進行簡化;

        public class TestDemo {     public static void main(String[] args) {         NoParameterNoReturn noParameterNoReturn = ()->{             System.out.println("無參數無返回值");         };         noParameterNoReturn.test();                  OneParameterNoReturn oneParameterNoReturn = (int a)->{             System.out.println("一個參數無返回值:"+ a);         };         oneParameterNoReturn.test(10);                  MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->{             System.out.println("多個參數無返回值:"+a+" "+b);         };         moreParameterNoReturn.test(20,30);                  NoParameterReturn noParameterReturn = ()->{             System.out.println("有返回值無參數!");             return 40;         };         //接收函數的返回值         int ret = noParameterReturn.test();         System.out.println(ret);                  OneParameterReturn oneParameterReturn = (int a)->{System.out.println("有返回值有一個參數!");             return a;         };         ret = oneParameterReturn.test(50);         System.out.println(ret);                  MoreParameterReturn moreParameterReturn = (int a,int b)->{             System.out.println("有返回值多個參數!");             return a+b;         };         ret = moreParameterReturn.test(60,70);         System.out.println(ret);     }}
        登錄后復制

        上面的的代碼根據開頭的省略規則還可以進一步省略, 如下:

        public class TestDemo {     public static void main(String[] args) {         NoParameterNoReturn noParameterNoReturn                = ()->System.out.println("無參數無返回值");         noParameterNoReturn.test();          OneParameterNoReturn oneParameterNoReturn                = a-> System.out.println("一個參數無返回值:"+ a);         oneParameterNoReturn.test(10);          MoreParameterNoReturn moreParameterNoReturn                = (a,b)-> System.out.println("多個參數無返回值:"+a+" "+b);         moreParameterNoReturn.test(20,30);          //有返回值無參數!         NoParameterReturn noParameterReturn = ()->40;         int ret = noParameterReturn.test();         System.out.println(ret);          //有返回值有一個參數!         OneParameterReturn oneParameterReturn = a->a;         ret = oneParameterReturn.test(50);         System.out.println(ret);          //有返回值多個參數!         MoreParameterReturn moreParameterReturn = (a,b)->a+b;         ret = moreParameterReturn.test(60,70);         System.out.println(ret);     }}
        登錄后復制

        還有一種寫法更加簡潔, 但可讀性就… , 比如:

        OneParameterNoReturn oneParameterNoReturn = a-> System.out.println(a);
        登錄后復制

        可以簡化成下面的樣子, 看不太懂了…

        OneParameterNoReturn oneParameterNoReturn = System.out::println;
        登錄后復制

        4. 變量捕獲

        Lambda 表達式中存在變量捕獲 ,了解了變量捕獲之后,我們才能更好的理解Lambda 表達式的作用域 。

        在匿名內部類中,只能捕獲到常量,或者沒有發生修改的變量,因為lambda本質也是實現函數式接口,所以lambda也滿足此變量捕獲的規則。

        下面的代碼捕獲的變量num未修改, 程序可以正常編譯和運行;

        Java總結分享之反射、枚舉、Lambda表達式

        當捕獲的變量num是修改過的, 則會報錯;

        Java總結分享之反射、枚舉、Lambda表達式

        5. Lambda在集合當中的使用

        5.1 Collection接口中的forEach方法

        注意:Collection的forEach()方 法是從接口 java.lang.Iterable 拿過來的。

        forEach方法需要傳遞的參數是Consumer<? super E> action,這個參數也是一個函數式接口,需要重寫里面的accept方法。

        Java總結分享之反射、枚舉、Lambda表達式

        使用匿名內部類,accept中的t參數表示集合中迭代出的元素,我們可以對該元素設定操作, 這里重寫的方法只做輸出操作;

        public static void main(String[] args) {     ArrayList<String> list = new ArrayList<>();     list.add("欣");     list.add("欣");     list.add("向");     list.add("榮");     list.forEach(new Consumer<String>(){         @Override         public void accept(String str){             //簡單遍歷集合中的元素。             System.out.print(str+" ");         }     });}
        登錄后復制

        執行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        我們可以將上面的匿名內部類使用lambda表示,它只有一個參數沒有返回值,上面的代碼變為

        public static void main(String[] args) {     ArrayList<String> list = new ArrayList<>();     list.add("欣");     list.add("欣");     list.add("向");     list.add("榮");     list.forEach(s -> System.out.print(s + " "));}
        登錄后復制

        5.2 Map中forEach方法

        map中的forEach方法和前面Collection中的forEach方法的使用其實都差不多,換了一個參數而已,這個參數BiConsumer<? super K, ? super V> action同樣是一個函數式接口,我們需要傳入一個實現該接口的實現類。

        Java總結分享之反射、枚舉、Lambda表達式

        使用匿名內部類:

        public static void main(String[] args) {     Map<Integer, String> map = new HashMap<>();     map.put(1, "欣");     map.put(2, "欣");     map.put(3, "向");     map.put(4, "榮");     map.forEach(new BiConsumer<Integer, String>(){         @Override         public void accept(Integer k, String v){             System.out.println(k + "=" + v);         }     });}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        同樣的對上面代碼可以使用lambda表達式來實現,這是一個含有兩個參數無返回值的函數式接口,上面的代碼改為:

        public static void main(String[] args) {     Map<Integer, String> map = new HashMap<>();     map.put(1, "欣");     map.put(2, "欣");     map.put(3, "向");     map.put(4, "榮");     map.forEach((k,v)-> System.out.println(k + "=" + v));}
        登錄后復制

        5.3 大部分接口中的sort方法

        大部分接口中的sort方法,默認都是按照升序的方式進行排序,如果需要對自定義類進行排序或者實現自定義規則的排序,需要額外傳入一個Comparator的實現類對象(比較器) ; 這里以List集合中的sort方法為例 .

        Java總結分享之反射、枚舉、Lambda表達式

        public static void main(String[] args) {     ArrayList<String> list = new ArrayList<>();     list.add("aaaa");     list.add("bbb");     list.add("cc");     list.add("d");     list.sort(new Comparator<String>() {         @Override         public int compare(String str1, String str2){             //注意這里比較的是長度             return str1.length()-str2.length();         }     });     System.out.println(list);}
        登錄后復制

        運行結果:

        Java總結分享之反射、枚舉、Lambda表達式

        同樣的對上面代碼可以使用lambda表達式來實現,這是一個含有兩個參數有返回值的函數式接口,上面的代碼改為:

         public static void main(String[] args) {     ArrayList<String> list = new ArrayList<>();     list.add("aaaa");     list.add("bbb");     list.add("cc");     list.add("d");     //調用帶有2個參數的方法,且返回長度的差值     list.sort((str1,str2)-> str1.length()-str2.length());     System.out.println(list);}
        登錄后復制

        6. 總結

        Lambda表達式的優點很明顯,在代碼層次上來說,使代碼變得非常的簡潔。缺點也很明顯,代碼不易讀。

        優點

        • 代碼簡潔,開發迅速

        • 方便函數式編程

        • 非常容易進行并行計算

        • Java 引入 Lambda,改善了集合操作

        缺點

        • 代碼可讀性變差

        • 在非并行計算中,很多計算未必有傳統的 for 性能要高

        • 不容易進行調試

        推薦學習:《java視頻教程》

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 在线中文字幕精品第5页| 无码欧精品亚洲日韩一区| 亚洲国产精品综合久久一线| 98视频精品全部国产| 亚洲A∨精品一区二区三区| 九九精品成人免费国产片| 久久精品国产99久久久| 四库影院永久四虎精品国产| 国产精品hd免费观看| 国产精品1区2区3区在线播放| 日韩精品无码中文字幕一区二区| 久久久精品无码专区不卡| 国产观看精品一区二区三区| 国产精品岛国久久久久| 九九热这里只有在线精品视| 99久久伊人精品综合观看| 精品国产三级a∨在线欧美| 亚洲午夜精品第一区二区8050| 国产精品自在线拍国产手机版| 日韩欧美精品不卡| 国产精品亚洲午夜一区二区三区| 精品人人妻人人澡人人爽人人| 亚洲精品二区国产综合野狼 | 欧美精品亚洲精品日韩精品 | 国产精品美女久久久久网| 日韩精品久久无码中文字幕| 伊在人亚洲香蕉精品区麻豆| 欧美精品三区| 日韩欧美国产精品第一页不卡| 麻豆精品视频在线观看| 男人的天堂精品国产一区| 久久丝袜精品中文字幕| 蜜桃麻豆www久久国产精品| 欧美日韩国产精品 | 精品国产三级a∨在线| 亚洲精品国偷自产在线| 中文字幕日本精品一区二区三区| 亚洲精品无码永久在线观看| 伊人久久精品无码av一区| 青青草原精品国产亚洲av| 国产成人精品日本亚洲|