Java 高級開發必修知識---反射
文章開始之前 提一下:
java反射操作其實就是主要圍繞Class,Field,Methon等幾個類來操作其中的方法
Class類的使用
1) 在面向對象的世界裏,萬事萬物皆對象
A. Java語言中,普通數據類型,靜態成員不是對象,其他皆對象
B. 每一個類也是對象
C. 類是java.lang.Class類的實例對象
There is a class named Class
對象的表示:
普通類對象表示:
Foo foo = new Foo();
Class類實例對象表示:
//Foo也是一個實例對象,是Class類的實例對象
任何一個類都是Class的實例對象,這個實例對象有三種表達方式
1. 任何一個類都有一個隱含的靜態成員變量class:
Class c1 = Foo.class;
2. 已經指定該類的對象通過getClass方法
Foo foo = new Foo();
Class c2 = foo.getClass();
官網:c1/c2 表示了Foo類的類類型(class type)
一個類,本身就是一個對象,它是Class類的對象
萬事萬物皆對象,類也是對象,是Class類的實例對象,這個對象我們稱為該類的類類型
3. ForName(“類的全稱”);
Class c3 = null;
c3=Class.forName(“包名+.某類名”);
不管是那種表達方式,都是
C1=c2=c3
完全可以通過類的類類型創建該類的對象(創建Foo的實例對象,但要做強制類型轉換,向下轉型)
前提要求:需要有無參數的構造方法
Comont comont = new Comont(); Class c1 = comont.getClass(); Class c2 = Comont.class; try { Comont c = (Comont) c2.newInstance(); } catch (InstantiationException | IllegalAccessException e) { // TODO Auto-generated catch blocke.printStackTrace(); } System.out.println(c1 == c2); comont.start();
動態加載類
Class.forName(“類的全稱”);
1. 不僅表示了類的類類型,還代表動態加載類
2. 編譯時刻加載類是靜態加載類,運行時刻加載類是動態加載類
(1) New 對象是靜態加載類,在變異時刻就需要加載所有的可能使用到的類
(2) 動態加載類,在運行時刻加載
編譯不會報錯
運行時刻報錯。找不到該類
Class c = Class.forName(arg[0]);
共同實現接口類 cc = (共同實現接口類) c.newInstance();
3. 使用記事本開發可明顯區分
Java 類 要運行類:動態加載類,不需要重新編譯測試類,直接運行即可
功能性的類:盡量使用動態加載
基本數據類型也有類類型
Class c1 = int.class; Class c2 = String.class;//String類的字節碼
數據類型和包裝類的類類型不同
Void也是類
Class c3 = void.class;
基本數據類型
Void關鍵字
都存在類類型
方法也是對象,方法是Method的對象
反射:某類的字節碼表示
獲取方法信息
1. c.getName()
(1) 基本數據類型返回類型名
(2) 類返回包名+類名類的名稱
2. c1.getSimpleName()
(1) 返回不帶包名的類的名稱
栗子:通過反射可以獲取到任何類的信息
需求:打印類的信息,獲取類的 成員函數
package cn.pro; import java.lang.reflect.Method; /** * * @author: 房上的貓 * * @time: 下午5:34:45 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class mymain { public static void printClassMessage(Object obj) { // 要獲取類的信息 首先要獲取類的類類型 // 形參obj 該對象所屬類的信息 Class c = obj.getClass();// 傳遞的是哪個子類的對象 c就是該 子類的類類型 // getClass()方法:native修飾代表 存在不是java 代碼 ,調度 外用 ///該方法java內調用底層c語言實現 // 獲取累的名稱 System.out.println("類的名稱是:" + c.getName()); // Method類是方法對象 // 一個成員方法就是一個Method // getMethods()方法獲取的是所有的public修飾的函數,包括父類 繼承而來的 Method[] ms = c.getMethods(); // c.getDeclaredMethods()獲取的是所有該類自己聲明的方法,不 問訪問權限.所有。所有。所有 String[] name = new String[ms.length]; for (int i = 0; i < ms.length; i++) { // 得到方法的返回值類型--得到的是返回值類型的類類型 Class returnType = ms[i].getReturnType(); // 得到返回值名字 String returnName = returnType.getName(); // 得到方法的名稱 name[i] = ms[i].getName(); // 獲取參數列表類型--得到的是參數列表的類型的類類型 Class[] parameterTypes = ms[i].getParameterTypes(); Int params=parameterTypes.length; String[] paramNames = new String[params]; for (int j = 0; j < params; j++) { // 得到參數列表名 paramNames[j] = ms[j].getName(); } } } }
通過反射可以獲取任何類的類信息
比較牛逼
獲取類的成員變量構造函數信息
成員變量也是對象
是Java.lang.reflect.Field的對象
Field類封裝了關於成員變量的操作
栗子:通過反射可以獲取到任何類的信息
需求:打印類的信息,獲取類的成員變量
package cn.reflect; import java.lang.reflect.Field; /** * * @author: 房上的貓 * * @time: 下午3:49:32 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class myField { public static void printFieldMessage(Object obj) { Class c = obj.getClass(); // getFidlds() 方法獲取的是類的所有的public的成員變量信息 Field[] fs = c.getFields(); // getDeclaredFields() 獲取的是該類自己聲明的成員信息 不問訪問權限.所有。所有。所有 // Field[] fs = c.getDeclaredFields(); for (Field field : fs) { // 得到成員變量的類型的類類型 Class fieldType = field.getType(); // 得到成員變量類型的名字 String typeName = fieldType.getName(); // 得到成員變量的名字 String fieldName = field.getName(); // } } }
構造函數也是對象
是Java.lang.Constructor的對象 其中封裝了構造函數的信息
栗子:通過反射可以獲取到任何類的信息
需求:打印類的信息,獲取類的構造函數信息
package cn.reflect; import java.lang.reflect.Constructor; /** * * @author: 房上的貓 * * @time: 下午3:49:32 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class myCon { public static void printConMessage(Object obj) { Class c = obj.getClass(); // getConstructors() 獲得所有的共有的構造方法 Constructor[] cs = c.getConstructors(); // getDeclaredConstructors() 得到所有的構造方法(必須是自己聲明的) 不問訪問權限.所有。所有。所有 // Constructor[] cs = c.getDeclaredConstructors(); for (Constructor constructor : cs) { // 獲取構造函數名 String name = constructor.getName(); // 獲取構造函數的參數列表------>得到的是參數列表的類類型 Class[] parameterTypes = constructor.getParameterTypes(); for (Class class1 : parameterTypes) { // 獲取構造函數參數列表名 String name2 = class1.getName(); } } } }
其他
Class類還可以獲取類的其他信息,這裏將不再詳細講解
想要了解的可以創建Class類的實例對象
Class c = obj.getClass();
c.get...
自行查看並嘗試
或閱讀幫助文檔,查看Class類的所有API
記住一點:在任何情況下想要獲取一個類的信息,首先要得到這個類的類類型
得到類的類類型,得到這個類的類信息就輕而易舉得到了
方法的反射
1. 如何獲取某個方法?
方法的名稱和方法的參數列表才能唯一決定某個方法
2. 方法反射的操作
Method.invoke(對象,參數列表)
栗子:
package cn.reflect; import java.lang.reflect.Method; /** * * @author: 房上的貓 * * @time: 下午4:25:25 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class MethodDemo1 { public static void main(String[] args) throws Exception { // 要獲取print(int,int)方法 A a = new A(); /* * 1.要獲取一個方法就是要獲取一個類的信息,獲取類的信息首先要獲取類的類類型 */ Class c = a.getClass(); /* * 2.獲取方法 名稱和參數列表 * * getMethod(name, parameterTypes)獲取的是public的方法 * * c.getDeclaredMethod(name, parameterTypes)獲取的是所有自己聲明的方法 不問訪問權限 * * * 參數解析: 1.name:方法名 2.parameterTypes:參數列表類型(0或多個) */ Method m = c.getMethod("print", new Class[] { int.class, int.class }); /* * 方法的反射操作 * * a.print(10,20);方法的反射操作是用m對象來進行方法調用 和a.print調用的效果 * * 方法如果沒有返回值返回null 有返回值返回具體的返回值 返回object類型,需要做強制類型轉換 */ Object o = m.invoke(a, new Object[] { 10, 20 }); // 就等於調用了print(int,int)方法 /* * 如果方法無參數列表則: * * c.getMethod("print"); m.invoke(a); */ } } class A { public void print(int a, int b) { System.out.println(a + b); } public void print(String a, String b) { // a的大寫形式 and b的小寫形式 System.out.println(a.toUpperCase() + "," + b.toLowerCase()); } }
升華操作:
通過反射了解集合泛型的本質
通過Class,Method來認識泛型的本質
相信讀者們看到這裏心中一定會有這樣兩個疑問:
什麽是泛型?
泛型什麽時候有效?
那我們探討一下這兩個話題:
package cn.reflect; import java.lang.reflect.Method; import java.util.ArrayList; /** * * @author: 房上的貓 * * @time: 下午6:31:38 * * @博客地址: https://www.cnblogs.com/lsy131479/ * */ public class MethodDemo2 { public static void main(String[] args) { // 普通集合 ArrayList list = new ArrayList<>(); // 泛型集合 集合內只能存放‘<>‘尖括號內類型的值 ArrayList<String> list1 = new ArrayList<>(); // 集合的泛型防止錯誤輸入 // 利用反射了解集合泛型 Class c1 = list.getClass(); Class c2 = list1.getClass(); list1.add("1"); System.out.println(c1 == c2); /* * 反射的操作都是編譯之後的操作(運行時) * * c1==c2結果返回true 表示兩個集合類運行時都是同一類類型 * * 說明編譯之後集合的泛型是去泛型化的(編譯完之後就沒有泛型存在了) * * java中集合的泛型,是防止錯誤輸入的,只在編譯階段有效,繞過編譯就無效了 * * 驗證:可以通過方法的反射來操作,繞過編譯 */ try { Method m = c2.getMethod("add", Object.class); m.invoke(list1, 100);// 大膽猜想:繞過編譯操作就繞過了泛型 // 集合大小返回2 .說明:繞過編譯階段後可以向泛型集合添加任何類型的值 System.out.println(list1.size()); // 嘗試查看泛型集合內的值 發現值已經添加進去 System.out.println(list1); /* * 在這裏如果使用for增強遍歷將會拋異常 * * 因為後面添加的100是int類型的值 * * 而集合泛型是String類型 * * 使用for增強遍歷內部其實做出的操作是:集合的每個值都使用一個集合泛型類型的數據類型來接受遍歷 * 而int類型使用String類型來接受,眾所周知將會報錯(這裏的泛型類型是String) * * 會有類型轉換錯誤 */ } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
結論:
反射(Class,Method,Field ... )的操作都是繞過編譯,都是在運行時刻來執行的
ok...到此為止!!!後續如果有新的想法將會繼續總結。。敬請期待
(C) 房上的貓 。 保留所有權利。
https://www.cnblogs.com/lsy131479/
如需轉載,請註明出處!!!
Java 高級開發必修知識---反射