Java—反射机制详解

news2024/11/19 13:34:24

介绍反射

反射的基本概念

反射(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;
    }

    // 实现打招呼方法,狗用自己的方式打招呼
    @Override
    public 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 类型的 List
        List<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();
        }
    }
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2161922.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s StorageClass 存储类

文章目录 一、概述1、StorageClass 对象定义2、StorageClass YAML 示例 二、StorageClass 字段1、provisioner&#xff08;存储制备器&#xff09;1.1、内置制备器1.2、第三方制备器 2、reclaimPolicy&#xff08;回收策略&#xff09;3、allowVolumeExpansion&#xff08;允许…

从碎片到整合:EasyCVR平台如何重塑城市感知系统的视频数据生态

随着城市化进程的加速&#xff0c;城市感知系统作为智慧城市的重要组成部分&#xff0c;正逐步成为提升城市管理效率、保障公共安全、优化资源配置的关键手段。EasyCVR视频汇聚融合平台&#xff0c;凭借其强大的数据整合、智能分析与远程监控能力&#xff0c;在城市感知系统中扮…

Sam Altman的博客:The Intelligence Age

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

thinkphp 做分布式服务+读写分离+分库分表+负载均衡(分区)(后续接着写)

thinkphp 做分布式服务读写分离分库分表负载均衡&#xff08;分区&#xff09; 引言 thinkphp* 大道至简负载均衡分布式服务一、读写分离1、读写分离的实现方式2、主从同步的三种模式2-1、异步模式&#xff08;mysql async-mode&#xff09;2-2、半同步模式&#xff08;mysql s…

红帽rhce含金量?考到能拿多少钱工资?

随着目前国内的it职业飞速发展&#xff0c;rhce已经成为it职业的首选认证、高薪亮点。主要是linux人才出现大比例短缺的状况&#xff0c;很多企业对linux人才的需求也继续升温。 这个时候大家如果抓住了机会&#xff0c;那么实力就能得到质的提升&#xff0c;那么小编针对红帽…

OpenAI GPT-3 API: What is the difference between davinci and text-davinci-003?

题意&#xff1a;OpenAI GPT-3 API&#xff1a;davinci 和 text-davinci-003 有什么区别 问题背景&#xff1a; Im testing the different models for OpenAI, and I noticed that not all of them are developed or trained enough to give a reliable response. 我正在测试…

论文阅读【时间序列】ModerTCN (ICLR2024)

【时间序列】ModerTCN (ICLR2024) 原文链接&#xff1a;ModernTCN: A Modern Pure Convolution Structure for General Time Series Analysis 代码仓库&#xff1a;ModerTCN 简易版本实现代码可以参考&#xff1a;&#xff08;2024 ICLR&#xff09;ModernTCN&#xff1a;A Mod…

谁是AI界的老司机?谁最“纯洁”?谁能通过暧昧小短文的终极考验?

AI的能力已经让人们惊叹不已&#xff0c;不管是帮你写文章、答疑解惑&#xff0c;还是生成艺术作品&#xff0c;几乎无所不能。但如果让AI来解读一篇暗藏玄机、暧昧十足的小短文&#xff0c;结果会怎样&#xff1f;今天&#xff0c;我们就把几款顶流AI大模型拉出来&#xff0c;…

Cobalt Strike的下载与基本用法

CobaltStrike4.8 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;tgf3 what Cobalt Strike是一款渗透测试神器&#xff0c;常被业界人称为CS神器。Cobalt Strike已经不再使用MSF而是作为单独的平台使用&#xff0c;它分为客户端与服务端&#xff0c;服务端是一个&…

C++的扩充和封装

作业&#xff1a; 手动封装一个顺序表&#xff08;SeqList&#xff09;,分文件编译实现 有私有成员&#xff1a;顺序表数组的起始地址 ptr、 顺序表的总长度&#xff1a;size、顺序表的实际长度&#xff1a;len 成员函数&#xff1a;初始化 init(int n) 判空&#xff1a;em…

Vue:默认插槽

