国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java中反射機(jī)制,枚舉,Lambda的使用

這篇具有很好參考價(jià)值的文章主要介紹了Java中反射機(jī)制,枚舉,Lambda的使用。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

目錄

一、反射機(jī)制

1、含義

2、作用

3、※反射相關(guān)的幾個(gè)類(lèi)

3.1、Class類(lèi)(Class對(duì)象是反射的基石)

3.2、Class類(lèi)中相關(guān)的方法

3.2.1 (※重要)常用獲得類(lèi)相關(guān)的方法

3.2.2 (※重要)常用獲得類(lèi)中屬性、變量Field相關(guān)的方法?

3.2.3 獲得類(lèi)中注解相關(guān)的方法?

3.2.4(※重要)獲得類(lèi)中構(gòu)造器相關(guān)的方法

3.2.5(※重要)獲得類(lèi)中方法相關(guān)的方法

4、使用反射來(lái)獲取類(lèi)的信息

4.1 獲取Class對(duì)象的三種方法

4.2 反射的一系列使用

4.2.1通過(guò)反射獲取對(duì)象的變量信息

4.2.2通過(guò)反射獲取對(duì)象的方法信息

4.2.3通過(guò)反射獲取對(duì)象的構(gòu)造方法信息

5、反射的優(yōu)點(diǎn)和缺點(diǎn)

二、枚舉

1、背景及定義

2、枚舉的使用

2.1、此處是簡(jiǎn)單的獲取枚舉對(duì)象,直接引用即可?

2.2、Enmu常用的方法

2.3、枚舉的原理

2.4、有關(guān)枚舉的values()方法和valueOf()方法

3、枚舉的優(yōu)缺點(diǎn)

4、枚舉 和 反射

三、Lambda表達(dá)式

1、背景和含義

1.1、Lambda表達(dá)式的語(yǔ)法

1.2、函數(shù)式接口

?2、Lambda表達(dá)式的使用

2.1、Lambda表達(dá)式的使用

2.2、Lambda表達(dá)式的精簡(jiǎn)

3、變量捕獲

3.1、 匿名內(nèi)部類(lèi)

3.2、匿名內(nèi)部類(lèi)的變量捕獲

3.3、Lambda變量捕獲

3.4、匿名內(nèi)部類(lèi)和Lambda的區(qū)別

4、Lambda在集合中的使用

4.1、Collection接口

4.2、List接口?

4.3、Map接口?

?5、總結(jié)


一、反射機(jī)制

1、含義

Java的反射(reflection)機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類(lèi),都能夠知道這個(gè)類(lèi)的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性,既然能拿到那么,我們就可以修改部分類(lèi)型信息;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱(chēng)為java語(yǔ)言的反射(reflection)機(jī)制。

可以想象在過(guò)安檢時(shí),傳送帶上防止我們的行李和物品,經(jīng)過(guò)安檢機(jī)器后,安檢員可以實(shí)時(shí)動(dòng)態(tài)的看到行李箱里的物品。跟java的反射機(jī)制有點(diǎn)類(lèi)似,在運(yùn)行中就可以知道一個(gè)類(lèi)、對(duì)象中的一切細(xì)節(jié)(成員方法、成員變量等等的一些列信息),并可以對(duì)其進(jìn)行修改.


2、作用

  • 當(dāng)在類(lèi)中定義了被private修飾的成員變量、成員方法等,類(lèi)外就無(wú)法再對(duì)其進(jìn)行訪(fǎng)問(wèn)或修改了,只有在那個(gè)類(lèi)內(nèi)部才可以進(jìn)行調(diào)用、訪(fǎng)問(wèn)或修改的操作。要是我們想在類(lèi)外訪(fǎng)問(wèn)被私有private修飾的變量或方法時(shí),就可以利用 “反射”機(jī)制來(lái)獲取類(lèi)中私有的成員,并進(jìn)行訪(fǎng)問(wèn)或修改等操作!
  • 反射最重要的用途就是開(kāi)發(fā)各種通用框架,比如在spring中,我們將所有的類(lèi)Bean交給spring容器管理,無(wú)論是XML配置Bean還是注解配置,當(dāng)我們從容器中獲取Bean來(lái)依賴(lài)注入時(shí),容器會(huì)讀取配置,而配置中給的就是類(lèi)的信息,spring根據(jù)這些信息,需要?jiǎng)?chuàng)建那些Bean,spring就動(dòng)態(tài)的創(chuàng)建這些類(lèi)。

3、※反射相關(guān)的幾個(gè)類(lèi)

類(lèi)名

用途

Class類(lèi) 【特殊】

代表類(lèi)的實(shí)體,在運(yùn)行的Java應(yīng)用程序中表示類(lèi)和接口

Field類(lèi)(字段)

代表類(lèi)的成員變量/類(lèi)的屬性

Method類(lèi)(方法)

代表類(lèi)的方法

Constructor類(lèi)(構(gòu)造方法)

代表類(lèi)的構(gòu)造方法


3.1、Class類(lèi)(Class對(duì)象是反射的基石)

※沒(méi)有class對(duì)象就沒(méi)法進(jìn)行反射!

“ 反射機(jī)制 ” 最重要的就是 “運(yùn)行時(shí)”(在程序運(yùn)行的時(shí)候,可以加載、探索編譯期間的未知.class文件)

??當(dāng)一個(gè).java程序編譯的時(shí)候,會(huì)生成一個(gè).class二進(jìn)制文件(字節(jié)碼文件),此時(shí)的JVM就會(huì)去解讀這個(gè).class文件,之后就會(huì)解析創(chuàng)建出一個(gè)Class對(duì)象(java.lang.Class),這樣當(dāng)程序在運(yùn)行時(shí),每個(gè)java文件就最終變成了Class類(lèi)對(duì)象的一個(gè)實(shí)例。我們通過(guò)Java的反射機(jī)制應(yīng)用到這個(gè)實(shí)例,就可以去獲得甚至去添加改變這個(gè)類(lèi)的屬性和動(dòng)作,使得這個(gè)類(lèi)成為一個(gè)動(dòng)態(tài)的類(lèi).

換句話(huà)說(shuō),Java 程序可以加載一個(gè)運(yùn)行時(shí)才得知名稱(chēng)的?.class?文件,然后獲悉其完整構(gòu)造,并生成其對(duì)象實(shí)體、或?qū)ζ?fields(變量)設(shè)值、或調(diào)用其 methods(方法)或傳參調(diào)用constructor構(gòu)造方法。


3.2、Class類(lèi)中相關(guān)的方法

3.2.1 (※重要)常用獲得類(lèi)相關(guān)的方法

方法

用途

getClassLoader()

獲得類(lèi)的加載器

getDeclaredClasses()

返回一個(gè)類(lèi)數(shù)組,數(shù)組中包含該類(lèi)中所有類(lèi)和接口類(lèi)的對(duì)象(Declared包括私有private的)

forName(String className)

傳入類(lèi)的完整路徑名字來(lái)獲取類(lèi)的Class對(duì)象

newInstance()

創(chuàng)建類(lèi)的實(shí)例

getName()

獲得類(lèi)的完整路徑名字(類(lèi)似com.demo.Reflect)


3.2.2 (※重要)常用獲得類(lèi)中屬性、變量Field相關(guān)的方法?

方法

用途

getField(String name)

根據(jù)成員變量名獲取該公有的屬性對(duì)象

getFields()

獲取公有的所有屬性對(duì)象

getDeclaredField(String name)

根據(jù)成員變量名獲取所有的屬性對(duì)象(包括private私有的)

getDeclaredFields()

獲取公有的所有屬性對(duì)象(包括private私有的)


3.2.3 獲得類(lèi)中注解相關(guān)的方法?

方法

用途

getAnnotation(Class annotationClass)

返回該類(lèi)中與參數(shù)類(lèi)型匹配的公有注解對(duì)象

getAnnotations()

返回該類(lèi)所有的公有注解對(duì)象

getDeclaredAnnotation(Class annotationClass)

返回該類(lèi)中與參數(shù)類(lèi)型匹配的所有注解對(duì)象

