反射機制:前提:jvm已經(jīng)加載這個類,通過一個抽象的類名能夠在自己記憶(加載類的內(nèi)存)中找到相匹配的類的具體信息
- JAVA reflection
反射機制允許程序在執(zhí)行期借助于Reflection API取得任何類的內(nèi)部信息窗悯,并能直接操作任意對象的內(nèi)部屬性及方法
Java反射機制提供的功能
在運行時判斷任意一個對象所屬的類
在運行時構(gòu)造任意一個類的對象
在運行時判斷任意一個類所具有的成員變量和方法
在運行時調(diào)用任意一個對象的成員變量和方法
生成動態(tài)代理
反射相關(guān)的主要API:
java.lang.Class:代表一個類
java.lang.reflect.Method:代表類的方法
java.lang.reflect.Field:代表類的成員變量
java.lang.reflect.Constructor:代表類的構(gòu)造方法
Class類
在Object類中定義了以下的方法蒂誉,此方法將被所有子類繼承:
public final Class getClass()
以上的方法返回值的類型是一個Class類挂捅,此類是Java反射的源頭, 實際上所謂反射從程序的運行結(jié)果來看也很好理解脐恩,即:可以通過對象反射求出類的名稱
通過Class類反射可以得到的信息:某個類的屬性舍肠、方法和構(gòu)造器、某個類到底實現(xiàn)了哪些接口呵燕。
實例化Class類對象的四種方法
- 前提:若已知具體的類棠绘,通過類的class屬性獲取,該方法最為安全可靠再扭,程序性能最高
實例:Class clazz = String.class; - 前提:已知某個類的實例氧苍,調(diào)用該實例的getClass()方法獲取Class對象
實例:Class clazz = “www.xyd.com”.getClass(); - 前提:已知一個類的全類名,且該類在類路徑下泛范,可通過Class類的靜態(tài)方法forName()獲取让虐,可能拋出ClassNotFoundException,最常用的方法
實例:Class clazz = Class.forName(“java.lang.String”); - 其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“類的全類名”);
通過反射調(diào)用類的完整結(jié)構(gòu):
通過反射可以取得:
- 獲得實現(xiàn)的所有接口
Class clazz = Class.forName("com.ying.javaBase.Student1"); // 通過包名.類名的字符串罢荡,獲取指定類的class實例
Class[] interfaces = clazz.getInterfaces(); // 獲取當前類的所有接口
for (Class anInterface : interfaces) {
System.out.println("接口: " + anInterface.getName());
}
輸出:
接口: com.ying.javaBase.Move
接口: com.ying.javaBase.Study
- 獲取所繼承的父類
Class clazz = Class.forName("com.ying.javaBase.Student1"); // 通過包名.類名的字符串赡突,獲取指定類的class實例
Class superClazz = clazz.getSuperclass(); // 獲取父類
System.out.println("父類: " + superClazz.getName());
輸出:
父類: com.ying.javaBase.Person1
- 獲取全部的構(gòu)造器
public Constructor<T>[] getConstructors()
返回此 Class 對象所表示的類的所有public構(gòu)造方法。
public Constructor<T>[] getDeclaredConstructors()
返回此 Class 對象表示的類聲明的所有構(gòu)造方法柠傍。包括私有和公有
Constructor類中:
取得修飾符: public int getModifiers();
取得方法名稱: public String getName();
取得參數(shù)的類型:public Class<?>[] getParameterTypes();
//測試獲取類的所有構(gòu)造方法麸俘,包括公有和私有
Constructor[] cons1 = clazz.getDeclaredConstructors();
for (Constructor con : cons1) {
System.out.println("---------------------------");
System.out.println("構(gòu)造方法名稱: " + con.getName());//取得方法名稱
//返回修飾符,返回數(shù)字1代表public惧笛,返回數(shù)字2,代表private
System.out.println("構(gòu)造方法" + con.getName() + "的修飾符名稱: " + con.getModifiers()); //取得方法修飾符
for (Class parameterType : con.getParameterTypes()) {
System.out.println("參數(shù)類型之一是: " +parameterType.getName());
}
System.out.println("---------------------------");
}
輸出:
---------------------------
構(gòu)造方法名稱: com.ying.javaBase.Student1
構(gòu)造方法com.ying.javaBase.Student1的修飾符名稱: 1
---------------------------
---------------------------
構(gòu)造方法名稱: com.ying.javaBase.Student1
構(gòu)造方法com.ying.javaBase.Student1的修飾符名稱: 2
參數(shù)類型之一是: java.lang.String
參數(shù)類型之一是: int
---------------------------
---------------------------
構(gòu)造方法名稱: com.ying.javaBase.Student1
構(gòu)造方法com.ying.javaBase.Student1的修飾符名稱: 1
參數(shù)類型之一是: java.lang.String
---------------------------
- 用反射的構(gòu)造方法創(chuàng)建對象
- 獲取默認的無參的構(gòu)造方法
Class clazz = Class.forName("com.ying.javaBase.Student1"); // 通過包名.類名的字符串从媚,獲取指定類的class實例
//通過反射的構(gòu)造方法來創(chuàng)建對象
Object o = clazz.newInstance();//相當于調(diào)用Student1的無產(chǎn),公有的構(gòu)造方法
Student1 stu = (Student1) o;
java.lang.reflect.Constructor:代表類的構(gòu)造方法
- 獲取指定的公有的構(gòu)造方法來創(chuàng)建對象
Constructor c = clazz.getConstructor(String.class);//指定獲取有一個參數(shù)為String類型的患整,公有的構(gòu)造方法//
Student1 stu1 = (Student1) c.newInstance("第一中學");
System.out.println(stu1.school);
- 獲取私有的構(gòu)造方法來創(chuàng)建對象
Constructor c = clazz.getDeclaredConstructor(String.class, int.class);//指定獲取帶有兩個參數(shù)的構(gòu)造方法
c.setAccessible(true);//解除私有的封裝拜效,便可以強制調(diào)用私有構(gòu)造方法
Student1 stu = (Student1) c.newInstance("張三", 12);
-
通過反射獲取所有的方法
public Method[] getDeclaredMethods()
返回此Class對象所表示的類或接口的全部方法喷众,不包括父類的
public Method[] getMethods()
返回此Class對象所表示的類或接口的public的方法,包括父類的Method類中:
public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的參數(shù)
public int getModifiers()取得修飾符 通過反射獲取類的屬性
public Field[] getFields()
返回此Class對象所表示的類或接口的public的Field紧憾。到千,包括父類的
public Field[] getDeclaredFields()
返回此Class對象所表示的類或接口的全部Field。不包括父類的
Field方法中:
public int getModifiers() 以整數(shù)形式返回此Field的修飾符
public Class<?> getType() 得到Field的屬性類型
public String getName() 返回Field的名稱赴穗。
- 通過反射獲取類的包
Package getPackage()
java.lang.reflect.Method:代表類的方法
- 通過反射獲取指定方法
Constructor con = clazz.getConstructor();//獲取無參構(gòu)造
Object obj = con.newInstance();//使用無參構(gòu)造創(chuàng)建對象
Student1 stu = (Student1)obj;
//得到名稱是公有的setInfo憔四,參數(shù)是string string的方法
Method m = clazz.getMethod("setInfo", String.class, String.class);
m.invoke(stu, "張三", "第一中學");//參數(shù)一對象,后面的參數(shù)是調(diào)用當前方法的實際參數(shù)
//如果想要調(diào)用私有方法
Method m1 = clazz.getDeclaredMethod("test", String.class);
m1.setAccessible(true); //解除私有的封裝
m1.invoke(stu, "李四");
//調(diào)用重載方法
Method m2 = clazz.getMethod("setInfo", int.class);
m2.invoke(stu, 2);
//有返回值的方法
Method m3 = clazz.getMethod("getSchool");//獲取方法名為getSchool般眉,沒有參數(shù)的方法
System.out.println(m3.invoke(stu));
java.lang.reflect.Field:代表類的成員變量
Constructor con = clazz.getConstructor();
Student1 stu = (Student1) con.newInstance();
Field f = clazz.getDeclaredField("school");//獲取名稱為school的屬性
f.setAccessible(true);
f.set(stu, "第三中學");//對stu對象的school屬性設(shè)置值
String school = (String) f.get(stu);
System.out.println(school);
java動態(tài)代理
Proxy :專門完成代理的操作類了赵,是所有動態(tài)代理類的父類。通過此類為一個或多個接口動態(tài)地生成實現(xiàn)類甸赃。
創(chuàng)建一個動態(tài)代理類所對應的Class對象
- 動態(tài)代理步驟:
1.創(chuàng)建一個實現(xiàn)接口InvocationHandler的類柿汛,它必須實現(xiàn)invoke方法,以完成代理的具體操作埠对。
public class ProxyDemo implements InvocationHandler {
Object obj;//被代理的對象
public ProxyDemo(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "方法開始執(zhí)行");
Object result = method.invoke(this.obj, args);//執(zhí)行的是指定代理對象的指定方法
System.out.println(method.getName() + "方法執(zhí)行完畢");
return result;
}
}
2.創(chuàng)建被代理的類以及接口
/**
- 注意:如果一個對象想要通過Proxy.newProxyInstance方法被代理络断,
- 那么這個對象的類一定要有相應的接口
- 就像本類中的ITestDemo接口和實現(xiàn)類TestDemoImpl
*/
public interface ITestDemo {
void test1();
void test2();
}
public class TestDemoImpl implements ITestDemo{
@Override
public void test1() {
System.out.println("執(zhí)行test1()方案");
}
@Override
public void test2() {
System.out.println("執(zhí)行test2()方案");
}
}
3.通過Proxy的靜態(tài)方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 創(chuàng)建一個Subject接口代理
4.通過 Subject代理調(diào)用RealSubject實現(xiàn)類的方法
ITestDemo test = new TestDemoImpl();
/**
* 需求:
* 在執(zhí)行方法前,打印 方法開始執(zhí)行
* 在執(zhí)行方法后项玛,打印 方法執(zhí)行完畢
* 打印的方法名和方法保持一致
*/
InvocationHandler handler = new ProxyDemo(test);
/**
* newProxyInstance(ClassLoader, interface, h)
* 參數(shù)一:ClassLoader: 代理對象的類加載器
* 參數(shù)二:interface:被代理對象的接口
* 參數(shù)三:H 代理對象
* 返回值:成功被代理后的對象
*/
ITestDemo t = (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(), test.getClass().getInterfaces(), handler);
t.test1();
System.out.println("-------------");
t.test2();