Java反射機制

魯春利的工作筆記,好記性不如爛筆頭



反射機制

爲了更好的理解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包中:

wKiom1iBZPHzzIkEAAd1TSmrJaU921.jpg


3、Class類是Reflection API 中的核心類   

    Class類是Reflection API 中的核心類。在類加載時,java虛擬機會自動創建相應的Class對象。

    在java.lang.Object 類中定義了getClass()方法,因此對於任意一個Java對象,都可以通過此方法獲得對象的類。
    Class類的實例用於表示運行時的java數據類型,包括類、接口、數組、枚舉、註解、基本數據類型甚至void等。

wKiom1iBZS2CZItVAAQgDgMzd_M397.jpg

獲得你想操作的類的 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());
        }
    }

}

在命令行裏輸入參數,此處參數必須爲類的全限定名

wKiom1iAWzeRIipbAAAyN0lL5RY394.jpg

運行輸出該類所具有的方法,包括private      
wKioL1iAW7GgDmI3AAC-vUfVaGM663.jpg

(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);
    }

}

打印該對象所屬的類的名字 後打印出原對象中所有屬性及對應值  最後打印出新建對象複製原對象後的屬性及對應值

wKiom1iAbNiRIvjPAAFHYe9KcDY052.jpg

(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);
        }
    }

}

wKiom1iAdLHh35XJAAAy62_Uz7E630.jpg


【實例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));
        }
    }
}

wKioL1iBc7vxaHp_AAC5SQMZ42o757.jpg





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章