getDeclaredAnnotations()

返回該類(lèi)所有的注解對(duì)象


3.2.4(※重要)獲得類(lèi)中構(gòu)造器相關(guān)的方法

方法

用途

getConstructor(構(gòu)造方法的參數(shù))----反射中的參數(shù)形式為:String.class、int.class等等

獲得該類(lèi)中與參數(shù)類(lèi)型匹配的公有構(gòu)造方法

getConstructors()

獲取該類(lèi)中所有的公有構(gòu)造方法

getDeclaredConstructor(構(gòu)造方法的參數(shù))

獲得該類(lèi)中與參數(shù)類(lèi)型匹配的所有構(gòu)造方法(包括私有private修飾的)

getDeclaredConstructors()

獲取該類(lèi)中所有的構(gòu)造方法(包括私有private修飾的)


?3.2.5(※重要)獲得類(lèi)中方法相關(guān)的方法

方法

用途

getMethod(String name, Class... parameterTypes)

獲得該類(lèi)某個(gè)公有的方法

getMethods()

獲得該類(lèi)所有公有的方法

getDeclaredMethod(String name, Class... parameterTypes)

獲得該類(lèi)某個(gè)方法(包括私有的)

getDeclaredMethods()

獲得該類(lèi)所有方法(包括私有的)


根據(jù)上述的方法,可以知道,可以利用反射機(jī)制在運(yùn)行時(shí),解析編譯的class文件獲取Class對(duì)象,并進(jìn)行實(shí)例,可以訪(fǎng)問(wèn)類(lèi)中的私有成員。像上述方法中,帶有Declared的方法都可以獲取對(duì)應(yīng)的私有成員。


4、使用反射來(lái)獲取類(lèi)的信息

首先,要使用反射獲取一個(gè)類(lèi)中的所有信息,必須先獲取其Class對(duì)象,然后通過(guò)Class對(duì)象的核心方法,達(dá)到反射的目的。

此處借助一個(gè)Student類(lèi)來(lái)演示:

class Student {
    //私有屬性name    
    private String name = "bit";    
    //公有屬性age    
    public int age = 18;    
    //不帶參數(shù)的構(gòu)造方法    
    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 eat");    
    }

    public void sleep() {
        System.out.println("i am pig");    
    }

    private void function(String str) {
        System.out.println(str);    
    }

    @Override    
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';    
    }
}

4.1 獲取Class對(duì)象的三種方法

第一種方法:是通過(guò)實(shí)例了的對(duì)象,來(lái)直接使用類(lèi)對(duì)象的getClass()方法獲取Class對(duì)象

第二種方法:(適合在編譯前就已經(jīng)明確了要操作的類(lèi)Class) 調(diào)用目標(biāo)對(duì)象類(lèi)的class()方法

第三種方法:(需要明確對(duì)象類(lèi)的完整路徑名字)調(diào)用Class類(lèi)的forName()方法

public static void main(String[] args) {
    //獲取Class對(duì)象的三種方法   
     //1.通過(guò)getClass獲取Class對(duì)象   
     Student student = new Student();    
     Class<?> c1 = student.getClass();    
     
     //2..直接通過(guò) 類(lèi)名.class 的方式得到,該方法最為安全可靠,程序性能更高    
     //這說(shuō)明任何一個(gè)類(lèi)都有一個(gè)隱含的靜態(tài)成員變量 class    
     Class<?> c2 = Student.class;    
     
     //3.通過(guò) Class 對(duì)象的 forName() 靜態(tài)方法來(lái)獲取,用的最多,    
     //但可能拋出 ClassNotFoundException 異常    
     Class<?> c3 = null;    
     try {
            c3 = Class.forName("Student");    //此處是類(lèi)的完整路徑名字
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);    
        }
    //因?yàn)槿齻€(gè)Class對(duì)象都是同一個(gè),說(shuō)明對(duì)象只有一個(gè),一個(gè)類(lèi)只對(duì)應(yīng)一個(gè)Class對(duì)象(一個(gè)類(lèi)在 JVM 中只會(huì)有一個(gè) Class 實(shí)例)    
    System.out.println(c1.equals(c2));    
    System.out.println(c2.equals(c3));    
    System.out.println(c1.equals(c3));
}

4.2 反射的一系列使用

通過(guò)反射獲取類(lèi)的方法Method、屬性Field、構(gòu)造方法Constructor(包括私有的,注意需要調(diào)用其對(duì)應(yīng)的setAccessible(true)方法來(lái)獲取訪(fǎng)問(wèn)修飾權(quán)限,是獲取,不是修改)

注意:所有和反射相關(guān)的包都在 import java.lang.reflect 包下面。并且必須要有創(chuàng)建一個(gè)Class對(duì)象,才可以去通過(guò)反射機(jī)制去獲取類(lèi)的所有信息

下面演示的是以People類(lèi)

class People {
    //私有屬性name
    private String name = "bit";
    //公有屬性age
    public int age = 18;

    //不帶參數(shù)的構(gòu)造方法
    public People() {
        System.out.println("People()");
    }

    private People(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("People(String,name)");
    }

    private void eat() {
        System.out.println("i am eat");
    }

    public void sleep() {
        System.out.println("i am pig");
    }

    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
4.2.1通過(guò)反射獲取對(duì)象的變量信息
public static void getField(){
    try {
        //1.通過(guò)People類(lèi)的完整路徑名去獲取Class對(duì)象        
        Class<?>c = Class.forName("review.reflectdemo2.People");    
            
        //此處需要注意的是:調(diào)用newInstance()返回的是一個(gè)Object類(lèi),所以需要強(qiáng)轉(zhuǎn)類(lèi)型        
        People people = (People) c.newInstance();       
        
        //2.根據(jù)變量名獲取類(lèi)中的變量        
        Field field = c.getDeclaredField("name");       //可以獲取任意類(lèi)型的一個(gè)變量(包括private私有修飾的變量)
                
        Field []fields = c.getFields();     //獲取所有的公有變量
                
        for(Field s:fields){
            //3.獲取變量的訪(fǎng)問(wèn)修飾權(quán)限            
            int modifier = s.getModifiers();            
            //打印變量的訪(fǎng)問(wèn)修飾權(quán)限+類(lèi)型名+變量名            
            System.out.println(Modifier.toString(modifier)+
                    " "+ s.getType().getName()+
                    " "+s.getName());        
         }
         //對(duì)私有的變量進(jìn)行修改,需要先獲取訪(fǎng)問(wèn)權(quán)限
         field.setAccessible(true);
         //此處要修改變量信息,調(diào)用的是Field類(lèi)的set方法,參數(shù)分別是:(1)指定修改的People對(duì)象(2)修改后的值
         field.set(people,"Danie");


        } catch (ClassNotFoundException e) {
            e.printStackTrace();    
        } catch (InstantiationException e) {
            e.printStackTrace();    
        } catch (IllegalAccessException e) {
            e.printStackTrace();    
        } catch (NoSuchFieldException e) {
            e.printStackTrace();    
        }
}
4.2.2通過(guò)反射獲取對(duì)象的方法信息
public static void getMethod() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException {
    Class<?> c = Class.forName("review.reflectdemo2.People");    
    People people = (People)c.newInstance();    
    //1.獲取公有的方法(參數(shù)是方法名和方法的參數(shù)),因?yàn)榇颂幍膕leep()方法沒(méi)有參數(shù),所有無(wú)需加參數(shù)    
    Method method = c.getMethod("sleep");    
    
    //2.獲取所有類(lèi)型的一個(gè)方法(此處的function()有一個(gè)參數(shù),根據(jù)反射的規(guī)則,其參數(shù)形式是形如-----類(lèi)型名.class )    
    Method method1 = c.getDeclaredMethod("function",String.class);    
    
    //3.獲取所有的公有方法(返回一個(gè)Method數(shù)組)    
    Method []methods = c.getMethods();    
    
    //獲取所有類(lèi)型的方法(包括私有的)    
    Method []methods1 = c.getDeclaredMethods();    
    
    for(Method temp:methods1){
        //獲取方法的訪(fǎng)問(wèn)修飾權(quán)限        
        int modifiers = temp.getModifiers();        
        //獲取方法的返回值類(lèi)型        
        Class<?> returnType = temp.getReturnType();        
        //獲取方法的所有參數(shù)        
        Parameter[]parameters = temp.getParameters();        
        System.out.println("\n"+"訪(fǎng)問(wèn)修飾權(quán)限為:"+Modifier.toString(modifiers)+
                " " + "返回類(lèi)型為:"+returnType+
                " " + "方法名:"+temp.getName());        
        System.out.print(" 參數(shù)為: ");        
        for(Parameter parameter:parameters){
            System.out.println("[類(lèi)型"+parameter.getType().getName()+
                    " "+"參數(shù)名:"+parameter.getName()+"]");        
        }
    }
}

輸出結(jié)果如下:

(引用類(lèi)型獲取返回類(lèi)型會(huì)直接返回其完整的路徑名)

Java中反射機(jī)制,枚舉,Lambda的使用

?

?4.2.3通過(guò)反射獲取對(duì)象的構(gòu)造方法信息
public static void getConstructor(){
    try {
        //基本跟上述的變量和方法無(wú)異        
        Class<?> c = Class.forName("review.reflectdemo2.People");        
        Constructor<?>constructor = c.getDeclaredConstructor(String.class,int.class);   
        //獲取兩個(gè)參數(shù)的構(gòu)造方法        
        //可以利用構(gòu)造方法來(lái)直接創(chuàng)建示例        
        //注意對(duì)于私有的構(gòu)造方法,調(diào)用也需要獲取訪(fǎng)問(wèn)修飾權(quán)限        
        constructor.setAccessible(true);        
        People people = (People) constructor.newInstance("Nero",18);        
        System.out.println(people);    
        } catch (ClassNotFoundException e) {
            e.printStackTrace();    
        } catch (InstantiationException e) {
            e.printStackTrace();    
        } catch (IllegalAccessException e) {
            e.printStackTrace();    
        } catch (NoSuchMethodException e) {
            e.printStackTrace();    
        } catch (InvocationTargetException e) {
            e.printStackTrace();    
        }

}

5、反射的優(yōu)點(diǎn)和缺點(diǎn)

(1)優(yōu)點(diǎn):

