反射和反射的方法

简介: 反射和反射的方法

反射是一种在运行时检查、探知和修改类的能力。通过反射,我们可以在程序运行过程中动态地获取类的信息,并且可以调用类的方法、访问类的属性,甚至可以实例化对象,即使在编译时我们并不知道这些类的存在或者没有对这些类进行硬编码。

在 Java 中,反射使用 java.lang.reflect 包提供的类和接口来实现。常用的反射类包括 ClassMethodFieldConstructor 等。

通过反射,我们可以完成以下一些操作:

  1. 获取类的信息:通过 Class 类可以获取类的名称、包名、父类、接口、注解等信息。
  2. 实例化对象:通过 Class 类的 newInstance() 方法可以创建类的实例,相当于调用了类的无参构造方法
  3. 获取和调用方法:通过 Method 类可以获取类的方法对象,然后可以通过 Method 对象调用相应的方法。
  4. 获取和设置属性:通过 Field 类可以获取类的字段对象,然后可以通过 Field 对象获取或设置相应的属性值。
  5. 调用私有方法和访问私有属性:通过反射,即使方法或字段是私有的,也可以进行访问和调用。

反射在某些场景下非常有用,比如框架和工具的设计与开发,它可以在运行时动态地处理对象和类,增加了程序的灵活性和扩展性。但是反射也会带来一些潜在的问题,比如性能开销较大、可读性较差、对访问权限的限制等,因此在使用反射时需要谨慎权衡利弊。

在 Java 中,通过反射可以获取和操作类的构造方法。构造方法主要用于创建对象实例,因此通过反射可以在运行时动态地实例化对象。下面是一些常用的构造方法相关的反射操作:

  1. 获取构造方法对象:
  • 通过 Class 类的 getConstructor(Class... parameterTypes) 方法可以获取公共的构造方法,需要传入参数类型的数组。
  • 通过 Class 类的 getDeclaredConstructor(Class... parameterTypes) 方法可以获取任意访问权限的构造方法,也需要传入参数类型的数组。
  1. 实例化对象:
  • 获取到构造方法对象后,可以使用 newInstance(Object... initargs) 方法通过构造方法创建对象的实例,需要传入相应的初始化参数。
  1. 调用私有构造方法:
  • 如果要获取并调用私有的构造方法,则需要先通过 setAccessible(true) 方法将构造方法设置为可访问,然后才能调用 newInstance() 方法实例化对象。

下面是一个示例代码,展示了如何通过反射获取构造方法并实例化对象:

import java.lang.reflect.Constructor;
public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取类的Class对象
            Class<?> cls = MyClass.class;
            // 获取公共的构造方法,并实例化对象
            Constructor<?> publicConstructor = cls.getConstructor();
            Object publicObject = publicConstructor.newInstance();
            // 获取私有的构造方法,设置为可访问,并实例化对象
            Constructor<?> privateConstructor = cls.getDeclaredConstructor(int.class);
            privateConstructor.setAccessible(true);
            Object privateObject = privateConstructor.newInstance(123);
            System.out.println("Public object: " + publicObject);
            System.out.println("Private object: " + privateObject);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class MyClass {
    // 公共构造方法
    public MyClass() {
        System.out.println("Public constructor called");
    }
    // 私有构造方法
    private MyClass(int num) {
        System.out.println("Private constructor called with " + num);
    }
}

在上述示例中,通过 getConstructor() 方法获取了 MyClass 类的公共构造方法,并使用 newInstance() 方法实例化了一个对象。然后通过 getDeclaredConstructor() 方法获取了私有构造方法,并使用 setAccessible(true) 设置为可访问,再使用 newInstance() 方法实例化了另一个对象。

需要注意的是,当获取构造方法时,需要根据构造方法的参数类型来选取相应的方法,参数类型可以通过 Class 对象或其它反射相关的方法来获取。同时,在进行反射操作时,需要处理可能抛出的异常,比如 NoSuchMethodExceptionIllegalAccessExceptionInstantiationException 等。

反射获取成员方法

