首页 > 编程语言 >JAVA反射

JAVA反射

时间:2024-11-20 22:18:45浏览次数:1  
标签:反射 JAVA String Person public class 获取 Class

基本概念

反射(Reflection)是 Java 中的一种机制,它允许程序在运行时动态地访问类的结构和行为,包括类、方法、字段、构造函数等,从而实现动态调用和操作。

通过反射,程序不需要在编译时就确定使用的类、方法或字段,而是在运行时动态地加载和使用它们。反射广泛用于框架设计、工具开发和动态操作等场景。

OK,反射的概念可能比较抽象,让我们来举个例子。

应用场景

在许多产品中,需要支持用户通过插件扩展功能,而插件的具体实现通常是在运行时由用户提供的。这种场景下,程序在设计时并不确定会加载哪些插件类或调用哪些方法,而是在运行时动态加载插件类,并通过反射调用相关方法。

以IDEA插件举例

  1. 插件以 JAR 包形式存在
    每个插件是一个 JAR 包,包含具体实现类和 plugin.xml 配置文件,用于描述插件的入口类。
  2. 运行时动态加载插件
    IDEA 使用 反射 配合 类加载器 动态加载插件入口类,实例化插件对象并调用其方法。

核心概念

  1. 运行时动态获取类型信息
    • 通过反射,可以获取某个类的完整信息,例如其类名、父类、接口、构造函数、方法和字段等。
  2. 动态操作对象的属性和方法
    • 即使在编译期并不知道具体的类和方法名,反射也可以在运行时访问和调用这些元素。
  3. 绕过访问限制
    • 通过反射,可以访问私有字段和方法(通过设置 AccessibleObject.setAccessible(true)),这为框架设计带来灵活性的同时,也可能引入安全隐患。

反射的实现方式

Java 反射主要依赖于 java.lang.reflect 包中的类,以下是常用的反射工具类:

  • Class:表示类的字节码对象,可以用来加载类、获取类的信息。
  • Field:表示类的字段,可以获取和设置字段的值。
  • Method:表示类的方法,可以动态调用方法。
  • Constructor:表示类的构造函数,可以通过它创建对象。

Class

在 Java 中,Class 类和反射密切相关。反射机制允许程序在运行时动态地查询、访问和操作类的元数据,而 Class 类是进行反射操作的基础。可以通过 Class 类获取一个类的详细信息,甚至可以通过反射实例化类、调用方法、获取字段等。

Class 类是 Java 中每个类的模板,Java 中的每个类(包括用户定义的类)都有一个对应的 Class 对象,它是类元数据的载体。

每个类的定义都可以通过 Class 对象来引用,通过 Class 对象可以获取类的结构信息,比如:

  • 类名
  • 类的构造方法
  • 类的字段和方法
  • 类的父类和接口

示例类

class Person {
    private String name;
    private int age;

    // 构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 方法
    public void introduce() {
        System.out.println("My name is " + name + " and I am " + age + " years old.");
    }

    // 获取年龄
    public int getAge() {
        return age;
    }
}

获取Class对象

有两种方法可以获取到Class对象

方式1

public static void main(String[] args) throws Exception {
	// 获取 Person 类的 Class 对象
    Class<?> clazz = Person.class;
}

在 Java 中,Person.class 是直接通过类字面量(Class Literal)获得 Person 类的 Class 对象。这个 Class 对象是 Java 类型系统中表示 Person 类的对象。

  • Person.class 表示 Person 类本身(而非 Person 的实例)。
  • Person.class 获取的是一个 Class<Person> 类型的对象,它表示 Person 类的元数据。
  • 在这里,Person.class 的类型是 Class<Person>,这意味着它是一个 Class 类型对象,且其泛型参数为 Person 类本身。

方式2

public static void main(String[] args) throws Exception {
	// 获取 Person 类的 Class 对象
    Class<? extends Person> clazz = Person.class.getClass();
}

Person.class.getClass() 调用的过程是先获取 Person.class 这个 Class 对象,然后对这个 Class 对象调用 getClass() 方法。getClass() 方法是 Object 类的方法,返回的是该对象的运行时类型的 Class 对象。

Person.class.getClass() 的目的是获取 Person.class 这个 Class 对象的类型。