  1. 在運(yùn)行時(shí)狀態(tài),對(duì)于任意一個(gè)類(lèi)都可以知道這個(gè)類(lèi)中的所有方法和變量,即使時(shí)private私有的修飾也可以,也可以進(jìn)行修改或調(diào)用;對(duì)于任意一個(gè)對(duì)象,也可以調(diào)用其所有的方法
  2. 增加程序的靈活性和擴(kuò)展性,降低耦合性,提高自適應(yīng)能力。
  3. 反射已經(jīng)運(yùn)用在了很多流行框架如:Struts、Hibernate、Spring 等等。

(2)缺點(diǎn):

  1. 使用反射會(huì)有效率問(wèn)題。會(huì)導(dǎo)致程序效率降低。畢竟只是單單訪(fǎng)問(wèn)一個(gè)私有變量,或者調(diào)用一個(gè)方法,就得多條代碼來(lái)實(shí)現(xiàn)。
  2. 反射技術(shù)繞過(guò)了源代碼的技術(shù),因而會(huì)帶來(lái)維護(hù)問(wèn)題。反射代碼比相應(yīng)的直接代碼更復(fù)雜 。

二、枚舉

1、背景及定義

枚舉類(lèi)型是java5中新增特性的一部分 / 在JDK1.5以后引入的),它是一種特殊的數(shù)據(jù)類(lèi)型。枚舉,主要就是將一組常量給組織起來(lái),方便統(tǒng)一管理。

一般我們平時(shí)創(chuàng)建常量最常見(jiàn)的方式如下:

public static final int January = 1;
public static final int February = 2;
public static final int March = 3;
public static final int April = 4;

此處定義的常量本身沒(méi)有問(wèn)題,但是有一定的缺陷,在類(lèi)型安全和使用方便性上并沒(méi)有多少好處,如果存在定義的int類(lèi)型的值剛好一樣的變量存在(就可能把另外一個(gè)變量值為1的直接認(rèn)定為January),可能會(huì)混淆,編譯器也不會(huì)進(jìn)行檢驗(yàn)提示。

現(xiàn)在使用利用枚舉類(lèi)型來(lái)定義組織管理常量,就不會(huì)出現(xiàn)這樣的問(wèn)題,且可以很直觀(guān)的觀(guān)察到常量值,且代碼簡(jiǎn)潔,易管理。

public enum Month {
    //此處為一個(gè)簡(jiǎn)單的枚舉類(lèi)
    //里面的Januar,February,March,April 每一個(gè)是一個(gè)枚舉對(duì)象
    January,February,March,April,May,June,July,August,
    September,October,November,December;
}

??枚舉的應(yīng)用場(chǎng)景:錯(cuò)誤狀態(tài)碼(例如404、405 Http的常見(jiàn)狀態(tài)碼等等),消息類(lèi)型,顏色的劃分,狀態(tài)機(jī)等等...

??本質(zhì):枚舉類(lèi)是 java.lang.Enum 的子類(lèi),也就是說(shuō),自己自定義的枚舉類(lèi),就算沒(méi)有顯示的繼承 Enum ,但是其默認(rèn)繼承了這個(gè)類(lèi)。


2、枚舉的使用

2.1、此處是簡(jiǎn)單的獲取枚舉對(duì)象,直接引用即可?

Java中反射機(jī)制,枚舉,Lambda的使用

枚舉類(lèi)型也是可以被Switch語(yǔ)句支持的,使用switch進(jìn)行條件判斷時(shí),條件參數(shù)一般只能是整型、字符型(需要注意的是使用在于switch條件進(jìn)行結(jié)合使用時(shí),無(wú)需使用Month引用)Java中反射機(jī)制,枚舉,Lambda的使用


?2.2、Enmu常用的方法

方法名稱(chēng)

描述

values()

以Enum數(shù)組的形式返回枚舉類(lèi)的所有對(duì)象

valueOf()

根據(jù)枚舉對(duì)象變量名獲取枚舉實(shí)例

ordinal()

獲取枚舉對(duì)象對(duì)應(yīng)的序號(hào)(索引位置)

compareTo()

比較兩個(gè)枚舉對(duì)象的定義時(shí)的順序

演示方法:

Java中反射機(jī)制,枚舉,Lambda的使用

?輸出結(jié)果:Java中反射機(jī)制,枚舉,Lambda的使用

?

?※ java中枚舉實(shí)際就是一個(gè)類(lèi),所以在定義枚舉時(shí),可以對(duì)其中的枚舉對(duì)象進(jìn)行傳參創(chuàng)建,那么就需要?jiǎng)?chuàng)建對(duì)應(yīng)的構(gòu)造方法。【需要注意的是,枚舉中的所有構(gòu)造方法都是默認(rèn)為“私有的”】

Java中反射機(jī)制,枚舉,Lambda的使用

如果把構(gòu)造方法的訪(fǎng)問(wèn)修飾權(quán)限改為其他的(例如下面把構(gòu)造方法改為公有的),編譯器會(huì)報(bào)錯(cuò)

?Java中反射機(jī)制,枚舉,Lambda的使用


2.3、枚舉的原理

對(duì)于我們使用enum關(guān)鍵字創(chuàng)建的枚舉類(lèi)型,在經(jīng)過(guò)編譯器編譯后,會(huì)為我們定義的枚舉再生成一個(gè)相關(guān)的枚舉類(lèi),該類(lèi)是繼承于java.lang.Enum的。

//查看目錄下的java文件

EnumTest.java
//利用javac命令編譯EnumTest.java

javac -encoidng utf-8 EnumTest.java
//查看生成的class文件,注意有Month.class和EnumDemo.class 兩個(gè)

Month.class  EnumTest.class  EnumTest.java

Java中反射機(jī)制,枚舉,Lambda的使用

