1.Java 反射:
Reflection(反射)是被視爲動態語言的關鍵,反射機制允許程序在執行期藉助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法
2.Java 反射的作用:
1)在運行時判斷任意一個對象所屬的類
2)在運行時構造任意一個類的對象
3)在運行時判斷任意一個類所具有的成員變量和方法
4)在運行時調用任意一個對象的成員變量和方法
5)生成動態代理
3.反射的API
java.lang.Class:代表一個類
java.lang.reflect.Method:代表類的方法
java.lang.reflect.Field:代表類的成員變量
java.lang.reflect.Constructor:代表類的構造方法
實例:
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
public class TestReflection {
@Test
public void testClass() throws Exception{
Class<Person> clazz = Person.class;
/**
* 創建對象
*/
//1.創建運行時類Person的對象:newInstance()
Person person = clazz.newInstance();
/**
* 屬性操作
*/
//1.獲取public屬性名:getFields()
Field[] fields = clazz.getFields();
for(int i = 0;i < fields.length;i++){
System.out.println("###" + fields[i].getName());
}
//2.獲取全部屬性名:getDeclaredFields()
Field[] fields1 = clazz.getDeclaredFields();
for(int i = 0;i < fields1.length;i++){
System.out.println("!!!" + fields1[i].getName());
}
//3.獲取public屬性:getField(String fieldname)
Field field = clazz.getField("name");
//通過set方法,爲對應的類的對應屬性賦值
field.set(person, "LiuDeHua");
System.out.println("name : " + person.getName());
//4.獲取非public屬性:getgetDeclaredField(String fieldName)
Field field2 = clazz.getDeclaredField("age");
//通過setAccessible方法,改變private屬性的封裝性
field2.setAccessible(true);
field2.set(person, 40);
System.out.println("age : " + person.getAge());
/**
* 方法操作
*/
//1.獲取運行時類方法的名稱:getDeclatedMethods() getMethods()
Method[] methods = clazz.getDeclaredMethods();
for(int i = 0;i < methods.length;i++){
System.out.println("@@@" + methods[i].getName());
}
//2.獲取運行時類指定的方法:getMethod(String methodName,Class...paramtype)
Method method = clazz.getMethod("show");
method.invoke(person);
Method method2 = clazz.getMethod("display",String.class,int.class);
method2.invoke(person, "xiaoming",10);
}
}
4.java.lang.Class:是反射的源頭
1)public final Class getClass():在Object類中定義了該的方法,此方法將被所有子類繼承。該方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱
2)生成Class實例之後,我們可以進行的操作
(1)創建對應運行時類對象
(2)獲取對應運行時類的完整結構(屬性、方法、構造器,父類,所在的包,註解,異常等等)
(3)調用對應的運行時類指定的結構(屬性,方法,構造器)
(4)反射的應用:動態代理
3)如何獲取Class實例(3種)
/**
* 獲取Class實例方法
* @throws Exception
*/
@Test
public void testGetClassInstance() throws Exception{
//1.調用運行時類本身的.class屬性
Class clazz = Person.class;
System.out.println(clazz.getName());
//2.通過運行時類的對象獲取
Person person = new Person();
Class clazz1 = person.getClass();
System.out.println(clazz1.getName());
//3.通過Class靜態方法forName(String classpath)
Class clazz2 = Class.forName("reflection.Person");
System.out.println(clazz2.getName());
//4.通過類的加載器:
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz3 = classLoader.loadClass("reflection.Person");
System.out.println(clazz3.getName());
}
4)Class類的常用方法
5)ClassLoader
/**
* ClassLoader獲取輸入流
* @throws Exception
*/
@Test
public void testClassLoeader() throws Exception{
ClassLoader classLoader = this.getClass().getClassLoader();
//通過getResourceStream(String filepath)可以得到一個輸入流
InputStream inputStream = classLoader.getResourceAsStream("classloader.txt");
FileOutputStream fos = new FileOutputStream("classloader1.txt");
byte[] b = new byte[10];
int len;
while((len = inputStream.read(b)) != -1){
fos.write(b,0,len);
}
}
5.創建類對象:調用Class對象的newInstance()方法 要 求:1)類必須有一個無參數的構造器。2)類的構造器的訪問權限需要足夠。
/**
* 創建運行時類對象,使用newInstance(),實際上就是調用了運行時類的空參構造器
* 要求:1)對應的運行時類要有空參構造器
* 2)構造器的權限要足夠
* @throws Exception
*/
@Test
public void testNewInstance() throws Exception{
String className = "reflection.Person";
Class clazz = Class.forName(className);
Person person = (Person)clazz.newInstance();
System.out.println(person);
}
6.通過反射調用類的完整結構1)獲取屬性
/**
* 獲取屬性相關信息
* @throws SecurityException
* @throws Exception
*/
@Test
public void testGetFieldInfo() throws Exception{<span style="background-color: rgb(255, 153, 0);">
</span> Class clazz = Person.class;
//1.獲取public屬性名:getFields()
Field[] fields = clazz.getFields();
//2.獲取全部屬性名:getDeclaredFields()
Field[] fields1 = clazz.getDeclaredFields();
//3.獲取public屬性:getField(String fieldname)
Field field = clazz.getField("name");
//4.獲取非public屬性:getgetDeclaredField(String fieldName)
Field field2 = clazz.getDeclaredField("age");
//5.獲取屬性的權限符 變量類型 變量名稱
for(Field f : fields1){
//1)權限符
int i = f.getModifiers();
String name = Modifier.toString(i);
System.out.println(name);
//2)變量類型
Class type = f.getType();
System.out.println(type.getName());
//3)變量名稱
String methodName = f.getName();
System.out.println(methodName);
}
}
2)獲取方法
/**
* 獲取方法相關屬性
* @throws SecurityException
* @throws Exception
*/
@Test
public void testGetMethodInfo() throws Exception{
Class clazz = Person.class;
//1.getMethods():返回運行時類及其父類中所有public的方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.err.println(m);
}
//getDeclatedMethods():返回運行時類自己本身聲明的所有方法
Method[] methods2 = clazz.getDeclaredMethods();
for(Method m : methods2){
System.out.println(m);
}
//2.獲取運行時類指定的方法:getMethod(String methodName,Class...paramtype)
Method method = clazz.getMethod("show");
Method method2 = clazz.getMethod("display",String.class,int.class);
}
3)獲取父類泛型
/**
* 獲取父類的泛型:JDBC中會用到
* @throws SecurityException
* @throws Exception
*/
@Test
public void testGenericType() throws Exception{
Class clazzClass = Person.class;
Type type = clazzClass.getGenericSuperclass();
ParameterizedType paraType = (ParameterizedType)type;
Type[] types = paraType.getActualTypeArguments();
for(Type t : types){
System.out.println(((Class)t).getName());
}
}
7.通過反射調用類中指定方法和屬性
1)調用指定屬性
public Field getField(String name) 返回此Class對象表示的類或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class對象表示的類或接口的指定的Field。
public Object get(Object obj) 取得指定對象obj上此Field的屬性內容
public void set(Object obj,Object value) 設置指定對象obj上此Field的屬性
public void setAccessible(true)訪問私有屬性時,讓這個屬性可見
/**
* 調用指定屬性
*/
@Test
public void test() throws Exception{
Class clazz = Person.class;
//1.創建Person對象
Person person = (Person)clazz.newInstance();
//2.爲public屬性賦值並獲取
Field field = clazz.getField("name");
field.set(person, "xiaoming");
field.get(person);
System.out.println(field.get(person));
//3.爲非public屬性賦值並獲取
Field field2 = clazz.getDeclaredField("age");
field2.setAccessible(true);
field2.set(person, 10);
field2.get(person);
System.out.println(field2.get(person));
}
2)調用指定方法
通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,並設置此方法操作時所需要的參數類型。
之後使用Object invoke(Object obj, Object[] args)進行調用,並向方法中傳遞要設置的obj對象的參數信息。
/**
* 調用指定方法
*/
@Test
public void test2() throws Exception{
Class clazz = Person.class;
Person person = (Person)clazz.newInstance();
Method method = clazz.getMethod("show");
Object returnVal = method.invoke(person);
System.out.println("show's returnVal : " + returnVal);
Method method2 = clazz.getMethod("toString");
Object returnVal2 = method2.invoke(person);
System.out.println("toString's returnVal : " + returnVal2);
Method method3 = clazz.getMethod("display", String.class,int.class);
Object returnVal3 = method3.invoke(person, "xiaohong",10);
System.out.println("display's returnVal : " + returnVal3);
}
8.動態代理1)動態代理是指客戶通過代理類來調用其它對象的方法,並且是在程序運行時根據需要動態創建目標類的代理對象。
2)代理設計模式的原理:使用一個代理將對象包裝起來, 然後用該代理對象取代原始對象. 任何對原始對象的調用都要通過代理. 代理對象決定是否以及何時將方法調用轉到原始對象上
3)代理類的作用:
(1)授權機制: 不同級別的用戶對同一對象擁有不同的訪問權利,如Jive論壇系統中,就使用Proxy進行授權機制控制,訪問論壇有兩種人:註冊用戶和遊客(未註冊用戶),Jive中就通過類似ForumProxy這樣的代理來控制這兩種用戶對論壇的訪問權限.
(2)某個客戶端不能直接操作到某個對象,但又必須和那個對象有所互動。舉例兩個具體情況:如果那個對象是一個是很大的圖片,需要花費很長時間才能顯示出來,那麼當這個圖片包含在文檔中時,使用編輯器或瀏覽器打開這個文檔,打開文檔必須很迅速,不能等待大圖片處理完成,這時需要做個圖片Proxy來代替真正的圖片。如果那個對象在Internet的某個遠端服務器上,直接操作這個對象因爲網絡速度原因可能比較慢,那我們可以先用Proxy來代替那個對象。總之原則是,對於開銷很大的對象,只有在使用它時才創建,這個原則可以爲我們節省很多寶貴的Java內存. 所以,有些人認爲Java耗費資源內存,我以爲這和程序編制思路也有一定的關係。
(3)現實中,Proxy應用範圍很廣,現在流行的分佈計算方式RMI和Corba等都是Proxy模式的應用
靜態代理--接口
interface ClothFactory{
public void productCloth();
}
靜態代理--被代理類
class NikeClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("生產一批Nike衣服");
}
}
class ADDSClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("生產一批ADDS衣服");
}
}
靜態代理--代理類
class Proxy implements ClothFactory{
<span style="white-space:pre"> </span>private ClothFactory cf;
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>public Proxy(ClothFactory cf) {
<span style="white-space:pre"> </span>this.cf = cf;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>@Override
<span style="white-space:pre"> </span>public void productCloth() {
<span style="white-space:pre"> </span>System.out.println("Proxy fee is $2000");
<span style="white-space:pre"> </span>cf.productCloth();
<span style="white-space:pre"> </span>}
}
靜態代理--實體類
public class StaticProxy {
@Test
public void test(){
Proxy proxy = new Proxy(new NikeClothFactory());
proxy.productCloth();
Proxy proxy2 = new Proxy(new ADDSClothFactory());
proxy2.productCloth();
}
}
動態代理類:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
interface Subject {
void action();
}
// 被代理類
class RealSubject implements Subject {
@Override
public void action() {
System.out.println("real subject ...");
}
}
/**
* 動態代理類: 1)實現InvocationHandler接口 2)調用java.lang.Reflect.Proxy類中的靜態方法:
* newProxyInstance(ClassLoader loader, Class<?>[] interfaces,
* InvocationHandler,h) ①.loader:與被代理類的類加載器一致 ②.interfaces:與被代理類實現的接口一致
* ③.h:實現了InvocationHandler接口的類對象,通常爲this
*/
class MyInvocationHandler implements InvocationHandler {
// 實現了接口的被代理類的對象聲明(這裏指RealSubject類對象)
Object object;
/**
* 作用: 1.給被代理類對象實例化:就是給上面的Object對象實例化(RealSubject)
* 2.返回一個代理類對象:返回MyInvocationHandler對象
*
* @param obj
* @return 返回MyInvocationHandler對象
*/
public Object blind(Object obj) {
this.object = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
/**
* 當代理對象發起對重寫方法的調用時,都會轉爲對如下invoke方法的調用
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
method.invoke(object, args);
return null;
}
}
public class DynamicProxy {
@Test
public void test() {
// 創建被代理類RealSubject對象
RealSubject realSubject = new RealSubject();
// 創建實現了InvocationHandler接口的類對象
MyInvocationHandler handler = new MyInvocationHandler();
// 調用blind(realSubject)方法,動態的返回一個同樣實現了被代理(RealSubject)
// 所實現接口的代理類(MyInvocationHandler)對象
Object obj = handler.blind(realSubject);
Subject subObject = (Subject)obj;
//轉到實現了InvocationHandler接口的類對象的invoke()方法
subObject.action();
}
}