Person.class 的类型是 Class<Person>,而 Class<Person> 继承自 Class<?>,因此 getClass() 返回的类型是 Class<? extends Class<?>>,即表示 Class 类型的 Class 对象。

也就是说,Person.class.getClass() 返回的是 Class<? extends Class<?>>,它表示的是 Class 的类(即 Class.class 的类)。

方式3

当你知道一个class的完整类名,可以通过静态方法Class.forName()获取

public static void main(String[] args) throws Exception {
    Class cls = Class.forName("com.test.Person");
}

获取构造方法

通过Class实例获取Constructor的方法如下:

  • getConstructor(Class...):获取某个publicConstructor
  • getDeclaredConstructor(Class...):获取某个Constructor
  • getConstructors():获取所有publicConstructor
  • getDeclaredConstructors():获取所有Constructor

调用非publicConstructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败。

public static void main(String[] args) throws Exception {
    // 获取 Person 类的 Class 对象
    Class<?> clazz = Person.class;
    // 获取public类型的所有构造方法
    Constructor<?>[] constructors = clazz.getConstructors();
    // 获取指定参数的Constructor
    Constructor<Person> constructor = clazz.getConstructor(new Class[]{String.class, Integer.class});
    // 获取所有构造方法,包括私有的
    Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
    
    // 记得上边提到的写法吗,我们还能这么写
	Class<? extends Class> clazz = new Person().getClass();
    Constructor<? extends Class>[] constructors = clazz.getConstructors();
    Constructor<? extends Class> constructor = clazz.getConstructor(new Class[]{String.class, Integer.class});
    Constructor<? extends Class>[] declaredConstructors = clazz.getDeclaredConstructors();
}

获取字段

getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。 getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的声明字段。

public static void main(String[] args) throws Exception {
    Person person = new Person("erosion2020", 14);
    // 获取 Person 类的 Class 对象
    Class<?> clazz = Person.class;
    // 获取所有有访问权限的字段
    Field[] pubFields = clazz.getFields();
    // 获取所有字段,包括私有的
    Field[] fields = clazz.getDeclaredFields();
    // 因为Person中name是private的,如果这里没有访问权限的话是获取不到name这个字段的
    Field name = clazz.getField("name");
    // 这样就可以获取到name字段了(即便没有访问权限也可以通过getDeclaredField直接将字段拿到
    name = clazz.getDeclaredField("name");
    
    // 当name字段没有访问权限时,如果想要修改字段值,则需要设置
    name.setAccessible(true);
    // 然后就能越权给name设置值了
    name.set(person, "AcidEtch")
}

获取方法

