当前位置: 首页 > news >正文

Java—反射机制详解

介绍反射

反射的基本概念

反射(Reflection)是Java语言中的一种机制,它允许程序在运行时检查和操作类、接口、字段和方法等类的内部结构。通过反射,你可以在运行时获取类的信息,包括类的构造器、字段、方法等,并且可以在运行时动态地创建对象、调用方法、访问或修改字段。

反射的核心类和接口

Java反射机制主要涉及以下几个核心类和接口:

  • Class:表示一个类的字节码文件对象,通过它可以获取类的所有信息。
  • Constructor:表示类的构造器对象,通过它可以获取构造器的参数、修饰符等信息,并可以用来创建类的实例。
  • Field:表示类的成员变量对象,通过它可以获取字段的类型、修饰符等信息,并可以用来访问或修改字段的值。
  • Method:表示类的方法对象,通过它可以获取方法的参数、返回值类型、修饰符等信息,并可以用来调用方法。

反射的优点

  • 可扩展性:反射允许程序在运行时动态地加载和使用类,这使得程序具有更好的可扩展性。例如,可以通过配置文件来加载不同的类,从而实现插件化架构。
  • 类浏览器和可视化开发环境:反射可以帮助IDE等开发工具获取类的详细信息,从而提供更好的代码提示、自动补全等功能。
  • 调试器和测试工具:反射可以帮助调试器和测试工具获取类的内部信息,从而实现更强大的调试和测试功能。

反射的缺点

  • 性能开销:反射操作通常比直接调用方法或访问字段要慢得多,因为反射涉及动态解析,JVM无法对其进行优化。
  • 安全限制:反射可以绕过访问控制,访问私有字段和方法,这可能会导致安全问题。
  • 内部暴露:反射可以访问类的内部实现细节,这可能会导致代码的可移植性和稳定性受到影响。

获取元素

获取类

在反射中,获取类的Class对象是第一步。Class对象代表了类的字节码文件,通过它可以获取类的所有信息。获取Class对象有三种主要方式:

通过类名获取:

Class<?> clazz = Class.forName("com.example.MyClass");

这种方式需要类的全限定名(包名+类名),适用于在运行时动态加载类。

通过对象获取:

MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();

这种方式适用于已经有一个类的实例对象的情况。

通过类字面量获取:

Class<?> clazz = MyClass.class;

这种方式适用于在编译时已经知道类名的情况。

Class类下的常用方法:

  • String getSimpleName():获取类的简单名称(不包括包名)。
  • String getName():获取类的全限定名(包括包名)。
  • T newInstance():创建Class对象关联类的实例对象,底层调用无参构造器。注意:这个方法已经被标记为@Deprecated,建议使用Constructor来创建对象。

获取构造器

获取构造器的方法主要通过Class对象来实现:

获取特定构造器:

  • Constructor getConstructor(Class... parameterTypes):根据参数类型获取某个public修饰的构造器。
  • Constructor getDeclaredConstructor(Class... parameterTypes):根据参数类型获取某个构造器,不关心权限修饰符。

获取所有构造器:

  • Constructor[] getConstructors():获取所有public修饰的构造器。
  • Constructor[] getDeclaredConstructors():获取所有构造器,不关心权限修饰符。

Constructor的常用方法:

  • T newInstance(Object... initargs):使用指定的参数创建类的实例对象。
  • void setAccessible(true):设置访问权限,true表示可以访问私有构造器(暴力反射)。
  • String getName():获取构造器的名称。
  • int getParameterCount():获取构造器的参数数量。
  • Class<?>[] getParameterTypes():获取构造器的参数类型数组。

获取成员变量

获取成员变量的方法主要通过Class对象来实现:

获取特定成员变量:

  • Field getField(String name):根据成员变量名获取public修饰的成员变量。
  • Field getDeclaredField(String name):根据成员变量名获取成员变量,不关心权限修饰符。

获取所有成员变量:

  • Field[] getFields():获取所有public修饰的成员变量。
  • Field[] getDeclaredFields():获取所有成员变量,不关心权限修饰符。

Field的常用方法:

  • void set(Object obj, Object value):给指定对象的成员变量赋值。
  • Object get(Object obj):获取指定对象的成员变量的值。
  • void setAccessible(true):设置访问权限,true表示可以访问私有成员变量(暴力反射)。
  • Class getType():获取成员变量的类型。
  • String getName():获取成员变量的名称。

获取方法

获取方法的方法主要通过Class对象来实现:

获取特定方法:

  • Method getMethod(String name, Class... args):根据方法名和参数类型获取public修饰的方法。
  • Method getDeclaredMethod(String name, Class... args):根据方法名和参数类型获取方法,不关心权限修饰符。

获取所有方法:

  • Method[] getMethods():获取所有public修饰的方法,包括父类的方法。
  • Method[] getDeclaredMethods():获取所有方法,不关心权限修饰符,只获取本类声明的方法。

Method的常用方法:

  • Object invoke(Object obj, Object... args):使用指定的参数调用方法,obj是调用方法的对象,args是方法的参数。

反射示例

