【JavaSE系列】反射机制

目录

前言

一、概述

二、获取Class对象

三、反射构造方法

1. 获取构造方法

2. 获取修饰符、名称和形参

3. 创建对象

四、反射成员变量 

1. 获取成员变量

2. 获取修饰符、名称和类型

3. 赋值/获取值

五、 反射成员方法

1. 获取成员方法

2. 获取修饰符、形参、返回值和异常

3. 运行成员方法

总结


前言

  在当今的软件开发领域,灵活性和可扩展性成为了软件设计的重要考量因素。Java反射机制作为实现这一目标的强大工具之一,为开发人员提供了在运行时动态获取类的信息或动态调用对象的方法的能力。通过使用反射,开发人员可以在不知道具体实现细节的情况下,操作类、构造函数、方法及字段等。

一、概述

  Java反射机制是指在运行时动态获取类的信息或动态调用对象的方法、修改属性等操作。主要核心就是Class类、Constructor类、Field类、Method类等API。 反射机制主要应用于框架开发、动态代理、ORM框架、JDBC驱动等方面。通过反射机制,程序员能够获得在编译期间不被知晓的类、属性、方法等信息。但是反射机制的性能较低,常常被认为是一种牺牲性能换取灵活性的实现方式。

二、获取Class对象

  在Java中,我们有三种方式获取Class对象,下面是这三种方式:

方式 示例使用时机
方式一:使用Class类的forName()方法Class clazz = Class.forName("java.lang.Object");源代码阶段
方式二:使用“类.class”语法Class clazz = Object.class;加载阶段
方式三:调用Object类的getClass()方法

Object obj = new Object();

Class clazz = obj.getClass();

运行阶段

  这三种方式其实就对应Java里面三种不同的阶段,如果我想创建一个类的对象,是分以下三个阶段的:第一个阶段,要先编写Java文件,然后把他编译成.class字节码文件,这个阶段还没有把代码加载到内存的,只是在硬盘里面进行的操作,所以这个阶段我们也叫做源代码阶段,这个阶段我们会用方式一去获取字节码文件对象。接下来要运行代码了,是不是首先要把这个类的字节码文件加载到内存?这个阶段我们叫做加载阶段,在这个阶段我们会用方式二。接下来去内存当中创建这个类的对象,比如A a = new A(),此时就叫做运行阶段,这个阶段用方式三。下图是对这三个方式的进一步使用的示例:

三、反射构造方法

1. 获取构造方法

  Class类中有四种用于获取构造方法的方法,这四种方法的简单描述如下所示:

方法描述
Constructor<?>[] getConstructors();返回所有“公共”构造方法对象的数组
Constructor<?>[] getDeclaredConstructors();返回所有构造方法对象的数组
Constructor getConstructor(Class<?>…parameterTypes);返回单个“公共”构造方法对象
Constructor getDeclaredConstructor(Class<?>…parameterTypes);返回单个构造方法对象

  下面,我们可以编写一个Student对象,该类中的属性和相关方法如下所示,后面的反射示例均会使用到这个类:

public class Student {

    private String name;
    private int age;
    public String gender;

    public Student() {}
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    protected Student(int age) {
        this.age = age;
    }
    private Student(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    void sayHello() {
        System.out.println("学生说你好!");
    }

    private String sayHello(String str) throws RuntimeException {
        System.out.println("你好你好!");
        return str;
    }

}

  下面我们编写Java代码来测试上面提到的四种获取构造方法的代码:

public static void main(String[] args) throws NoSuchMethodException {
    Class<Student> studentClass = Student.class;
    // 方式一
    Constructor<?>[] constructors = studentClass.getConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println(constructor); // 仅输出public修饰的构造方法
    }
    System.out.println("==================================================================");
    // 方式二
    Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
    for (Constructor<?> constructor : declaredConstructors) {
        System.out.println(constructor);
    }
    System.out.println("==================================================================");
    // 方式三
    Constructor<Student> constructor = studentClass.getConstructor(String.class, int.class);
    System.out.println(constructor); // 仅能获取public修饰的构造方法,如果不是,则会报错
    System.out.println("==================================================================");
    // 方式四
    Constructor<Student> declaredConstructor = studentClass.getDeclaredConstructor(int.class);
    System.out.println(declaredConstructor);
}

  测试结果如下图所示:

2. 获取修饰符、名称和形参

  如何获取构造方法上的修饰符、名称和形参呢?下面是我们的测试代码:

public static void main(String[] args) throws NoSuchMethodException {
    Class<Student> studentClass = Student.class;
    Constructor<Student> constructor = studentClass.getConstructor(String.class, int.class);
    int modifiers = constructor.getModifiers();
    System.out.println("修饰符整数标识:" + modifiers);
    String stringModifiers = Modifier.toString(modifiers);
    System.out.println(stringModifiers);
    System.out.println("===========================================");
    String name = constructor.getName();
    System.out.println(name); // 打印构造方法名称
    System.out.println("===========================================");
    Class<?>[] parameterTypes = constructor.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        System.out.println(parameterType); // 打印参数类型
    }
    System.out.println("===========================================");
    Parameter[] parameters = constructor.getParameters();
    for (Parameter parameter : parameters) {
        System.out.println(parameter); // 打印参数名称
    }
}

  测试结果如下图所示:

3. 创建对象

  Constructor类中用于创建对象的方法有:

方法描述
T newInstance(Object… initargs)根据指定构造方法创建对象
setAccessible(boolean flag)设置为true,表示取消访问检查

  下面是一个示例代码:

