魯春利的工作筆記,好記性不如爛筆頭
反射機制
爲了更好的理解java的反射機制,最好先對java的泛型有所瞭解。java泛型就是參數化類型,即爲所操作的數據類型指定一個參數。如果只指定了<?>,而沒有extends,則默認是允許Object及其下的任何Java類。
List<? extends Map<String, String>> list = null; { // 具體實現時限定了List所能add的數據類型 list = new ArrayList<Map<String, String>>(); } { // 具體實現時限定了List所能add的數據類型 list = new ArrayList<HashMap<String, String>>(); } logger.info("list is {}", list);
Java運行時,對任意一個類,想知道它有哪些屬性和方法,對於任意一個對象,想調用它的任意一個方法,都是可以實現的,這來自JAVA的反射機制。
1、JAVA的反射機制主要功能:
a、在運行時判斷任意一個對象所屬的類。
b、在運行時構造任意一個類的對象。
c、在運行時判斷任意一個類所具有的成員變量和方法。
d、在運行時調用任意一個對象的方法
前提是在運行時,不是編譯時,也就是在運行前並不知道調用哪一個類,通過反射就可以做到這些。
2、在JDK中,主要由以下類來實現JAVA反射機制,這些類位於java.lang.reflect包中:
3、Class類是Reflection API 中的核心類
Class類是Reflection API 中的核心類。在類加載時,java虛擬機會自動創建相應的Class對象。
在java.lang.Object 類中定義了getClass()方法,因此對於任意一個Java對象,都可以通過此方法獲得對象的類。
Class類的實例用於表示運行時的java數據類型,包括類、接口、數組、枚舉、註解、基本數據類型甚至void等。
獲得你想操作的類的 java.lang.Class 對象
針對引用數據類型:
調用靜態方法:Class.forName();
Class.forName("p1.Person"); Class.forName("com.mysql.jdbc.Driver");
調用Object類中定義的getClass()方法
Person person = new Person(); Class cs = p.getClass(); Class strClass = "Hello World".getClass();
使用.class表達式
Class strClass = String.class; Class personClass = p1.Person.class; Class jdbcClass = com.mysql.jdbc.Driver.class;
針對基本數據類型及void
使用.class表達式
Class intClass = int.class; Class dobClass = double.class; Class voidClass = void.class;
調用相應封裝類的Type屬性
Class IntegerClass = Integer.TYPE; Class voicClass = Void.TYPE;
【實例1】
讀取命令行參數指定的類名,然後打印這個類所具有的方法信息。即JAVA的反射機制功能中的“在運行時判斷任意一個類所具有的方法”
package com.invicme.tools.reflect; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.invicme.tools.utils.StringUtils; /** * * @author lucl * @version 2017-01-19 * * 測試運行時獲取類的實例及參數 */ public class ReflectTest001 { // private static Logger logger = LoggerFactory.getLogger(ReflectTest001.class); public static void main(String[] args) throws Exception { if (null == args || args.length <= 0 || StringUtils.isBlank(args[0])) { return; } // 加載並初始化命令行參數指定的類 Class<?> clazz = Class.forName(args[0]); String className = clazz.getName(); logger.info("class name is {}", className); // 獲得類的所有方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { logger.info("\tmethod is {}", method.toString()); } } }
在命令行裏輸入參數,此處參數必須爲類的全限定名
(1). Class.forName(args[0]) 傳入的是類的全稱,返回的是與這個類所對應的一個Class類的實例;
(2). Method methods[] = classType.getDeclaredMethods() 獲得該類所有的方法,包括private的。
【實例2】
這個例子只能複製簡單的JavaBean,假定JavaBean 的每個屬性都有public 類型的getXXX()和setXXX()方法。體現了JAVA的反射機制中的“在運行時判斷任意一個類所具有的屬性”、“在運行時調用任意一個對象的方法”和“在運行時構造任意一個類的對象”
package com.invicme.tools.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.invicme.common.model.test.TestUserInfo; import com.invicme.tools.utils.GUIDUtils; /** * * @author lucl * @version 2017-01-19 * * 測試運行時獲取對象的拷貝 */ public class ReflectTest002 { // private static Logger logger = LoggerFactory.getLogger(ReflectTest002.class); public Object copy (Object userInfo) throws Exception { // 獲得對象的類的類型 Class<? extends Object> clazz = userInfo.getClass(); logger.info("class name is {}", clazz.getName()); // { // // 先調用Class類的getConstructor()方法獲得一個Constructor對象 // Constructor<? extends Object> constructor = clazz.getConstructor(new Class[]{}); // // 它代表默認的構造方法,然後調用Constructor對象的newInstance()方法構造一個實例。 // Object newInstance = constructor.newInstance(new Object[]{}); // } // 或者直接通過Class類的newInstance方法 Object newInstance = clazz.newInstance(); // 獲得對象的所有屬性 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { String fieldName = field.getName(); if ("serialVersionUID".equals(fieldName)) { continue; } if (fieldName.startsWith("is")) { continue; } String firstLetter = fieldName.substring(0, 1).toUpperCase(); // 獲得和屬性對應的getXXX()方法的名字 String getMethodName = "get" + firstLetter + fieldName.substring(1); // 獲得和屬性對應的setXXX()方法的名字 String setMethodName = "set" + firstLetter + fieldName.substring(1); // 獲取源實例的數據 Object invoke = null; { Method method = clazz.getMethod(getMethodName, new Class[]{}); // 調用原對象的getXXX()方法 invoke = method.invoke(userInfo, new Object[]{}); logger.info("field name is {}, field value is {}, get method name is {}, set method name is {}", fieldName, invoke, getMethodName, setMethodName); } // 設置爲新複製出來的實例 { Method method = clazz.getMethod(setMethodName, new Class[]{field.getType()}); // 調用拷貝對象的setXXX()方法 method.invoke(newInstance, new Object[]{invoke}); } } return newInstance ; } public static void main(String[] args) throws Exception { // ReflectTest002 reflect = new ReflectTest002(); // TestUserInfo userInfo = new TestUserInfo(); userInfo.setPkid(GUIDUtils.getGUID()); userInfo.setUserName("maneo.lu"); userInfo.setUserPwd("123456"); userInfo.setRealName("魯春利"); logger.info("source user info is {}", userInfo); // Object objectCopy = reflect.copy(userInfo); logger.info("copy user info is {}", objectCopy); } }
打印該對象所屬的類的名字 後打印出原對象中所有屬性及對應值 最後打印出新建對象複製原對象後的屬性及對應值
(1). 獲得對象的類的類型 Class<?> classType = object.getClass();與Class.forName()是一樣的結果,返回一個對象運行時的Class,這個Class描述了當前這個對象所具有的一些屬性和方法,也就是內部的構造。getClass方法定義在Object類裏面,也就是JAVA中任何一個類都有這個方法
(2). Object objectCopy = classType.getConstructor(new Class[] {}).newInstance(new Object[] {});這句話主要的目的是通過默認的構造方法創建一個該Class類的實例。通過Class實例調用getConstructor方法,可以獲得當前對象的構造方法。參數是用來辨別返回哪個構造方法的,所以是Class類型數組,無參數表示返回默認構造方法。
newInstance方法,通過當前構造方法生成當前類的一個實例。
【實例3】
該類的main()方法中,運用反射機制調用一個ReflectTest003對象的add()和echo()方法。add()方法的兩個參數爲int 類型,獲得表示add()方法的Method對象的代碼如下:
Method method = clazz.getMethod("add", new Class[]{int.class, int.class});
Method類的invoke(Object obj,Object args[])方法接收的參數必須爲對象,如果參數爲基本類型數據,必須轉換爲相應的包裝類型的對象。
Method類的invoke()方法的返回值總是對象,如果實際被調用的方法的返回類型是基本類型數據,那麼invoke()方法會把它轉換爲相應的包裝類型的對象,再將其返回。
體現了JAVA的反射機制功能中的“在運行時獲得任意一個類的方法”、“在運行時調用任意一個對象的方法”
package com.invicme.tools.reflect; import java.lang.reflect.Method; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author lucl * @version 2017-01-19 * * 通過反射獲取類的方法並調用 * */ public class ReflectTest003 { // private static Logger logger = LoggerFactory.getLogger(ReflectTest003.class); public int add (int param1, int param2) { return param1 + param2; } public String echo (String msg) { return "echo : " + msg; } public static void main(String[] args) throws Exception { // 獲得類的類型 Class<? extends ReflectTest003> clazz = ReflectTest003.class; // 生成實例 ReflectTest003 newInstance = clazz.newInstance(); // 調用ReflectTest003對象的add()方法 { Method method = clazz.getMethod("add", new Class[]{int.class, int.class}); Object invoke = method.invoke(newInstance, new Object[]{1, 2}); logger.info("the add method result is {}", invoke); } // 調用ReflectTest003對象的echo()方法 { Method method = clazz.getMethod("echo", new Class[]{String.class}); Object invoke = method.invoke(newInstance, new Object[]{"Hello"}); logger.info("the echo method result is \"{}\"", invoke); } } }
【實例4】
通過反射機制獲取類的註解。
com.invicme.common.persistence.annotation.MyBatisDao
package com.invicme.common.persistence.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.stereotype.Component; /** * @author lucl * @version 2013-8-28 * * 標識MyBatis的DAO,方便{@link org.mybatis.spring.mapper.MapperScannerConfigurer}的掃描。 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Component public @interface MyBatisDao { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any */ String value() default ""; }
com.invicme.tools.reflect.ReflectTest004
package com.invicme.tools.reflect; import java.lang.annotation.Annotation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.invicme.common.persistence.annotation.MyBatisDao; /** * * @author lucl * @version 2017-01-19 * * 獲取類的註解 * */ @MyBatisDao(value="@MyBatisDao") public class ReflectTest004 { // private static Logger logger = LoggerFactory.getLogger(ReflectTest004.class); public static void main(String[] args) { // Class<? extends ReflectTest004> clazz = ReflectTest004.class; MyBatisDao myBatisDao = clazz.getAnnotation(MyBatisDao.class); String value = myBatisDao.value(); logger.info("annotation value is {}", value); Class<? extends Annotation> annotationType = myBatisDao.annotationType(); logger.info("annotation type is {}", annotationType.getName()); Annotation[] annotations = annotationType.getAnnotations(); for (Annotation annotation : annotations) { logger.info("sub annotation is {}", annotation.annotationType()); } // Class<?>[] interfaces = clazz.getInterfaces(); for (Class<?> interfacz : interfaces) { logger.info("interface name is {}", interfacz.getName()); } // if (null != clazz.getComponentType()) { logger.info("component type is {}", clazz.getComponentType().getName()); } } }
【實例4】
通過Array操作數組
package com.invicme.tools.reflect; import java.lang.reflect.Array; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.invicme.common.model.test.TestUserInfo; /** * * @author lucl * @version 2017-01-20 * * 動態數組 * */ public class ReflectTest005 { // private static Logger logger = LoggerFactory.getLogger(ReflectTest005.class); public static void main(String[] args) { // 初始化數組 int[] records = { 1, 2, 3 }; TestUserInfo[] ps = { new TestUserInfo("張三", "123456"), new TestUserInfo("李四", "123456"), new TestUserInfo("王五", "123456") }; // 使用Array類來操作數組對象 records = (int[]) incrementArray(records); ps = (TestUserInfo[]) incrementArray(ps); // 打印擴容之後的數組內容 list(records); list(ps); } /** * * @param array * @return */ public static Object incrementArray(Object array) { // 返回表示數組組件類型的 Class Class<?> componentType = array.getClass().getComponentType(); // component type is int // component type is class com.invicme.common.model.test.TestUserInfo logger.info("component type is {}", componentType); // 返回指定數組對象的長度 int size = Array.getLength(array); // 新數組 Object newArray = Array.newInstance(componentType, size * 2); // 創建一個具有指定的組件類型和長度的新數組。 for (int i = 0; i < size; i++) { Object o = Array.get(array, i);// 返回指定數組對象中索引組件的值 Array.set(newArray, i, o); // 將指定數組對象中索引組件的值設置爲指定的新值。newArray中i位置的值設爲o指定的值 } // System.arraycopy(array, 0, newArray, 0, size); return newArray; } /** * * @param array */ public static void list(Object array) { int size = Array.getLength(array); for (int i = 0; i < size; i++) { logger.info("{} [ {} ]的 值是 {}", array.getClass().getName(), i, Array.get(array, i)); } } }