反射簡介
反射是Java的高級特性之一,但是在實際的開發中,使用Java反射的案例卻非常的少,但是反射確實在底層框架中被頻繁的使用。
比如:JDBC中的加載數據庫驅動程序,Spring框架中加載bean對象,以及態代理,這些都使用到反射,因為我們要想理解一些框架的底層原理,反射是我們必須要掌握的。
理解反射我們先從他的概念入手,那么什么是反射呢?
反射就是在運行狀態能夠動態的獲取該類的屬性和方法,並且能夠任意的使用該類的屬性和方法,這種動態獲取類信息以及動態的調用對象的方法的功能就是反射。
實現上面操作的前提是能夠獲取到該類的字節碼對象,也就是.class文件,在反射中獲取class文件的方式有三種:
- 類名.class 如:Person.class
- 對象.class 如:person.class
- Class.forName(全類名)獲取 如:Class.forName("ldc.org. demo.person")
這里有點區別的就是使用1,2(.class)方式獲取Class對象,並不會初始化Class對象,而使用3(forName("全類名"))的方式會自動初始化Class對象。
反射
反射對應到Java中的類庫就是在java.lang.reflect下,在該包下包含着Field、Method和Constructor類。
Field: 表示一個類的屬性信息
Method: 表示類的方法信息
Constructor: 表示的是類的構造方法的信息
在反射中常用的方法,我這里做了一個列舉,當然更加詳細的可以查官方的API文檔進行學習。
方法名 | 作用 |
---|---|
getConstructors() | 獲取公共構造器 |
getDeclaredConstructors() | 獲取所有構造器 |
newInstance() | 獲取該類對象 |
getName() | 獲取類名包含包路徑 |
getSimpleName() | 獲取類名不包含包路徑 |
getFields() | 獲取類公共類型的所有屬性 |
getDeclaredFields() | 獲取類的所有屬性 |
getField(String name) | 獲取類公共類型的指定屬性 |
getDeclaredField(String name) | 獲取類全部類型的指定屬性 |
getMethods() | 獲取類公共類型的方法 |
getDeclaredMethods() | 獲取類的所有方法 |
getMethod(String name, Class[] parameterTypes) | 獲得類的特定公共類型方法 |
getDeclaredClasses() | 獲取內部類 |
getDeclaringClass() | 獲取外部類 |
getPackage() | 獲取所在包 |
User 類:
點擊查看代碼
package com.example.zhangchonghu.demo.controller.reflect;
/**
* @Description:
* @author: 張重虎
* @Date: 2022/2/17 11:20
* @Version 1.0
*/
public class User {
private String name;
public Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
private void privateMethod() {
System.err.println("私有方法執行了");
}
public void publicMethod(String param){
System.err.println("公有方法執行了,參數為:"+param);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
在User的實體類中,有兩個屬性age和name,並且除了有兩個測試方法privateMethod和publicMethod用於測試私有方法和公共方法的獲取。接着執行如下代碼:
點擊查看代碼
package com.example.zhangchonghu.demo;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Description:
* @author: 張重虎
* @Date: 2022/2/17 11:54
* @Copyright: Xi'an Dian Tong Software Co., Ltd. All Rights Reserved.
* @Version 1.0
*/
public class TestReflect {
@Test
public void test() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException, ClassNotFoundException {
//1、類名.class
/**
* Class clazz = User.class;
*/
//2、對象.class
/**
* User user = new User();
* Class clazz = user.getClass();
*/
//3、Class.forName(全類名)獲取
Class clazz = Class.forName("com.example.zhangchonghu.demo.controller.reflect.User");
Constructor constructor = clazz.getConstructor(String.class, Integer.class);
//獲取該類對象並設置屬性的值
Object obj = constructor.newInstance("張三", 18);
//獲得類全類名,既包含包路徑
String fullClassName = clazz.getName();
//獲得類名
String className = clazz.getSimpleName();
//獲取類中公共類型(public)屬性
Field[] fields = clazz.getFields();
String fieldName = "";
for (Field field : fields) {
//獲取屬性名
fieldName = field.getName();
System.out.println("public 屬性名為:" + fieldName);
}
//獲取類中所有的屬性,包括 private 屬性
Field[] fieldsAll = clazz.getDeclaredFields();
fieldName = "";
for (Field field : fieldsAll) {
fieldName = field.getName();
System.out.println("private 屬性名為:" + fieldName);
}
//獲取指定公共屬性值
Field age = clazz.getField("age");
Object value = age.get(obj);
System.err.println("公共指定屬性:" + value);
//獲得指定的私有屬性值
Field name = clazz.getDeclaredField("name");
//設置為true才能獲取私有屬性
name.setAccessible(true);
Object value2 = name.get(obj);
System.err.println("私有指定屬性值:" + value2);
//獲取所有公共類型方法 這里包括 Object 類的一些方法
Method[] methods = clazz.getMethods();
String methodsName = "";
for (Method method : methods) {
methodsName = method.getName();
System.out.println("公開的方法:" + methodsName);
}
//獲取該類中的所有方法(包括private)
Method[] methodsAll = clazz.getDeclaredMethods();
methodsName = "";
for (Method method : methodsAll) {
methodsName = method.getName();
System.out.println("私有的方法:" + methodsName);
}
//獲取並使用指定方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");//獲取無參私有方法
privateMethod.setAccessible(true);
privateMethod.invoke(obj);//調用方法
Method publicMethod = clazz.getMethod("publicMethod", String.class);//獲取有參數方法
publicMethod.invoke(obj, "張三");//調用有參方法
}
}
運行結果(多次運行結果順序不相同,盲猜是和多線程有關。埋個雷,沒有深入挖掘):
反射在jdk 1.5的時候允許對Class對象能夠支持泛型,也稱為泛化Class,具體的使用如下:
Class<User> user= User.class;
//泛化class可以直接得到具體的對象,而不再是Object
Useruser= user.newInstance();
泛化實現了在獲取實例的時候直接就可以獲取到具體的對象,因為在編譯器的時候就會做類型檢查。當然也可以使用通配符的方式,例如:Class<?>
反射優點和缺點
優點:反射可以動態的獲取對象,調用對象的方法和屬性,並不是寫死的,比較靈活,比如你要實例化一個bean對象,你可能會使用new User()寫死在代碼中。
但是使用反射就可以使用class.forName(user).newInstance(),而變量名user可以寫在xml配置文件中,這樣就不用修改源代碼,靈活、可配置。
缺點:反射的性能問題一直是被吐槽的地方,反射是一種解釋操作,用於屬性字段和方法的接入時要遠遠慢於直接使用代碼,因此普通程序也很少使用反射。