public static void main(String[] args) throws Exception {
    Class<Student> studentClass = Student.class;
    Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class);
    constructor.setAccessible(true); // 取消访问检查
    Student student = constructor.newInstance("张三");
    System.out.println(student);
}

  运行结果如下图所示:

四、反射成员变量 

1. 获取成员变量

  Class类中用于获取成员变量的方法有四种,这四类方法如下:

  下面我们编写Java代码对这四个方法进行测试:

public static void main(String[] args) throws NoSuchFieldException {
    Class<Student> studentClass = Student.class;
    // 方式一
    Field[] fields = studentClass.getFields();
    for (Field field : fields) {
        System.out.println(field);
    }
    System.out.println("===========================================================================");
    // 方式二
    Field[] declaredFields = studentClass.getDeclaredFields();
    for (Field field : declaredFields) {
        System.out.println(field);
    }
    System.out.println("===========================================================================");
    // 方式三
    Field gender = studentClass.getField("gender");
    System.out.println(gender);
    System.out.println("===========================================================================");
    // 方式四
    Field name = studentClass.getDeclaredField("name");
    System.out.println(name);
}

  代码运行结果如下图所示:

2. 获取修饰符、名称和类型

  如何获取成员变量上的修饰符、名称和形参呢?下面是我们的测试代码:

public static void main(String[] args) throws NoSuchFieldException {
    Class<Student> studentClass = Student.class;
    Field gender = studentClass.getField("gender");
    System.out.println(Modifier.toString(gender.getModifiers())); // 修饰符
    System.out.println(gender.getName()); // 名称
    System.out.println(gender.getType()); // 类型
}

  测试结果如下图所示:

 

3. 赋值/获取值

  Field类中用于赋值和获取值的方法:

  下面是一个示例代码: 

public static void main(String[] args) throws Exception {
    Student student = new Student("张三", 18);
    Class<? extends Student> studentClass = student.getClass();
    Field name = studentClass.getDeclaredField("name");
    name.setAccessible(true);
    Object oldValue = name.get(student);
    System.out.println(oldValue); // 获取值
    name.set(student, "李四"); // 设置值
    System.out.println(student);
}

  运行结果如下: 

五、 反射成员方法

1. 获取成员方法

  Class类中用于获取成员方法的方法有四个,这四个方法如下所示:

  下面是这四个方法的使用示例代码: 

public static void main(String[] args) throws NoSuchMethodException {
    // 方式一
    Class<Student> studentClass = Student.class;
    Method[] methods = studentClass.getMethods();
    for (Method method : methods) {
        System.out.println(method);
    }
    System.out.println("=======================================================================");
    // 方式二
    Method[] declaredMethods = studentClass.getDeclaredMethods();
    for (Method method : declaredMethods) {
        System.out.println(method);
    }
    System.out.println("=======================================================================");
    // 方式三
    Method setName = studentClass.getMethod("setName", String.class);
    System.out.println(setName);
    System.out.println("=======================================================================");
    // 方式四
    Method sayHello = studentClass.getDeclaredMethod("sayHello");
    System.out.println(sayHello);
}

  运行结果如下图所示: 

2. 获取修饰符、形参、返回值和异常

  如何获取成员变量上的修饰符、形参、返回值和异常呢?下面是我们的测试代码:

public static void main(String[] args) throws NoSuchMethodException {
    Class<Student> studentClass = Student.class;
    Method sayHello = studentClass.getDeclaredMethod("sayHello", String.class);
    System.out.println(Modifier.toString(sayHello.getModifiers())); // 修饰符
    System.out.println("=============================================");
    Class<?>[] parameterTypes = sayHello.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        System.out.println(parameterType); // 形参类型
    }
    System.out.println("=============================================");
    Parameter[] parameters = sayHello.getParameters();
    for (Parameter parameter : parameters) {
        System.out.println(parameter); // 形参
    }
    System.out.println("=============================================");
    Class<?> returnType = sayHello.getReturnType();
    System.out.println(returnType); // 返回值类型
    System.out.println("=============================================");
    Class<?>[] exceptionTypes = sayHello.getExceptionTypes();
    for (Class<?> exceptionType : exceptionTypes) {
        System.out.println(exceptionType);
    } // 异常
}

  测试结果如下图所示:

 

补充:我们还可以反射方法上的注解,进一步提高程序的扩展性,下面是一篇参考博文:

Java Reflect - 利用反射获取类上的注解

3. 运行成员方法

  Method类中用于创建对象的方法如下所示:

  下面是我们的测试代码: 

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    Student student = new Student();
    Class<Student> studentClass = Student.class;
    Method sayHello = studentClass.getDeclaredMethod("sayHello", String.class);
    sayHello.setAccessible(true);
    Object res = sayHello.invoke(student, "人生入戏");
    System.out.println(res);
}

  运行结果如下:

 

总结

  本文探讨了Java反射机制的基础知识,包括获取Class对象的不同方式、反射构造方法、获取成员方法和成员变量等。通过一系列的实际代码示例,我们展示了如何利用反射机制来动态地获取类的信息、创建对象、获取构造方法和成员方法的细节、以及如何对成员变量进行操作。尽管反射机制为Java开发带来了极大的灵活性,但也应注意到它可能会带来的性能影响。因此,在实际项目中,应当合理使用反射,确保在灵活性与性能之间取得良好的平衡。掌握反射机制不仅有助于深入理解Java语言的内部工作原理,而且还能帮助开发者更有效地使用现有的框架和技术栈,提高软件的可维护性和扩展性。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

边城仔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值