Java 反射机制

Java反射机制是移动架构的重要知识体系中的一部分,可以说想要成为合格的移动架构师必须了解Java的反射机制

(1)概念

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

以上定义是在科学百科中找到的。

(2)本质

所谓反射,其实就是获取字节码文件,即.class文件,最后生成Class对象,拿到这个对象之后就可以对这个对象为所欲为了。

(3)获取Class对象的三种方式

上面说到,反射机制的本质是获取.class文件,生成Class对象,那么该如何获取Class对象呢?

css 复制代码
[第一种方式]

如果我们可以拿到某对象的实例如对象Test,那么获取Test的Class对象的代码如下:

ini 复制代码
Class<?> cls = new Test().getClass();
[第二种方式]

在知道类名的情况下:

ini 复制代码
Class<?> cls = Test.class;
[第三种方式]

前两种获取Class对象的方式比较简单,但是必须保证Test对象能直接访问,但是,在很多时候,我们想要获取的Class对象不能直接访问,这个时候必须采用第三种获取Class对象的方式,如下:

ini 复制代码
    Class<?> cls = null;
    try {
        cls = Class.forName("com.juexing.mytest.Test");
        System.out.println(cls.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

forName()方法里传递的参数是Class的Name,所以,如果Test对象可以直接访问,也可以写成:

ini 复制代码
    Class<?> cls = null;
    try {
        cls = Class.forName(Test.class.getName());
        System.out.println(cls.getName());
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
[三种方式如何选择]

第一种方式显然已经获取到对象了,所以没必要使用复杂化的反射机制; 第二种方式会导致模块与模块之间的耦合性,也不建议使用; 第三种方式是反射机制常用的方法,建议使用。

(4)获取构造方法
scss 复制代码
// 1.获取参数列表是parameterTypes,访问控制符是public的构造函数
public Constructor getConstructor(Class[] parameterTypes)
​
// 2.获取所有访问控制符是public的构造函数
public Constructor[] getConstructors()
​
// 3.获取参数列表是parameterTypes,并且是类自身声明的构造函数,访问控制符包含public、protected和private的函数。
public Constructor getDeclaredConstructor(Class[] parameterTypes)
​
//4.获取类自身声明的全部的构造函数,访问控制符包含public、protected和private的函数。
public Constructor[] getDeclaredConstructors()
​
//5.如果类声明在其它类的构造函数中,返回该类所在的构造函数,如果存在则返回,不存在返回null
public Constructor getEnclosingConstructor()

举例:

arduino 复制代码
Constructor<?> constructor = cls.getDeclaredConstructor();//没有参数
Constructor<?> constructor = cls.getDeclaredConstructor(int.class);//一个参数
Constructor<?> constructor = cls.getDeclaredConstructor(String.class, String.class);//两个参数

拿到constructor对象之后可以新建当前对象的一个实例:

ini 复制代码
constructor.newInstance();

这个方法在实际运用中会经常用到。

(5)获取成员函数
scss 复制代码
// 1.获取函数名是name,参数是parameterTypes的public的函数(包括从基类继承的、从接口实现的所有public函数)
 public Method getMethod(String name, Class[] parameterTypes)

 //2.获取全部的public的函数(包括从基类继承的、从接口实现的所有public函数)
 public Method[] getMethods()

 //3.获取函数名name,参数是parameterTypes,并且是类自身声明的函数,包含public、protected和private方法。
 public Method getDeclaredMethod(String name, Class[] parameterTypes)

 //4.获取全部的类自身声明的函数,包含public、protected和private方法。
 public Method[] getDeclaredMethods()

 //5.如果这个类是其它类中某个方法的内部类,调用getEnclosingMethod()就是这个类所在的方法;若不存在,返回null。
 public Method getEnclosingMethod()

举例:

ini 复制代码
        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Method methodA = cls.getDeclaredMethod("testA");
        System.out.println(methodA.getName());
        Method methodB = cls.getDeclaredMethod("testB", String.class);
        System.out.println(methodB.getName());
        Method[] methods = cls.getDeclaredMethods();
        for(Method method : methods){
            System.out.println(method.getName());
        }

那么,得到Method对象之后又可以做什么操作呢?

css 复制代码
[获取方法名]
ini 复制代码
methodA.getName();
[调用方法]

使用invoke方法调用指定方法,如:

arduino 复制代码
methodA.invoke(constructor.newInstance());

invoke方法第一个参数传递指定类的实例(如果能获取到对应类的实例最好,如果获取不到,只用使用constructor.newInstance()代替了),后面几个参数传递方法的参数。

假设有这样一个类

csharp 复制代码
public class ATest {

    public ATest(){

    }

    public void testA(){
        System.out.println("调用了testA");
    }

    public String testB(String a1, int b, float c){
        System.out.println(a1);
        return a1;
    }

    private void testC(){
        System.out.println("调用了testC");
    }
}

那么,该如何使用反射机制调用testA、testB、testC这三个方法呢?

调用testA(最简单的调用方式)

ini 复制代码
        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Constructor constructor = cls.getDeclaredConstructor();
        Method methodA = cls.getDeclaredMethod("testA");
        methodA.invoke(constructor.newInstance());

调用testB(注意返回值和参数)

ini 复制代码
        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Constructor constructor = cls.getDeclaredConstructor();
        Method methodB = cls.getDeclaredMethod("testB", String.class, int.class, float.class);
        String aa = (String) methodB.invoke(constructor.newInstance(), "调用了testB", 2, 2.0f);
        System.out.println(aa);

invoke方法除了第一个参数外,剩下的几个参数都是testB方法的参数传值,aa是返回值。

调用testC(注意private修饰符)

ini 复制代码
        Class<?> cls = Class.forName("com.juexing.mytest.ATest");
        Constructor constructor = cls.getDeclaredConstructor();
        Method methodC = cls.getDeclaredMethod("testC");
        methodC.invoke(constructor.newInstance());

我们都知道,Java是无法访问其他类中的私有方法,如果使用以上代码调用testC方法会直接报错,如下:

图片.png

那么,反射机制能否调用私有方法?答案是可以的。

在代码中加入以下代码:

arduino 复制代码
        methodC.setAccessible(true);

可以解决访问受限的问题。

(6)获取成员变量
scss 复制代码
//1.获取"名称是name"的public的成员变量(包括从基类继承的、从接口实现的所有public成员变量)
public Field getField(String name)
​
//2.获取全部的public成员变量(包括从基类继承的、从接口实现的所有public成员变量)
public Field[] getFields()
​
//3.获取"名称是name",并且是类自身声明的成员变量,包含public、protected和private成员变量。
public Field getDeclaredField(String name)
​
//4.获取全部的类自身声明的成员变量,包含public、protected和private成员变量。
public Field[] getDeclaredFields()

拿到字段信息就可以访问这些字段了,如:

ini 复制代码
        field.get(obj);
        field.getBoolean(obj);
        field.getByte(obj);
        field.getChar(obj);
        field.getFloat(obj);
        field.getDouble(obj);
        field.getLong(obj);

当然,如果字段的修饰否是private,需要添加以下代码:

arduino 复制代码
        field.setAccessible(true);

既然可以获取字段,当然也可以修改字段:

ini 复制代码
        field.set(obj, null);
        field.setBoolean(obj, false);
        field.setByte(obj, (byte)1);
        field.setChar(obj, 'C');
        field.setFloat(obj, 1.0f);
        field.setDouble(obj, 1.0);
        field.setLong(obj, 1);
(7)总结

反射机制的相关方法还有很多,我想,了解以上提到的内容应该就差不多了,在一些主流框架中,反射机制运用非常的广泛,了解反射机制是成为架构师的必备功课之一。

相关推荐
零千叶34 分钟前
【面试】AI大模型应用原理面试题
java·设计模式·面试
坐吃山猪5 小时前
SpringBoot01-配置文件
java·开发语言
我叫汪枫5 小时前
《Java餐厅的待客之道:BIO, NIO, AIO三种服务模式的进化》
java·开发语言·nio
yaoxtao6 小时前
java.nio.file.InvalidPathException异常
java·linux·ubuntu
Swift社区7 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT8 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy8 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss9 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续10 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升