java反射機制(精講)

一、概述

Java反射機制定義

Java反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類中的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制。枚舉除外

** 反射機制的功能**

  • 在運行時判斷任意一個對象所屬的類。
  • 在運行時構(gòu)造任意一個類的對象。

  • 在運行時判斷任意一個類所具有的成員變量和方法。

  • 在運行時調(diào)用任意一個對象的方法。

  • 生成動態(tài)代理。

    import java.util.Date;
    public class ReflactDemo_01 {
    public static void main(String[] args) throws Exception {
    /**
    需求1:通過obj對象,調(diào)用java.util.Date類中的toLocaleString方法.
    Object obj =new java.util.Date();
    java.util.Date d = (java.util.Date) obj;
    d.toLocaleString(); (過時了 )
    // 創(chuàng)建Class對象 如何來表示一個字節(jié)碼對象

    // 需求2: 獲取java.util.Date類的字節(jié)碼對象
    // 方式1.使用class屬性
    Class<java.util.Date> clz1 = java.util.Date.class;

    // 方式2,通過對象的getClass方法來獲取,getClass是Object類中的方法
    java.util.Date date = new java.util.Date();
    Class<?> clz2 = date.getClass();

    // 方式3,通過Class類中的靜態(tài)方法forName(String className);
    Class<?> clz3 = Class.forName("java.util.Date");
    /
    /
    *

    • 所有的數(shù)據(jù)類型都有class屬性. Class clz = 數(shù)據(jù)類型.class; 九大內(nèi)置Class實例:
    • JVM中預(yù)先提供好的Class實例,
      分別:byte,short,int,long,float,double,boolea,char,void.
    • 表示:byte.class,short.class,int.class,....void.class.
      /
      /
      *
    • 在8大基本數(shù)據(jù)類型的包裝類中,都有一個常量:TYPE,用于返回該包裝類對應(yīng)基本類的字節(jié)碼對象.
      System.out.println(Integer.TYPE == int.class);//true
    • 注意:Integer和int是不同的數(shù)據(jù)類型
      System.out.println(Integer.class == int.class);//false
      /
      // System.out.println(Integer.TYPE == int.class);
      // System.out.println(Integer.class == int.class);
      /
      *
    • 如何來表示數(shù)組的Class實例. 方式1: 數(shù)組類型.class; 方式2: 數(shù)組對象.getClass();
    • 注意:所有的具有相同的維數(shù)和相同元素類型的數(shù)組共享同一份字節(jié)碼對象,和元素沒有關(guān)系.
      /
      /
      *
    • Class<?> clz4 = int[].class;
      System.out.println(clz4); String[] arr ={ "1", "3", "e" };
      Class<?> clz5 = arr.getClass();
    • System.out.println(clz5);
      */
      }
      }

    **通過反射獲取構(gòu)造器 **

    import java.lang.reflect.Constructor;
    

    /**

    • 構(gòu)造器最大的作用:創(chuàng)建對象. 為什么使用反射創(chuàng)建對象,為什么不直接來new呢? 在框架中,提供給我們的都是字符串.

    • 使用反射創(chuàng)建對象: 步驟:
    • 1);找到構(gòu)造器所在類的字節(jié)碼對象. 2):獲取構(gòu)造器對象. 3):使用 反射,創(chuàng)建對象

    • Constructor<T>類:表示類中構(gòu)造器的類型,Constructor的實例就是某一個類中的某一個構(gòu)造器 常用方法:
    • public T newInstance(Object... initargs):如調(diào)用帶參數(shù)的構(gòu)造器,只能使用該方式. 參數(shù):initargs:表示調(diào)用構(gòu)造器的實際參數(shù)
    • 返回:返回創(chuàng)建的實例,T表示Class所表示類的類型
    • 如果:一個類中的構(gòu)造器是外界可以直接訪問,同時沒有參數(shù).,那么可以直接使用Class類中的newInstance方法創(chuàng)建對象.
    • public Object newInstance():相當(dāng)于new 類名(); 調(diào)用私有的構(gòu)造器:
      */
      class User {
      String name;
      int age;
      // create construct
      public User() {
      }
      public User(String name) {
      this.name = name;
      }
      private User(String name, int age) {
      this.name = name;
      this.age = age;
      }
      // 需求:通過反射來獲取某一個類的構(gòu)造器:
      // 1):獲取該類的字節(jié)碼對象.
      // 2):從該字節(jié)碼對象中去找需要獲取的構(gòu)造器.

    // 獲取指定的構(gòu)造器
    public static void getOne() throws Exception {
    // 獲取構(gòu)造器所在類的字節(jié)碼對象
    Class<User> clz = User.class;
    // 獲取clz對象中的所有構(gòu)造器
    // 需求1.獲取public User()
    Constructor<User> con = clz.getConstructor();
    System.out.println(con);

      // 需求2.獲取public User(String name)
      con = clz.getDeclaredConstructor(String.class);
      System.out.println(con);
      
      // 3.獲取private User(String name, int age)
      con = clz.getDeclaredConstructor(String.class, int.class);
      System.out.println(con);
    

    }

    // 獲取所有的構(gòu)造器
    public static void getAll() {
    // 獲取構(gòu)造器所在類的字節(jié)碼
    Class<User> clz1 = User.class;
    // 獲取clz1對象中所有的構(gòu)造器
    Constructor<?>[] con = clz1.getConstructors();
    System.out.println(con.length);// 2

      Constructor<?>[] cons = clz1.getDeclaredConstructors();
      System.out.println(cons.length);// 3
      for (Constructor<?> c : cons) {
      System.out.println(c);
      }
    }
    

    }

    public class ReflactDemo_02 {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException {
    // User.getOne();
    User.getAll();
    }
    }