package com.example;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;// 定义接口,用于打招呼
interface Greetable {void greet(); // 实现打招呼的方法
}// 定义一个父类,用于表示动物
class Animal {protected String species; // 动物的种类// 构造函数,初始化动物的种类public Animal(String species) {this.species = species;}// 让动物发出声音public void makeSound() {System.out.println(species + " 发出声音。");}
}// 定义一个子类,继承自Animal,并实现Greetable接口
class Dog extends Animal implements Greetable {private String name; // 狗的名字// 构造函数,初始化狗的名字public Dog(String name) {super("狗");this.name = name;}// 实现打招呼方法,狗用自己的方式打招呼@Overridepublic void greet() {System.out.println(name + " 说:汪汪!");}
}// 用于演示高级反射用法的示例类
public class AdvancedReflectionExample {// 主函数public static void main(String[] args) {try {// 获取Dog类的Class对象Class<?> dogClass = Dog.class;// 获取构造器并创建实例Constructor<?> dogConstructor = dogClass.getConstructor(String.class);Object dogInstance = dogConstructor.newInstance("小白");// 调用父类的方法Method makeSoundMethod = dogClass.getSuperclass().getDeclaredMethod("makeSound");makeSoundMethod.invoke(dogInstance);// 调用接口方法Method greetMethod = dogClass.getDeclaredMethod("greet");greetMethod.invoke(dogInstance);// 使用反射获取和修改私有字段Field nameField = dogClass.getDeclaredField("name");nameField.setAccessible(true);System.out.println("更新前的名字: " + nameField.get(dogInstance));nameField.set(dogInstance, "小黑");System.out.println("更新后的名字: " + nameField.get(dogInstance));// 创建一个泛型列表并添加Dog对象List<Dog> dogList = new ArrayList<>();dogList.add((Dog) dogInstance);System.out.println("狗狗列表包含: " + dogList.size() + " 只狗。");} catch (Exception e) {e.printStackTrace();}}
}

暴力反射与泛型约束的破坏

泛型在编译阶段提供类型安全,但在运行时被擦除,反射则允许绕过这些安全检查。

核心点:

封装性破坏:通过反射可以访问和修改私有字段和方法,导致原本受保护的对象状态被随意改变。

import java.lang.reflect.Field;class Person {private String name;public Person(String name) {this.name = name;}public String getName() {return name;}
}public class ReflectionEncapsulationDemo {public static void main(String[] args) {Person person = new Person("Alice");// 使用反射访问私有字段try {Field nameField = Person.class.getDeclaredField("name");nameField.setAccessible(true);  // 允许访问私有字段// 修改私有字段的值nameField.set(person, "Bob");// 输出修改后的值System.out.println("修改后的名字: " + person.getName());} catch (Exception e) {e.printStackTrace();}}
}

泛型约束消失:由于泛型类型信息在运行时不可用,使用反射可以插入不符合泛型限制的对象,可能引发类型错误。

package com.example;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class GenericReflectionDemo {public static void main(String[] args) {// 创建一个存储 Double 类型的 ListList<Double> scores = new ArrayList<>();scores.add(99.3);scores.add(199.3);scores.add(89.5);// 使用反射插入一个不符合泛型的字符串try {Class<?> clazz = scores.getClass();Method addMethod = clazz.getDeclaredMethod("add", Object.class);addMethod.invoke(scores, "字符串");  // 插入一个不符合泛型的数据// 输出结果,包含了不符合类型的数据System.out.println("List 内容: " + scores);// List 内容: [99.3, 199.3, 89.5, 字符串]} catch (Exception e) {e.printStackTrace();}}
}

虽然反射可以带来灵活性,但在生产代码中应谨慎使用,以避免引入潜在的错误和安全隐患。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • [linux][证书]证书导出公钥
  • 探索MemGPT:AI界的新宠儿
  • spring boot导入多个配置文件
  • C#用SDK打开海康工业相机,callback取图Bitmap格式,并保存
  • React启动时 Error: error:0308010C:digital envelope routines::unsupported
  • 三维手势 handpose 3D RGB 手势3D建模 三维建模-手势舞 >> DataBall
  • C语言 | Leetcode C语言题解之第434题字符串中的单词数
  • 【我的 PWN 学习手札】fastbin reverse into tcache —— tcache key 绕过
  • 科大讯飞智能体Python SDK接入流程
  • 我能禁止使用某协议的ip禁止访问我的资源吗
  • provide 和 inject
  • 容器化安装Jenkins部署devops
  • 基于SpringBoot的在线点餐系统【附源码】
  • 【Unity设计模式】Unity MVC/MVP架构介绍,及MVC/MVP框架的简单应用
  • json格式互相转换
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 【347天】每日项目总结系列085(2018.01.18)
  • 2018一半小结一波
  • Joomla 2.x, 3.x useful code cheatsheet
  • markdown编辑器简评
  • Material Design
  • Vue学习第二天
  • XML已死 ?
  • 闭包--闭包之tab栏切换(四)
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 前端性能优化——回流与重绘
  • 说说动画卡顿的解决方案
  • 算法---两个栈实现一个队列
  • 线性表及其算法(java实现)
  • ​configparser --- 配置文件解析器​
  • ###STL(标准模板库)
  • #if和#ifdef区别
  • #Linux杂记--将Python3的源码编译为.so文件方法与Linux环境下的交叉编译方法
  • #pragam once 和 #ifndef 预编译头
  • #传输# #传输数据判断#
  • #知识分享#笔记#学习方法
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (WSI分类)WSI分类文献小综述 2024
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (七)Java对象在Hibernate持久化层的状态
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • . NET自动找可写目录
  • ./include/caffe/util/cudnn.hpp: In function ‘const char* cudnnGetErrorString(cudnnStatus_t)’: ./incl
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .NetCore+vue3上传图片 Multipart body length limit 16384 exceeded.
  • .NET连接数据库方式
  • .NET中 MVC 工厂模式浅析
  • @NestedConfigurationProperty 注解用法
  • @Not - Empty-Null-Blank
  • @property括号内属性讲解
  • [ 云计算 | AWS ] AI 编程助手新势力 Amazon CodeWhisperer:优势功能及实用技巧