一,什么是反射
1.1,反射:所謂框架的靈魂
框架:半成品軟件,可以在框架的基礎上進行軟件開發,簡化代碼。
反射:將類的各個組成部分封裝為其他對象,反射機制。
簡單來說反射就是在程序運行期間,動態的獲取類或者對象中的屬性。
什么是動態獲取。
反射的原理就是通過類的字節碼文件(class文件)反向獲取該類或者對象中的屬性,既然是通過字節碼獲取,這就需要JVM的操作了。下面請看API文檔的說明:
在上圖最后一句話中,文檔說的很清楚,反射是在加載類時由JVM進行操作。
1.2,動靜態編譯
- 靜態編譯:在編譯期就確定類或者方法的屬性,有點一次到位的意思。
- 動態編譯:在運行期確定類或者方法的屬性,好比什么時候用就什么時候編譯。
但是這兩種編譯方式有什么區別,先說靜態編譯吧。我想大家都遇到過項目需求頻繁變更的情況,可能是更改需求,可能是添加新的需求。對於靜態編譯,因為這是一次性編譯,對於確定的代碼是不能更改的,除非下線,更改,測試,再重新上線,顯然這是不妥的。
因此就需要動態編譯,即在程序運行期間也可以進行相應的操作,一切操作方式都是靈活的,所以說反射對於程序是多么重要。
1.3,優缺點
先來說說反射的優點:
1,可以在程序運行的過程中,操作這些對象。
2,可以解耦,提高程序的可擴展性。
缺點:
1,因為是JVM操作,所以對於性能來說會有所下降。
2,容易對程序源碼造成一定的混亂。
1.4,反射圖解
注意:同一個字節碼文件(*.class)在程序運行過程中,只會被加載一次。
1.5,反射獲取方式
獲取Class對象的方式:
1,Class.forName("全類名"):將字節碼文件加載進內存,返回class對象
多用於配置文件中,將類名定義在配置文件中,讀取文件並加載類。
2,類名.class:通過類名的屬性class獲取。
多用於參數的構造。
3,對象.getClass():該方法定義在Object中
多用於對象的字節碼獲取。
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
// Class.forName("");
Class c1 = Class.forName("com.api.reflect.User");
System.out.println(c1);
// 類名.class
Class<User> c2 = User.class;
System.out.println(c2);
// .getClass
User user = new User();
Class c3 = user.getClass();
System.out.println(c3);
System.out.println(c1 == c2);
System.out.println(c1 == c3);
System.out.println(c2 == c3);
}
}
運行結果:
分析:
通過以上三種反射方式都可以獲得實例對象,同樣也證明三個對象是相同的,也就是對象只會被創建一次。
二,反射常用方法
2.1,成員變量
public Field getField(String name)
:獲取指定名稱的成員變量(public)。public Field[] getFields()
:獲取全部成員變量(public)。public Field getDeclaredField(String name)
:不考慮修飾符。public Field[] getDeclaredFields()
:不考慮修飾符。
創建一個User對象。
public class User implements Serializable {
public String name;
protected Integer age;
Integer sex;
private String phone;
public User() {
}
public User(String name, Integer age, Integer sex, String phone) {
this.name = name;
this.age = age;
this.sex = sex;
this.phone = phone;
}
private User(String name, Integer age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", phone='" + phone + '\'' +
'}';
}
public void run() {
System.out.println("跑步...");
}
private void eat(String username) {
System.out.println(username + "正在吃飯...");
}
}
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
// 1,通過Class.forName方式獲取User對象
Class aClass = Class.forName("com.api.reflect.User");
// 獲取public修飾的成員變量
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println("1,public修飾的成員變量--->" + field);
}
// 2,獲取指定成員變量名稱
Field name = aClass.getField("name");
System.out.println("2,指定成員變量名稱--->" + name);
// 3,獲取全部的成員變量,忽略修飾符
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("3,不考慮修飾符,獲取全部成員變量--->" + declaredField);
}
// 4,獲取指定成員變量
Field phone = aClass.getDeclaredField("phone");
// 獲取訪問權限,暴力反射
phone.setAccessible(true);
System.out.println("4,暴力反射--->" + phone);
}
}
以上代碼運行結果:
2.2,構造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getConstructors()
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
public Constructor<?>[] getDeclaredConstructors()
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
// 通過Class.forName方式獲取User對象
Class aClass = Class.forName("com.api.reflect.User");
// 1,獲取指定構造方法(public),參數是可變參數
Constructor constructor = aClass.getConstructor(String.class, Integer.class, Integer.class, String.class);
// 實例化對象
Object user = constructor.newInstance("張三", 20, 1, "123456");
System.out.println(user);
// 2,獲取全部構造方法(public)
Constructor[] constructors = aClass.getConstructors();
for (Constructor constructor1 : constructors) {
System.out.println(constructor1);
}
// 3,不考慮修飾符,獲取指定構造方法
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class);
// 獲取權限
declaredConstructor.setAccessible(true);
Object declaredUser = declaredConstructor.newInstance("李四", 21);
System.out.println("不考慮修飾符--->" + declaredUser);
// 4,獲取全部構造方法
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor declaredConstructor1 : declaredConstructors) {
System.out.println("不考慮修飾符--->" + declaredConstructor1);
}
}
}
以上代碼運行結果:
2.3,成員方法
public Method getMethod(String name,Class<?>... parameterTypes)
public Method[] getMethods()
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
public Method[] getDeclaredMethods()
public class ReflectMethod {
public static void main(String[] args) throws Exception {
// 通過Class.forName方式獲取User對象
Class aClass = Class.forName("com.api.reflect.User");
// 1,獲取指定成員方法,public修飾
Method methodRun = aClass.getMethod("run");
// 實例化User,並調用invoke()執行方法
Object user = aClass.newInstance();
methodRun.invoke(user);
// 2,獲取全部成員方法,public修飾
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
// 3,獲取私有成員方法
Method eat = aClass.getDeclaredMethod("eat", String.class);
// 獲取權限
eat.setAccessible(true);
// 執行方法
eat.invoke(user,"小李");
}
}
執行結果為:
也許你有疑惑,哪里來這么多的方法。
請詳細看除了User對對象中的屬性構造方法外,還有Object類中的方法。如下:
這是為什么,請看API文檔解釋說明:
包括那些由該類或接口聲明的以及從超類和超接口繼承的那些的類或接口。
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
三,總結
關於反射中的常用方法就總結到此,反射的用處還是很多的,比如Spring中IOC,DI都是利用反射機制實現的,當然這些會在另一篇博客中總結出來。
以上內容均是自主總結,如有不適之處歡迎留言指正。
感謝閱讀!