通過反射來獲取某一個類的構(gòu)造器

需求:通過反射來獲取某一個類的構(gòu)造器:
1):獲取該類的字節(jié)碼對象.
2)從該字節(jié)碼對象中去找需要獲取的構(gòu)造器.
Class類獲取構(gòu)造器方法:
Constructor類:表示類中構(gòu)造器的類型,Constructor的實例就是某一個類中的某一個構(gòu)造器
public Constructor<?>[] getConstructors():該方法只能獲取當(dāng)前Class所表示類的public修飾的構(gòu)造器
public Constructor<?>[] getDeclaredConstructors():獲取當(dāng)前Class所表示類的所有的構(gòu)造器,和訪問權(quán)限無關(guān)
public Constructor<T> getConstructor(Class<?>... parameterTypes)

注意獲取當(dāng)前Class所表示類中指定的一個public的構(gòu)造器
參數(shù):parameterTypes表示:構(gòu)造器參數(shù)的Class類型
如:
public User(String name)
Constructor c = clz.getConstructor(String.class);
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
獲取當(dāng)前Class所表示類中指定的一個的構(gòu)造
訪問私有的成員: 必須先設(shè)置可訪問的 對象setAccessible(true);

import java.lang.reflect.Constructor;
class User1 {
String name;
int age;
// create constructor
public User1() {
    System.out.println("無參構(gòu)造器");
}
public User1(String name) {
    this.name = name;
    System.out.println("構(gòu)造器" + name);
}

private User1(String name, int age) {
    this.name = name;
    this.age = age;
    System.out.println("構(gòu)造器" + name + age);
  }
}

public class ReflactDemo_03 {
public static void main(String[] args) throws Exception {
// 傳統(tǒng)的方式創(chuàng)建對象
// new User1();
// new User1("CoderZS");
// 獲取類的字節(jié)碼
Class<User1> clz = User1.class;
// 獲取public User1() 注意有public 修飾的 一定與方法相匹配
Constructor<User1> con = clz.getConstructor();
// 用構(gòu)造器的newInstance方法來創(chuàng)建對象 會調(diào)用構(gòu)造器的方法
con.newInstance();
System.out.println(con);

// 調(diào)用public User1(String name)
con = clz.getConstructor(String.class);
con.newInstance("CoderZS");

// 調(diào)用 private User1(String name,int age)!!!!!!!!!!!!!!
con = clz.getDeclaredConstructor(String.class, int.class);
// 設(shè)置當(dāng)前構(gòu)造器可以訪問
// 訪問私有的成員:必須先設(shè)置可訪問的對象
// setAccessible(true);
con.setAccessible(true);
con.newInstance("CoderZS", 18);
  }
}
使用反射獲取方法
 import java.lang.reflect.Method;
