反射(Reflect)
反射就是在運行時才知道要操作的類是什麼,並且可以在運行時獲取類的完整構造,並調用對應的方法。
是Java被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法。
- 在運行時構造任意一個類的對象。
- 運行時獲取任意一個類所具有的成員變量和方法。
- 在運行時調用任意一個對象的方法(屬性)。
Java 是一門面向對象的語言。在面向對象的世界裏,萬事萬物皆對象,既然萬事萬物皆對象。我們寫的每一個類都可以看成一個對象,是 java.lang.Class 類的對象。當我們寫完一個類的Java文件,編譯成class文件的時候,編譯器都會將這個類的對應的class對象放在class文件的末尾。保存了類的元數據信息,一個類的元數據信息包括屬性,方法,構造器,實現了哪些接口等等,這些信息在Java裏都有對應的類來表示。
Class類
Class是一個類,封裝了當前對象所對應的類的信息。
Class類是一個對象照鏡子的結果,對象可以看到自己有哪些屬性,方法,構造器,實現了哪些接口等等。
對於每個類而言,JRE 都爲其保留一個不變的 Class 類型的對象。一個 Class 對象包含了特定某個類的有關信息。對象只能由系統建立對象,一個類(而不是一個對象)在 JVM 中只會有一個Class實例。
獲取Class對象的三種方式
- 通過類名獲取 類名.class
- 通過對象獲取 對象名.getClass()
- 通過全類名獲取 Class.forName(全類名)
Class類的常用方法
方法名 | 功能說明 |
---|---|
static Class forNmae(String name) | 返回指定類名的Class對象 |
Object newInstance() | 調用缺省構造函數,返回該Class對象的一個實例 |
Object newInstantce(Object []args) | 調用當前格式構造函數,返回Class對象的一個實例 |
getName() | 返回Class對象鎖表示的實體(類,接口,數組類,基本類型或者void)名稱 |
Class getSuperClass() | 返回當前Class對象的父類Class對象 |
Class [] getInterfases() | 獲取當前Class對象的接口 |
ClassLoader getClassLoader() | 返回該類的類加載器 |
類加載器、構造器、Method、Field
ClassLoader
public static void testClassLoader() throws ClassNotFoundException,
FileNotFoundException {
//1. 獲取一個系統的類加載器(可以獲取)
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//2. 獲取系統類加載器的父類加載器(擴展類加載器,可以獲取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//3. 獲取擴展類加載器的父類加載器(引導類加載器,不可獲取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//4. 測試當前類由哪個類加載器進行加載(系統類加載器):
classLoader = Class.forName("xx.xx.xxxx")
.getClassLoader();
System.out.println(classLoader);
//5. 測試 JDK 提供的 Object 類由哪個類加載器負責加載(引導類)
classLoader = Class.forName("java.lang.Object")
.getClassLoader();
System.out.println(classLoader);
}
Constructor
/*構造器相關*/
public void testConstructor() throws Exception{
String className = "xx.xx.xxxx.Person";
Class<Person> clazz = (Class<Person>) Class.forName(className);
System.out.println("獲取全部Constructor對象-----");
Constructor<Person>[] constructors
= (Constructor<Person>[]) clazz.getConstructors();
for(Constructor<Person> constructor: constructors){
System.out.println(constructor);
}
System.out.println("獲取某一個Constructor 對象,需要參數列表----");
Constructor<Person> constructor
= clazz.getConstructor(String.class, int.class);
System.out.println(constructor);
//2. 調用構造器的 newInstance() 方法創建對象
System.out.println("調用構造器的 newInstance() 方法創建對象-----");
Person obj = constructor.newInstance("Mark", 18);
System.out.println(obj.getName());
}
Field
/*域相關*/
public void testField() throws Exception{
String className = "xx.xx.xxxx.Person";
Class clazz = Class.forName(className);
System.out.println("獲取公用和私有的所有字段,但不能獲取父類字段");
Field[] fields = clazz.getDeclaredFields();
for(Field field: fields){
System.out.print(" "+ field.getName());
}
System.out.println();
System.out.println("---------------------------");
System.out.println("獲取指定字段");
Field field = clazz.getDeclaredField("name");
System.out.println(field.getName());
Person person = new Person("ABC",12);
System.out.println("獲取指定字段的值");
Object val = field.get(person);
System.out.println(field.getName()+"="+val);
System.out.println("設置指定對象指定字段的值");
field.set(person,"DEF");
System.out.println(field.getName()+"="+person.getName());
System.out.println("字段是私有的,不管是讀值還是寫值," +
"都必須先調用setAccessible(true)方法");
field = clazz.getDeclaredField("age");
field.setAccessible(true);
System.out.println(field.get(person));
}
Method
/*方法相關*/
public void testMethod() throws Exception{
Class clazz = Class.forName("xx.xx.xxxx.Person");
System.out.println("獲取clazz對應類中的所有方法," +
"不能獲取private方法,且獲取從父類繼承來的所有方法");
Method[] methods = clazz.getMethods();
for(Method method:methods){
System.out.print(" "+method.getName()+"()");
}
System.out.println("");
System.out.println("---------------------------");
System.out.println("獲取所有方法,包括私有方法," +
"所有聲明的方法,都可以獲取到,且只獲取當前類的方法");
methods = clazz.getDeclaredMethods();
for(Method method:methods){
System.out.print(" "+method.getName()+"()");
}
System.out.println("");
System.out.println("---------------------------");
System.out.println("獲取指定的方法," +
"需要參數名稱和參數列表,無參則不需要寫");
// 方法public void setName(String name) { }
Method method = clazz.getDeclaredMethod("setName", String.class);
System.out.println(method);
System.out.println("---");
// 方法public void setAge(int age) { }
/* 這樣寫是獲取不到的,如果方法的參數類型是int型
如果方法用於反射,那麼要麼int類型寫成Integer: public void setAge(Integer age) { }
要麼獲取方法的參數寫成int.class*/
method = clazz.getDeclaredMethod("setAge", int.class);
System.out.println(method);
System.out.println("---------------------------");
System.out.println("執行方法,第一個參數表示執行哪個對象的方法" +
",剩下的參數是執行方法時需要傳入的參數");
Object obje = clazz.newInstance();
method.invoke(obje,18);
/*私有方法的執行,必須在調用invoke之前加上一句method.setAccessible(true);*/
method = clazz.getDeclaredMethod("privateMethod");
System.out.println(method);
System.out.println("---------------------------");
System.out.println("執行私有方法");
method.setAccessible(true);
method.invoke(obje);
}