利用javac編譯前面定義的EnumTest.java文件后分別生成了Month.class和EnumTest.class文件,而Month.class就是枚舉類(lèi)型,這也就驗(yàn)證前面所說(shuō)的使用關(guān)鍵字enum定義枚舉類(lèi)型并編譯后,編譯器會(huì)自動(dòng)幫助我們生成一個(gè)與枚舉相關(guān)的類(lèi)。

我們?cè)賮?lái)看看反編譯Month.class文件:?

//反編譯Month.class
final class Month extends Enum
{
    //編譯器為我們添加的靜態(tài)的values()方法
    public static Month[] values()
    {
        return (Month[])$VALUES.clone();
    }
    //編譯器為我們添加的靜態(tài)的valueOf()方法,注意間接調(diào)用了Enum也類(lèi)的valueOf方法
    public static Month valueOf(String s)
    {
        return (Month)Enum.valueOf(review/Month, s);
    }
    //私有構(gòu)造函數(shù)
    private Month(String s, int i)
    {
        super(s, i);
    }
     //前面定義的7種枚舉實(shí)例
    public static final Month January;
    public static final Month February;
    public static final Month March;
    public static final Month April;
    public static final Month May;
    public static final Month June;
    public static final Month July;
    public static final Month August;
    public static final Month September;
    public static final Month October;
    public static final Month November;
    public static final Month December;    
    private static final Month $VALUES[];

    static 
    {    
        //實(shí)例化枚舉實(shí)例
        January = new Month("January", 0);
        February = new Month("February", 1);
        March = new Month("March", 2);
        April = new Month("April", 3);
        May = new Month("May", 4);
        June = new Month("June", 5);
        July = new Month("July",6)
        August = new Month("August", 7);
        September = new Month("September", 8);
        October = new Month("August", 9);
        November = new Month("August", 10);
        December = new Month("August", 11);
        $VALUES = (new Month[] {
            January,February,March,April,May,June,July,August,September,October,November,December;
        });
    }
}

??????

????????從反編譯的代碼可以看出編譯器確實(shí)幫助我們生成了一個(gè)Month類(lèi)(注意該類(lèi)是final類(lèi)型的,將無(wú)法被繼承)而且該類(lèi)繼承自java.lang.Enum類(lèi),該類(lèi)是一個(gè)抽象類(lèi)除此之外,編譯器還幫助我們生成了12個(gè)Month類(lèi)型的實(shí)例對(duì)象分別對(duì)應(yīng)枚舉中定義的12個(gè)月份,這也充分說(shuō)明了我們前面使用關(guān)鍵字enum定義的Month類(lèi)型中的每種月份枚舉常量也是實(shí)實(shí)在在的Month實(shí)例對(duì)象,只不過(guò)代表的內(nèi)容不一樣而已。

????????注意編譯器還為我們生成了兩個(gè)靜態(tài)方法,分別是values()和valueOf(),稍后會(huì)分析它們的用法,到此我們也就明白了,使用關(guān)鍵字enum定義的枚舉類(lèi)型,在編譯期后,也將轉(zhuǎn)換成為一個(gè)實(shí)實(shí)在在的類(lèi),而在該類(lèi)中,會(huì)存在每個(gè)在枚舉類(lèi)型中定義好變量的對(duì)應(yīng)實(shí)例對(duì)象,如上述的January枚舉類(lèi)型對(duì)應(yīng)

public static final Month January;,同時(shí)編譯器會(huì)為該類(lèi)創(chuàng)建兩個(gè)方法,分別是values()和valueOf()。


2.4、有關(guān)枚舉的values()方法和valueOf()方法

Java中反射機(jī)制,枚舉,Lambda的使用

??雖然自定義的枚舉都是繼承自Enum的,那么在我們調(diào)用枚舉的values()方法以及valueOf()等方法時(shí),其實(shí)這些方法不存在于我們自定義的枚舉類(lèi)中,而是繼承自Enum父類(lèi)?!敬颂幪厥獾奈ㄓ衯alues()方法在Enum類(lèi)中并沒(méi)有找到,而Enum中的valueOf()方法是兩個(gè)參數(shù)的,咱們調(diào)用的是只有一個(gè)參數(shù)的valueOf()方法?!?但為什么我們還可以調(diào)用該方法呢?

????????雖然 values() 方法并沒(méi)有在 Enum 類(lèi)中定義,但是我們卻可以調(diào)用枚舉類(lèi)的values()方法!

????????這是因?yàn)槊杜e類(lèi)在編譯時(shí)會(huì)自動(dòng)添加一些方法和字段,以便實(shí)現(xiàn)枚舉類(lèi)型的相關(guān)功能。具體來(lái)說(shuō),當(dāng)我們定義一個(gè)枚舉時(shí),編譯器會(huì)自動(dòng)將其轉(zhuǎn)換為一個(gè)類(lèi),該類(lèi)繼承自 Enum 類(lèi)。在該類(lèi)中,編譯器會(huì)自動(dòng)添加一些方法和字段,以便實(shí)現(xiàn)枚舉類(lèi)型的相關(guān)功能。其中,values() 靜態(tài)方法和valueOIf()就是其中編譯器添加的靜態(tài)方法。

????????values()方法和valueOf(String name)方法是編譯器生成的static方法,因此從前面的分析中,在Enum類(lèi)中并沒(méi)出現(xiàn)values()方法,但valueOf()方法還是有出現(xiàn)的,只不過(guò)編譯器生成的valueOf()方法需傳遞一個(gè)name參數(shù),而Enum自帶的靜態(tài)方法valueOf()則需要傳遞兩個(gè)方法【且編譯器創(chuàng)建的枚舉類(lèi)中的靜態(tài)方法valueOf()其實(shí)簡(jiǎn)潔的調(diào)用了Enum類(lèi)中的valueOf()方法】

Java中反射機(jī)制,枚舉,Lambda的使用

//可以反編譯Class文件來(lái)查看,編譯器生成的枚舉類(lèi)中的靜態(tài)valueOf()方法
public static Month valueOf(String name) {
    return (Month )Enum.valueOf(Month.class, name);
}

????????因此values()方法和valueOf(String name)方法都是編譯器生成的,我們前面調(diào)用的一個(gè)參數(shù)的valueOf()方法,其實(shí)就是編譯器提供的靜態(tài)方法,而不是Enum類(lèi)中的。而由于values()方法是編譯器插入到枚舉類(lèi)中來(lái)方便實(shí)現(xiàn)枚舉功能的,如果把枚舉類(lèi)向上轉(zhuǎn)型為Enum類(lèi),那么values()方法就無(wú)法被調(diào)用了,因?yàn)镋num類(lèi)中并沒(méi)有values方法?


3、枚舉的優(yōu)缺點(diǎn)

【重要】:枚舉是不可以被繼承的,因?yàn)槊杜e對(duì)象的構(gòu)造方法是默認(rèn)私有的,繼承的類(lèi)是無(wú)法訪(fǎng)問(wèn)調(diào)用其構(gòu)造方法,子類(lèi)沒(méi)法幫助父類(lèi)去構(gòu)造!

優(yōu)點(diǎn):

  1. 可讀性好,枚舉常量也更簡(jiǎn)單安全
  2. 枚舉具有內(nèi)置的方法(values、valueOf、compareTo、ordinal),代碼更簡(jiǎn)便優(yōu)雅

缺點(diǎn):

  1. 不可繼承,無(wú)法拓展
  2. 連反射機(jī)制也無(wú)法干涉到枚舉,無(wú)法利用反射機(jī)制傳參調(diào)用其構(gòu)造方法創(chuàng)建實(shí)例

4、枚舉 和 反射

????????雖然前面說(shuō)了,可以借助反射機(jī)制在運(yùn)行時(shí),獲取類(lèi)中的所有信息(即使是私有的成員也可以訪(fǎng)問(wèn)并進(jìn)行修改),也可以通過(guò)反射獲取其構(gòu)造方法去創(chuàng)建實(shí)例對(duì)象。但是,對(duì)于枚舉來(lái)說(shuō)(其構(gòu)造方法都是私有的),即使時(shí)反射也無(wú)法獲取修改枚舉類(lèi)中的信息