/**
 * 需求:使用反射獲取類中的方法: 1):獲取方法所在類的字節(jié)碼對象.  2):獲取方法.

 * Class類中常用方法: public Method[] getMethods():獲取包括自身和繼承過來的所有的public方法

*  public Method[] getDeclaredMethods():獲取自身類中所有的方法(不包括繼承的,和訪問權(quán)限無關(guān)) 

 * public Method getMethod(String methodName, Class<?>...parameterTypes)
 * :表示調(diào)用指定的一個公共的方法(包括繼承的) 參數(shù): methodName: 表示被調(diào)用方法的名字
 * parameterTypes:表示被調(diào)用方法的參數(shù)的Class類型如String.class

* public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 * :表示調(diào)用指定的一個本類中的方法(不包括繼承的) 參數(shù): methodName: 表示被調(diào)用方法的名字
 * parameterTypes:表示被調(diào)用方法的參數(shù)的Class類型如String.class
 */
class User2 {
public void doWork() {
}
public static void doWork(String name) {
}

private String doNotWork(String name, int age) {

    return name + "有參有返回值" + age;
}

// 獲取User2類中指定的一個方法
static void getOne1() throws Exception {
    // 獲取類的字節(jié)碼
    Class<User2> clz = User2.class;
    
// 需求1:獲取doWork()
    Method m = clz.getMethod("doWork");
    System.out.println(m);
    
// 需求:獲取private String doNotWork(String name,int age)
    m = clz.getDeclaredMethod("doNotWork", String.class, int.class);
    System.out.println(m);
}

static void getAll() {
    // 獲取類的字節(jié)碼
    Class<User2> clz = User2.class;
    // 需求1:獲取doWork()
    Method[] ms = clz.getMethods();
    
    System.out.println(ms);
    System.out.println(ms.length);
    for (Method m : ms) {
        System.out.println(m);
    }
  }
}

// 使用反射獲取類中方法
public class ReflactDemo_04 {
public static void main(String[] args) throws Exception {
    // User2.getOne1();
    User2.getAll();
  }
}
使用反射調(diào)用私有靜態(tài)方法(public-private--static)
import java.lang.reflect.Method;
/**
 * 使用反射調(diào)用方法: 1):獲取方法所在類的字節(jié)碼對象. 2):獲取方法對象. 3):使用反射調(diào)用方法. 
 * 如何使用反射調(diào)用一個方法:
 * 在Method類中有方法: public Object invoke(Object obj,Object...args):表示調(diào)用當(dāng)前Method所表示的方法 
 * 參數(shù): obj: 表示被調(diào)用方法底層所屬對象
 *  Method m =clz.getMethod("doWork",String.class);
 * args:表示調(diào)用方法是傳遞的實際參數(shù) 返回: 底層方法的返回結(jié)果
 * 調(diào)用私有方法: 在調(diào)用私有方法之前:應(yīng)該設(shè)置該方法為可訪問的 又因為Method是AccessibleObject子類,所以Method中具有該方法.
 * doWorkMethod.setAccessible(true);
*/
class User3 {
public void doWork() {
    System.out.println("使用反射調(diào)用--無參--方法--doWork");
}

public void doWork(String name) {

    System.out.println("使用反射調(diào)用--有參--方法---doWork");
}

private String doWork(String name, int age) {

    return name + "使用反射調(diào)用--私有--方法" + age;
}

public static String doWork(String name, String sex, int age) {

    return name + "使用反射調(diào)用--靜態(tài)--方法" + sex + age;
}
}

