解析反射

反射離不開Class.forName(),咱們先從Class.forName提及。java

上一篇咱們說要獲得一個類的實例有4個方法:new,反射,克隆,反序列化。bootstrap

反射能夠跟new一個對象有相同的效果。例如設計模式

public class Company {
    private String a;
    private String b;

    @Override
    public String toString() {
        return "Company{" +
                "a='" + a + '\'' +
                ", b='" + b + '\'' +
                '}';
    }

    public Company() {
        this.a = "A";
        this.b = "B";
    }
}
public class CompanyInstance {
    private static Company company = new Company();

    public static void main(String[] args) {
        System.out.println(company);
    }
}

運行結果數組

Company{a='A', b='B'}jvm

又能夠寫成以下ide

public class CompanyInstance {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        System.out.println(Class.forName("com.guanjian.Company").newInstance());
    }
}

運行結果函數

Company{a='A', b='B'}this

雖然效果同樣,但他們的過程並不同。 首先,newInstance( )是一個方法,而new是一個關鍵字;其次,Class下的newInstance()的使用有侷限,由於它生成對象只能調用無參的構造函數,而使用 new關鍵字生成對象沒有這個限制。編碼

newInstance()的時候是使用的上篇說的類裝載機制的,它會走徹底部過程。具體能夠看 淺析類裝載 ,而new一個實例的時候,走的流程不太同樣,它會先在JVM內部先去尋找該類的Class實例,而後依照該Class實例的定義,依葫蘆畫瓢,把該類的實例給生成出來。但若是找不到該類的Class實例,則會走上篇說的裝載流程。 其中JDK的Class實例通常是在jvm啓動時用啓動類加載器完成加載,用戶的Class實例則是在用到的時候再加載。spa

Class.forName()被重載爲有一個參數和三個參數的,咱們來看一下其源碼

@CallerSensitive
public static Class<?> forName(String className)
            throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
                               ClassLoader loader)
    throws ClassNotFoundException
{
    Class<?> caller = null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        // Reflective call to get caller class is only needed if a security manager
        // is present.  Avoid the overhead of making this call otherwise.
        caller = Reflection.getCallerClass();
        if (sun.misc.VM.isSystemDomainLoader(loader)) {
            ClassLoader ccl = ClassLoader.getClassLoader(caller);
            if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                sm.checkPermission(
                    SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
        }
    }
    return forName0(name, initialize, loader, caller);
}

其中Reflection.getCallerClass()源碼以下

@CallerSensitive
public static native Class<?> getCallerClass();

這是一個跟C語言交互的,用戶無權限調用的方法,只能被 bootstrap class loaderextension class loader 調用的,這兩個加載類後面再說。意思是返回調用者的Class實例。

private static native Class<?> forName0(String name, boolean initialize,
                                        ClassLoader loader,
                                        Class<?> caller)
    throws ClassNotFoundException;

它的第二個參數boolean initialize表示是否要初始化該類,單參Class.forName()默認true是要初始化的,三參的Class.forName()由你本身選擇。一旦初始化,就會觸發目標對象的 static塊代碼執行,static參數也也會被再次初始化。固然若是你使用了三個參數的Class.forName(),並調用了newInstance()之後,是確定會初始化的。

public class CompanyInstance {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        System.out.println(Class.forName("com.guanjian.Company",false,Thread.currentThread().getContextClassLoader()).newInstance());
    }
}

運行結果

Company{a='A', b='B'}

如今咱們重點要說的是它的ClassLoader,這個纔是真正裝載類的核心組件。全部的Class都是由ClassLoader進行加載的,ClassLoader負責經過各類方式將Class信息的二進制字節碼數據流讀入系統,而後交給JVM虛擬機進行鏈接、初始化等操做。ClassLoader是一個抽象類,咱們來看一下它的部分源碼。

public abstract class ClassLoader {

