目录
前言
反射技术包括如下内容:
根据一个字符串得到一个类的对象
获取一个类的所有公用或私有、静态或实例的字段,方法,属性
对泛类型的反射
正文
本文介绍反射之获取类的构造函数以及其属性,方法。
在反射前,需要新增一个类用于测试,这创建一个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
参考文章
《