文章目录
反射技术包括如下内容:
- 根据一个字符串得到一个类的对象 
- 获取一个类的所有公用或私有、静态或实例的字段,方法,属性 
- 对泛类型的反射 
正文
本文介绍反射之获取类的构造函数以及其属性,方法。
在反射前,需要新增一个类用于测试,这创建一个Book类。
package com.biumall.biutextview.book;  public class Book { //设置默认值 private String name = "笔友城堡"; private int page = 100; //构造函数一:private修饰 private Book() { } //构造函数二:protected修饰 protected Book(String name){ this.name = name; } //构造函数三:public修饰 public Book(String name, int page) { this.name = name; this.page = page; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPage() { return page; } public void setPage(int page) { this.page = page; } }
构造函数
getConstructors()只能获取Book类中public修饰的构造函数。
getConstructors()
try { //加载Book的Class Class bookClass = Class.forName("com.biumall.biutextview.book.Book"); //查找所有public的构造函数 Constructor constructor[] = bookClass.getConstructors(); for (Constructor value : constructor) { int mod = value.getModifiers(); //获取构造函数中的参数信息 Class[] parameterTypes = value.getParameterTypes(); Log.d(TAG, "invoke mod : " + Modifier.toString(mod) + " : " + parameterTypes.length); for (Class parameterType : parameterTypes) { //打印参数类型 Log.d(TAG, "invoke : " + parameterType.getName()); } } } catch (Exception e) { throw new RuntimeException(e); }
输出结果
# mod --> 构造函数修饰类型:构造函数参数个数 # type --> 参数类型 invoke mod : public : 2 invoke type : java.lang.String invoke type : int
也就只能获取到public修饰的构造函数
public Book(String name, int page) { this.name = name; this.page = page; }
getDeclaredConstructors()
可以获取所有的构造函数,包括private和protected修饰的。
把getConstructors()换成getDeclaredConstructors()即可,运行输出结果。
# 构造函数一 invoke mod : private : 0 # 构造函数二 invoke mod : public : 1 invoke type : java.lang.String # 构造函数三 invoke mod : public : 2 invoke type : java.lang.String invoke type : int
getDeclaredConstructor()
这个方法可以带参数和不带参数
不带参数
不带参数,就是获取无参数的构造函数
try { Class bookClass = Class.forName("com.biumall.biutextview.book.Book"); Constructor constructor = bookClass.getDeclaredConstructor(); Log.d(TAG, "invoke constructor : "+ constructor); } catch (ClassNotFoundException | NoSuchMethodException e) { throw new RuntimeException(e); }
输出结果
invoke constructor : private com.biumall.biutextview.book.Book()
带参数
try { Class bookClass = Class.forName("com.biumall.biutextview.book.Book"); //指定参数类型 Class[] parameterTypes = {String.class}; //查找指定参数类型的构造函数,如果没有对应的就会抛出异常 Constructor constructor = bookClass.getDeclaredConstructor(parameterTypes); Log.d(TAG, "invoke constructor : " + constructor); } catch (ClassNotFoundException | NoSuchMethodException e) { throw new RuntimeException(e); }
Book类中存在参数类型为String的构造函数,所以可以查找到
invoke constructor : protected com.biumall.biutextview.book.Book(java.lang.String)
如果把parameterTypes中的String.class换成int.class,就会弹出异常:
java.lang.NoSuchMethodException: <init> [int]
上面只是查找一个参数的构造函数,现实中构造函数的参数可能存在多个。
下面举例查找public Book(String name, int page),只需要修改parameterTypes:
Class[] parameterTypes = {String.class,int.class};
输出结果
invoke constructor : public com.biumall.biutextview.book.Book(java.lang.String,int)
构造函数实例化
上面我们通过反射,可以获取类的构造函数,但,大多数时不仅要获取构造函数,还需要对其实例化。要得到类的实例,就需要借用Constructor的newInstance方法。
private构造函数
Book类中有个无参数的private修饰的构造函数,这么写是不让其他人调用,但如果一定要调用时这个构造函数时,就需要反射然后通过newInstance方法。
try {
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    Constructor constructor = bookClass.getDeclaredConstructor();
    //private或protected必须要设置,public可以省略
    constructor.setAccessible(true);
    //通过newInstance()调用private Book()实例化
    Book book = (Book) constructor.newInstance();
    Log.d(TAG, "invoke name : "+ book.getName() + " , page :"+ book.getPage());
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
         IllegalAccessException | InstantiationException e) {
    throw new RuntimeException(e);
}
输出结果
# Book类中特意写了默认值 invoke name : 笔友城堡 , page :100
也就是通过通过private Book()创建了一个Book对象。
protected构造函数
public void invoke() {
    try {
        Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
        Class[] parameterTypes = {String.class};
        Constructor constructor = bookClass.getDeclaredConstructor(parameterTypes);
        //private或protected必须要设置,public可以省略
        constructor.setAccessible(true);
        //通过newInstance()调用protected Book(String)实例化
        //传入String,初始化为[中国历史]
        Book book = (Book) constructor.newInstance("中国历史");
        Log.d(TAG, "invoke name : "+ book.getName() + " , page :"+ book.getPage());
    } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
             IllegalAccessException | InstantiationException e) {
        throw new RuntimeException(e);
    }
}
输出结果
invoke name : 中国历史 , page :100
public构造函数
try {
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    Class[] parameterTypes = {String.class, int.class};
    Constructor constructor = bookClass.getDeclaredConstructor(parameterTypes);
    //通过newInstance()调用public Book(String name, int page)实例化
    //传入String和int,初始化为[中国历史, 5000]
    Book book = (Book) constructor.newInstance("中国历史", 5000);
    Log.d(TAG, "invoke name : " + book.getName() + " , page :" + book.getPage());
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
         IllegalAccessException | InstantiationException e) {
    Log.d(TAG, "invoke e : " + e);
    throw new RuntimeException(e);
}
输出结果
invoke name : 中国历史 , page :5000
属性
这个分静态属性和非静态属性。
- 改变静态属性是针对类来说; 
- 改变非静态属性是针对某个对象来说,也就是对这个对象有效。 
非静态属性
这里通过实例化私有的Book(),然后改变其对象中的name属性。
try {
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    Constructor constructor = bookClass.getDeclaredConstructor();
    //private或protected必须要设置
    constructor.setAccessible(true);
    //通过newInstance()调用private Book()实例化
    Book book = (Book) constructor.newInstance();
    //获取Book中name属性
    Field field = bookClass.getDeclaredField("name");
    //private或protected必须要设置,public可以省略
    field.setAccessible(true);
    //传入需要获取的类对象,针对book对象
    Object nameObject = field.get(book);
    Log.d(TAG, "invoke 1 nameObject : "+ nameObject);
    //改变book对象的name的值
    field.set(book, "天下第一");
    Log.d(TAG, "invoke 2 nameObject : "+ book.getName());
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
         IllegalAccessException | InstantiationException | NoSuchFieldException e) {
    throw new RuntimeException(e);
}
输出结果
invoke 1 nameObject : 笔友城堡 invoke 2 nameObject : 天下第一
book中的name的值被改变了。name是非静态属性,所以上面的修改也就是针对book这个对象来说。
静态属性
由于上面的Book类中没有静态属性,因此在之前的基础上新增一个company静态变量
private static String company = "月球基地一号公司";
public static String getCompany(){
    return company;
}
public static void setCompany(String company){
    Book.company = company;
}
下面我们就获取company变量,然后改变其值。
try {
    Log.d(TAG, "invoke 1 company : "+ Book.getCompany());
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    Field companyField = bookClass.getDeclaredField("company");
    //private或protected必须要设置,public可以省略
    companyField.setAccessible(true);
    //获取Book中company变量
    String company = (String) companyField.get(null);
    //改变companyObject的值
    companyField.set(company,"地球一号基地");
    Log.d(TAG, "invoke 2 company : "+ Book.getCompany());
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
    throw new RuntimeException(e);
}
输出结果
invoke 1 company : 月球基地一号公司 invoke 2 company : 地球一号基地
也就是改变成功的。
方法
方法也分静态和非静态,这里也单独分开介绍。
- invoke静态方法是针对类来说; 
- invoke非静态方法是针对某个对象来说,也就是对这个对象有效。 
静态方法
静态方法中分为,带参数和不带参数。
无参数的方法
try {
    Log.d(TAG, "invoke: ");
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    //获取getCompany方法
    Method getCompanyMethod = bookClass.getDeclaredMethod("getCompany");
    //private或protected必须要设置,public可以省略
    getCompanyMethod.setAccessible(true);
    //调用getCompany,无参函数,且静态的,第一个传入null
    String company = (String) getCompanyMethod.invoke(null);
    Log.d(TAG, "invoke company : " + company);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
         IllegalAccessException e) {
    Log.d(TAG, "invoke e: "+ e);
    throw new RuntimeException(e);
}
输出结果
invoke company : 月球基地一号公司
打印的是默认值
有参数的方法
try {
    Log.d(TAG, "invoke 1 company: "+ Book.getCompany());
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    //获取setCompany,有参数的,需要带上参数类型,这里只有一个
    Class[] parameterTypes = {String.class};
    Method setCompanyMethod = bookClass.getDeclaredMethod("setCompany", parameterTypes);
    //private或protected必须要设置,public可以省略
    setCompanyMethod.setAccessible(true);
    //参数列表,上面只有一个
    Object[] argList = {"太阳基地2号"};
    //调用setCompany方法,静态方法,第一个传入null,第二个写入参数列表
    setCompanyMethod.invoke(null, argList);
    Log.d(TAG, "invoke 1 company: "+ Book.getCompany());
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
         IllegalAccessException e) {
    Log.d(TAG, "invoke e: " + e);
    throw new RuntimeException(e);
}
结果
invoke 1 company: 月球基地一号公司 invoke 1 company: 太阳基地2号
第一个是默认值,第二个是我们调用setCompany()改变的值。
非静态方法
非静态方法也分为带参数无不带参数。
不带参数
try {
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    Constructor constructor = bookClass.getDeclaredConstructor();
    //private或protected必须要设置,public可以省略
    constructor.setAccessible(true);
    //实例化对象,这里调用的是priave Book()
    Book book = (Book) constructor.newInstance();
    //获取getPage()的Method
    Method method = bookClass.getDeclaredMethod("getPage");
    //private或protected必须要设置,public可以省略
    //method.setAccessible(true);
    int page = (int) method.invoke(book);
    Log.d(TAG, "invoke page : "+ page);
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException |
         NoSuchMethodException | InvocationTargetException e) {
    throw new RuntimeException(e);
}
输出结果
invoke page : 100
带参数
try {
    Class bookClass = Class.forName("com.biumall.biutextview.book.Book");
    Constructor constructor = bookClass.getDeclaredConstructor();
    constructor.setAccessible(true);
    Book book = (Book) constructor.newInstance();
    //获取setPage()的Method
    //setPage(int page)带一个int类型的参数
    Class[] parameterTypes = {int.class};
    Method method = bookClass.getDeclaredMethod("setPage", parameterTypes);
    //private或protected必须要设置,public可以省略
    //method.setAccessible(true);
    //带一个参数
    Object[] argsObject = {200};
    method.invoke(book, argsObject);
    Log.d(TAG, "invoke page : " + book.getPage());
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException |
         NoSuchMethodException | InvocationTargetException e) {
    throw new RuntimeException(e);
}
输出结果
invoke page : 200
参考文章
- 《 