    private static native void registerNatives();
    static {
        registerNatives();
    }

    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final ClassLoader parent;

咱們來看一下它部分對外公開的public方法。

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
static ClassLoader getClassLoader(Class<?> caller) {
    // This can be null if the VM is requesting it
    if (caller == null) {
        return null;
    }
    // Circumvent security check since this is package-private
    return caller.getClassLoader0();
}

public loadClass方法的做用爲給定一個類名,加載一個類,返回表明這個類的Class實例,若是找不到類,則返回ClassNotFoundException異常。它調用了protected loadClass方法。源碼以下(加了註釋)

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    //對這個名稱產生一個鎖對象,並進行加鎖處理
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        //findLoadedClass的底層也是C語言交互實現的,應該是在JVM內存中查找該類的Class實例
        Class<?> c = findLoadedClass(name);
        //若是在JVM內存中找不到該類的Class實例
        if (c == null) {
            long t0 = System.nanoTime();
            try {//parent爲該加載器的雙親,具體會在後面介紹,若是雙親對象不爲null,使用雙親加載
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    //若是找不到雙親,啓用最高權限的BootstrapClassLoader加載,BootstrapClassLoader在Java中沒有對象,是用C語言實現的
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            //若是找不到最高權限的加載器
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                //直接拋出異常
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        //若是在JVM內存中找到該類的Class實例,當前加載器本身處理
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

加鎖處理片斷代碼

private final ConcurrentHashMap<String, Object> parallelLockMap;
protected Object getClassLoadingLock(String className) {
    //加載器對象賦給一個鎖對象
    Object lock = this;
    //若是該hashmap不爲空
    if (parallelLockMap != null) {
        //產生一把新鎖
        Object newLock = new Object();
        //若是該hashmap中存在className的key,則返回key的value,若是不存在,則將className和newLock做爲key,value放入hashmap中,返回null
        lock = parallelLockMap.putIfAbsent(className, newLock);
        if (lock == null) {
            lock = newLock;
        }
    }
    return lock;
}

查找最高權限加載器源碼

private Class<?> findBootstrapClassOrNull(String name)
{
    if (!checkName(name)) return null;

    return findBootstrapClass(name);
}

// return null if not found
private native Class<?> findBootstrapClass(String name);

findClass源碼

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

resolveClass源碼

protected final void resolveClass(Class<?> c) {
    resolveClass0(c);
}

private native void resolveClass0(Class<?> c);

ClassLoader的分類

在標準的Java程序中,Java虛擬機會建立3類ClassLoader爲整個應用程序服務。它們分別是:BootStrap ClassLoader(啓動類加載器),Extension ClassLoader(擴展類加載器),App ClassLoader(應用類加載器,也稱爲系統類加載器)。此外,每個應用程序還能夠擁有自定義的ClassLoader,擴展Java虛擬機獲取Class數據的能力。其中,應用類加載器的雙親爲擴展類加載器,擴展類加載器的雙親爲啓動類加載器。當系統須要使用一個類時,在判斷類是否已經被加載時,會先從當前底層類加載器進行判斷。當系統須要加載一個類時,會從頂層類開始加載,依次向下嘗試,直到成功。

這裏根類加載器即爲啓動類加載器。經過代碼驗證

public class PrintClassLoaderTree {
    public static void main(String[] args) {
        ClassLoader cl = PrintClassLoaderTree.class.getClassLoader();
        while (cl != null) {
            System.out.println(cl);
            cl = cl.getParent();
        }
        System.out.println(String.class.getClassLoader());
    }
}

運行結果

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@4554617c
null

由此可知,PrintClassLoaderTree用戶類加載於AppClassLoader中,而AppClassLoader的雙親爲ExtClassLoader.而從ExtClassLoader沒法再取得啓動類加載器,由於這是一個純C實現。所以,任何加載在啓動類加載器中的類時沒法得到其ClassLoader實例的,好比String屬於Java核心類,所以會被啓動類加載器加載,因此最後一條打印爲null.

反射的使用場景通常注意如下幾點
一、編碼階段不知道須要實例化的類名是哪一個,須要在runtime從配置文件中加載:
Class clazz = class.forName("xxx.xxx.xxx")
clazz.newInstance();

二、在runtime階段,須要臨時訪問類的某個私有屬性
ClassA objA = new ClassA();
Field xxx = objA.getClass().getDeclaredField("xxx")
xxx.setAccessible(true);

三、當使用標籤的時候,咱們要獲取標籤
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotionTest {
    String value() default "哈士奇";
}
@AnnotionTest
public class Dog {
    private String type;
    private String name;
    public Dog() {
        type = "金毛";
        name = "大黃";
    }

public class CheckDog {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.guanjian.Dog");
        if (clazz.isAnnotationPresent(AnnotionTest.class)) {
            Field field = clazz.getDeclaredField("type");
            field.setAccessible(true);
            System.out.println(field.get(clazz.newInstance()));
            AnnotionTest test = (AnnotionTest)clazz.getAnnotation(AnnotionTest.class);
            System.out.println(test.value());
        }
    }
}

四、獲取具體的構造器來構造類自己的實例
Class<?>[] constructorParams = {String.class,String.class};
        Constructor<?> cons = clazz.getConstructor(constructorParams);
        System.out.println(cons.newInstance("哈士奇","神哈"));
五、其餘(能夠用到的地方還有不少,好比獲取父類的方法,判斷是否是一個接口等等)

由於後面還有一些雙親委託的東西,這個不是個人重點,就不重點寫了。重點是結合我以前的一篇文章,作一個解析,見 @Compenent,@Autowired,@PostConstruct自實現

程序主入口

public class Test {
    public static void main(String[] args) {
        Manager.scanAndImp("com.guanjian.test");
        Test2 test2 = (Test2)Manager.getBean(Test2.class);
        test2.show();
    }
}

Manager.scanAndImp("com.guanjian.test")代碼以下

public static void scanAndImp(String basePackage) {
    ClassHelper.setClassSet(basePackage);
    BeanHelper.setBeanMap();
    IocHelper.ioc();
}
/**
 * 定義類集合(用於存放所加載的類)
 * @param basePackage
 */
private static  Set<Class<?>> CLASS_SET;

/**
 * 掃描全部的包,獲取類集合放入CLASS_SET
 * @param basePackage
 */
public static void setClassSet(String basePackage) {
    CLASS_SET = ClassUtil.getClassSet(basePackage);
}

很明顯,第一步是掃描包,獲取全部的Class實例,咱們根據前面的介紹知道,要裝載類,就必需要有一個類裝載器,而這個類裝載器就是

/**
 * 獲取類加載器
 * @return
 */
public static ClassLoader getClassLoader() {
    return Thread.currentThread().getContextClassLoader();
}

由於要裝載的類都是咱們本身寫的類,而不是系統類,因此此時在JVM內存中是確定沒有它們的Class實例的,而裝載它們的確定也就是應用類裝載器。

/**
 * 獲取制定包名下的全部類
 * @param packageName
 * @return
 */
public static Set<Class<?>> getClassSet(String packageName) {
    ...

代碼就不詳細分析了,只要知道這裏使用了加載器裝載了這些類,併產生了Class實例,但並未初始化。其中調用了方法

private static void doAddClass(Set<Class<?>> classSet,String className) {
    Class<?> cls = loadClass(className,false);
    classSet.add(cls);
}
/**
 * 加載類
 * @param className
 * @param isInitialized
 * @return
 */
public static Class<?> loadClass(String className,boolean isInitialized) {
    Class<?> cls;
    try {
        cls = Class.forName(className,isInitialized,getClassLoader());
    } catch (ClassNotFoundException e) {
        LOGGER.error("load class failure",e);
        throw new RuntimeException(e);
    }
    return cls;
}

即爲咱們以前說的Class.forName()的三參形式。這些全部的Class實例被放入了一個HashSet集合中,即爲private static Set<Class<?>> CLASS_SET;

這樣第一條語句ClassHelper.setClassSet(basePackage);就分析完了。

--------------------------------------------------------------------------------------------------

而後是第二條語句BeanHelper.setBeanMap();

/**
 * 獲取全部Class實例跟類自己實例的映射關係
 */
public static void setBeanMap() {
    Set<Class<?>> beanClassSet = ClassHelper.getBeanClassSet();
    for (Class<?> beanClass:beanClassSet) {
        Object obj = ReflectionUtil.newInstance(beanClass);
        BEAN_MAP.put(beanClass,obj);
    }
}
/**
 * 定義Bean映射(用於存放Bean類與Bean實例的映射關係)
 */
private static final Map<Class<?>,Object> BEAN_MAP = new HashMap<Class<?>, Object>();
/**
 * 獲取應用包名下全部Bean類
 * @return
 */
public static Set<Class<?>> getBeanClassSet() {
    Set<Class<?>> beanClassSet = new HashSet<Class<?>>();
    beanClassSet.addAll(getComponentClassSet());
    return beanClassSet;
}
/**
 * 獲取應用包名下全部Comonent類
 * @return
 */
public static Set<Class<?>> getComponentClassSet() {
    Set<Class<?>> classSet = new HashSet<Class<?>>();
    for (Class<?> cls:CLASS_SET) {
        //這個Class實例是否帶有@Component標籤
        if (cls.isAnnotationPresent(Component.class)) {
            classSet.add(cls);
        }
    }
    return classSet;
}
/**
 * 建立實例
 * @param cls
 * @return
 */
public static Object newInstance(Class<?> cls) {
    Object instance;
    try {
        instance = cls.newInstance();
    } catch (Exception e) {
        LOGGER.error("new instance failure",e);
        throw new RuntimeException(e);
    }
    return instance;
}

這裏是被@Component標籤識別加載的稱爲bean,咱們以前的確獲取了全部的類,而且加載了,但並無初始化。但有一些可能並無打上@Component標籤的就不能稱爲bean.咱們須要對有@Component標籤的進行初始化。把bean類跟帶有@Component的類分離,是爲了方便擴展,之後有其餘的標籤的能夠方便修改。

BeanHelper.setBeanMap();的意思就是把全部的bean都給初始化了,並創建了一個Class實例跟bean類自己的實例的映射關係的HashMap.其中cls.isAnnotationPresent(Component.class)就是檢測Class實例是否被咱們自定義的標籤@Component標記,這是一個比較重點的地方吧。

--------------------------------------------------------------------------------------------------------

而後是第三條語句IocHelper.ioc();

public static void ioc(){
    //獲取全部的Bean類與Bean實例之間的映射關係
    Map<Class<?>,Object> beanMap = BeanHelper.getBeanMap();
    if (CollectionUtil.isNotEmpty(beanMap)) {
        //遍歷Bean Map
        for (Map.Entry<Class<?>,Object> beanEntry:beanMap.entrySet()) {
            //從BeanMap中獲取Bean類與Bean實例
            Class<?> beanClass = beanEntry.getKey();
            Object beanInstance = beanEntry.getValue();
            //獲取Bean類定義的全部成員變量
            Field[] beanFields = beanClass.getDeclaredFields();
            if (ArrayUtil.isNotEmpty(beanFields)) {
                //遍歷Bean Field
                for (Field beanField:beanFields) {
                    //判斷當前Bean Field是否帶有Autowired註解
                    if (beanField.isAnnotationPresent(Autowired.class)) {
                        //獲取當前Bean Field的Class實例
                        Class<?> beanFieldClass = beanField.getType();
                        //經過該Class實例在HashMap中獲取對應的Bean Field類自己的實例,此時並無設置到Field中
                        //且注意beanFieldInstance是beanInstance某個屬性的實例,他們不是同一個實例
                        Object beanFieldInstance = beanMap.get(beanFieldClass);
                        if (beanFieldInstance != null) {
                            //經過反射初始化BeanField的值,把在HashMap中找到的Bean類實例設置給beanInstance
                            //的beanField
                            ReflectionUtil.setField(beanInstance,beanField,beanFieldInstance);
                        }
                    }
                }
            }
            Method[] beanMethods = beanClass.getMethods();
            if (ArrayUtil.isNotEmpty(beanMethods)) {
                //遍歷method
                for (Method method:beanMethods) {
                    //判斷當前註解是否有PostConstruct註解
                    if (method.isAnnotationPresent(PostConstruct.class)) {
                        try {
                            //執行該方法
                            method.invoke(beanInstance,null);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}
/**
 * 獲取Class實例和Bean類自己實例的映射
 * @return
 */
public static Map<Class<?>,Object> getBeanMap() {
    return BEAN_MAP;
}
/**
 * 設置成員變量值
 * @param obj
 * @param field
 * @param value
 */
public static void setField(Object obj, Field field,Object value) {
    try {
        //若是field在類中爲private,則必須setAccessible(true)才能夠在反射中正常訪問
        field.setAccessible(true);
        //obj爲要注入的Bean類的實例,value爲該Bean類的field字段的要注入的值
        field.set(obj,value);
    } catch (IllegalAccessException e) {
        LOGGER.error("set field failure",e);
        throw new RuntimeException(e);
    }
}

這裏主要是爲了實現IOC依賴注入(@Autowired標籤)以及@PostConstruct標籤方法的自動運行。其中Class<?> beanFieldClass = beanField.getType();用來獲取類的屬性的Class實例,比較重要,再去HashMap中查找該Class實例對應的Bean類自己的實例,這裏Class實例跟類自己的實例必定要分清楚。而後ReflectionUtil.setField(beanInstance,beanField,beanFieldInstance);把找到的實例設置給要設置的Bean類的field屬性,完成初始化。一樣method.invoke(beanInstance,null);也是調用beanInstance自身的方法。

整體思路就是在Class實例中查找各類屬性,方法以及類自定義標籤、屬性自定義標籤,方法自定義標籤,再結合類自己的實例,經過Class實例的Field,method進行屬性賦值,方法運行,這些又都必須在反射(Class實例)中調用類自己的實例。

具體談一下反射在抽象工廠模式下的應用。

抽象工廠模式下有3個概念,抽象工廠,抽象零件,抽象產品。它是一個以抽象對抽象的處理,無需具體的實現類參與。

具體例子能夠參考 設計模式整理

如今咱們來看一下反射中比較難理解的幾個概念,Type,TypeVariable,Object,Interface

咱們仍是以例子來講明,假設有兩個接口,一個實現類

public interface Greeting<T,V> extends Serializable {
    T sayHello(V name);
    T pay(BigDecimal count);
}
public interface ChildGreeting<T,V> extends Greeting<T,V> {
    T want(String a,V b);
}
public class ChildGreetingImpl implements ChildGreeting<Integer,String> {
    @Override
    public Integer want(String a, String b) {
        return Integer.parseInt(a) + Integer.parseInt(b);
    }

    @Override
    public Integer sayHello(String name) {
        return Integer.parseInt(name);
    }

    @Override
    public Integer pay(BigDecimal count) {
        return count.intValue();
    }
}

咱們先用第一個接口來代入這樣一段代碼

public class TestType {
    public static void main(String[] args) {
        Method[] methods = Greeting.class.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "方法所屬類爲" + method.getDeclaringClass());
            //若是方法所屬類爲對象類
            if (method.getDeclaringClass() == Object.class) {
                System.out.println("跳過" + method.getName());
                continue;
            }
            //獲取方法的全部參數類型
            Type[] types = method.getGenericParameterTypes();
            for (Type type : types) {
                System.out.println(method.getName() + "參數類型爲" + type.getTypeName());
                //若是該參數類型爲泛型
                if (type instanceof TypeVariable) {
                    TypeVariable typeVariable = (TypeVariable) type;
                    //獲取該參數類型所屬的類
                    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                    if (genericDeclaration instanceof Class) {
                        Class clazz = (Class) genericDeclaration;
                        System.out.println(method.getName() + "泛型所屬類爲" + clazz.getName());
                        //若是泛型所屬類爲接口
                        if (clazz.isInterface()) {
                            //獲取接口的父接口
                            Class[] interfaces = clazz.getInterfaces();
                            for (int i = 0;i < interfaces.length;i++) {
                                System.out.println(method.getName() + "父接口包含" + interfaces[i].getName());
                                if (interfaces[i].isAssignableFrom(clazz)) {
                                    System.out.println(interfaces[i].getName() + "爲" + clazz.getName() + "的父接口");
                                }
                            }
                            //獲取接口的帶泛型的父接口類型
                            Type[] genericInterfaces = clazz.getGenericInterfaces();
                            for (int i = 0;i < genericInterfaces.length;i++) {
                                System.out.println(method.getName() + "父接口帶泛型名爲" + genericInterfaces[i].getTypeName());
                            }

                        }
                    }
                }
            }
        }
    }
}

運行結果

sayHello方法所屬類爲interface com.guanjian.demo.proxy.Greeting
sayHello參數類型爲V
sayHello泛型所屬類爲com.guanjian.demo.proxy.Greeting
sayHello父接口包含java.io.Serializable
java.io.Serializable爲com.guanjian.demo.proxy.Greeting的父接口
sayHello父接口帶泛型名爲java.io.Serializable
pay方法所屬類爲interface com.guanjian.demo.proxy.Greeting
pay參數類型爲java.math.BigDecimal

這裏都沒有什麼好說的,而後咱們來看代入第二個接口

Method[] methods = ChildGreeting.class.getMethods();

其餘代碼相同,運行結果

want方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
want參數類型爲java.lang.String
want參數類型爲V
want泛型所屬類爲com.guanjian.demo.proxy.ChildGreeting
want父接口包含com.guanjian.demo.proxy.Greeting
com.guanjian.demo.proxy.Greeting爲com.guanjian.demo.proxy.ChildGreeting的父接口
want父接口帶泛型名爲com.guanjian.demo.proxy.Greeting<T, V>
sayHello方法所屬類爲interface com.guanjian.demo.proxy.Greeting
sayHello參數類型爲V
sayHello泛型所屬類爲com.guanjian.demo.proxy.Greeting
sayHello父接口包含java.io.Serializable
java.io.Serializable爲com.guanjian.demo.proxy.Greeting的父接口
sayHello父接口帶泛型名爲java.io.Serializable
pay方法所屬類爲interface com.guanjian.demo.proxy.Greeting
pay參數類型爲java.math.BigDecimal

帶入實現類

Method[] methods = ChildGreetingImpl.class.getMethods();

運行結果

want方法所屬類爲class com.guanjian.demo.proxy.ChildGreetingImpl
want參數類型爲java.lang.String
want參數類型爲java.lang.Object
want方法所屬類爲class com.guanjian.demo.proxy.ChildGreetingImpl
want參數類型爲java.lang.String
want參數類型爲java.lang.String

sayHello方法所屬類爲class com.guanjian.demo.proxy.ChildGreetingImpl
sayHello參數類型爲java.lang.Object
sayHello方法所屬類爲class com.guanjian.demo.proxy.ChildGreetingImpl
sayHello參數類型爲java.lang.String
pay方法所屬類爲class com.guanjian.demo.proxy.ChildGreetingImpl
pay參數類型爲java.math.BigDecimal
pay方法所屬類爲class com.guanjian.demo.proxy.ChildGreetingImpl
pay參數類型爲java.math.BigDecimal
wait方法所屬類爲class java.lang.Object
跳過wait
wait方法所屬類爲class java.lang.Object
跳過wait
wait方法所屬類爲class java.lang.Object
跳過wait
equals方法所屬類爲class java.lang.Object
跳過equals
toString方法所屬類爲class java.lang.Object
跳過toString
hashCode方法所屬類爲class java.lang.Object
跳過hashCode
getClass方法所屬類爲class java.lang.Object
跳過getClass
notify方法所屬類爲class java.lang.Object
跳過notify
notifyAll方法所屬類爲class java.lang.Object
跳過notifyAll

根據結果,但凡實現了帶泛型接口的實現類,每個方法會被一拆而二,一種方法參數類型爲Object,一種方法參數爲你代入的具體類,因此咱們在作某些判斷的時候須要注意。

如今咱們來修改一下ChildGreeting接口

public interface ChildGreeting<T,V> extends Greeting<T,V> {
    T want(String[] a,V b);
    T findlist(List<String> list);
}

TestType修改以下

public class TestType {
    public static void main(String[] args) {
        Method[] methods = ChildGreeting.class.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "方法所屬類爲" + method.getDeclaringClass());
            //若是方法所屬類爲對象類
            if (method.getDeclaringClass() == Object.class) {
                System.out.println("跳過" + method.getName());
                continue;
            }
            //獲取方法的全部參數類型
            Type[] types = method.getGenericParameterTypes();
            for (Type type : types) {
                System.out.println(method.getName() + "參數類型名爲" + type.getTypeName());
                //若是該參數類型爲泛型
                if (type instanceof TypeVariable) {
                    TypeVariable typeVariable = (TypeVariable) type;
                    //獲取該參數類型所屬的類
                    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                    if (genericDeclaration instanceof Class) {
                        Class clazz = (Class) genericDeclaration;
                        System.out.println(method.getName() + "泛型所屬類爲" + clazz.getName());
                        //若是泛型所屬類爲接口
                        if (clazz.isInterface()) {
                            //獲取接口的父接口
                            Class[] interfaces = clazz.getInterfaces();
                            for (int i = 0;i < interfaces.length;i++) {
                                System.out.println(method.getName() + "父接口包含" + interfaces[i].getName());
                                if (interfaces[i].isAssignableFrom(clazz)) {
                                    System.out.println(interfaces[i].getName() + "爲" + clazz.getName() + "的父接口");
                                }
                            }
                            //獲取接口的帶泛型的父接口類型
                            Type[] genericInterfaces = clazz.getGenericInterfaces();
                            for (int i = 0;i < genericInterfaces.length;i++) {
                                System.out.println(method.getName() + "父接口帶泛型名爲" + genericInterfaces[i].getTypeName());
                            }

                        }
                    }
                }
                //若是參數類型爲Class,且爲數組
                if (type instanceof Class && ((Class) type).isArray()) {
                    System.out.println(method.getName() + "數組類型爲" + ((Class) type).getComponentType());
                }
                //若是參數類型爲參數化類型(參數化類型指相似於List<String>的類型)
                if (type instanceof ParameterizedType) {
                    System.out.println(method.getName() + "參數化類型" + ((ParameterizedType) type).getRawType());
                }

            }
        }
    }
}

運行結果

findlist方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
findlist參數類型名爲java.util.List<java.lang.String>
findlist參數化類型interface java.util.List
want方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
want參數類型名爲java.lang.String[]
want數組類型爲class java.lang.String
want參數類型名爲V
want泛型所屬類爲com.guanjian.demo.proxy.ChildGreeting
want父接口包含com.guanjian.demo.proxy.Greeting
com.guanjian.demo.proxy.Greeting爲com.guanjian.demo.proxy.ChildGreeting的父接口
want父接口帶泛型名爲com.guanjian.demo.proxy.Greeting<T, V>
sayHello方法所屬類爲interface com.guanjian.demo.proxy.Greeting
sayHello參數類型名爲V
sayHello泛型所屬類爲com.guanjian.demo.proxy.Greeting
sayHello父接口包含java.io.Serializable
java.io.Serializable爲com.guanjian.demo.proxy.Greeting的父接口
sayHello父接口帶泛型名爲java.io.Serializable
pay方法所屬類爲interface com.guanjian.demo.proxy.Greeting
pay參數類型名爲java.math.BigDecimal

由結果可知,參數化類型ParameterizedType,取出來((ParameterizedType) type).getRawType()是一個Class(包含了interface),而數組類型((Class) type).getComponentType()取出來也是一個Class,即數組的原類型。這裏須要注意的是,參數化類型ParameterizedType以及泛型TypeVariable都不屬於Class,都不能強轉成Class,不然都會報錯。

再修改ChildGreeting接口

public interface ChildGreeting<T,V> extends Greeting<T,V> {
    T want(String[] a,V[] b);
    T findlist(List<V> list);
}

TestType修改以下

public class TestType {
    public static void main(String[] args) {
        Method[] methods = ChildGreeting.class.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "方法所屬類爲" + method.getDeclaringClass());
            //若是方法所屬類爲對象類
            if (method.getDeclaringClass() == Object.class) {
                System.out.println("跳過" + method.getName());
                continue;
            }
            //獲取方法的全部參數類型
            Type[] types = method.getGenericParameterTypes();
            for (Type type : types) {
                System.out.println(method.getName() + "參數類型名爲" + type.getTypeName());
                //若是該參數類型爲泛型
                if (type instanceof TypeVariable) {
                    TypeVariable typeVariable = (TypeVariable) type;
                    //獲取該參數類型所屬的類
                    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                    if (genericDeclaration instanceof Class) {
                        Class clazz = (Class) genericDeclaration;
                        System.out.println(method.getName() + "泛型所屬類爲" + clazz.getName());
                        //若是泛型所屬類爲接口
                        if (clazz.isInterface()) {
                            //獲取接口的父接口
                            Class[] interfaces = clazz.getInterfaces();
                            for (int i = 0;i < interfaces.length;i++) {
                                System.out.println(method.getName() + "父接口包含" + interfaces[i].getName());
                                if (interfaces[i].isAssignableFrom(clazz)) {
                                    System.out.println(interfaces[i].getName() + "爲" + clazz.getName() + "的父接口");
                                }
                            }
                            //獲取接口的帶泛型的父接口類型
                            Type[] genericInterfaces = clazz.getGenericInterfaces();
                            for (int i = 0;i < genericInterfaces.length;i++) {
                                System.out.println(method.getName() + "父接口帶泛型名爲" + genericInterfaces[i].getTypeName());
                            }

                        }
                    }
                }
                //若是參數類型爲Class,且爲數組
                if (type instanceof Class && ((Class) type).isArray()) {
                    System.out.println(method.getName() + "數組類型爲" + ((Class) type).getComponentType());
                }
                //若是參數類型爲參數化類型(參數化類型指相似於List<String>的類型)
                if (type instanceof ParameterizedType) {
                    System.out.println(method.getName() + "參數化類型" + ((ParameterizedType) type).getRawType());
                }
                //若是參數類型爲泛型數組類型
                if (type instanceof GenericArrayType) {
                    System.out.println(method.getName() + "泛型數組類型" + ((GenericArrayType) type).getGenericComponentType());
                }

            }
        }
    }
}

運行結果

findlist方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
findlist參數類型名爲java.util.List<V>
findlist參數化類型interface java.util.List
want方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
want參數類型名爲java.lang.String[]
want數組類型爲class java.lang.String
want參數類型名爲V[]
want泛型數組類型V
pay方法所屬類爲interface com.guanjian.demo.proxy.Greeting
pay參數類型名爲java.math.BigDecimal
sayHello方法所屬類爲interface com.guanjian.demo.proxy.Greeting
sayHello參數類型名爲V
sayHello泛型所屬類爲com.guanjian.demo.proxy.Greeting
sayHello父接口包含java.io.Serializable
java.io.Serializable爲com.guanjian.demo.proxy.Greeting的父接口
sayHello父接口帶泛型名爲java.io.Serializable

由結果可見,泛型數組類型GenericArrayType跟普通數組的判斷方式不一樣,獲取方法爲((GenericArrayType) type).getGenericComponentType(),獲取的也不是一個Class,就是一個泛型。

再修改ChildGreeting接口

public interface ChildGreeting<T,V> extends Greeting<T,V> {
    T want(String[] a,V[] b);
    T findlist(Map.Entry<String,String> entry);
}

TestType修改以下

public class TestType {
    public static void main(String[] args) {
        Method[] methods = ChildGreeting.class.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "方法所屬類爲" + method.getDeclaringClass());
            //若是方法所屬類爲對象類
            if (method.getDeclaringClass() == Object.class) {
                System.out.println("跳過" + method.getName());
                continue;
            }
            //獲取方法的全部參數類型
            Type[] types = method.getGenericParameterTypes();
            for (Type type : types) {
                System.out.println(method.getName() + "參數類型名爲" + type.getTypeName());
                //若是該參數類型爲泛型
                if (type instanceof TypeVariable) {
                    TypeVariable typeVariable = (TypeVariable) type;
                    //獲取該參數類型所屬的類
                    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                    if (genericDeclaration instanceof Class) {
                        Class clazz = (Class) genericDeclaration;
                        System.out.println(method.getName() + "泛型所屬類爲" + clazz.getName());
                        //若是泛型所屬類爲接口
                        if (clazz.isInterface()) {
                            //獲取接口的父接口
                            Class[] interfaces = clazz.getInterfaces();
                            for (int i = 0;i < interfaces.length;i++) {
                                System.out.println(method.getName() + "父接口包含" + interfaces[i].getName());
                                if (interfaces[i].isAssignableFrom(clazz)) {
                                    System.out.println(interfaces[i].getName() + "爲" + clazz.getName() + "的父接口");
                                }
                            }
                            //獲取接口的帶泛型的父接口類型
                            Type[] genericInterfaces = clazz.getGenericInterfaces();
                            for (int i = 0;i < genericInterfaces.length;i++) {
                                System.out.println(method.getName() + "父接口帶泛型名爲" + genericInterfaces[i].getTypeName());
                            }

                        }
                    }
                }
                //若是參數類型爲Class,且爲數組
                if (type instanceof Class && ((Class) type).isArray()) {
                    System.out.println(method.getName() + "數組類型爲" + ((Class) type).getComponentType());
                }
                //若是參數類型爲參數化類型(參數化類型指相似於List<String>的類型)
                if (type instanceof ParameterizedType) {
                    System.out.println(method.getName() + "參數化類型" + ((ParameterizedType) type).getRawType());
                    System.out.println(method.getName() + "參數化全部者類型" + ((ParameterizedType) type).getOwnerType());
                    System.out.println(method.getName() + "參數化各參數類型" + Arrays.toString(((ParameterizedType) type).getActualTypeArguments()));
                }
                //若是參數類型爲泛型數組類型
                if (type instanceof GenericArrayType) {
                    System.out.println(method.getName() + "泛型數組類型" + ((GenericArrayType) type).getGenericComponentType());
                }

            }
        }
    }
}

運行結果

findlist方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
findlist參數類型名爲java.util.Map$Entry<java.lang.String, java.lang.String>
findlist參數化類型interface java.util.Map$Entry
findlist參數化全部者類型interface java.util.Map
findlist參數化各參數類型[class java.lang.String, class java.lang.String]

want方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
want參數類型名爲java.lang.String[]
want數組類型爲class java.lang.String
want參數類型名爲V[]
want泛型數組類型V
sayHello方法所屬類爲interface com.guanjian.demo.proxy.Greeting
sayHello參數類型名爲V
sayHello泛型所屬類爲com.guanjian.demo.proxy.Greeting
sayHello父接口包含java.io.Serializable
java.io.Serializable爲com.guanjian.demo.proxy.Greeting的父接口
sayHello父接口帶泛型名爲java.io.Serializable
pay方法所屬類爲interface com.guanjian.demo.proxy.Greeting
pay參數類型名爲java.math.BigDecimal

根據結果可知,若是參數類型爲一個類的內部類,這裏的類都包含接口,則((ParameterizedType) type).getRawType())會打印內部類,而((ParameterizedType) type).getOwnerType())會打印外部類。((ParameterizedType) type).getActualTypeArguments()會打印全部參數化類型的各個參數類型。