public class ReflactDemo_05 {
public static void main(String[] args) throws Exception {
    // TODO Auto-generated method stub
    // 使用反射調(diào)用方法
    // 需求1:調(diào)用public void doWork()
    // 獲取類的字節(jié)碼
    Class<User3> clz = User3.class;
    // 獲取指定方法
    Method m = clz.getMethod("doWork");
    // 創(chuàng)建對象
    Object result = m.invoke(clz.newInstance());
    System.out.println(result);// null

    // 需求2:public static void doWork(String name)
    m = clz.getMethod("doWork", String.class);
    result = m.invoke(clz.newInstance(), "CoderZS");
    
    // 需求3:調(diào)用private String doNotWork(String name, int age)
    m = clz.getDeclaredMethod("doWork", String.class, int.class);
    m.setAccessible(true);
    result = m.invoke(clz.newInstance(), "CoderZS", 18);
    System.out.println(result);

    // 需求4,使用反射調(diào)用靜態(tài)方法: 靜態(tài)方法不屬于任何對象,靜態(tài)方法屬于類本身.此時把invoke方法的第一個參數(shù)設(shè)置為null即可.
    m = clz.getDeclaredMethod("doWork", String.class, String.class, int.class);
    m.setAccessible(true);
    result = m.invoke(null, "CoderZS", "boy", 18);
    System.out.println(result);
  }
}
概括
  • 1.獲取class對象的成員變量

    //獲取class對象的所有屬性
    Field[] allFields = class1.getDeclaredFields();
    //獲取class對象的public屬性
    Field[] publicFields = class1.getFields();
    //獲取class指定屬性
    Field ageField = class1.getDeclaredField("age");
    //獲取class指定的public屬性
    Field desField = class1.getField("des");
    
  • 2.獲取class對象的方法
    //獲取class對象的所有聲明方法
    Method[] methods = class1.getDeclaredMethods();
    //獲取class對象的所有public方法 包括父類的方法
    Method[] allMethods = class1.getMethods();
    //返回次Class對象對應(yīng)類的、帶指定形參列表的public方法
    Method method = class1.getMethod("info", String.class);
    //返回次Class對象對應(yīng)類的、帶指定形參列表的方法
    Method declaredMethod = class1.getDeclaredMethod("info", String.class);

  • 3.獲取class對象的構(gòu)造函數(shù)

     //獲取class對象的所有聲明構(gòu)造函數(shù)
    Constructor<?>[] allConstructors = class1.getDeclaredConstructors();
     //獲取class對象public構(gòu)造函數(shù)
    Constructor<?>[] publicConstructors = class1.getConstructors();
      //獲取指定聲明構(gòu)造函數(shù)
    Constructor<?> constructor = class1.getDeclaredConstructor(String.class);
      //獲取指定聲明的public構(gòu)造函數(shù)
     Constructor publicConstructor = class1.getConstructor(String.class);
    
  • 4.其他方法
    //獲取class對象的所有注解
    Annotation[] annotations = (Annotation[]) class1.getAnnotations();
    //獲取class對象指定注解
    Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);
    //獲取class對象的直接超類的 Type
    Type genericSuperclass = class1.getGenericSuperclass();
    //獲取class對象的所有接口的type集合
    Type[] interfaceTypes = class1.getGenericInterfaces();

獲取class對象的其他信息

boolean isPrimitive = class1.isPrimitive();//判斷是否是基礎(chǔ)類型
boolean isArray = class1.isArray();//判斷是否是集合類
boolean isAnnotation = class1.isAnnotation();//判斷是否是注解類
boolean isInterface = class1.isInterface();//判斷是否是接口類
boolean isEnum = class1.isEnum();//判斷是否是枚舉類
boolean isAnonymousClass = class1.isAnonymousClass();//判斷是否是匿名內(nèi)部類
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判斷是否被某個注解類修飾
String className = class1.getName();//獲取class名字 包含包名路徑
Package aPackage = class1.getPackage();//獲取class的包信息
String simpleName = class1.getSimpleName();//獲取class類名
int modifiers = class1.getModifiers();//獲取class訪問權(quán)限
Class<?>[] declaredClasses = class1.getDeclaredClasses();//內(nèi)部類
Class<?> declaringClass = class1.getDeclaringClass();//外部類