?此處以這段代碼為例:

package enumdemo;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public enum ReflectEnum {
    Black("黑色",0),RED("紅色",1),BLUE("藍(lán)色",2);    
    //因?yàn)榇颂幍拿杜e對(duì)象有參數(shù),需要提供對(duì)應(yīng)的構(gòu)造方法(一定是private)    
    private String color;    
    private int origial;    
    ReflectEnum(String color,int origial){
        this.color = color;        
        this.origial = origial;    
        }

    private static ReflectEnum getValue(int origial){
        for(ReflectEnum temp:ReflectEnum.values()){
            if(temp.origial == origial){
                return temp;            }
        }
        return null;    }
    public static void createEnumReflect(){
        //利用反射機(jī)制獲取枚舉類(lèi)的構(gòu)造方法,并創(chuàng)建實(shí)例        
        try {
            Class<?>c = Class.forName("enumdemo.ReflectEnum");         
            //注意傳入對(duì)應(yīng)的參數(shù),獲得對(duì)應(yīng)的構(gòu)造方法來(lái)構(gòu)造對(duì)象,當(dāng)前枚舉類(lèi)是提供了兩個(gè)參數(shù)分別是String和int   
            Constructor<?>constructor = c.getDeclaredConstructor(String.class,int.class);            
            constructor.setAccessible(true);            
            ReflectEnum reflectEnum = (ReflectEnum) constructor.newInstance("黑色",0);            
            System.out.println(reflectEnum.color+" "+reflectEnum.origial);        
            } catch (ClassNotFoundException e) {
                e.printStackTrace();        
            } catch (NoSuchMethodException e) {
                e.printStackTrace();        
            } catch (InvocationTargetException e) {
                e.printStackTrace();        
            } catch (InstantiationException e) {
                e.printStackTrace();        
            } catch (IllegalAccessException e) {    
                e.printStackTrace();        
            }
    }

    public static void main(String[] args) {
        createEnumReflect();    
    }
}

Java中反射機(jī)制,枚舉,Lambda的使用

雖然代碼看似沒(méi)問(wèn)題,但是編譯運(yùn)行后就報(bào)錯(cuò)了![錯(cuò)誤:java.lang.NoSuchMethodException: ReflectEnum.(java.lang.String, int),沒(méi)有找到對(duì)應(yīng)的構(gòu)造方法方法]

????????但此處我們傳的是兩個(gè)參數(shù)的構(gòu)造方法,對(duì)應(yīng)其枚舉中的構(gòu)造方法,并沒(méi)有錯(cuò),那問(wèn)題是出在哪?

????????從上面的枚舉的原理,可以知道,使用Enum關(guān)鍵創(chuàng)建的枚舉類(lèi),在編譯器編譯后會(huì)創(chuàng)建一個(gè)類(lèi)繼承子Enum(也可以說(shuō)是所有的枚舉類(lèi)都默認(rèn)繼承自java.lang.Enum),而且我們調(diào)用枚舉類(lèi)的構(gòu)造方法,那么子類(lèi)也得幫助父類(lèi)構(gòu)建構(gòu)造方法。但是我們寫(xiě)的類(lèi)并沒(méi)有幫助父類(lèi)構(gòu)造,這也不意味著我們需要在構(gòu)造方法中提供super()方法,枚舉相對(duì)特殊一點(diǎn),雖然寫(xiě)的是兩個(gè)參數(shù),但默認(rèn)還有兩個(gè)參數(shù)。

?下面觀(guān)察Enum的源碼:

Java中反射機(jī)制,枚舉,Lambda的使用

????????這意味著,前面我們定義的枚舉類(lèi)中的構(gòu)造方法,不僅要提供給他兩個(gè)參數(shù),而且默認(rèn)后面還要給父類(lèi)Enum提供兩個(gè)參數(shù),那么我們?cè)诜瓷涞臅r(shí)候獲取構(gòu)造方法就需要4個(gè)參數(shù),前兩個(gè)代表傳給ReflectEnum的構(gòu)造方法,后兩個(gè)傳給Enum父類(lèi)中的構(gòu)造方法。

Java中反射機(jī)制,枚舉,Lambda的使用

Java中反射機(jī)制,枚舉,Lambda的使用

但是修改之后,還是會(huì)報(bào)錯(cuò)!從報(bào)錯(cuò)信息,可知,報(bào)錯(cuò)的代碼是34行,newInstance()方法出現(xiàn)問(wèn)題,通過(guò)觀(guān)察這個(gè)方法的源碼,可以看到:

Java中反射機(jī)制,枚舉,Lambda的使用

?枚舉在此處直接被過(guò)濾掉了,此處的(clazz.getModifiers() & Modifier.ENUM) != 0,用來(lái)判斷該類(lèi)是否為一個(gè)Enum類(lèi),如果是直接拋出異常。

  • clazz.getModifiers()?返回的是一個(gè)整型值,表示該類(lèi)的訪(fǎng)問(wèn)修飾符(訪(fǎng)問(wèn)修飾符是一個(gè)整型值,其中每個(gè)二進(jìn)制位表示一個(gè)訪(fǎng)問(wèn)修飾符,可以使用?Modifier?類(lèi)中的常量來(lái)解析該整型值)
  • Modifier.ENUM?是一個(gè)表示?enum?訪(fǎng)問(wèn)修飾符的常量,它的值是一個(gè)二進(jìn)制數(shù),表示?enum?訪(fǎng)問(wèn)修飾符對(duì)應(yīng)的二進(jìn)制位

????????&?運(yùn)算符表示按位與運(yùn)算,如果?clazz.getModifiers()?中包含?enum?訪(fǎng)問(wèn)修飾符,則?clazz.getModifiers() & Modifier.ENUM?的結(jié)果不為 0,否則為 0。

????????因此,clazz.getModifiers() & Modifier.ENUM != 0?表示判斷該類(lèi)是否包含?enum?訪(fǎng)問(wèn)修飾符,即判斷該類(lèi)是否為枚舉類(lèi)型。

?因此,我們無(wú)法利用反射機(jī)制來(lái)獲取枚舉類(lèi)的實(shí)例


三、Lambda表達(dá)式

1、背景和含義

????????Lambda表達(dá)式是Java SE 8中一個(gè)重要的新特性。lambda表達(dá)式允許你通過(guò)表達(dá)式來(lái)代替功能接口。 lambda表達(dá)式就和方法一樣,它提供了一個(gè)正常的參數(shù)列表和一個(gè)使用這些參數(shù)的主體(body,可以是一個(gè)表達(dá)式或一個(gè)代碼塊)。Lambda 表達(dá)式(Lambda expression)是一個(gè)?匿名函數(shù)(即沒(méi)有函數(shù)名的函數(shù))?,Lambda表達(dá)式基于數(shù)學(xué)中的?λ演算?得名,。Lambda表達(dá)式可以表示閉包(Closure)。

?1.1、Lambda表達(dá)式的語(yǔ)法

Lambda的基本語(yǔ)法為這兩種:

(parameters) -> expression
(parameters) -> { statements; }

其中,Lambda表達(dá)式可以分為三部分:

  1. parameters?:表示參數(shù)列表(此處的參數(shù)是函數(shù)式接口中的參數(shù)),可以為空或包含一個(gè)或多個(gè)參數(shù)。如果只有一個(gè)參數(shù),可以省略小括號(hào)。這里的參數(shù)類(lèi)型可以明確的聲明也可不聲明而由JVM隱含的推斷。
  2. expression?或?{ statements; }?(方法體):表示函數(shù)體,可以是表達(dá)式,也可以是一條語(yǔ)句或多條語(yǔ)句的代碼塊,是函數(shù)式接口里方法的實(shí)現(xiàn)。
  3. ->: 可以理解成 “被用于”

例子:

//1.無(wú)參有返回值的方法,返回10
 () -> 10;

//2.有參有返回值的方法,返回兩個(gè)參數(shù)之和 
(a,b) -> a+b;

