目錄
一、反射機(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ì)直接返回其完整的路徑名)
?
?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):
- 在運(yùn)行時(shí)狀態(tài),對(duì)于任意一個(gè)類(lèi)都可以知道這個(gè)類(lèi)中的所有方法和變量,即使時(shí)private私有的修飾也可以,也可以進(jìn)行修改或調(diào)用;對(duì)于任意一個(gè)對(duì)象,也可以調(diào)用其所有的方法
- 增加程序的靈活性和擴(kuò)展性,降低耦合性,提高自適應(yīng)能力。
- 反射已經(jīng)運(yùn)用在了很多流行框架如:Struts、Hibernate、Spring 等等。
(2)缺點(diǎn):
- 使用反射會(huì)有效率問(wèn)題。會(huì)導(dǎo)致程序效率降低。畢竟只是單單訪(fǎng)問(wèn)一個(gè)私有變量,或者調(diào)用一個(gè)方法,就得多條代碼來(lái)實(shí)現(xiàn)。
- 反射技術(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ì)象,直接引用即可?
枚舉類(lèi)型也是可以被Switch語(yǔ)句支持的,使用switch進(jìn)行條件判斷時(shí),條件參數(shù)一般只能是整型、字符型(需要注意的是使用在于switch條件進(jìn)行結(jié)合使用時(shí),無(wú)需使用Month引用)
?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í)的順序 |
演示方法:
?輸出結(jié)果:
?
?※ 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)為“私有的”】
如果把構(gòu)造方法的訪(fǎng)問(wèn)修飾權(quán)限改為其他的(例如下面把構(gòu)造方法改為公有的),編譯器會(huì)報(bào)錯(cuò)
?
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
利用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()方法
??雖然自定義的枚舉都是繼承自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()方法】
//可以反編譯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):
- 可讀性好,枚舉常量也更簡(jiǎn)單安全
- 枚舉具有內(nèi)置的方法(values、valueOf、compareTo、ordinal),代碼更簡(jiǎn)便優(yōu)雅
缺點(diǎn):
- 不可繼承,無(wú)法拓展
- 連反射機(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();
}
}
雖然代碼看似沒(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的源碼:
????????這意味著,前面我們定義的枚舉類(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)造方法。
但是修改之后,還是會(huì)報(bào)錯(cuò)!從報(bào)錯(cuò)信息,可知,報(bào)錯(cuò)的代碼是34行,newInstance()方法出現(xiàn)問(wèn)題,通過(guò)觀(guān)察這個(gè)方法的源碼,可以看到:
?枚舉在此處直接被過(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á)式可以分為三部分:
- parameters?:表示參數(shù)列表(此處的參數(shù)是函數(shù)式接口中的參數(shù)),可以為空或包含一個(gè)或多個(gè)參數(shù)。如果只有一個(gè)參數(shù),可以省略小括號(hào)。這里的參數(shù)類(lèi)型可以明確的聲明也可不聲明而由JVM隱含的推斷。
- expression?或?{ statements; }?(方法體):表示函數(shù)體,可以是表達(dá)式,也可以是一條語(yǔ)句或多條語(yǔ)句的代碼塊,是函數(shù)式接口里方法的實(shí)現(xiàn)。
- ->: 可以理解成 “被用于”
例子:
//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 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)。
?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ī)則!
?
?因?yàn)镃omparator接口是一個(gè)函數(shù)式接口,所以可以用Lambda表達(dá)式來(lái)表示里面的抽象方法。而如果使用Lambda表達(dá)式來(lái)簡(jiǎn)化上述的代碼,結(jié)果如下:
//以下演示都是帶返回值的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ò)。
上述代碼中的name和i變量就是被捕獲的變量,這個(gè)變量不是被final修飾就是非final修飾的,如果是非final修飾的,就要保證其變量值不被修改,要是在內(nèi)部類(lèi)中對(duì)外部變量進(jìn)行修改,那么編譯器就報(bào)錯(cuò)!
3.3、Lambda變量捕獲
Lambda變量捕獲跟匿名內(nèi)部類(lèi)的變量捕獲差不多,如果在Lambda方法體中修改了外部變量,編譯器一樣會(huì)報(bào)錯(cuò)
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接口中的原型:
而forEach()方法中調(diào)用了Consumer接口accept()方法,而Consumer接口是一個(gè)函數(shù)式接口
?(1)采用匿名內(nèi)部類(lèi)的表現(xiàn)形式如下:
?(2)用Lambda表達(dá)式表示為:
4.2、List接口?
sort()方法的演示
List.sort()方法的參數(shù)是Comparator比較器接口
該方法時(shí)指定一個(gè)c的比較規(guī)則對(duì)容器元素去比較進(jìn)行排序的。
(1)采用匿名內(nèi)部類(lèi)的表現(xiàn)形式如下:
(2)用Lambda表達(dá)式表示為:?
4.3、Map接口?
forEach()方法的演示----HashMap中的forEach()方法如下:
該方法通過(guò)entrySet()方法映射獲取map中的所有的key-value,然后再傳入action中的accept方法執(zhí)行操作
(1)采用匿名內(nèi)部類(lèi)的表現(xiàn)形式如下:
?(2)用Lambda表達(dá)式表示為:
?5、總結(jié)
Lambda表達(dá)式的優(yōu)點(diǎn)很明顯,在代碼層次上來(lái)說(shuō),使代碼變得非常的簡(jiǎn)潔。缺點(diǎn)也很明顯,代碼不易讀。
優(yōu)點(diǎn):文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-510444.html
- 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)!