動態(tài)代理是指在運行時動態(tài)生成代理類。即,代理類的字節(jié)碼將在運行時生成并載入當(dāng)前代理的 ClassLoader。與靜態(tài)處理類相比,動態(tài)類有諸多好處。

  • 不需要為(RealSubject )寫一個形式上完全一樣的封裝類,假如主題接口(Subject)中的方法很多,為每一個接口寫一個代理方法也很麻煩。如果接口有變動,則目標(biāo)對象和代理類都要修改,不利于系統(tǒng)維護;
  • 使用一些動態(tài)代理的生成方法甚至可以在運行時制定代理類的執(zhí)行邏輯,從而大大提升系統(tǒng)的靈活性。
動態(tài)代理涉及的主要類

主要涉及兩個類,這兩個類都是java.lang.reflect包下的類,內(nèi)部主要通過反射來實現(xiàn)的。
java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類。
Proxy提供了用戶創(chuàng)建動態(tài)代理類和代理對象的靜態(tài)方法,它是所有動態(tài)代理類的父類。
java.lang.reflect.InvocationHandler: 這里稱他為"調(diào)用處理器",它是一個接口。當(dāng)調(diào)用動態(tài)代理類中的方法時,將會直接轉(zhuǎn)接到執(zhí)行自定義的InvocationHandler中的invoke()方法。即我們動態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個類,而這個類必須實現(xiàn) InvocationHandler 接口,通過重寫invoke()方法來執(zhí)行具體內(nèi)容。

Proxy提供了如下兩個方法來創(chuàng)建動態(tài)代理類和動態(tài)代理實例。

static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) 返回代理類的java.lang.Class對象。第一個參數(shù)是類加載器對象(即哪個類加載器來加載這個代理類到 JVM 的方法區(qū)),第二個參數(shù)是接口(表明你這個代理類需要實現(xiàn)哪些接口),第三個參數(shù)是調(diào)用處理器類實例(指定代理類中具體要干什么),該代理類將實現(xiàn)interfaces所指定的所有接口,執(zhí)行代理對象的每個方法時都會被替換執(zhí)行InvocationHandler對象的invoke方法。

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回代理類實例。參數(shù)與上述方法一致。

對應(yīng)上述兩種方法創(chuàng)建動態(tài)代理對象的方式:

    //創(chuàng)建一個InvocationHandler對象
    InvocationHandler handler = new MyInvocationHandler(.args..);
    //使用Proxy生成一個動態(tài)代理類
    Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
    //獲取proxyClass類中一個帶InvocationHandler參數(shù)的構(gòu)造器
    Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
    //調(diào)用constructor的newInstance方法來創(chuàng)建動態(tài)實例
    RealSubject real = (RealSubject)constructor.newInstance(handler);

    //創(chuàng)建一個InvocationHandler對象
    InvocationHandler handler = new MyInvocationHandler(.args..);
    //使用Proxy直接生成一個動態(tài)代理對象
    RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);

newProxyInstance這個方法實際上做了兩件事:第一,創(chuàng)建了一個新的類【代理類】,這個類實現(xiàn)了Class[] interfaces中的所有接口,并通過你指定的ClassLoader將生成的類的字節(jié)碼加載到JVM中,創(chuàng)建Class對象;第二,以你傳入的InvocationHandler作為參數(shù)創(chuàng)建一個代理類的實例并返回。
Proxy 類還有一些靜態(tài)方法,比如:
InvocationHandler getInvocationHandler(Object proxy):獲得代理對象對應(yīng)的調(diào)用處理器對象。
Class getProxyClass(ClassLoader loader, Class[] interfaces):根據(jù)類加載器和實現(xiàn)的接口獲得代理類。
InvocationHandler 接口中有方法:
invoke(Object proxy, Method method, Object[] args)
這個函數(shù)是在代理對象調(diào)用任何一個方法時都會調(diào)用的,方法不同會導(dǎo)致第二個參數(shù)method不同,第一個參數(shù)是代理對象(表示哪個代理對象調(diào)用了method方法),第二個參數(shù)是 Method 對象(表示哪個方法被調(diào)用了),第三個參數(shù)是指定調(diào)用方法的參數(shù)。