Class类提供了以下几个方法来获取Method

  • Method getMethod(name, Class...):获取某个publicMethod(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有publicMethod(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
public static void main(String[] args) throws Exception {
    Class<?> clazz = Person.class;
    // 获取所有包含了访问权限的方法
    Method[] pubMethods = clazz.getMethods();
    
    // 获取所有方法,然后输出(即便这个方法是被禁止访问的
	Method[] methods = clazz.getDeclaredMethods();
    
    // 来看个例子,动态调用String.substring(int)方法
    String name = "AcidEtch";
	Method substring = String.class.getMethod("substring", int.class);
	System.out.println(substring.invoke(name,3));
}

如果调用的方法是静态方法。那么invoke方法传入的第一个参数永远为null

public static void main(String[] args) throws Exception {
	// 获取Integer.parseInt(String)方法,参数为String:
    Method m = Integer.class.getMethod("parseInt", String.class);
    // 调用该静态方法并获取结果:
    Integer n = (Integer) m.invoke(null, "114514");
    System.out.println(n);
}

创建实例并调用方法

public static void main(String[] args) throws Exception {
    Class<?> clazz = Person.class;
    // 获取指定参数的构造方法
    Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
    // 通过反射来创建对象
    Object personInstance = constructor.newInstance("John", 30);
    Method introduceMethod = clazz.getMethod("introduce");
    // 通过反射调用打印方法
    introduceMethod.invoke(personInstance);
}

反射执行exec命令

Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime")),"cmd /c start");

修改被final修饰的字段

class Example {
    private final String message = "Initial Value";

    public String getMessage() {
        return message;
    }
}

public class ModifyFinalField {
    public static void main(String[] args) throws Exception {
        Example example = new Example();
        // 输出原始值
        System.out.println("Before modification: " + example.getMessage());
        // 获取 Example 类中 message 字段的 Field 对象
        Field messageField = Example.class.getDeclaredField("message");
        // 设置为可访问
        messageField.setAccessible(true);
        // 修改 final 字段的值
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        // 清除 final 修饰符的影响
        modifiersField.setInt(messageField, messageField.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
        // 修改字段值
        messageField.set(example, "Modified Value");
        // 输出修改后的值
        System.out.println("After modification: " + example.getMessage());
    }
}

标签:反射,JAVA,String,Person,public,class,获取,Class
From: https://www.cnblogs.com/erosion2020/p/18559481

相关文章

  • 基于Java的一鸣企业人事管理系 统的设计与实现
    开发技术简介开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9浏览器:谷歌浏览器后台路径地址:localhost:8080/项目名称/admin/dist/index.html前台路径地......
  • 基于Java的养老院管理系统的设计与实现
    开发技术简介开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:Maven3.3.9浏览器:谷歌浏览器后台路径地址:localhost:8080/项目名称/admin/dist/index.html前台路径地......
  • 前端:JavaScript (学习笔记)【1】
    目录​​​​​​​一,介绍JavaScript二,JavaScript的特点1,脚本语言 2,基于对象的语言 3,事件驱动4,简单性5,安全性6,跨平台性7,JS和java的区别(1)公司不同,前身不同(2)基于对象和面向对象(3)变量类型强弱不同(4)运行的位置不同8,HTML和CSS和JS这之间的关系三,JavaScript的引入......
  • Java从入门到放弃之 IO (一)
    JavaIOIO基本概念与计算机相关知识介绍文件类型文本文件地编码文件读写File类构造方法文件信息文件操作IOIO是指Input/Output,即输入和输出。以内存为中心。在未接触IO的时候,大部分时间,我们的程序都是跟内存打交道,代码实际运行在内存中,但是内存是掉电之后数据就......
  • 基于圆柱体镜子和光线跟踪实现镜反射观测全景观图的matlab模拟仿真
    1.程序功能描述基于圆柱体镜子和光线跟踪实现镜反射观测全景观图.模拟的场景如下所示: 2.测试软件版本以及运行结果展示MATLAB2022a版本运行 3.核心程序%%step1fori=1:mmx_new(i)=i-round(mm/2);endfori=1:nny_new(i)=i-round(nn/2);......
  • 基于java+springboot的hive的安顺旅游景点数据分析的设计与实现
    课题简介基于Java+SpringBoot和Hive的安顺旅游景点数据分析系统,全力挖掘安顺旅游数据宝藏。后端借SpringBoot构建,SpringDataJPA与MySQL存储关键信息,SpringSecurity筑牢安全防线。Hive存储海量景点数据,如客流、客源地、消费等,HiveQL深度分析挖掘价值。......
  • 基于java+springboot的Hadoop的豆瓣电子图书推荐系统
    课题简介基于Java+SpringBoot和Hadoop的豆瓣电子图书推荐系统,致力于为读者精准推送契合其兴趣的电子图书。后端运用SpringBoot构建稳固架构,SpringDataJPA与MySQL数据库协作存储图书信息、用户数据、评分记录等内容,SpringSecurity保障系统安全,规范用户认......
  • java基础
    01注释单行注释//这里是注释多行注释/*这里是多行注释这里是多行注释这里是多行注释*/文档注释/*****/02标识符和关键字关键字标识符规则所有的标识符都应该以字母(A-Z或者a-z),美元符($)、或者下划线()开始首字符之后可以是字母(A-Z或者a-z),美元符($)、下划线()或数......
  • javaweb学习 day5 Vue
    Vue是一套前端框架,免除原生JavaScript中的DOM操作,简化书写。基于MVVM思想,实现数据的双向绑定,将编程的关注点放在数据上。引入Vue.js库:常用指令:v-bind:为HTML标签绑定属性值,如设置href,css样式等v-model:在表单元素上创建双向数据绑定<body><divid="app"><av-bind......
  • JavaAPI.05.JDBC的使用(改查)
    JDBC对象与方法目录JDBC对象与方法ConnectionPreparedStatementResultSetJDBC修改操作:SQL语句根据id修改信息JDBC查询操作:SQL语句模糊查询集合承载查询原始查询区间查询(价格)查询分六步走ConnectionConnection接口表示应用程序与特定数据库之间的连接(会话)......