目錄java
反射機制:將類的各個組成部分封裝成其餘對象,在運行狀態中,能夠動態獲取類信息和調用類對象的方法。程序員
java語言有着」一處編譯,到處運行「的優良特色,可以很好地適應不一樣的平臺,過程大體以下:數組
javac
命令將.java
文件編譯,會在磁盤上生成不面向平臺的字節碼文件.class
。.class
文件加載進內存中。java.lang.Class
的對象,該對象包含類的信息,做爲程序從方法區訪問各類數據的接口。這個Class對象包含類的全部信息,也就是說,咱們得到了這個Class類的對象,就能夠訪問到JVM中的這個類。那麼,如何獲取呢?主要有如下三種方式。安全
經過Class的靜態方法forName("全類名")
,返回和全類名對應類的Class對象。數據結構
//Class.forName("全類名"); Class cls1 = Class.forName("com.my.base.Person"); System.out.println(cls1);
經過類名的class屬性
獲取,返回該類對應的Class對象。性能
//類名.class; Class cls2 = Person.class; System.out.println(cls2);
getClass()
方法在Object中定義,返回該對象所屬類對應得Class對象。code
//對象.getClass(); Person p = new Person(); Class cls3 = p.getClass(); System.out.println(cls3);
同一個字節碼文件.class
在一次程序運行過程當中,只會被加載一次,不論經過哪一種方式獲取的Class對象都是同一個。視頻
// == 比較三個對象,都是true System.out.println(cls1 == cls2); System.out.println(cls1 == cls3); System.out.println(cls3 == cls2);
Class對象包含着類的全部信息,而這些信息被封裝在java.lang.reflect
包下,成了一個個的類,不徹底統計以下:對象
Method | Field | Annotation | Constructor | Package | Modifier | Parameter |
---|---|---|---|---|---|---|
方法類 | 字段類 | 註解類 | 構造器類 | 包類 | 修飾符類 | 方法參數類 |
Class類中提供了相應的方法,獲取這些信息,因爲方法衆多,具體的還須要參看JDK官方文檔。接口
我在這裏總結幾點通用的:
調用對應的getXxx
一般是得到對應public修飾的單個信息,getDeclaredXxx
則不考慮修飾符,找不到,則往超類找。
調用對應的getXxxs
一般得到對應的public修飾信息的數組,getDeclaredXxxs
則不考慮修飾符,找不到,則往超類找。
"參數名"
。"方法名",參數類型對應的類
。"參數對應的類"
。Object get(Object obj)
,也有設置值的方法:void set(Object obj, Object value)
。Object invoke(Object obj, Object... args)
。Constructor類能夠建立實例:T newInstance(Object... initargs)
,若是類中有無參構造器,能夠直接利用Class類中的newInstance()方法建立實例。
xxx.setAccessible(true)
暴力破解。判斷修飾符時,getModifiers()
返回的時int值,是各個修飾符表示數的和,能夠用位運算判斷,(xx.getModifiers()&Modifier.STATIC)!=0
表示存在static修飾符。
ps:確定有遺漏的API,到時候用的時候,翻翻API就完事了。
package com.my.reflect.practice; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; /** * @auther Summerday */ @SuppressWarnings("unchecked") public class ImplementClone { public Object clone(Object o) throws Exception{ //獲取對象的實際類型 Class<Object> clz = (Class<Object>) o.getClass(); //獲取全部構造方法 Constructor<Object>[] cs = (Constructor<Object>[]) clz.getDeclaredConstructors(); //任取一個構造方法 Constructor<Object> c = cs[0]; //防止取出構造方法爲私有 c.setAccessible(true); //該構造方法參數有or無? //獲取參數類型 Class[] ps = c.getParameterTypes(); //存儲參數的數組 Object[] os = new Object[ps.length]; for(int i = 0;i<ps.length;i++){ //判斷是否爲基本類型 if(ps[i].isPrimitive()){ if(ps[i] == boolean.class) os[i] = false; else if(ps[i] == char.class) os[i] = '\u0000'; else os[i] = 0; }else{ os[i] = null; } } //執行構造方法建立對象 Object obj = c.newInstance(os); //獲取屬性數組 Field[] fs = clz.getDeclaredFields(); for (Field f : fs){ //若是final修飾則返回,沒法修改 if((f.getModifiers()&Modifier.FINAL)!=0){ continue; } //暴力破解 f.setAccessible(true); //取出原屬性值 Object value = f.get(o); //將取出的屬性值賦值給新對象的屬性 f.set(obj, value); } return obj; } }
package com.my.reflect.practice; /** * @auther Summerday */ import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties; /** * * 參考黑馬程序員教學視頻 * * 實現:一、配置文件 二、反射 * * 1、將須要建立的對象的全類名和須要執行的方法定義在配置文件中 * * 2、在程序中加載讀取配置文件 * * 3、使用反射來加載類文件進內存 * * 4、建立對象 * * 5、執行方法 * */ public class ReflectTest { public static void main(String[] args) throws Exception { //一、加載配置文件 //1.1 建立Properties對象 Properties pro = new Properties(); //1.2 加載配置文件,轉換爲一個集合 //1.2.1獲取class目錄下的配置文件 //建立類加載器 ClassLoader classLoader = ReflectTest.class.getClassLoader(); InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties"); pro.load(resourceAsStream); //二、獲取配置文件中定義的數據 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //三、加載該類進內存 Class cls = Class.forName(className); //四、建立對象 Object obj = cls.newInstance(); //五、獲取方法 Method method = cls.getMethod(methodName); //六、執行方法 method.invoke(obj); } }
之後會有愈來愈多真實的場景須要用到反射這項靈魂技術,總之,好好看,好好學。