反射之获取类的构造函数等

Android  Java  小知识  2024年1月11日 am8:08发布11个月前更新 城堡大人
119 0 0

前言

记录一下反射相关内容,摘抄于此,方便自己查阅。

反射技术包括如下内容:

  1. 根据一个字符串得到一个类的对象

  2. 获取一个类的所有公用或私有、静态或实例的字段,方法,属性

  3. 对泛类型的反射

正文

本文介绍反射之获取类的构造函数以及其属性,方法。

在反射前,需要新增一个类用于测试,这创建一个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

属性

这个分静态属性和非静态属性。

  1. 改变静态属性是针对类来说;

  2. 改变非静态属性是针对某个对象来说,也就是对这个对象有效。

非静态属性

这里通过实例化私有的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 : 地球一号基地

也就是改变成功的。

方法

方法也分静态和非静态,这里也单独分开介绍。

  1. invoke静态方法是针对类来说;

  2. 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

参考文章

  1. Java基础之—反射2

 历史上的今天

  1. 2023: 监听和获取Android 设备的USER_UNLOCKED状态(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

Visual Studio 2017常用部分快捷键

前言最近在使用VS2017温习C语言,这工具很多快捷键都忘了,这里记录部分,方便自己查阅,后续慢慢添加。正文1、代码片段多行注释step1、选中要注释的代码段;step2、按 Ctrl + K 键;step3、再按 Ctrl +C 键。2、代码片段取消注释step1、选中要取消注释的...

git revert简单记录

前言git revert是撤销中间某次commit。今天就简单记录部分常用的,方便自己查阅。正文git revert 格式:$ git revert -husage: git revert [<options>] <commit-ish>...   or: git ...

Android的5个进程等级

 一、进程:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。进程是系统进行资源分配和调度的一个独立单位。可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体,是一个“执行中的程序”。不只是程序的代码,还包括当前的活动。二、线程:线程是进程的一个实体,是CPU调度和...

聂鲁达:我喜欢你是寂静的

我喜欢你是寂静的,仿佛你消失了一样。你从远处聆听我,我的声音却无法触及你。好像你的双眼已经飞离远去,如同一个吻,封缄了你的嘴。如同所有的事物充满了我的灵魂,你从所有的事物中浮现,充满了我的灵魂。你像我灵魂,一只梦的蝴蝶,你如同忧郁这个字。我喜欢你是寂静的,好像你已远去。你听起来像在...

Android画中画简介

前言Android 8.0 Oreo(API Level 26)允许活动启动画中画 Picture-in-picture(PIP)模式。PIP 是一种特殊类型的多窗口模式,主要用于视频播放。要将画中画添加到您的应用中,您需要注册支持画中画的 Activity、根据需要将 Activity 切换为...

MySQL中like查询的简单使用

前言在数据库中,like关键字用于搜索匹配字段中的指定内容。本文就摘抄于网上的内容,整理于此,方便自己查阅。正文like语法[not] like '字符串'not:可选参数,字段中的内容与指定字符串不匹配时满足条件字符串 :指定用来匹配的字符串,可以是完整的字符串(完整匹配),也...