動態(tài)代理模式的簡單實現(xiàn)

public class DynamicProxyDemo {
public static void main(String[] args) {
    //1.創(chuàng)建目標(biāo)對象
    RealSubject realSubject = new RealSubject();    
    //2.創(chuàng)建調(diào)用處理器對象
    ProxyHandler handler = new ProxyHandler(realSubject);    
   //3.動態(tài)生成代理對象
    Subject proxySubject = (Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(),                                                    
RealSubject.class.getInterfaces(), handler);   
    //4.通過代理對象調(diào)用方法   
    proxySubject.request();    
   }
}

/**
 * 主題接口
 */
interface Subject{
void request();
}

/**
 * 目標(biāo)對象類
 */
class RealSubject implements Subject{
public void request(){
    System.out.println("====RealSubject Request====");
}
}
/**
 * 代理類的調(diào)用處理器
 */
class ProxyHandler implements InvocationHandler{
private Subject subject;
public ProxyHandler(Subject subject){
    this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
    //定義預(yù)處理的工作,當(dāng)然你也可以根據(jù) method 的不同進行不同的預(yù)處理工作
    System.out.println("====before====");
   //調(diào)用RealSubject中的方法
    Object result = method.invoke(subject, args);
    System.out.println("====after====");
    return result;
}
}

可以看到,我們通過newProxyInstance就產(chǎn)生了一個Subject 的實例,即代理類的實例,然后就可以通過Subject .request(),就會調(diào)用InvocationHandler中的invoke()方法,傳入方法Method對象,以及調(diào)用方法的參數(shù),通過Method.invoke調(diào)用RealSubject中的方法的request()方法。同時可以在InvocationHandler中的invoke()方法加入其他執(zhí)行邏輯。

泛型和Class類

從JDK 1.5 后,Java中引入泛型機制,Class類也增加了泛型功能,從而允許使用泛型來限制Class類,例如:String.class的類型實際上是Class<String>。如果Class對應(yīng)的類暫時未知,則使用Class<?>(?是通配符)。通過反射中使用泛型,可以避免使用反射生成的對象需要強制類型轉(zhuǎn)換。
泛型的好處眾多,最主要的一點就是避免類型轉(zhuǎn)換,防止出現(xiàn)ClassCastException,即類型轉(zhuǎn)換異常。以下面程序為例:

public class ObjectFactory {
public static Object getInstance(String name){
    try {
        //創(chuàng)建指定類對應(yīng)的Class對象
        Class cls = Class.forName(name);
        //返回使用該Class對象創(chuàng)建的實例
        return cls.newInstance();
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
        return null;
    }
  }
}

上面程序是個工廠類,通過指定的字符串創(chuàng)建Class對象并創(chuàng)建一個類的實例對象返回。但是這個對象的類型是Object對象,取出實例后需要強制類型轉(zhuǎn)換。
如下例:

Date date = (Date) ObjectFactory.getInstance("java.util.Date");

String string = (String) ObjectFactory.getInstance("java.util.Date");

上面代碼在編譯時不會有任何問題,但是運行時將拋出ClassCastException異常,因為程序試圖將一個Date對象轉(zhuǎn)換成String對象。
但是泛型的出現(xiàn)后,就可以避免這種情況。

public class ObjectFactory {
public static <T> T getInstance(Class<T> cls) {
    try {
        // 返回使用該Class對象創(chuàng)建的實例
        return cls.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
        return null;
    }
  }
}

在上面程序的getInstance()方法中傳入一個Class<T>參數(shù),這是一個泛型化的Class對象,調(diào)用該Class對象的newInstance()方法將返回一個T對象。

String instance = ObjectFactory.getInstance(String.class);

通過傳入String.class便知道T代表String,所以返回的對象是String類型的,避免強制類型轉(zhuǎn)換。

當(dāng)然Class類引入泛型的好處不止這一點,在以后的實際應(yīng)用中會更加能體會到。

使用反射來獲取泛型信息

