Java反射機制
反射機制概述
反射視為動態語言的關鍵,反射機制允許程式在執行期藉助於Reflection API取得任何類的內部資訊,並能直接操作認一物件的內部屬性及方法。 載入完類之後在,在堆記憶體的方法區就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊。我們可以透過這個物件看到類的結構。這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射。反射相關的主要API
獲取Class類的四種結構:
呼叫執行時類的屬性
Class clazz1 = Person.class; System.out.prinln(clazz1);
2. 透過執行時類的物件,呼叫getClass() Person p1 = new Person(); Class clazz2 = p1.getClass(); System.out.println(clazz2); 3. 呼叫Class的靜態方法:forName(String classPath) Class clazz3 = Class.forName("java.lang.String"); System.out.println(clazz3); 4. 使用類的載入器:ClassLoader ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.atguigu.java.Person"); System.out.println(clazz4);
理解Class類並獲取Class例項
在反射之前,在一個類的外部,不可以透過Person類的物件呼叫其內部私有結構。(如:name、showNation()以及私有的構造器)。反射之後對於Person的常規操作:
透過反射,可以呼叫Person類的私有構造器,私有屬性和私有方法。
呼叫私有構造器
Constructor cons1 = clazzz.getDeclaredConstructor(String.class); cons1.setAccessible(true); // 私有的都需要執行這一步 Person p1 = (Person) cons1.newInstance("jerry"); System.out.println(p1); 2. 呼叫私有屬性 Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"HanMeimei"); System.out.prinln(p1); 3. 呼叫私有方法 Method showNation = clazz.getDeclaredMethod("showNation",String.class) ; showNamtion.setAccessible(true); showNation.invoke(p1,"中國"); // 執行私有方法 String nation = (String) showNation.invoke(p1,”中國"); //
System.out.prinln(nation); 相關疑惑?
透過直接new的方式或反射的方式都可以呼叫公共的結構,開發中到底用哪個?
建議:使用new的方式 什麼時候使用反射: 當我們編譯階段不確定要造哪個類的物件,那我們這時需要用到反射 2. 反射與封裝是否相矛盾
類的載入與ClassLoader的理解
類的載入過程:
程式經過java.exec命令以後,會生成一個或多個位元組碼檔案(.class結尾)。 接著我們使用java.exe命令對某個位元組碼檔案進行解釋執行。相當於將某個位元組碼檔案載入到記憶體中。此過程成為類的載入。載入到記憶體中的類,我們稱為執行時類,此執行時的類,就作為Class的一個例項。 2. Class的例項對應著一個執行時類 3. 載入到記憶體中的執行時類,會快取一定的時間。在此時間之內,我們可以透過不同的方式來獲取此執行時類
什麼是類的載入器
三種類載入器:
普通讀取配置檔案的方式:
使用系統類載入器來讀取配置檔案
透過反射建立執行時類的物件
Class clazz = Person.class; Person obj = (Person)clazz.newInstance();
獲取執行時類的完整結構
獲取類的屬性:
Class clazz = Person.class;
Field[] fields = clazz.getFields(); // 可以獲取當前類以及父類中定義的public屬性
Field[] declaredFields = clazz.getDeclaredFields(); // 可以獲取當前類中所有定義的屬性
獲取類屬性的內部結構:
for(Field f: declaredFields){
獲取類屬性的許可權修飾符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier));
獲取類屬性的資料型別
Class type = f.getType();
System.out.println(type.getName());
獲取類屬性的變數名
String fName = f.getName();
System.out.print(fName);
}
## 獲取類的方法
Class clazz = Person.class;
Method[] methods = clazz.getMethods(); // 可以獲取當前類以及父類中定義的public方法
Method[] methods = clazz.getDeclaredMethods(); // 可以獲取當前類中所有定義的方法
獲取類方法的內部結構:
獲取方法的註解:
Annotation[] annos = m.getAnnotations();
for(){sout(a));
獲取許可權修飾符:
System.out.print(Modifier.toString(m.getModifiers()));
獲取返回值型別:
System.out.print(m.getReturnType().getName());
獲取形參列表:
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null || parameterTyupes.length ==0)){
for(Class p:parameterTypes){
System.out.print(p.getName)
}
}
獲取丟擲的異常:
Class[] exceptionTypes = m.getExceptionTypes();
if(!(exceptionTypes == null || exceptionTypes.length ==0)){
for(Class p:exception Types){
System.out.print(p.getName)
}
}
獲取執行時類構造器結構
Class clazz = Person.class;
Constructor[] construcotrs = clazz.getConstructors(); // 獲取當前執行時類中宣告為public的構造器
for(Constructor c : constructors){
System.out.println(c);
}
Constructor[] construcotrs = clazz.getDeclaredConstructors(); // 獲取當前執行時類中所有構造器
for(Constructor c : constructors){
System.out.println(c);
}
獲取執行時類父類和父類的型別(功能性程式碼vs邏輯性程式碼)
// 獲取執行時類的父類
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
// 獲取執行時類帶泛型的父類
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(superclass);
// 獲取執行時類帶泛型父類的泛型
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = paramType.getActualTypeArguments(); // 為什麼是陣列呢,因為像Map型別的泛型會有兩個引數:Map
System.out.println(genericSuperclassj[0]o.getTypeName());
獲取執行時類實現的介面
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
Class[] interfaces1 = clazz.getSuperclass.getInterfaces(); // 獲取父類的介面
for(Class c: interfaces){
System.out.println(c);
}
獲取執行時類所在的包
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.prinln(pack);
獲取執行時類宣告的註解
Class clazz = Person.class;
Annotation[] annotaions = clazz.getAnnotations();
for(Annotation annos: annotations){
System.out.println(annos);
}
呼叫執行時類的指定結構(重要)
獲取和操作執行時類指定的屬性
Class clazz = Person.class; // 首先建立執行時類的物件 Person p = clazz.newInstance(); Field id = clazz.getField("id"); // 要求執行時類屬性宣告為public,通常不用此方法 Field id = clazz.getDeclaredField("id"); id.setAccessible(true); // public以下的許可權需要設定為true // 設定當前屬性的值 id.set(p,1001); // 獲取當前屬性的值 int pid = (int)id.get(p);
獲取和操作執行時類中指定的方法(掌握)
Class clazz = Person.class; Person p = (Person) clazz.newInstance(); Method show = clazz.getDecalaredMethod("show",String.class); //引數1: 獲取指定方法的名稱,引數2:指明獲取方法的形參列表 show.setAccessible(true); show.invoke(p,"CHN") // 引數1:方法的呼叫者 引數2:給方法形參賦值的實參 String nation = (String) show.invoke(p,"CHN") // 有返回值的方法的呼叫 // 呼叫靜態方法 呼叫靜態方法不需要例項化 Method showStatic = clazz.getDeclaredMethod("showStatic"); showStatic.setAccessible(true); Object returnVal = showStatic.invoke(Person.class); // 括號裡面為null也可以,因為每個物件的static方法都是一樣的 System.out.prinln(returnVal); //null 如果是void方法的話返回值為null
獲取和操作執行時類中指定的構造器(不常用,常用newInstance)
Class clazz = Peson.class; Constructor cons = clazz.getDeclaredConstructor(String.class); cons.setAccessible(true); Person per = (Person) cons.newInstance("Tom"); System.out.println(per);
反射的應用:動態代理AOP(aspect orient proxy)(Sprint裡面會再講)
靜態代理
// 工廠介面:代理類需要代理做的事情
interface ClothFactory{
void produceCloth();
}
// 代理類
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;
public ProxyClothFactory(ClothFactory factory){
this.factory = factory; // 建立被代理類的物件
}
@Override
public void produceCloth() {
System.out.println("代理工廠的準備工作");
// 被代理類做的事情
factory.produceCloth();
System.out.println("代理工廠做的後續工作");
}
}
// 被代理類
class NikeClothFactory implements ClothFactory{
public void produceCloth(){
System.out.println("Nike生產一批運動服");
}
}
public class StaticProxyTest {
public static void main(String[] argv){
// 建立被代理類的物件
ClothFactory nike = new NikeClothFactory();
// 建立代理類物件
ClothFactory proxyFactory = new ProxyClothFactory(nike);
proxyFactory.produceCloth();
}
}
動態代理:
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
// 被代理類需要讓代理類需要做的事情
interface Human{
String getBelief();
void eat(String food);
}
// 被代理類
class SuperMan implements Human{
@Override
public String getBelief(){
return "I believe I can fly";
}
@Override
public void eat(String food){
System.out.println("我喜歡吃" + food);
}
}
// 問題:
// 1. 如何根據發載入到記憶體中的被代理類,動態的建立一個代理類及其物件?
// 2. 當透過代理類的物件呼叫方法時,如何動態的去呼叫被代理類中的同名方法?
//建立代理類物件
class ProxyFactory{
// obj為被代理類
public static Object getProxyInstance(Object obj){
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
// 建立一個代理類物件:引數1:獲取obj類的類載入器,引數2:獲取obj實現的介面,引數3:獲取obj中呼叫的方法
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object obj;
// 繫結被代理類
public void bind(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 呼叫被代理類中的方法:method是反射的
HumanUti1 util1 = new HumanUti1();
HumanUtil2 util2 = new HumanUtil2();
util1.method1();
Object returnValue = method.invoke(obj,args);
util2.method2();
return returnValue;
}
}
// 呼叫通用方法:
class HumanUti1{
public void method1(){
System.out.println("-------通用方法1-------");
}
}
class HumanUtil2{
public void method2(){
System.out.println("-----通用方法2---------");
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
// 例項化動態代理類
Human proxyInstance = (Human)ProxyFactory.getProxyInstance(superMan);
// 透過代理類物件呼叫方法時呼叫的都是被代理類中的方法
String belief = proxyInstance.getBelief();
System.out.println(belief);
proxyInstance.eat("雞腿");
// 代理類動態性的體現:
NikeClothFactory nikeClothFactory = new NikeClothFactory();
ClothFactory proxyClothFactory = (ClothFactory)ProxyFactory.getProxyInstance(nikeClothFactory);
proxyClothFactory.produceCloth();
}
}
動態代理複習:靜態代理的舉例說明:
靜態代理的缺點:
動態代理的特點:
動態代理的實現:
如何根據載入到記憶體中的被代理類,動態建立一個代理類及其物件:透過Proxy.newProxyInstance()實現
當透過代理類的物件呼叫方法a時,如何動態的去呼叫被代理類中的同名方法a:透過InvocationHandler介面的實現類及其方法invoke()
作者:dandeseed
連結:
來源:掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/854/viewspace-2797067/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java核心反射機制Java反射
- java利器——反射機制Java反射
- Java的反射機制Java反射
- Java反射機制(轉)Java反射
- Java反射機制研究Java反射
- Java反射機制那些事Java反射
- Java 中的 反射機制Java反射
- Java反射機制簡答Java反射
- 說說 Java 反射機制Java反射
- JAVA(五)反射機制/AnnotationJava反射
- Java 反射機制分析指南Java反射
- Java 反射機制詳解Java反射
- Java中的類反射機制Java反射
- Java筆記-反射機制(一)Java筆記反射
- java進階(41)--反射機制Java反射
- 淺析java的反射機制Java反射
- java反射機制的學習心得Java反射
- 關於Java中的反射機制Java反射
- Java基礎之反射機制(續)Java反射
- Java重點基礎:反射機制Java反射
- Java動態代理和反射機制Java反射
- Java 反射機制的三種方式Java反射
- Java 反射機制應用實踐Java反射
- Java反射機制應用實踐Java反射
- Java反射機制實現與原理Java反射
- java中的反射機制淺析Java反射
- Java 反射機制學習資料Java反射
- JAVA中的反射機制詳解Java反射
- 菜鳥學Java(十四)——Java反射機制(一)Java反射
- 菜鳥學Java(十五)——Java反射機制(二)Java反射
- JAVA的反射機制==>用反射分析類的實現Java反射
- Java 從入門到精通-反射機制Java反射
- 淺談Java的反射機制和作用Java反射
- 基礎篇:深入解析JAVA反射機制Java反射
- Java筆記-反射機制(三)-小demoJava筆記反射
- Java安全基礎之Java反射機制和ClassLoader類載入機制Java反射
- Java進階 | 泛型機制與反射原理Java泛型反射
- Java反射機制開發經驗總結Java反射