//3.接受兩個(gè)int類(lèi)型的方法,返回兩者乘積(如果參數(shù)的類(lèi)型都一樣,類(lèi)型名可以省略) 
(int a,int b) -> a*b;

//4.接受一個(gè)String類(lèi)型,并執(zhí)行打印到控制臺(tái)的操作(因?yàn)橹挥幸粭l語(yǔ)句,{}可以省略) 
(String s) -> {System.out.println(s);};

1.2、函數(shù)式接口

當(dāng)一個(gè)接口被@FunctionalInterface注解修飾時(shí),說(shuō)明這個(gè)接口是一個(gè)函數(shù)式接口,有且僅有一個(gè)抽象方法。加了這個(gè)注解之后,編譯器會(huì)幫我們?nèi)z查這個(gè)接口是否符合函數(shù)式接口的規(guī)范!

函數(shù)式接口:其實(shí)說(shuō)白了就是只存在一個(gè)抽象方法的接口

在定義函數(shù)式接口時(shí)要注意,一個(gè)函數(shù)式接口有且僅有一個(gè)抽象方法(反過(guò)來(lái)說(shuō),只要一個(gè)接口只有一個(gè)抽象方法,那么這個(gè)接口就是一個(gè)函數(shù)式接口),如果有兩個(gè)及兩個(gè)以上的抽象方法存在,且有@FunctionalInterface注解修飾時(shí),編譯器檢查之后程序就會(huì)報(bào)錯(cuò).

諸如此類(lèi)接口,都是函數(shù)式接口(接口中只能存在抽象方法)

Java中反射機(jī)制,枚舉,Lambda的使用

但是,在 Java 8 中,引入了 default 和 static 修飾符,允許在接口中定義具有默認(rèn)實(shí)現(xiàn)的方法和靜態(tài)方法。這樣做的目的是為了讓接口也能夠包含一些具體的實(shí)現(xiàn),而不僅僅是定義規(guī)范。

default 修飾的方法是具有默認(rèn)實(shí)現(xiàn)的方法,也可以稱(chēng)為默認(rèn)方法,它可以在接口中定義方法體,實(shí)現(xiàn)類(lèi)可以選擇性地覆蓋它。

Java 8 引入 default 和 static 方法是為了增強(qiáng)接口的功能和靈活性,讓接口不再是一組純粹的規(guī)范,而是具有一些默認(rèn)的實(shí)現(xiàn)。

Java中反射機(jī)制,枚舉,Lambda的使用


?2、Lambda表達(dá)式的使用

2.1、Lambda表達(dá)式的使用

此處以一下幾個(gè)函數(shù)式接口為例,來(lái)演示Lambda表達(dá)式的使用:

@FunctionalInterface
interface NoParameterNoReturn {
    void test();    
}

//無(wú)返回值一個(gè)參數(shù)
@FunctionalInterface
interface OneParameterNoReturn {
    void test(int a);
}

//無(wú)返回值多個(gè)參數(shù)
@FunctionalInterface
interface MoreParameterNoReturn {
    void test(int a, int b);
}

//有返回值無(wú)參數(shù)@FunctionalInterface
interface NoParameterReturn {
    int test();
}

//有返回值一個(gè)參數(shù)@FunctionalInterface
interface OneParameterReturn {
    int test(int a);
}

//有返回值多參數(shù)@FunctionalInterface
interface MoreParameterReturn {
    int test(int a, int b);
}

?我們?cè)谏厦嫣岬竭^(guò),Lambda表達(dá)式本質(zhì)是一個(gè)匿名函數(shù),其實(shí)可以理解為:Lambda就是匿名內(nèi)部類(lèi)的簡(jiǎn)化,實(shí)際上是創(chuàng)建了一個(gè)類(lèi),實(shí)現(xiàn)了接口,重寫(xiě)了接口的方法 。

?匿名內(nèi)部類(lèi):是一種沒(méi)有名字的內(nèi)部類(lèi),它可以在創(chuàng)建一個(gè)對(duì)象的時(shí)候進(jìn)行定義,通常用于創(chuàng)建只需要使用一次的類(lèi)。匿名內(nèi)部類(lèi)的定義語(yǔ)法比較特殊,它需要在創(chuàng)建對(duì)象的時(shí)候定義類(lèi)的結(jié)構(gòu),并且在定義的同時(shí)實(shí)現(xiàn)其方法,因此它通常會(huì)比較簡(jiǎn)短。

//匿名內(nèi)部類(lèi)的創(chuàng)建
new 接口名/類(lèi)名(參數(shù)列表) {
    // 類(lèi)的成員變量
    // 類(lèi)的構(gòu)造方法
    // 類(lèi)的成員方法
};
/*
    其中,接口名/類(lèi)名表示要實(shí)現(xiàn)的接口或要繼承的類(lèi),
    參數(shù)列表是用于調(diào)用構(gòu)造方法時(shí)傳遞的參數(shù)。
    花括號(hào)中的部分表示類(lèi)的具體實(shí)現(xiàn),包括成員變量、構(gòu)造方法和成員方法等。
*/

例如,在Java中使用匿名內(nèi)部類(lèi)來(lái)實(shí)現(xiàn)重寫(xiě)Comparator接口中的compare()方法.此處是創(chuàng)建了優(yōu)先級(jí)隊(duì)列,以該接口來(lái)定義隊(duì)列的比較規(guī)則!

Java中反射機(jī)制,枚舉,Lambda的使用

?Java中反射機(jī)制,枚舉,Lambda的使用

?因?yàn)镃omparator接口是一個(gè)函數(shù)式接口,所以可以用Lambda表達(dá)式來(lái)表示里面的抽象方法。而如果使用Lambda表達(dá)式來(lái)簡(jiǎn)化上述的代碼,結(jié)果如下:

Java中反射機(jī)制,枚舉,Lambda的使用

//以下演示都是帶返回值的Lambda表達(dá)式
/*    
    NoParameterReturn noParameterReturn = ()->{return 10;};    
    該Lambda表達(dá)式表示,()不帶參數(shù)有返回值的一個(gè)方法,返回值是10* */
//優(yōu)化版本:
    NoParameterReturn noParameterReturn = ()->10;
    System.out.println(noParameterReturn.test());
//==========================================================
//帶一個(gè)參數(shù)有返回值的方法(如果只是返回一個(gè)值,可以連return都省略)
    OneParameterReturn oneParameterReturn = a->a;
    System.out.println(oneParameterReturn.test(15));
//==========================================================
//帶兩個(gè)參數(shù)有返回值的方法
    MoreParameterReturn moreParameterReturn = (a,b)->a+b;
    System.out.println(moreParameterReturn.test(1,2));

2.2、Lambda表達(dá)式的精簡(jiǎn)

其實(shí)從上述代碼就可以知道,Lambda表達(dá)式可以精簡(jiǎn)哪些地方。

  • 如果參數(shù)列表只有一個(gè)參數(shù),那么小括號(hào)()可以省略。 a -> {return a};
  • 如果參數(shù)列表的參數(shù)都是同一類(lèi)型,可以省略類(lèi)型名,且所有參數(shù)類(lèi)型都要省略,不能只省略一部分。 (a,b) -> a+b;
  • 如果方法體只有一條語(yǔ)句,那么花括號(hào)可以省略掉。 a -> System.out.println(a);
  • 如果方法體只有一條語(yǔ)句,且是return語(yǔ)句,可以省略掉return和花括號(hào). a -> a;

3、變量捕獲

變量捕獲(Variable Capturing)是指在編寫(xiě)嵌套代碼塊時(shí),內(nèi)部代碼塊可以訪(fǎng)問(wèn)外部代碼塊中定義的變量的過(guò)程。在 Java 中,Lambda 表達(dá)式和匿名內(nèi)部類(lèi)都支持變量捕獲。

Lambda 表達(dá)式中的變量捕獲指的是在 Lambda 表達(dá)式中引用的外部變量的處理方式。Lambda 表達(dá)式可以訪(fǎng)問(wèn)外部變量,但是需要注意的是,Lambda 表達(dá)式中引用的外部變量必須是 final 的,即一旦被賦值之后就不能再被修改。