再修改ChildGreeting接口

public interface ChildGreeting<T,V> extends Greeting<T,V> {
    T want(String[] a,V[] b);
    T findlist(Map.Entry<String,String> entry);
    String wide(List<? extends V> list);
}

TestType修改以下

public class TestType {
    public static void main(String[] args) {
        Method[] methods = ChildGreeting.class.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "方法所屬類爲" + method.getDeclaringClass());
            //若是方法所屬類爲對象類
            if (method.getDeclaringClass() == Object.class) {
                System.out.println("跳過" + method.getName());
                continue;
            }
            //獲取方法的全部參數類型
            Type[] types = method.getGenericParameterTypes();
            for (Type type : types) {
                System.out.println(method.getName() + "參數類型名爲" + type.getTypeName());
                //若是該參數類型爲泛型
                if (type instanceof TypeVariable) {
                    TypeVariable typeVariable = (TypeVariable) type;
                    //獲取該參數類型所屬的類
                    GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
                    if (genericDeclaration instanceof Class) {
                        Class clazz = (Class) genericDeclaration;
                        System.out.println(method.getName() + "泛型所屬類爲" + clazz.getName());
                        //若是泛型所屬類爲接口
                        if (clazz.isInterface()) {
                            //獲取接口的父接口
                            Class[] interfaces = clazz.getInterfaces();
                            for (int i = 0;i < interfaces.length;i++) {
                                System.out.println(method.getName() + "父接口包含" + interfaces[i].getName());
                                if (interfaces[i].isAssignableFrom(clazz)) {
                                    System.out.println(interfaces[i].getName() + "爲" + clazz.getName() + "的父接口");
                                }
                            }
                            //獲取接口的帶泛型的父接口類型
                            Type[] genericInterfaces = clazz.getGenericInterfaces();
                            for (int i = 0;i < genericInterfaces.length;i++) {
                                System.out.println(method.getName() + "父接口帶泛型名爲" + genericInterfaces[i].getTypeName());
                            }

                        }
                    }
                }
                //若是參數類型爲Class,且爲數組
                if (type instanceof Class && ((Class) type).isArray()) {
                    System.out.println(method.getName() + "數組類型爲" + ((Class) type).getComponentType());
                }
                //若是參數類型爲參數化類型(參數化類型指相似於List<String>的類型)
                if (type instanceof ParameterizedType) {
                    System.out.println(method.getName() + "參數化類型" + ((ParameterizedType) type).getRawType());
                    System.out.println(method.getName() + "參數化全部者類型" + ((ParameterizedType) type).getOwnerType());
                    System.out.println(method.getName() + "參數化各參數類型" + Arrays.toString(((ParameterizedType) type).getActualTypeArguments()));
                    Type[] argTypes = ((ParameterizedType) type).getActualTypeArguments();
                    for (Type typeach : argTypes) {
                        //若是該類型爲通配符類型(相似於<? extends String>)
                        if (typeach instanceof WildcardType) {
                            if (((WildcardType) typeach).getLowerBounds().length == 1) {
                                System.out.println(method.getName() + "通配符下界類型" + ((WildcardType) typeach).getLowerBounds()[0]);
                            }
                            if (((WildcardType) typeach).getUpperBounds().length == 1) {
                                System.out.println(method.getName() + "通配符上界類型" + ((WildcardType) typeach).getUpperBounds()[0]);
                            }
                        }
                    }
                    System.out.println("克隆前" + argTypes);
                    argTypes = argTypes.clone();
                    System.out.println("克隆後" + argTypes);
                }
                //若是參數類型爲泛型數組類型
                if (type instanceof GenericArrayType) {
                    System.out.println(method.getName() + "泛型數組類型" + ((GenericArrayType) type).getGenericComponentType());
                }
            }
        }
    }
}

