Java开发之路—Java反射机制
Java反射機制
- (重要)
- 1、反射的概述
- 1.1、本章的主要內容
- 1.2、關于反射的理解
- 1.3、體會反射機制的“動態性”
- 1.4、反射機制能提供的功能
- 1.5、相關API
- 2、Class類的理解與獲取Class的實例
- 2.1、Class類的理解
- 2.2、獲取Class實例的幾種方式:(前三種方式需要掌握)
- 2.3、總結
- 2.4、Class實例可以是哪些結構的說明
- 3、了解ClassLoader
- 3.1、類的加載過程 ---- 了解
- 3.2、類的加載器的作用
- 3.3、類的加載器的分類
- 3.4、Java類編譯、運行的執行的流程
- 3.5、使用Classloader加載src目錄下的配置文件(需要掌握)
- 4、反射應用一:創建運行時類的對象(掌握)
- 4.1、代碼舉例
- 4.2、說明
- 5、反射應用二:獲取運行時類的完整結構(了解)
- 5.1、獲取屬性結構
- 5.2、獲取方法結構
- 5.3、獲取構造器結構
- 5.4、獲取運行時類的父類getSuperclass()
- 5.5、獲取運行時類的帶泛型的父類 getGenericSuperclass()
- 5.6、獲取運行時類的帶泛型的父類的泛型 getActualTypeArguments()
- 5.7、獲取運行時類實現的接口 getInterfaces()
- 5.8、獲取運行時類所在的包 getPackage()
- 5.9、獲取運行時類聲明的注解 getAnnotations()
- 6、反射應用三:調用運行時類的指定結構(掌握)
- 6.1、調用指定的屬性
- 6.2、調用指定的方法(使用的最多)
- 6.3、調用指定的構造器(此方法不通用)
- 7、動態代理
- 7.1、代理模式的原理
- 7.2、靜態代理
- 7.2.1、舉例
- 7.2.2、靜態代理的缺點
- 7.3、動態代理的特點
- 7.4、動態代理的實現
- 7.4.1、需要解決的兩個主要問題
- 7.4.2、代碼實現
(重要)
1、反射的概述
1.1、本章的主要內容
1.2、關于反射的理解
Reflection(反射):是被視為動態語言的關鍵,反射機制允許程序在執行期借助于Reflection API取得任何
類的內部信息,并能直接操作任意對象的內部屬性及方法。
框架 = 反射 + 注解 + 設計模式
1.3、體會反射機制的“動態性”
//體會反射的動態性 @Test public void test2(){for(int i = 0;i < 100;i++){int num = new Random().nextInt(3);//0,1,2String classPath = "";switch(num){case 0:classPath = "java.util.Date";break;case 1:classPath = "java.lang.Object";break;case 2:classPath = "com.atguigu.java.Person";break;}try {Object obj = getInstance(classPath);System.out.println(obj);} catch (Exception e) {e.printStackTrace();}}} /* 創建一個指定類的對象。 classPath:指定類的全類名*/ public Object getInstance(String classPath) throws Exception {Class clazz = Class.forName(classPath);return clazz.newInstance(); }1.4、反射機制能提供的功能
1.5、相關API
- java.lang.Class:反射的源頭
- java.lang.reflect.Method
- java.lang.reflect.Field
- java.lang.reflect.Constructor
…
2、Class類的理解與獲取Class的實例
2.1、Class類的理解
1、類的加載過程:
- 程序經過javac.exe命令以后,會生成一個或多個字節碼文件(.class結尾)。接著我們使用java.exe命令對某個字節碼文件進行解釋運行。相當于將某個字節碼文件加載到內存中。此過程就稱為類的加載。加載到內存中的類,我們就稱為運行時類,此運行時類,就作為Class的一個實例。
2、換句話說,Class的實例就對應著一個運行時類。
3、加載到內存中的運行時類,會緩存一定的時間。在此時間之內,我們可以通過不同的方式
來獲取此運行時類。
2.2、獲取Class實例的幾種方式:(前三種方式需要掌握)
方式三用的最多
//方式一:調用運行時類的屬性:.classClass clazz1 = Person.class;System.out.println(clazz1);//方式二:通過運行時類的對象,調用getClass()Person p1 = new Person();Class clazz2 = p1.getClass();System.out.println(clazz2);//方式三:調用Class的靜態方法:forName(String classPath) ---用的最多Class clazz3 = Class.forName("com.atguigu.java.Person"); // clazz3 = Class.forName("java.lang.String");System.out.println(clazz3);System.out.println(clazz1 == clazz2);System.out.println(clazz1 == clazz3);//方式四:使用類的加載器:ClassLoader (了解)ClassLoader classLoader = ReflectionTest.class.getClassLoader();Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");System.out.println(clazz4);System.out.println(clazz1 == clazz4);2.3、總結
創建類的對象的方式?
- 方式一:new + 構造器;
- 方式二:要創建Xxx類的對象,可以考慮:Xxx、Xxxs、XxxFactory、XxxBuilder類中查看是否有靜態方法的存在。可以調用其靜態方法,創建Xxx對象;
- 方式三:通過反射;
2.4、Class實例可以是哪些結構的說明
3、了解ClassLoader
3.1、類的加載過程 ---- 了解
3.2、類的加載器的作用
3.3、類的加載器的分類
3.4、Java類編譯、運行的執行的流程
3.5、使用Classloader加載src目錄下的配置文件(需要掌握)
@Testpublic void test2() throws Exception {Properties pros = new Properties();//此時的文件默認在當前的module下。//讀取配置文件的方式一: // FileInputStream fis = new FileInputStream("jdbc.properties"); // FileInputStream fis = new FileInputStream("src\\jdbc1.properties"); // pros.load(fis);//讀取配置文件的方式二:使用ClassLoader//配置文件默認識別為:當前module的src下ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();InputStream is = classLoader.getResourceAsStream("jdbc1.properties");pros.load(is);String user = pros.getProperty("user");String password = pros.getProperty("password");System.out.println("user = " + user + ",password = " + password);}4、反射應用一:創建運行時類的對象(掌握)
4.1、代碼舉例
Class<Person> clazz = Person.class;Person obj = clazz.newInstance(); System.out.println(obj);4.2、說明
newInstance():調用此方法,創建對應的運行時類的對象。內部調用了運行時類的空參的構造器。
要想此方法正常的創建運行時類的對象,要求:
- 運行時類必須提供空參的構造器;
- 空參的構造器的訪問權限得滿足。通常,設置為public;
在javabean中要求提供一個public的空參構造器。原因:
- 便于通過反射,創建運行時類的對象;
- 便于子類繼承此運行時類時,默認調用super()時,保證父類此構造器;
5、反射應用二:獲取運行時類的完整結構(了解)
我們可以通過反射,獲取對應的運行時類中所有的屬性、方法、構造器、父類、接口、父類的泛型、包、注解、異常等…
5.1、獲取屬性結構
@Test public void test1(){Class clazz = Person.class;//獲取屬性結構//getFields():獲取當前運行時類及其父類中聲明為public訪問權限的屬性Field[] fields = clazz.getFields();for(Field f : fields){System.out.println(f);}System.out.println();//getDeclaredFields():獲取當前運行時類中聲明的所屬性。(不包含父類中聲明的屬性Field[] declaredFields = clazz.getDeclaredFields();for(Field f : declaredFields){System.out.println(f);} }5.2、獲取方法結構
@Test public void test1(){Class clazz = Person.class;//獲取方法結構//getMethods():獲取當前運行時類及其父類中聲明為public權限的方法Method[] methods = clazz.getMethods();for(Method m : methods){System.out.println(m);}System.out.println();//getDeclaredMethods():獲取當前運行時類中聲明的所方法。(不包含父類中聲明的方法)Method[] declaredMethods = clazz.getDeclaredMethods();for(Method m : declaredMethods){System.out.println(m);} }5.3、獲取構造器結構
/*獲取構造器結構 */@Test public void test1(){Class clazz = Person.class;//getConstructors():獲取當前運行時類中聲明為public的構造器Constructor[] constructors = clazz.getConstructors();for(Constructor c : constructors){System.out.println(c);}System.out.println();//getDeclaredConstructors():獲取當前運行時類中聲明的所有的構造器Constructor[] declaredConstructors = clazz.getDeclaredConstructors();for(Constructor c : declaredConstructors){System.out.println(c);}}5.4、獲取運行時類的父類getSuperclass()
/*獲取運行時類的父類getSuperclass()*/@Test public void test2(){Class clazz = Person.class;Class superclass = clazz.getSuperclass();System.out.println(superclass); }5.5、獲取運行時類的帶泛型的父類 getGenericSuperclass()
/* 獲取運行時類的帶泛型的父類 getGenericSuperclass()*/@Test public void test3(){Class clazz = Person.class;Type genericSuperclass = clazz.getGenericSuperclass();System.out.println(genericSuperclass); }5.6、獲取運行時類的帶泛型的父類的泛型 getActualTypeArguments()
/* 獲取運行時類的帶泛型的父類的泛型 getActualTypeArguments()代碼:邏輯性代碼 vs 功能性代碼 */@Test public void test4(){Class clazz = Person.class;Type genericSuperclass = clazz.getGenericSuperclass();ParameterizedType paramType = (ParameterizedType) genericSuperclass;//獲取泛型類型Type[] actualTypeArguments = paramType.getActualTypeArguments();// System.out.println(actualTypeArguments[0].getTypeName());System.out.println(((Class)actualTypeArguments[0]).getName()); }5.7、獲取運行時類實現的接口 getInterfaces()
/* 獲取運行時類實現的接口 getInterfaces()*/@Test public void test5(){Class clazz = Person.class;Class[] interfaces = clazz.getInterfaces();for(Class c : interfaces){System.out.println(c);}System.out.println();//獲取運行時類的父類實現的接口 getSuperclass().getInterfaces()Class[] interfaces1 = clazz.getSuperclass().getInterfaces();for(Class c : interfaces1){System.out.println(c);} }5.8、獲取運行時類所在的包 getPackage()
/*獲取運行時類所在的包 getPackage()*/@Test public void test6(){Class clazz = Person.class;Package pack = clazz.getPackage();System.out.println(pack); }5.9、獲取運行時類聲明的注解 getAnnotations()
/*獲取運行時類聲明的注解 getAnnotations()*/@Test public void test7(){Class clazz = Person.class;Annotation[] annotations = clazz.getAnnotations();for(Annotation annos : annotations){System.out.println(annos);} }6、反射應用三:調用運行時類的指定結構(掌握)
調用運行時類指定的結構:屬性、方法、構造器。
6.1、調用指定的屬性
@Test public void testField1() throws Exception {Class clazz = Person.class;//創建運行時類的對象Person p = (Person) clazz.newInstance();//1. getDeclaredField(String fieldName):獲取運行時類中指定變量名的屬性Field name = clazz.getDeclaredField("name");//2.保證當前屬性是可訪問的(private的也可以訪問)name.setAccessible(true);//3.獲取、設置指定對象的此屬性值name.set(p,"Tom"); //設置System.out.println(name.get(p)); //獲取 }6.2、調用指定的方法(使用的最多)
@Testpublic void testMethod() throws Exception {Class clazz = Person.class;//創建運行時類的對象Person p = (Person) clazz.newInstance();/*1.獲取指定的某個方法getDeclaredMethod():參數1 :指明獲取的方法的名稱 參數2:指明獲取的方法的形參列表*/Method show = clazz.getDeclaredMethod("show", String.class);//2.保證當前方法是可訪問的show.setAccessible(true);/*3. 調用方法的invoke():參數1:方法的調用者 參數2:給方法形參賦值的實參invoke()的返回值即為對應類中調用的方法的返回值。*/Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");System.out.println(returnValue);System.out.println("*************如何調用靜態方法*****************");// private static void showDesc()Method showDesc = clazz.getDeclaredMethod("showDesc");showDesc.setAccessible(true);//如果調用的運行時類中的方法沒返回值,則此invoke()返回null // Object returnVal = showDesc.invoke(null);Object returnVal = showDesc.invoke(Person.class);System.out.println(returnVal);//null}6.3、調用指定的構造器(此方法不通用)
@Test public void testConstructor() throws Exception {Class clazz = Person.class;//private Person(String name)/*1.獲取指定的構造器getDeclaredConstructor():參數:指明構造器的參數列表*/Constructor constructor = clazz.getDeclaredConstructor(String.class);//2.保證此構造器是可訪問的constructor.setAccessible(true);//3.調用此構造器創建運行時類的對象Person per = (Person) constructor.newInstance("Tom");System.out.println(per);}7、動態代理
7.1、代理模式的原理
使用一個代理將對象包裝起來, 然后用該代理對象取代原始對象。任何對原始對象的調用都要通過代理。代理對象決定是否以及何時將方法調用轉到原始對象上。
7.2、靜態代理
7.2.1、舉例
實現Runnable接口的方法創建多線程。
Class MyThread implements Runnable{} //相當于被代理類 Class Thread implements Runnable{} //相當于代理類 main(){MyThread t = new MyThread();Thread thread = new Thread(t);thread.start();//啟動線程;調用線程的run() }7.2.2、靜態代理的缺點
① 代理類和目標對象的類都是在編譯期間確定下來,不利于程序的擴展。
② 每一個代理類只能為一個接口服務,這樣一來程序開發中必然產生過多的代理。
7.3、動態代理的特點
動態代理是指客戶通過代理類來調用其它對象的方法,并且是在程序運行時根據需要動態創建目標類的代理對
象。
7.4、動態代理的實現
7.4.1、需要解決的兩個主要問題
- 問題一:如何根據加載到內存中的被代理類,動態的創建一個代理類及其對象;
(通過Proxy.newProxyInstance()實現) - 問題二:當通過代理類的對象調用方法a時,如何動態的去調用被代理類中的同名方法a;
(通過InvocationHandler接口的實現類及其方法invoke())
7.4.2、代碼實現
/**** 動態代理的舉例** @author shkstart* @create 2019 上午 10:18*/interface Human{String getBelief();void eat(String food);} //被代理類 class SuperMan implements Human{@Overridepublic String getBelief() {return "I believe I can fly!";}@Overridepublic void eat(String food) {System.out.println("我喜歡吃" + food);} }class HumanUtil{public void method1(){System.out.println("====================通用方法一====================");}public void method2(){System.out.println("====================通用方法二====================");}}//生成代理類的工廠 class ProxyFactory{//調用此方法,返回一個代理類的對象。解決問題一// 問題一:根據加載到內存中的被代理類,動態的創建一個代理類及其對象。//(通過Proxy.newProxyInstance()實現)public static Object getProxyInstance(Object obj){//obj:被代理類的對象//解決問題二:當通過代理類的對象調用方法a時,如何動態的去調用被代理類中的同名方法a。// (通過InvocationHandler接口的實現類及其方法invoke())MyInvocationHandler handler = new MyInvocationHandler();handler.bind(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;}//當我們通過代理類的對象,調用方法a時,就會自動的調用如下的方法:invoke()//將被代理類要執行的方法a的功能就聲明在invoke()中@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {HumanUtil util = new HumanUtil();util.method1();//method:即為代理類對象調用的方法,此方法也就作為了被代理類對象要調用的方法//obj:被代理類的對象Object returnValue = method.invoke(obj,args);util.method2();//上述方法的返回值就作為當前類中的invoke()的返回值。return returnValue;} }public class ProxyTest {public static void main(String[] args) {SuperMan superMan = new SuperMan();//proxyInstance:代理類的對象Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);//當通過代理類對象調用方法時,會自動的調用被代理類中同名的方法String belief = proxyInstance.getBelief();System.out.println(belief);proxyInstance.eat("四川麻辣燙");System.out.println("*****************************");NikeClothFactory nikeClothFactory = new NikeClothFactory();ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);proxyClothFactory.produceCloth();} }體會:反射的動態性
總結
以上是生活随笔為你收集整理的Java开发之路—Java反射机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 被“需要”的无功功率
- 下一篇: 阿联酋选出首位 AI 国务部长(附You