在 Java 8 之前,匿名內(nèi)部類(lèi)中引用外部變量的處理方式是將外部變量轉(zhuǎn)換成內(nèi)部類(lèi)的成員變量,但是在 Lambda 表達(dá)式中,由于 Lambda 表達(dá)式?jīng)]有成員變量的概念,所以需要使用一種不同的方式來(lái)處理外部變量的訪(fǎng)問(wèn)。

Lambda 表達(dá)式中引用的外部變量可以分為兩種類(lèi)型:局部變量和對(duì)象實(shí)例變量。對(duì)于局部變量,Lambda 表達(dá)式會(huì)將其復(fù)制一份,并存儲(chǔ)在 Lambda 表達(dá)式內(nèi)部,這樣即使外部變量被修改,Lambda 表達(dá)式內(nèi)部也不會(huì)受到影響。而對(duì)于對(duì)象實(shí)例變量,Lambda 表達(dá)式可以直接訪(fǎng)問(wèn),因?yàn)閷?duì)象實(shí)例變量是共享的。


3.1 匿名內(nèi)部類(lèi)

匿名內(nèi)部類(lèi):是一種沒(méi)有名字的內(nèi)部類(lèi),它可以在創(chuàng)建一個(gè)對(duì)象的時(shí)候進(jìn)行定義,通常用于創(chuàng)建只需要使用一次的類(lèi)。匿名內(nèi)部類(lèi)的定義語(yǔ)法比較特殊,它需要在創(chuàng)建對(duì)象的時(shí)候定義類(lèi)的結(jié)構(gòu),并且在定義的同時(shí)實(shí)現(xiàn)其方法,因此它通常會(huì)比較簡(jiǎn)短。

此處演示上述出現(xiàn)過(guò)的優(yōu)先級(jí)隊(duì)列,并實(shí)現(xiàn)Comparator接口重寫(xiě)其Compare()方法

PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
    @Override    
    public int compare(Integer o1, Integer o2) {
        return o1.compareTo(o2);    }
});

3.2、匿名內(nèi)部類(lèi)的變量捕獲

與 Lambda 表達(dá)式不同的是,匿名內(nèi)部類(lèi)中引用的外部變量可以是 final、非 final 或沒(méi)有顯式修飾符的局部變量。如果外部變量沒(méi)有顯式修飾符,則默認(rèn)為 final。但是需要注意的是,如果在匿名內(nèi)部類(lèi)中修改非 final 的局部變量,則編譯器會(huì)報(bào)錯(cuò)。

Java中反射機(jī)制,枚舉,Lambda的使用

上述代碼中的name和i變量就是被捕獲的變量,這個(gè)變量不是被final修飾就是非final修飾的,如果是非final修飾的,就要保證其變量值不被修改,要是在內(nèi)部類(lèi)中對(duì)外部變量進(jìn)行修改,那么編譯器就報(bào)錯(cuò)!

Java中反射機(jī)制,枚舉,Lambda的使用


3.3、Lambda變量捕獲

Lambda變量捕獲跟匿名內(nèi)部類(lèi)的變量捕獲差不多,如果在Lambda方法體中修改了外部變量,編譯器一樣會(huì)報(bào)錯(cuò)

Java中反射機(jī)制,枚舉,Lambda的使用


3.4匿名內(nèi)部類(lèi)和Lambda的區(qū)別

匿名內(nèi)部類(lèi)

Lambda

沒(méi)有名字的類(lèi)

沒(méi)有名稱(chēng)的方法(匿名函數(shù))

可以實(shí)現(xiàn)擁有任意方法的接口或者抽象類(lèi)

只能使用在僅有一個(gè)抽象方法的接口中

可以實(shí)例化匿名內(nèi)部類(lèi)

Lambda 表達(dá)式無(wú)法實(shí)例化

每當(dāng)我們創(chuàng)建對(duì)象時(shí),內(nèi)存分配都是按需的

它駐留在JVM的永久內(nèi)存中

在匿名內(nèi)部類(lèi)內(nèi)部,“ this”始終是指當(dāng)前匿名內(nèi)部類(lèi)對(duì)象,而不是外部對(duì)象

在Lambda表達(dá)式內(nèi)部,“ this”始終引用當(dāng)前的外部類(lèi)對(duì)象,即包圍類(lèi)對(duì)象

如果我們要處理多種方法,這是最佳選擇

如果我們要處理接口,這是最佳選擇

匿名類(lèi)可以具有實(shí)例變量和方法局部變量

Lambda表達(dá)式只能具有局部變量

在編譯時(shí),將生成一個(gè)單獨(dú)的.class文件

在編譯時(shí),不會(huì)生成單獨(dú)的.class文件。只是將其轉(zhuǎn)換為外部類(lèi)的私有方法

Lambda表達(dá)式的變量捕獲:?

  • 1、處理方式

Lambda 表達(dá)式中引用的外部變量的處理方式是將其復(fù)制一份,并存儲(chǔ)在 Lambda 表達(dá)式內(nèi)部

  • 2、變量類(lèi)型

Lambda 表達(dá)式中引用的外部變量必須是 final 或 effectively final 的,即一旦被賦值之后就不能再被修改。

匿名內(nèi)部類(lèi)的變量捕獲:

  • 1、處理方式

匿名內(nèi)部類(lèi)中引用外部變量的處理方式是將外部變量轉(zhuǎn)換成內(nèi)部類(lèi)的成員變量

  • 2、變量類(lèi)型

匿名內(nèi)部類(lèi)中引用的外部變量可以是 final、非 final 或沒(méi)有顯式修飾符的局部變量。如果外部變量沒(méi)有顯式修飾符,則默認(rèn)為 final。


4、Lambda在集合中的使用

為了能夠讓Lambda和Java的集合類(lèi)集更好的一起使用,集合當(dāng)中,也新增了部分接口,以便與Lambda表達(dá)式對(duì)接。

接口

新增的方法

Collection

removeIf() spliterator() stream() parallelStream() forEach()

List

replaceAll() sort()

Map

getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()

4.1、Collection接口

forEach()方法的演示:【該方法是在接口Iterable中的,該方法主要是對(duì)容器中的每一個(gè)元素執(zhí)行action中的操作,通過(guò)重寫(xiě)action中的accept方法】

而下面是forEach()方法在Iterable接口中的原型:

Java中反射機(jī)制,枚舉,Lambda的使用

而forEach()方法中調(diào)用了Consumer接口accept()方法,而Consumer接口是一個(gè)函數(shù)式接口

Java中反射機(jī)制,枚舉,Lambda的使用

?(1)采用匿名內(nèi)部類(lèi)的表現(xiàn)形式如下:

Java中反射機(jī)制,枚舉,Lambda的使用

?(2)用Lambda表達(dá)式表示為:

Java中反射機(jī)制,枚舉,Lambda的使用


4.2、List接口?

sort()方法的演示

List.sort()方法的參數(shù)是Comparator比較器接口

Java中反射機(jī)制,枚舉,Lambda的使用

Java中反射機(jī)制,枚舉,Lambda的使用

Java中反射機(jī)制,枚舉,Lambda的使用

該方法時(shí)指定一個(gè)c的比較規(guī)則對(duì)容器元素去比較進(jìn)行排序的。

(1)采用匿名內(nèi)部類(lèi)的表現(xiàn)形式如下:

Java中反射機(jī)制,枚舉,Lambda的使用

(2)用Lambda表達(dá)式表示為:?

Java中反射機(jī)制,枚舉,Lambda的使用


4.3、Map接口?

forEach()方法的演示----HashMap中的forEach()方法如下:

Java中反射機(jī)制,枚舉,Lambda的使用

該方法通過(guò)entrySet()方法映射獲取map中的所有的key-value,然后再傳入action中的accept方法執(zhí)行操作

(1)采用匿名內(nèi)部類(lèi)的表現(xiàn)形式如下:

Java中反射機(jī)制,枚舉,Lambda的使用

?(2)用Lambda表達(dá)式表示為:

Java中反射機(jī)制,枚舉,Lambda的使用