通過指定類對應(yīng)的 Class 對象,可以獲得該類里包含的所有 Field,不管該 Field 是使用 private 修飾,還是使用 public 修飾。獲得了 Field 對象后,就可以很容易地獲得該 Field 的數(shù)據(jù)類型,即使用如下代碼即可獲得指定 Field 的類型。
// 獲取 Field 對象 f 的類型
Class<?> a = f.getType();
但這種方式只對普通類型的 Field 有效。如果該 Field 的類型是有泛型限制的類型,如 Map<String, Integer> 類型,則不能準(zhǔn)確地得到該 Field 的泛型參數(shù)。
為了獲得指定 Field 的泛型類型,應(yīng)先使用如下方法來獲取指定 Field 的類型。
// 獲得 Field 實例的泛型類型
Type type = f.getGenericType();
然后將 Type 對象強制類型轉(zhuǎn)換為 ParameterizedType 對象,ParameterizedType 代表被參數(shù)化的類型,也就是增加了泛型限制的類型。ParameterizedType 類提供了如下兩個方法。
getRawType():返回沒有泛型信息的原始類型。
getActualTypeArguments():返回泛型參數(shù)的類型。

下面是一個獲取泛型類型的完整程序。
public class GenericTest
{
private Map<String , Integer> score;
public static void main(String[] args)
    throws Exception
{
    Class<GenericTest> clazz = GenericTest.class;
    Field f = clazz.getDeclaredField("score");
    // 直接使用getType()取出Field類型只對普通類型的Field有效
    Class<?> a = f.getType();
    // 下面將看到僅輸出java.util.Map
    System.out.println("score的類型是:" + a);
    // 獲得Field實例f的泛型類型
    Type gType = f.getGenericType();
    // 如果gType類型是ParameterizedType對象
    if(gType instanceof ParameterizedType)
    {
        // 強制類型轉(zhuǎn)換
        ParameterizedType pType = (ParameterizedType)gType;
        // 獲取原始類型
        Type rType = pType.getRawType();
        System.out.println("原始類型是:" + rType);
        // 取得泛型類型的泛型參數(shù)
        Type[] tArgs = pType.getActualTypeArguments();
        System.out.println("泛型類型是:");
        for (int i = 0; i < tArgs.length; i++) 
        {
            System.out.println("第" + i + "個泛型類型是:" + tArgs[i]);
        }
    }
    else
    {
        System.out.println("獲取泛型類型出錯!");
    }
  }
}

輸出結(jié)果:

score 的類型是: interface java.util.Map
原始類型是: interface java.util.Map
泛型類型是:
第 0 個泛型類型是: class java.lang.String
第 1 個泛型類型是:class java.lang.Integer

從上面的運行結(jié)果可以看出,直接使用 Field 的 getType() 方法只能獲取普通類型的 Field 的數(shù)據(jù)類型:對于增加了泛型參數(shù)的類型的 Field,應(yīng)該使用 getGenericType() 方法來取得其類型。
Type 也是 java.lang.reflect 包下的一個接口,該接口代表所有類型的公共高級接口,Class 是 Type 接口的實現(xiàn)類。Type 包括原始類型、參數(shù)化類型、數(shù)組類型、類型變量和基本類型等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 一、概述 Java反射機制定義 Java反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類中的所有屬性和方法...
    Android進階與總結(jié)閱讀 503評論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 前言,本來只是想研究一下注解的,不過發(fā)現(xiàn),要懂注解先得懂反射,別問我為什么,你可以自己試試 JAVA反射 主要是指...
    justCode_閱讀 1,232評論 2 9
  • 小鎮(zhèn)里有一座地標(biāo)建筑號稱是世界第一傘,當(dāng)它落成的那天就被瘋狂的廣場舞大媽占據(jù)了。三年前那塊地兒還是一片斑駁的老房子...
    拜托別胖了閱讀 482評論 0 0
  • 一直練字,卻不懂筆畫里的真諦。 寫得一筆瀟灑的字,卻未必活得瀟灑。 很小的時候,就開始練字,一筆一劃,中規(guī)中矩的正...
    淺笑安然焉閱讀 615評論 0 4