Java語言——反射、枚舉以及l(fā)ambda表達式
一.反射
1.1 反射的基本情況
定義:Java在 運行 狀態(tài)時,對于任意一個類,都能知道這個類的所有屬性和方法。
這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的功能稱為java語言的反射(reflection)機制
用途:
1.在日常的第三方應(yīng)用開發(fā)過程中,經(jīng)常會遇到某個類的某個成員變量、方法或是屬性是私有的或是只對系統(tǒng)應(yīng)用開放,這時候就可以利用 Java的反射機制 來獲取所需的私有成員或是方法 。
2. 反射最重要的用途就是開發(fā)各種通用框架,比如在spring中,我們將所有的類Bean交給spring容器管理,無論是XML配置Bean還是注解配置,當(dāng)我們從容器中獲取Bean來依賴注入時,容器會讀取配置,而配置中給的就是類的信息,spring根據(jù)這些信息,需要創(chuàng)建那些Bean,spring就動態(tài)的創(chuàng)建這些類。
1.2 反射中最重要的類
在講解這些類之前,我們需要先構(gòu)建一個類,方便進行反射的操作:
class Student{
//私有屬性name
private String name = "tq02";
//公有屬性age
public int age = 22;
//不帶參數(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 +
'}';
}
}
注:1.反射私有的構(gòu)造方法、屬性、方法時,Java具有安全性,因此我們需要使用.setAccessible("boolean");
2.使用Class類、Field、Constructor類時,需要處理異常。
1.2.1 Class類
在反射之前,第一步就是先拿到當(dāng)前需要反射的類的Class對象,然后通過Class對象的核心方法,達到反射的目的,即:在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象, 都能夠調(diào)用它的任意方法和屬性,既然能拿到,我們就可以修改部分類型信息。
使用Class獲取 類 的三種方法:
第一種:使用Class.forName("類的全路徑名“”); //靜態(tài)方法
第二種:使用.class方法。
第三種:使用類對象的getClass()方法;
注:無論哪種方法獲取,其實獲取的都是同一個類。
代碼實例 :
public class TestDemo {
public static void main(String[] args) {
//1.通過getClass獲取Class對象
Student s1 = new Student();
Class c1 = s1.getClass();
//2.直接通過 類名.class 的方式得到,該方法最為安全可靠,程序性能更高
//這說明任何一個類都有一個隱含的靜態(tài)成員變量 class
Class c2 = Student.class;
//3、通過 Class 對象的 forName() 靜態(tài)方法來獲取,用的最多,
//但可能拋出 ClassNotFoundException 異常
Class c3 = null;
try {
//注意這里是類的全路徑,如果有包需要加包的路徑
c3 = Class.forName("Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//一個類在 JVM 中只會有一個 Class 實例,即我們對上面獲取的
//c1,c2,c3進行 equals 比較,發(fā)現(xiàn)都是true
System.out.println(c1.equals(c2));
System.out.println(c1.equals(c3));
System.out.println(c2.equals(c3));
}
1.2.2 Field類
作用:可對類中屬性進行操作
public static void reflectPrivateField() {
try {
Class<?> classStudent = Class.forName("Student");
//獲取name成員變量
Field field = classStudent.getDeclaredField("name");
field.setAccessible(true);
Student student= (Student)classStudent.newInstance();
//修改成員變量,將student中的name值改成"小明";
field.set(student, "小明");
String name = (String) field.get(student);
System.out.println("反射私有屬性修改了name:" + name);
} catch (Exception ex) {
ex.printStackTrace();
}
}
1.2.3 Constructor類
作用:對構(gòu)造方法進行操作
代碼實例:
//反射構(gòu)造方法
public static void reflect() {
try {
Class<?> c1 = Class.forName("Student");
Constructor<?> c2= c1.getDeclaredConstructor(String.class,int.class);
c2.setAccessible(true);
c2.newInstance("湯琦",22);
}catch(Exception ex)
{
ex.printStackTrace();
}
}
1.2.4 Method類
作用:對類中方法進行操作
實例代碼:
public static void reflectPrivateMethod() {
try {
Class<?> c1 = Class.forName("Student");
Method m1=c1.getDeclaredMethod("function", String.class);
m1.setAccessible(true);
Student fw=(Student) c1.newInstance();
m1.invoke(fw,"給私有的function函數(shù)傳的參數(shù)");
}catch(Exception ex)
{
ex.printStackTrace();
}
}
1.3 反射優(yōu)缺點
優(yōu)點: 1. 對于任意一個類,都能夠知道這個類的所有屬性和方法;對 于任意一個對象,都能夠調(diào)用它的任意一個方法
2. 增加程序的靈活性和擴展性,降低耦合性,提高自適應(yīng)能力
3. 反射已經(jīng)運用在了很多流行框架如:Struts、Hibernate、Spring 等等。
缺點: 1. 使用反射會有效率問題。會導(dǎo)致程序效率降低。
2. 反射技術(shù)繞過了源代碼的技術(shù),因而會帶來維護問題。反射代碼比相應(yīng)的直接代碼更復(fù)雜
二.枚舉
2.1 概念
在Java中,可以說是一個集合,從下標(biāo)0開始的集合。注:枚舉是jdk1.5以后引用的。
使用格式: public enum 類名{
常量1、常量2、常量3;
}
就是將class換成了enum
代碼實例:
public enum TestEnum {
RED,BLACK,GREEN,WHITE;//相當(dāng)于集合,第一個常量下標(biāo)值為0,第二個常量下標(biāo)值為1......
public static void main(String[] args) {
TestEnum testEnum2 = TestEnum.BLACK;
switch (testEnum2) {
case RED: System.out.println("red"); break;
case BLACK:System.out.println("black");break;
case WHITE:System.out.println("WHITE");break;
case GREEN:System.out.println("black");break;
default:break;
}
}
2.2 枚舉(enum)類方法
2.3 枚舉的構(gòu)造
枚舉的構(gòu)造方法默認是私有的。
public enum TestEnum {
RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
private String name;
private int key;
/**
* 1、當(dāng)枚舉對象有參數(shù)后,需要提供相應(yīng)的構(gòu)造函數(shù)
* 2、枚舉的構(gòu)造函數(shù)默認是私有的 這個一定要記住
* @param name
* @param key
*/
private TestEnum (String name,int key) {
this.name = name;
this.key = key;
}
public static TestEnum getEnumKey (int key) {
for (TestEnum t: TestEnum.values()) {
if(t.key == key) {
return t;
}
}
return null;
}
public static void main(String[] args) {
System.out.println(getEnumKey(2));
}
}
注:自己寫的枚舉類,默認繼承與enum這個類的。
三.Lambda表達式
3.1 Lambda介紹
Lambda本質(zhì)是匿名函數(shù),基于數(shù)學(xué)中的λ演算得名,也可稱為閉包(Closure)
語法格式:(parameters)->expression 或 (parameters)->{ statements;}
parameters:類似方法中的形參列表,這里的參數(shù)是函數(shù)式接口里的參數(shù)。這里的參數(shù)類型可以明確的聲明也可不聲明而由JVM隱含的推斷。另外當(dāng)只有一個推斷類型時可以省略掉圓括號。
->:可理解為“被用于”的意思
方法體:可以是表達式也可以代碼塊,是函數(shù)式接口里方法的實現(xiàn)。代碼塊可返回一個值或者什么都不反回,這里的代碼塊塊等同于方法的方法體。如果是表達式,也可以返回一個值或者什么都不反回。
常見表達式:
// 1. 不需要參數(shù),返回值為 2
() -> 2
// 2. 接收一個參數(shù)(數(shù)字類型),返回其2倍的值
x -> 2 * x
// 3. 接受2個參數(shù)(數(shù)字),并返回他們的和
(x, y) -> x + y
// 4. 接收2個int型整數(shù),返回他們的乘積
(int x, int y) -> x * y
// 5. 接受一個 string 對象,并在控制臺打印,不返回任何值(看起來像是返回void)
(String s) -> System.out.print(s)
3.2 函數(shù)式接口
定義:該接口有且只有一個 抽象方法
注:如果某接口含有@FunctionalInterface 注解,那么編譯器就會按照函數(shù)式接口的定義來要求該接口,這樣如果有兩個抽象方法,程序編譯就會報錯的。
代碼實例:
@FunctionalInterface
interface NoParameterNoReturn {
//注意:只能有一個方法
void test();
}
3.3 使用lambda表達式
先建立幾個接口:
//無返回值無參數(shù)
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
//無返回值一個參數(shù)
@FunctionalInterface
interface OneParameterNoReturn {
void test(int a);
}
//無返回值多個參數(shù)
@FunctionalInterface
interface MoreParameterNoReturn {
void test(int a,int b);
}
//有返回值無參數(shù)
@FunctionalInterface
interface NoParameterReturn {
int test();
}
//有返回值一個參數(shù)
@FunctionalInterface
interface OneParameterReturn {
int test(int a);
}
//有返回值多參數(shù)
@FunctionalInterface
interface MoreParameterReturn {
int test(int a,int b);
}
Lambda就是匿名內(nèi)部類的簡化,實際上是創(chuàng)建了一個類,實現(xiàn)了接口,重寫了接口的方法 。
3.3.1 不使用Lambda表達式調(diào)用
public class TestDemo {
public static void main(String[] args) {
//接口使用匿名內(nèi)部類
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
@Override
public void test() {
System.out.println("hello");
}
};
noParameterNoReturn.test();
}
3.3.2 使用Lambda表達式
public class TestDemo {
public static void main(String[] args) {
NoParameterNoReturn noParameterNoReturn = ()->{
System.out.println("無參數(shù)無返回值");
};
noParameterNoReturn.test();
OneParameterNoReturn oneParameterNoReturn = (int a)->{
System.out.println("一個參數(shù)無返回值:"+ a);
};
oneParameterNoReturn.test(10);
MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->{
System.out.println("多個參數(shù)無返回值:"+a+" "+b);
};
moreParameterNoReturn.test(20,30);
NoParameterReturn noParameterReturn = ()->{
System.out.println("有返回值無參數(shù)!");
return 40;
};
//接收函數(shù)的返回值
int ret = noParameterReturn.test();
System.out.println(ret);
OneParameterReturn oneParameterReturn = (int a)->{
System.out.println("有返回值有一個參數(shù)!");
return a;
};
ret = oneParameterReturn.test(50);
System.out.println(ret);
MoreParameterReturn moreParameterReturn = (int a,int b)->{
System.out.println("有返回值多個參數(shù)!");
return a+b;
};
ret = moreParameterReturn.test(60,70);
System.out.println(ret);
}
}
3.3.3 二者區(qū)別
代碼實例:
1. 參數(shù)類型可以省略,如果需要省略,每個參數(shù)的類型都要省略。
2. 參數(shù)的小括號里面只有一個參數(shù),那么小括號可以省略
3. 如果方法體當(dāng)中只有一句代碼,那么大括號可以省略
4. 如果方法體中只有一條語句,且是return語句,那么大括號可以省略,且去掉return關(guān)鍵字。
3.4 變量捕獲
變量捕獲,在匿名內(nèi)部類中也存在,而類似匿名內(nèi)部類的Lambda表達式,自然而然也存在。
3.4.1 匿名內(nèi)部類的變量捕獲
外,已經(jīng)定義了a的值,因此匿名內(nèi)部類直接捕獲了外部的a變量。
3.4.2 Lambda變量捕獲
如上圖,使用直接捕獲了外部的a變量。
注:無論是匿名內(nèi)部類的變量捕獲還是Lambda變量捕獲,方法體里,不可修改外部變量的值。
總結(jié)
反射、枚舉以及Lambda表達式很少使用,算是偏僻的知識點,因此不要求掌握,只要求熟悉。