?5、總結(jié)

Lambda表達(dá)式的優(yōu)點(diǎn)很明顯,在代碼層次上來(lái)說(shuō),使代碼變得非常的簡(jiǎn)潔。缺點(diǎn)也很明顯,代碼不易讀。

優(yōu)點(diǎn):

  • 1. 代碼簡(jiǎn)潔,相較于傳統(tǒng)的匿名內(nèi)部類(lèi),可以減少代碼的冗余和嵌套,開(kāi)發(fā)迅速
  • 2. 方便函數(shù)式編程
  • 3. 非常容易進(jìn)行并行計(jì)算(Lambda 表達(dá)式可以提高代碼的執(zhí)行效率,因?yàn)樗鼈兛梢允褂貌⑿刑幚恚瑥亩枚嗪颂幚砥鞯膬?yōu)勢(shì)。)
  • 4. Java 引入 Lambda,改善了集合操作

缺點(diǎn):文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-510444.html

  • 1. 代碼可讀性變差,像有些相同類(lèi)型的參數(shù),其省略了類(lèi)型,就很難直觀(guān)地看出變量的類(lèi)型
  • 2. 在非并行計(jì)算中,很多計(jì)算未必有傳統(tǒng)的 for 性能要高
  • 3. 不容易進(jìn)行調(diào)試,因?yàn)樗鼈冸[藏了一些細(xì)節(jié)和實(shí)現(xiàn)細(xì)節(jié)。因此需要在使用時(shí)注意,盡可能保持代碼的可讀性和可維護(hù)性。

到了這里,關(guān)于Java中反射機(jī)制,枚舉,Lambda的使用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀(guān)點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Java重點(diǎn):反射機(jī)制的使用

    Java重點(diǎn):反射機(jī)制的使用

    目錄 一、概念 二、類(lèi)類(lèi) 1、類(lèi)類(lèi)的獲取方式 1)類(lèi)名.Class 2)對(duì)象.getClass() 3)Class.forName() 三、反射實(shí)例化 1、調(diào)用一個(gè)公有的無(wú)參構(gòu)造方法 2、調(diào)用一個(gè)公有的一個(gè)參構(gòu)造方法 3、調(diào)用一個(gè)公有的兩個(gè)參構(gòu)造方法 4、調(diào)用一個(gè)私有的一個(gè)參構(gòu)造方法 四、反射:方法調(diào)用 1、公有

    2024年02月07日
    瀏覽(20)
  • Java的反射(reflection)機(jī)制的簡(jiǎn)單使用

    Java的反射(reflection)機(jī)制的簡(jiǎn)單使用

    目錄 一、定義 二、用途 三、反射基本信息 四、反射相關(guān)的類(lèi) 五、反射示例 六、反射的優(yōu)點(diǎn)和缺點(diǎn) ? ? ? ? Java的反射機(jī)制是 運(yùn)行時(shí)的狀態(tài) ,可以通過(guò)反射來(lái) 調(diào)用類(lèi)里面的屬性和方法 , 私有的屬性和方法 也可以調(diào)用,也可以 對(duì)它們進(jìn)行修改 。 ? ? ? ? (1)、在第三方

    2024年02月08日
    瀏覽(20)
  • Java基礎(chǔ)篇——反射&枚舉

    B友:https://www.bilibili.com/video/BV1QG4y1J76q/ VIP服務(wù)課程 :https://edu.51cto.com/course/32767.html 當(dāng)程序要使用某個(gè)類(lèi)時(shí),如果該類(lèi)還未被加載到內(nèi)存中,則系統(tǒng)會(huì)通過(guò)類(lèi)的【加載】,【連接】,【初始化】 這三個(gè)步驟來(lái)對(duì)類(lèi)進(jìn)行初始化。 如果不出現(xiàn)意外情況,JVM將會(huì)連續(xù)完成這三個(gè)步

    2024年02月12日
    瀏覽(19)
  • JAVA使用反射機(jī)制和注解實(shí)現(xiàn)對(duì)信息的處理-----JAVA入門(mén)基礎(chǔ)教程

    import java.lang.annotation.Annotation; import java.lang.reflect.Field; public class AnnotationTest { public static void main(String[] args) throws Exception { Class c = Class.forName(\\\"Customer\\\"); c = Customer.class; Table table = (Table)c.getDeclaredAnnotation(Table.class); System.out.println(table.value()); Annotation[] annotations = c.getDeclaredAnnotatio

    2024年02月15日
    瀏覽(20)
  • Java筆記040-反射/反射機(jī)制、Class類(lèi)

    Java筆記040-反射/反射機(jī)制、Class類(lèi)

    目錄 反射(reflection) 一個(gè)需求引出反射 反射機(jī)制 Java反射機(jī)制原理圖 Java反射機(jī)制可以完成 反射相關(guān)的主要類(lèi) 反射機(jī)制的優(yōu)點(diǎn)和缺點(diǎn) 反射調(diào)用優(yōu)化-關(guān)閉訪(fǎng)問(wèn)檢查 Class類(lèi) 基本介紹 代碼解釋部分 類(lèi)加載方法 應(yīng)用實(shí)例:Class02.java 獲取Class類(lèi)對(duì)象 代碼解釋部分 哪些類(lèi)型有Class對(duì)象

    2024年02月09日
    瀏覽(20)
  • Java反射、代理機(jī)制

    官方解釋?zhuān)悍瓷湓试S對(duì)封裝類(lèi)的字段、方法和構(gòu)造方法的信息進(jìn)行編程訪(fǎng)問(wèn)。 虛擬機(jī)加載類(lèi)文件后,會(huì)在方法區(qū)生成一個(gè)類(lèi)對(duì)象,包含了類(lèi)的結(jié)構(gòu)信息,如字段、方法、構(gòu)造方法等。反射是一種能夠在程序運(yùn)行時(shí)動(dòng)態(tài)訪(fǎng)問(wèn)、修改類(lèi)對(duì)象中任意屬性的機(jī)制(包括private屬性)。

    2024年02月10日
    瀏覽(17)
  • Java的反射機(jī)制

    Java 的反射機(jī)制允許在程序運(yùn)行期間,借助反射 API 獲取類(lèi)的內(nèi)部信息,并能直接操作對(duì)象的內(nèi)部屬性及方法。 Java 反射機(jī)制提供的功能: 在運(yùn)行時(shí),使用反射分析類(lèi)的能力,獲取有關(guān)類(lèi)的一切信息(類(lèi)所在的包、類(lèi)實(shí)現(xiàn)的接口、標(biāo)注的注解、類(lèi)的數(shù)據(jù)域、類(lèi)的構(gòu)造器、類(lèi)的

    2024年02月02日
    瀏覽(22)
  • Java的反射機(jī)制(2)

    目錄 Class類(lèi)基本介紹 Class類(lèi)的常用方法 如何獲取class類(lèi)對(duì)象 哪些類(lèi)型有Class對(duì)象 Class類(lèi)基本介紹 在Java語(yǔ)言中,每個(gè)對(duì)象都有一個(gè)運(yùn)行時(shí)類(lèi),即其所屬的類(lèi)。而這個(gè)運(yùn)行時(shí)類(lèi)在Java中是以Class類(lèi)的實(shí)例形式存在的,該Class類(lèi)實(shí)例就是所謂的Class對(duì)象。Class類(lèi)表示一個(gè)類(lèi)或接口的元

    2024年02月08日
    瀏覽(20)
  • Java反射機(jī)制深入詳解

    一.概念 反射就是把Java的各種成分映射成相應(yīng)的Java類(lèi)。 Class類(lèi)的構(gòu)造方法是private,由JVM創(chuàng)建。 反射是java語(yǔ)言的一個(gè)特性,它允程序在運(yùn)行時(shí)(注意不是編譯的時(shí)候)來(lái)進(jìn)行自我檢查并且對(duì)內(nèi)部的成員進(jìn)行操作。例如它允許一個(gè)java的類(lèi)獲取他所有的成員變量和方法并且顯示

    2024年02月06日
    瀏覽(40)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包