一、什么是反射
JAVA反射機制是在運行狀態中,對於任何一個類,都能夠知道這個類的所有屬性和方法;對於任何一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。
想要使用反射機制,就必須要先獲取到該類的字節碼文件對象(.class),通過字節碼文件對象,就能夠通過該類中的方法獲取到我們想要的所有信息(方法,屬性,類名,父類名,實現的所有接口等等),每一個類對應着一個字節碼文件也就對應着一個Class類型的對象,也就是字節碼文件對象。
獲取字節碼文件對象(獲取class對象的方式)的三種方式:
1、根據類名:類名.class
2、根據對象:對象.getClass()
3、根據全限定類名:Class.forName(全限定類名)
二、通過反射機制獲取信息
1、構造函數
//獲取字節碼文件 Class classs = Class.forName("com.zcbq.reflect.User"); //先獲取有參構造,parameterTypes:表述參數列表,也可以不寫 Constructor constructor = classs.getConstructor(int.class,String.class,int.class,String.class); //通過構造器來實例化對象,將實際的參數傳進去 User user = (User) constructor.newInstance(01,"小軒",13,"打球");
獲取全部構造函數:
//獲取字節碼文件 Class classs = Class.forName("com.zcbq.reflect.User"); //獲取所有構造函數 Constructor constructor[] = classs.getConstructors(); //遍歷所有構造函數 for(int i=0;i<constructor.length;i++){ //獲取每個構造函數中的參數類型字節碼對象 Class[] parameterTypes = constructor[i].getParameterTypes(); System.out.println("第"+i+"個構造函數:"); for (int j = 0; j < parameterTypes.length; j++) { System.out.println(parameterTypes[j].getName()+","); } }
console:
2、屬性
在學習spring ioc之時,對未提供set方法的private屬性依然可以注入感到神奇萬分,現在看來,這神奇的根源自然是來自於java的反射,常用的方法如下:
2.1獲取指定成員變量
//獲取字節碼文件 Class classs = Class.forName("com.zcbq.reflect.User"); //獲取其實例對象 User user = (User) classs.newInstance(); //獲取成員變量classs.getField(name);通過name來獲取指定成員變量 //如果該成員變量是私有的,則應該使用getDeclaredField(name); Field declaredField = classs.getDeclaredField("userName"); //因為屬性是私有的,獲得其對象后,還要讓打開可見權限 declaredField.setAccessible(true); //對成員變量進行操作 //賦值操作 declaredField.set(user, "Richard"); System.out.println(user.getUserName());
2.2獲取全部屬性
//獲取字節碼文件 Class classs = Class.forName("com.zcbq.reflect.User"); //獲取其實例對象 User user = (User) classs.newInstance(); //賦值操作 user.setUserNum(01); user.setUserName("小軒"); //將私有屬性一並獲得 Field[] fields = classs.getDeclaredFields(); //遍歷所有屬性 for (int i = 0; i < fields.length; i++) { //打開可見權限 fields[i].setAccessible(true); System.out.println(fields[i].get(user)); }
3、方法
3.1不帶參數的方法
//獲取字節碼文件 Class classs = Class.forName("com.zcbq.reflect.User"); //獲取其實例對象 User user = (User) classs.newInstance(); //不帶參數的方法,name為不帶參數的方法 /* * classs.getMethod(name,paraMeterTypes) * name:方法的名稱 * paraMeterTypes:方法的參數類型,沒有則什么都不填 例如:String.class */ Method method = classs.getMethod("name"); //調用方法 /* * method.invoke(obj,args) * obj:方法的對象 * args:實際的參數值,沒有則不填 */ method.invoke(user);
3.2帶參數的方法
//獲取字節碼文件 Class classs = Class.forName("com.zcbq.reflect.User"); //獲取其實例對象 // User user = (User) classs.newInstance(); //獲取帶參數的方法,為方法名 // Method method = classs.getDeclaredMethod("namess", String.class); //設置可見性 // method.setAccessible(true); //調用方法 // method.invoke(user, "text");
3.3獲取所有的方法
//獲取字節碼文件 Class classs = Class.forName("com.zcbq.reflect.User"); //獲取其實例對象 User user = (User) classs.newInstance(); //獲取所有的方法 Method[] methods = classs.getMethods(); //遍歷所有方法 for (Method method : methods) { //設置可見性 method.setAccessible(true); System.out.println(method.getName()); //獲得方法的參數 Class<?>[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { //獲得構造函數中參數類型 System.out.println(parameterTypes[i].getName()+","); } }
三、動態代理的概述及實現
1、動態代理概述
動態代理:利用Java的反射技術(Java Reflection),在運行時創建一個實現某些給定接口的新類(也稱“動態代理類”)及其實例(對象);
代理的是接口(Interfaces),不是類(Class),更不是抽象類。
2、動態代理的實現
分三步,但是注意JDK提供的代理正能針對接口做代理,也就是下面的第二步返回的必須要是一個接口。
2.1 new出代理對象,通過實現InvacationHandler接口,然后new出代理對象來。
2.2 通過Proxy類中的靜態方法newProxyInstance,來將代理對象假裝成那個被代理的對象,也就是如果叫人幫我們代買火車票一樣,那個代理就假裝成我們自己本人。
2.3 執行方法,代理成功
附屬代碼:
package com.zcbq.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHander implements InvocationHandler { private Object target; public MyInvocationHander() { super(); } public MyInvocationHander(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.err.println("開始"); method.invoke(target, args); //執行被代理的target對象的方法 System.out.println("結束"); return null; } }
附屬代碼:
Student student = new StuImp(); MyInvocationHander m = new MyInvocationHander(student); /** * student.getClass().getClassLoader():類加載器 * student.getClass().getInterfaces():被代理對象的接口 * m:代理對象 */ Student s = (Student) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), m); s.login(); s.logout();
注意newProxyInstance的三個參數,第一個,類加載器,第二個被代理對象的接口,第三個代理對象。