運行結果

want方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
want參數類型名爲java.lang.String[]
want數組類型爲class java.lang.String
want參數類型名爲V[]
want泛型數組類型V
findlist方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
findlist參數類型名爲java.util.Map$Entry<java.lang.String, java.lang.String>
findlist參數化類型interface java.util.Map$Entry
findlist參數化全部者類型interface java.util.Map
findlist參數化各參數類型[class java.lang.String, class java.lang.String]
克隆前[Ljava.lang.reflect.Type;@63961c42
克隆後[Ljava.lang.reflect.Type;@65b54208
wide方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
wide參數類型名爲java.util.List<? extends V>
wide參數化類型interface java.util.List
wide參數化全部者類型null
wide參數化各參數類型[? extends V]
wide通配符上界類型V
克隆前[Ljava.lang.reflect.Type;@1be6f5c3
克隆後[Ljava.lang.reflect.Type;@6b884d57

sayHello方法所屬類爲interface com.guanjian.demo.proxy.Greeting
sayHello參數類型名爲V
sayHello泛型所屬類爲com.guanjian.demo.proxy.Greeting
sayHello父接口包含java.io.Serializable
java.io.Serializable爲com.guanjian.demo.proxy.Greeting的父接口
sayHello父接口帶泛型名爲java.io.Serializable
pay方法所屬類爲interface com.guanjian.demo.proxy.Greeting
pay參數類型名爲java.math.BigDecimal

從結果可知,((WildcardType) typeach).getUpperBounds()[0]能夠取到通配符的上界類型,該類型也可能爲泛型。另外Type[]數組能夠克隆爲一個新內存地址的數組。

再修改ChildGreeting接口

public interface ChildGreeting<T,V> extends Greeting<T,V> {
    T want(String[] a,V[] b);
    T findlist(Map.Entry<String,String> entry);
    String wide(List<? super V> list);
}

TestType不變

運行結果

want方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
want參數類型名爲java.lang.String[]
want數組類型爲class java.lang.String
want參數類型名爲V[]
want泛型數組類型V
findlist方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
findlist參數類型名爲java.util.Map$Entry<java.lang.String, java.lang.String>
findlist參數化類型interface java.util.Map$Entry
findlist參數化全部者類型interface java.util.Map
findlist參數化各參數類型[class java.lang.String, class java.lang.String]
克隆前[Ljava.lang.reflect.Type;@63961c42
克隆後[Ljava.lang.reflect.Type;@65b54208
wide方法所屬類爲interface com.guanjian.demo.proxy.ChildGreeting
wide參數類型名爲java.util.List<? super V>
wide參數化類型interface java.util.List
wide參數化全部者類型null
wide參數化各參數類型[? super V]
wide通配符下界類型V
wide通配符上界類型class java.lang.Object

克隆前[Ljava.lang.reflect.Type;@1be6f5c3
克隆後[Ljava.lang.reflect.Type;@6b884d57
pay方法所屬類爲interface com.guanjian.demo.proxy.Greeting
pay參數類型名爲java.math.BigDecimal
sayHello方法所屬類爲interface com.guanjian.demo.proxy.Greeting
sayHello參數類型名爲V
sayHello泛型所屬類爲com.guanjian.demo.proxy.Greeting
sayHello父接口包含java.io.Serializable
java.io.Serializable爲com.guanjian.demo.proxy.Greeting的父接口
sayHello父接口帶泛型名爲java.io.Serializable

由結果可知,<? super V>與<? extends V>不一樣,<? super V>的下界類型爲super後的類型,有上界類型Object。而<? extends V>有上界類型爲extands後面的類型,無下界類型。則上下界的劃分是以繼承方向來肯定的,子類爲下屆,父類爲上界。

相關文章
相關標籤/搜索