目录 一.性质 1.内容分发 2.无名称标识 3.作用域 4.使用方式 二.使用 1.父组件 2.子组件 三.代码 1.父组件代码 2.子组件代码 四.效果 一.性质 1.内容分发 默认插槽允许组件的使用者定义一些内容&#xff0c;这些内容会被插入到组件模板中的特定位置。这有助于实…

C++:类中的特殊关键字,运算重载符

1.My_string类中重载以下的运算符&#xff1a; 、[] 、>、<、、>、<、&#xff01;、、输入输出(>>、<<) 主函数&#xff1a; #include <iostream> #include "my_string.h"using namespace std;int main() {My_string s1("cat…

QT客户端发送HTTP请求此时服务器到底收到了哪些数据?

一个Http请求包括 请求行 请求头 空行 请求体 下面是示例&#xff1a; 1,2,3,4分别代表上面的四个部分&#xff0c;我只是做了一些解析&#xff0c;具体可以结合代码 1. post / HTTP/1.1 2.GET请求头包括Host(主机名),user-agent&#xff08;客户端标识符&#xff09;&am…

AI Agent智能应用从0到1定制开发Langchain+LLM全流程解决方案与落地实战

大模型微调实战&#xff1a;精通、指令微调、开源大模型微调、对齐与垂直领域应用29套AI全栈大模型项目实战&#xff0c;人工智能视频课程-多模态大模型&#xff0c;微调技术训练营&#xff0c;大模型多场景实战&#xff0c;AI图像处理&#xff0c;AI量化投资&#xff0c;OPenC…

fiddler抓包11_列表显示服务器IP (配置文件)

请求列表默认不显示服务器IP字段&#xff0c;也无法从定制列窗口添加&#xff0c;可以修改CustomRules.js实现。 ① 菜单栏“Rules”&#xff08;规则&#xff09; - “Customize Rules...”&#xff08;自定义规则&#xff09;&#xff0c;打开CustomRules.js文件。 &#xf…

HarmonyOS NEXT:解密从概念到实践的技术创新与应用前景

HarmonyOS是目前华为手机所搭载的鸿蒙系统&#xff0c;它在Open Harmony的基础上兼容了安卓的AOSP&#xff0c;所以可以使用安卓APK应用&#xff0c;HarmonyOS属于华为在当前阶段过渡使用的系统&#xff0c;原生鸿蒙的应用生态尚未发展起来&#xff0c;兼容安卓应用可以让用户有…

【AI大模型】通义大模型API接口实现

目录 一、基础环境安装 &#xff08;一&#xff09;OpenAI Python SDK安装 &#xff08;二&#xff09;DashScope SDK安装 二、OPENAI接口实现 &#xff08;一&#xff09;文本输入 &#xff08;二&#xff09;流式输出 &#xff08;三&#xff09;图像输入 &#xff0…

Python 字符串的常见方法

Python 字符串的常见方法 字符串是 Python 中非常重要的数据类型之一。在日常编程中&#xff0c;我们经常需要对字符串进行各种操作&#xff0c;比如分割、连接、替换等。Python 提供了丰富的字符串方法&#xff0c;使得这些操作变得简单而高效。本文将详细介绍一些常见的字符…

【Docker】Docker快速入门

Docker学习笔记 一、Docker概述 为什么会出现Docker? 安卓开发流程&#xff1a;apk(java开发的)发布到应用商店&#xff0c;用户安装apk即可使用。 后端开发流程&#xff1a; jar(java开发的)带上环境发布到Docker仓库&#xff0c;用户从Docker仓库拉取镜像并部署。 总结…

关于Python升级以后脚本不能运行的问题

近日将Python从3.11升级到了3.12&#xff0c;然后把几个包例如numpy等也通过pip给upgrade了一下&#xff0c;结果原来运行的好好的脚本&#xff0c;都运行不了了&#xff0c;还出现各种报错。怀疑是自己升级了环境导致的&#xff0c;因此通过搜索引擎检索了一下&#xff0c;有这…