通过反射可以获取类的成员方法,并且可以在运行时动态地调用这些方法。下面是一些常用的成员方法相关的反射操作:

  1. 获取公共方法:
  • 通过 Class 类的 getMethod(String name, Class... parameterTypes) 方法可以获取指定名称和参数类型的公共方法。
  • 通过 Class 类的 getMethods() 方法可以获取所有公共方法的数组。
  1. 获取任意访问权限的方法:
  • 通过 Class 类的 getDeclaredMethod(String name, Class... parameterTypes) 方法可以获取指定名称和参数类型的任意访问权限的方法。
  • 通过 Class 类的 getDeclaredMethods() 方法可以获取所有方法的数组,包括公共方法和非公共方法。
  1. 调用方法:
  • 获取到方法对象后,可以使用 invoke(Object obj, Object... args) 方法调用方法,需要传入对象实例和相应的参数。如果该方法是静态方法,则可以将对象实例设为 null
  1. 调用私有方法:
  • 如果要调用私有方法,则需要先通过 setAccessible(true) 方法将方法设置为可访问,然后才能调用 invoke() 方法。
import java.lang.reflect.Method;
public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取类的Class对象
            Class<?> cls = MyClass.class;
            // 获取公共方法,并调用
            Method publicMethod = cls.getMethod("publicMethod");
            publicMethod.invoke(null);
            // 获取私有方法,设置为可访问,并调用
            Method privateMethod = cls.getDeclaredMethod("privateMethod");
            privateMethod.setAccessible(true);
            privateMethod.invoke(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class MyClass {
    public static void publicMethod() {
        System.out.println("Public method called");
    }
    private static void privateMethod() {
        System.out.println("Private method called");
    }
}
  • 在上述示例中,通过 getMethod() 方法获取了 MyClass 类的公共方法,并使用 invoke() 方法调用了该方法。然后通过 getDeclaredMethod() 方法获取了私有方法,并使用 setAccessible(true) 设置为可访问,再使用 invoke() 方法调用了该方法。
    需要注意的是,在获取方法时,需要根据方法的名称和参数类型来选取相应的方法。对于重载方法,需要确保提供准确匹配的参数类型。同时,在进行反射操作时,需要处理可能抛出的异常,比如 NoSuchMethodExceptionIllegalAccessException 等。

反射获取成员变量

通过反射可以获取类的成员变量,并且可以在运行时动态地访问和修改这些变量的值。下面是一些常用的成员变量相关的反射操作:

  1. 获取公共变量:
  • 通过 Class 类的 getField(String name) 方法可以获取指定名称的公共变量。
  • 通过 Class 类的 getFields() 方法可以获取所有公共变量的数组。
  1. 获取任意访问权限的变量:
  • 通过 Class 类的 getDeclaredField(String name) 方法可以获取指定名称的任意访问权限的变量。
  • 通过 Class 类的 getDeclaredFields() 方法可以获取所有变量的数组,包括公共变量和非公共变量。
  1. 获取和修改变量的值:
  • 获取到变量对象后,可以使用 get(Object obj) 方法获取变量的值,需要传入对象实例。
  • 使用 set(Object obj, Object value) 方法可以修改变量的值,需要传入对象实例和新的值。如果该变量是静态变量,则可以将对象实例设为 null
  1. 获取和设置私有变量:
  • 如果要获取和设置私有变量,则需要先通过 setAccessible(true) 方法将变量设置为可访问,然后才能调用相应的方法。

下面是一个示例代码,展示了如何通过反射获取成员变量并访问和修改其值:

import java.lang.reflect.Field;
public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取类的Class对象
            Class<?> cls = MyClass.class;
            // 获取公共变量,并访问和修改其值
            Field publicField = cls.getField("publicVar");
            System.out.println("Public variable value: " + publicField.get(null));
            publicField.set(null, "New value");
            System.out.println("Updated public variable value: " + publicField.get(null));
            // 获取私有变量,设置为可访问,并访问和修改其值
            Field privateField = cls.getDeclaredField("privateVar");
            privateField.setAccessible(true);
            System.out.println("Private variable value: " + privateField.get(null));
            privateField.set(null, "New value");
            System.out.println("Updated private variable value: " + privateField.get(null));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class MyClass {
    public static String publicVar = "Public value";
    private static String privateVar = "Private value";
}

在上述示例中,通过 getField() 方法获取了 MyClass 类的公共变量,并使用 get() 方法获取该变量的值,同时使用 set() 方法修改了变量的值。然后通过 getDeclaredField() 方法获取了私有变量,并使用 setAccessible(true) 设置为可访问,再次使用 get()set() 方法访问和修改了私有变量的值。

需要注意的是,在获取变量时,需要根据变量的名称来选取相应的变量。同时,在进行反射操作时,需要处理可能抛出的异常,比如 NoSuchFieldExceptionIllegalAccessException 等。


相关文章
|
Linux Shell
Centos或Linux编写一键式Shell脚本删除用户、组指导手册
Centos或Linux编写一键式Shell脚本删除用户、组指导手册
434 4
|
10月前
|
Java 调度 容器
(Java)JFrame窗体的基本操作(1-窗体的创建)
什么是窗体? 窗体,是某个程序,在电脑中,手机中的窗口化程序。它在非固定状态下,可以自由的变换大小。同时,窗体,也是容器,窗体这个容器可以在内部处理很多的小组件。 窗体的具体例子:例如,所有的浏览器,浏览器中网页里打开的提示框,电脑的QQ,手机上的小窗模式,都是窗体; 在本篇内容中,我们会使用JFrame和JDialog两个类,在实现java中的窗体; 在JAVA中如何创建窗体? 在java中,我们需要引入javax.swing包下的JFrame类或者JDialog类,这样才能使用两个窗体;但是,一些窗
439 0
|
9月前
|
存储 缓存 关系型数据库
为什么MySQL8.0 要取消查询缓存?
MySQL查询缓存曾用于提升SELECT性能,通过缓存结果避免重复执行,但因频繁失效、内存开销大、一致性问题及不均查询分布等缺陷,自8.0版本起被彻底移除,以优化整体性能与维护性。
352 1
|
9月前
|
Web App开发 网络协议 Java
Windows 终端命令详解:PowerShell 初学者指南
Windows 终端是一个命令行工具,允许用户通过文本命令与系统交互,执行文件管理、系统配置和网络诊断等操作。PowerShell 是 Windows 终端的现代版本,相比传统的命令提示符(CMD),它功能更强大,支持脚本编写和复杂任务处理。本文将以 PowerShell 为主,带你从零开始学习。
1235 6
|
10月前
|
Java 容器
(Java)Java里JFrame窗体的基础操作(组件-1)
如果不熟悉组件,可以将组件看作是某个Form表单中的表单元素,当然这只是在不熟悉组件的前提下。如果深入了解了组件,那么它有着非常多的组件,这些组件可以完善JFrame窗口的布局,以及一些功能; 本篇内容中所有的组件所用到的类全都来自于javax.swing这个包中,记得引入
202 1
|
9月前
|
SQL 存储 缓存
【Java架构师体系课 | MySQL篇】① 全面理解MySQL架构设计
本文详解MySQL一条SQL查询与更新语句的执行流程,涵盖连接器、分析器、优化器、执行器及存储引擎层协作机制,并深入解析redo log与binlog日志如何通过两阶段提交保障数据一致性与恢复能力。
326 2
|
10月前
|
Java 编译器 Go
【Golang】(1)Go的运行流程步骤与包的概念
初次上手Go语言!先来了解它的运行流程吧! 在Go中对包的概念又有怎样不同的见解呢?
452 4
|
10月前
|
前端开发 JavaScript 算法
【CSS】前端三大件之一,如何学好?从基本用法开始吧!(七):学习ransform属性;本文学习 rotate旋转、scale缩放、skew扭曲、tanslate移动、matrix矩阵 多个参数
transform变形 css3在原来的基础上新增了变形和动画相关属性,通过这些属性可以实现以前需要大段JavaScript才能实现的 功能。 CSS3的变形功能可以对HTML组件执行位移、旋转、缩放、倾斜4种几何变换,这样的变换可以控制HTML组件 呈现出丰富的外观。 借助于位移、旋转、缩放、倾斜这4种几何变换,CSS3提供了transition动画。 transition动画比较简单,只要指定HTML组件的哪些CSS属性需要使用动画效果来执行变化,并指定动画时间,就可保证动画播放。 比transitio
443 1
|
11月前
|
存储 安全 Unix
七、Linux Shell 与脚本基础
别再一遍遍地敲重复的命令了,把它们写进Shell脚本,就能一键搞定。脚本本质上就是个存着一堆命令的文本文件,但要让它“活”起来,有几个关键点:文件开头最好用#!/usr/bin/env bash来指定解释器,并用chmod +x给它执行权限。执行时也有讲究:./script.sh是在一个新“房间”(子Shell)里跑,不影响你;而source script.sh是在当前“房间”里跑,适合用来加载环境变量和配置文件。
904 9
|
10月前
|
SQL Oracle 关系型数据库
SQL语言小结
针对数据库、表单和数据行的增删改,没有涉及到sql真正的用途也就是查询,sql提供的查询语句的关键字占 sql 语言的一半之多,查询语句还是得单拿出来讲,不然太多了。 因为没有涉及到查询,所以sql的新增和修改都是很笼统的做法,drop、alter drop、delete这些很容易,逻辑性也不强,再次说明sql的真正精髓在于查询,不然为啥叫做结构化查询语言
483 0