- Java Reflection
Reflection(反射)是被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法(包括私有的方法和屬性)。
Java反射機制提供的功能:
- 在運行時判斷任意一個對象所屬的類
- 在運行時構造任意一個類的對象
- 在運行時判斷任意一個類所具有的成員變量和方法
- 在運行時調用任意一個對象的成員變量和方法
- 生成動態代理
- 對反射內涵的理解
其實,自己理解一下Java反射機制,再複述出來就是:它就是一面“鏡子”,能夠映照出所有,Java中,一切皆爲對象,無論是class,或者裏面的屬性、方法、接口等等,通通可以理解爲對象,如果程序處在運行當中,想要改變它的方法參數或者其他對象時,該怎麼處理呢?
對這個問題的解釋就要涉及程序的兩種編譯方式了,一種是靜態編譯,在編譯時就確定類型,綁定對象;另一種就是運用了反射機制的動態編譯,在程序運行時確定類型,綁定對象。
而要想利用Java反射機制去解剖一個類,其前提是,必須先要獲取到該類的字節碼文件(*.class)對象。爲什麼是這樣呢?從java程序的編譯過程來看,它最終要變成字節碼文件(*.class),才能被移植到各個裝有JVM的機器上運行。因此,反射機制利用的工具源頭就是這個字節碼文件。每一個類對應着一個字節碼文件也就對應着一個class類型的對象,即字節碼文件對象。
如圖是類的正常加載過程:Class對象的由來是將class文件讀入內存,併爲之創建一個Class對象。
也就是說,正常創建並加載類的方式是:
而反射方式則是:
這裏的getClass()方法是什麼意思呢?其實,在Object類(我們知道它是所有類的鼻祖)中定義了以下的方法,此方法將被所有子類繼承:
● public final Class getClass()
以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解, 即:可以通過對象反射求出類的名稱。
- Class類
通過閱讀Class類的源碼,我們可以知道以下關於Class類的事:
對於每個類而言,JRE 都爲其保留一個不變的 Class 類型的對象。一個Class 對象包含了特定某個類的有關信息。
- Class本身也是一個類,只是名字與class關鍵字高度相似,而Java是區分大小寫的語言
- Class 對象只能由系統(JVM)建立對象,因爲這個類沒有public構造函數,構造函數爲private
- 一個類在 JVM 中只會有一個Class實例
- 一個Class對象對應的是一個加載到JVM中的一個.class文件
- 每個類的實例都會記得自己是由哪個 Class 實例所生成
- 通過Class可以完整地得到一個類中的完整結構
Class類的常用方法(見表,後面會有代碼演示,其他方法請參考API手冊)
實例化Class類對象(四種方法)
其實這小節講解的就是獲取字節碼文件對象的四種方法。
1.在源文件階段:
Class class1 = Class.forName("包名.類名");
//裝入類,並做類的靜態初始化,返回Class的對象
2.在字節碼階段
Class class2 = Person.class;
//JVM將使用類裝載器,將類裝入內存(前提是:類還沒有裝入內存),不做類的初始化工作,返回Class的對象
3.在創建對象階段
Person person = new Person();
Class c3=Person.getClass();
//對類進行靜態初始化、非靜態初始化;返回引用運行時真正所指的對象(子對象的引用會賦給父對象的引用變量中)所屬的類的Class的對象
4.在類加載器階段
//第四種(瞭解)利用類加載器,返回class類型
//得到類的加載器
ClassLoader classLoader = this.getClass().getClassLoader();
//加載類 返回class對象 傳入包類名稱
Class class4=classLoader.loadClass("com.my.Person");
由於篇幅所限,這四種實例化Class對象的方法的區別就說到這裏,關於他們之間的區別爲什麼是這樣,這位博主從源碼的角度去闡述,我將鏈接附在這裏:
https://blog.csdn.net/zhaominpro/article/details/89810941
Class創建對象的方式
好了,現在我們有了Class對象,能做什麼?我們可以創建類的對象:調用Class對象的newInstance()方法。要求:1)類必須有一個無參數的構造器。2)類的構造器的訪問權限需要足夠。
難道沒有無參的構造器就不能創建對象了嗎?
不是!只要在操作的時候明確的調用類中的構造方法,並將參數傳遞進去之後,纔可以實例化操作。步驟如下: 1)通過Class類的getDeclaredConstructor(Class ... parameterTypes) 取得本類的指定形參類型的構造器 2)向構造器的形參中傳遞一個對象數組進去,裏面包含了構造器中所需的各個參數。3)通過Constructor實例化對象。
下面我們來演示一下。
首先是新建一個Dog類。
package com.my;
public class Dog {
String name;
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
//無參構造
public Dog() {
System.out.println("dog無參構造");
}
//有參構造
public Dog(String name, String color) {
System.out.println("dog有參構造");
this.name = name;
this.color = color;
}
//play
public void play() {
System.out.println(name+"在玩耍");
}
@Override
public String toString() {
return "Dog [name=" + name + ", color=" + color + "]";
}
}
package com.my;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//Class創建對象的方式
public class DogTest {
public static void main(String[] args) {
//這裏trycatch拋出一下異常
try {
createDog();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
createDog2();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//第一種 無參構造,拋異常
public static void createDog() throws InstantiationException, IllegalAccessException {
Class class1=Dog.class;
//必須有無參構造 注意權限修飾符 建議使用public
Dog d1=(Dog) class1.newInstance();
System.out.println(d1);
}
//第二種 使用其他構造方法,拋異常
public static void createDog2() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class class1=Dog.class;
//當我們使用其他構造方法 得到類的實例 注意 得到構造器指定構造方法 對應參數類型
Constructor cc1= class1.getDeclaredConstructor(String.class,String.class);
//注意事項 此newInstance方法是 構造器對象 調用出來的 不是class對象
Dog d1=(Dog) cc1.newInstance("小花","黑白相間");
System.out.println(d1);
}
}
通過反射調用類的完整結構
使用反射可以取得:
1.實現的全部接口
public Class<?>[] getInterfaces() 確定此對象所表示的類或接口實現的接口。
2.所繼承的父類
public Class<? Super T> getSuperclass() 返回表示此 Class 所表示的實體(類、接口、基本 類型)的父類的 Class。
3.全部的構造器
public Constructor<T>[] getConstructors()返回此 Class 對象所表示的類的所有public構造方法。
public Constructor<T>[] getDeclaredConstructors()返回此 Class 對象表示的類聲明的所有構造方法。
- Constructor類中:
- 取得修飾符: public int getModifiers();
- 取得方法名稱: public String getName();
- 取得參數的類型:public Class<?>[] getParameterTypes();
4.全部的方法
public Method[] getDeclaredMethods() 返回此Class對象所表示的類或接口的全部方法
public Method[] getMethods() 返回此Class對象所表示的類或接口的public的方法
- Method類中:
- public Class<?> getReturnType()取得全部的返回值
- public Class<?>[] getParameterTypes()取得全部的參數
- public int getModifiers()取得修飾符
- public Class<?>[] getExceptionTypes()取得異常信息
5.全部的Field
public Field[] getFields() 返回此Class對象所表示的類或接口的public的Field。
public Field[] getDeclaredFields() 返回此Class對象所表示的類或接口的全部Field。
- Field方法中:
- publicintgetModifiers() 以整數形式返回此Field的修飾符
- publicClass<?>getType() 得到Field的屬性類型
- publicStringgetName() 返回Field的名稱。
6.泛型相關
獲取父類泛型類型:Type getGenericSuperclass()
泛型類型:ParameterizedType
獲取實際的泛型類型參數數組:getActualTypeArguments()
7.類所在的包 Package getPackage()
下面我們來演示一下:
首先新建一個Pet類:
package com.my;
//他是cat的父類 <>就是泛型 注意泛型不僅可以在集合中使用 還可以在類中 方法中應用 T 佔位符 (字母隨意都可以 一般使用T E B等)
public class Pet<T> {
String color;
@Override
public String toString() {
return "Pet [color=" + color + "]";
}
}
然後實現一個接口:
package com.my;
//接口
public interface MyInter {
}
然後新建一個Cat類,讓它繼承Pet父類並實現接口:
package com.my;
//cat繼承pet類
public class Cat extends Pet<String> implements MyInter {
private String name;
public int age;
//有參構造
Cat(String name, int age) {
super();
this.name = name;
this.age = age;
}
//無參構造
public Cat() {
super();
}
@Override
public String toString() {
return "Cat [name=" + name + ", age=" + age + "]";
}
//成員方法
public void eat() {
System.out.println(name+"在吃");
}
void play() {
System.out.println(name+"在玩");
}
}
下面進入我們的測試類,可以看到,Cat類中繼承了父類,實現了接口,有public和private兩種類型的成員變量,不僅有public修飾的無參構造,還有默認default修飾的有參構造,我們重寫了toString方法,併爲它寫了兩個權限不同的成員方法,這些應該足夠我們測試了。
package com.my;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
// 通過反射 ==》父類 父類泛型 接口 屬性 方法 構造方法 包名
public class OtherTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//這裏我們trycatch拋出一下異常
try {
fun1();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
fun2();
fun3();
fun4();
fun5();
System.out.println("------------------fun6----------");
fun6();
System.out.println("------------------fun7----------");
fun7();
}
//1.獲取運行時父類
public static void fun1() throws InstantiationException, IllegalAccessException {
Class<Cat> c1=Cat.class;
Class p1=c1.getSuperclass();
Pet pp1=(Pet) p1.newInstance();
System.out.println(p1);
System.out.println(pp1);
}
//2.獲取父類帶泛型
public static void fun2() {
Class<Cat> c1=Cat.class;
Type t1 = c1.getGenericSuperclass();
System.out.println(t1);
}
//3.獲取接口
public static void fun3() {
Class<Cat> c1=Cat.class;
Class [] cc1=c1.getInterfaces();
for (Class class1 : cc1) {
System.out.println(class1);
}
}
//4.獲取包
public static void fun4() {
Class<Cat> c1=Cat.class;
Package package1=c1.getPackage();
System.out.println(package1);
}
//5.獲取構造方法
public static void fun5() {
Class<Cat> c1=Cat.class;
//返回public修飾的所有構造方法
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//返回所有構造方法
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println("----"+constructor);
}
}
//6.獲取所有成員方法
public static void fun6() {
Class<Cat> c1=Cat.class;
//返回帶public修飾的所有成員方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//返回所有成員方法
//返回包含一個數組 方法對象反射的類或接口的所有聲明的方法,通過此表示 類對象,包括公共,保護,默認(包)訪問和私有方法,但不包括繼承的方法。
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println("Declared======"+method);
}
}
//7.獲取所有屬性
public static void fun7() {
Class<Cat> c1=Cat.class;
//返回public修飾的所有屬性
Field[] fields = c1.getFields();
for (Field field : fields) {
System.out.println(field);
}
//返回所有屬性 忽略修飾符
Field[] declaredFields = c1.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
}
}