什麼是反射?
反射機制是在【運行狀態】中:
對於任意一個類,都能夠知道這個類的所有屬性和方法;
對於任意一個對象,都能夠調用它的任意一個方法和屬性;
反射提供的功能:
在運行時判斷任意一個對象所屬的類;
在運行時構造任意一個類的對象;
在運行時判斷任意一個類所具有的成員變量和方法;
在運行時調用任意一個對象的方法。
生成動態代理
反射機制原理
Java程序在運行時,Java運行時系統一直對所有的對象進行所謂的運行時類型標識。這項信息紀錄了每個對象所屬的類。虛擬機通常使用運行時類型信息選準正確方法去執行,用來保存這些類型信息的類是Class類。也就是說,ClassLoader找到了需要調用的類時(java爲了調控內存的調用消耗,類的加載都在需要時再進行,很摳但是很有效),就會加載它,然後根據.class文件內記載的類信息來產生一個與該類相聯繫的獨一無二的Class對象。該Class對象記載了該類的字段,方法等等信息。以後jvm要產生該類的實例,就是根據內存中存在的該Class類所記載的信息(Class對象應該和我所瞭解的其他類一樣會在堆內存內產生、消亡)來進行。而java中的Class類對象是可以人工自然性的(也就是說開放的)得到的(雖然你無法像其他類一樣運用構造器來得到它的實例,因爲Class對象都是jvm產生的。不過話說回來,客戶產生的話也是無意義的),而且,更偉大的是,基於這個基礎,java實現了反射機制。
獲取Class對象三種方式:
- 1.通過Object類的getClass()方法。例如:
Class perClazz = new String("").getClass(); - 2.通過Class類的靜態方法——forName()來實現:
Class perClazz = Class.forName(“MyObject”); - 3.如果T是一個已定義的類型的話,在java中,它的.class文件名:T.class就代表了與其匹配的Class對象,例如:Class perClazz = Manager.class;Class c4 = int.class;
Class perClazz = Double[].class;
獲取方法
- //獲取所有的公共方法
Method[] methods = perClazz.getMethods(); - //獲取當前類的所有方法(當前類,忽略訪問修飾符限制)
Method[] declaredMethods = perClazz.getDeclaredMethods();
獲取所有的接口
- //Java多實現,獲取所有的接口
Class<?>[] interfaces = perClazz.getInterfaces();
獲取父類
- //Java單繼承,獲取父類
Class<?> superClass = perClazz.getSuperclass();
獲取構造方法
- //獲取所有的構造方法
Constructor<?>[] constructors = perClazz.getConstructors(); - //獲取指定的構造放方法,獲取類型方法時,基本類型和包裝類是不同的
Constructor constructor = perClazz.getConstructor(int.class);
獲取類的屬性
- //公共屬性
Field[] fields = perClazz.getFields(); - //所有屬性
Field[] declaerdields = perClazz.getDeclaredFields();
修改訪問權限
- Field.setAccessible(true);//屬性
- Method.setAccessible(true);//方法
- Constructor.setAccessible(true);//構造方法
調用Class屬性/方法/構造器
-
屬性
Field idField = perClazz.getDeclaredField(“id”);
//修改訪問權限Field/Method.setAccessible(true);/
idField.setAccessible(true);
idField.set(person,1);
System.out.println(person.getId()); -
方法
Method priMethod = perClazz.getDeclaredMethod(“privateMethod”,null);
priMethod.setAccessible(true);
priMethod.invoke(person,null);//invoke()調用方法Method priMethod2 = perClazz.getDeclaredMethod(“privateMethod2”,String.class);
priMethod2.setAccessible(true);
priMethod2.invoke(person,“JavaMan”);//invoke()調用方法 -
構造器
Constructor constructor = perClazz.getConstructor(int.class);
Person person = (Person) constructor.newInstance(15);
System.out.println(person.getId());
繞過範性類型檢查
編譯器編譯帶參數說明的集合時會去掉類型的信息,轉化爲普通的鏈表,所以運行時,將不會受到泛型的影響。所以可以這樣來繞開泛型的限制;
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(2);
//獲取鏈表的add方法,注意這裏是Object.class,如果寫int.class會拋出NoSuchMethodException異常
Method m = al.getClass().getMethod("add", Object.class);
//調用反射中的add方法加入一個string類型的元素,因爲add方法的實際參數是Object
m.invoke(al, "hello");
配置文件+反射機制
通過配置文件配置類的全路徑和部分屬性,可以創造工具類利用反射機制實現類的各種操作。
XXX.properties
ClassPath=com.javaman.reflect.Person
//Dome.class.getClassLoader()獲取文件路徑類加載器
InputStream in = Dome.class.getClassLoader().getResourceAsStream("XXX.properties");
//讀取文件
Properties properties=new Properties();
properties.load(in);
String ClassPath = properties.getProperty("ClassPath");
其他方法
-
1.getName()
一個Class對象描述了一個特定類的特定屬性,而這個方法就是返回String形式的該類的簡要描述。 -
2.newInstance()
該方法可以根據某個Class對象產生其對應類的實例。需要強調的是,它調用的是此類的默認構造方法。 -
3.getClassLoader()
返回該Class對象對應的類的類加載器。 -
4.getComponentType()
該方法針對數組對象的Class對象,可以得到該數組的組成元素所對應對象的Class對象。例如:
int[] ints = new int[]{1,2,3};
Class class1 = ints.getClass();
Class class2 = class1.getComponentType();
而這裏得到的class2對象所對應的就應該是int這個基本類型的Class對象。 -
5.getSuperClass()
返回某子類所對應的直接父類所對應的Class對象。 -
6.isArray()
判定此Class對象所對應的是否是一個數組對象。
代碼測試
package com.javaman.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.FileHandler;
/**
* Created by zhahongsheng on 2019/4/14.
*/
public class TestReflect {
/**
* 獲取反射對象(反射入口)
*/
public static void demo1(){
//獲取反射對象(反射入口)1。Class.forName(全類名)2.xx.Class(),3.對象.getclass()
try {
Class<?> perClazz = Class.forName("com.javaman.reflect.Person");
System.out.println(perClazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class<?> perClazz2= Person.class;
System.out.println(perClazz2);
Person per = new Person();
Class<?> perClazz3 = per.getClass();
System.out.println(perClazz3);
}
//獲取方法
public static void demo2(){
//Class入口
Class<?> perClazz = null;
try {
perClazz = Class.forName("com.javaman.reflect.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//獲取所有的公共方法
Method[] methods = perClazz.getMethods();
for(Method method : methods){
System.out.println(method);
}
System.out.println("=========================================================");
//獲取當前類的所有方法(當前類,忽略訪問修飾符限制)
Method[] declaredMethods = perClazz.getDeclaredMethods();
for (Method method : declaredMethods){
System.out.println(method);
}
}
//獲取所有的接口
public static void demo3(){
Class<?> perClazz =null;
try {
perClazz = Class.forName("com.javaman.reflect.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class<?>[] interfaces = perClazz.getInterfaces();
for(Class<?> inter : interfaces ){
System.out.println(inter);
}
}
//獲取父類
public static void demo4(){
Class<?> perClazz =null;
try {
perClazz = Class.forName("com.javaman.reflect.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class<?> superClass = perClazz.getSuperclass();
System.out.println(superClass);
}
//獲取所有的構造方法
public static void demo5() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> perClazz =null;
try {
perClazz = Class.forName("com.javaman.reflect.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Constructor<?>[] constructors = perClazz.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor);
}
//獲取指定的構造放方法
//在獲取類型方法時,基本類型和包裝類是不同的
Constructor constructor = perClazz.getConstructor(int.class);
Person person = (Person) constructor.newInstance(15);
System.out.println(person.getId());
}
//獲取所有的公共屬性
public static void demo6(){
Class<?> perClazz =null;
try {
perClazz = Class.forName("com.javaman.reflect.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//公共屬性
Field[] fields = perClazz.getFields();
for(Field field : fields){
System.out.println(field);
}
//所有屬性
Field[] declaerdields = perClazz.getDeclaredFields();
for(Field field : declaerdields){
System.out.println(field);
}
}
public static void demo7() throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
Class<?> perClazz =null;
try {
perClazz = Class.forName("com.javaman.reflect.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Object instance = perClazz.newInstance();
if(instance instanceof Person){
Person person = (Person)instance;
person.interfaceMethod();
Field idField = perClazz.getDeclaredField("id");
//修改訪問權限Field/Method.setAccessible(true);/
idField.setAccessible(true);
idField.set(person,1);
System.out.println(person.getId());
Method priMethod = perClazz.getDeclaredMethod("privateMethod",null);
priMethod.setAccessible(true);
priMethod.invoke(person,null);//invoke()調用方法
Method priMethod2 = perClazz.getDeclaredMethod("privateMethod2",String.class);
priMethod2.setAccessible(true);
priMethod2.invoke(person,"JavaMan");//invoke()調用方法
}
}
public static void main(String[] args) {
// demo1();
// demo2();
// demo3();
// demo4();
try {
demo5();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
// demo6();
try {
demo7();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
總結
反射機制是java框架的核心,反射機制的重點在於運行狀態下工作