百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Java 反射与注解详解

zhezhongyun 2025-05-14 18:25 33 浏览

一、反射(Reflection)

反射允许程序在运行时动态获取类的信息(如类名、方法、字段、构造器等),并操作类或对象(如创建实例、调用方法、访问字段)。它是实现框架(如Spring、MyBatis)和动态代理的核心技术。


1. 反射核心 API

1.1 获取 Class 对象

// 方式1:通过类名.class
Class<?> clazz1 = String.class;

// 方式2:通过对象.getClass()
String str = "hello";
Class<?> clazz2 = str.getClass();

// 方式3:通过Class.forName()
Class<?> clazz3 = Class.forName("java.lang.String");

1.2 获取类信息

// 获取类名
String className = clazz.getName();      // java.lang.String
String simpleName = clazz.getSimpleName(); // String

// 获取字段(包括私有字段)
Field[] fields = clazz.getDeclaredFields();

// 获取方法(包括私有方法)
Method[] methods = clazz.getDeclaredMethods();

// 获取构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();

1.3 操作对象

// 创建实例(通过无参构造器)
Object instance = clazz.newInstance(); // 已过时,建议用构造器
Constructor<?> constructor = clazz.getDeclaredConstructor();
Object instance = constructor.newInstance();

// 调用方法
Method method = clazz.getDeclaredMethod("methodName", int.class, String.class);
method.setAccessible(true); // 访问私有方法
Object result = method.invoke(instance, 123, "arg");

// 访问/修改字段
Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true); // 访问私有字段
Object value = field.get(instance);
field.set(instance, "new value");

2. 应用场景

  • 动态代理:通过 Proxy 和 InvocationHandler 实现接口的代理。
  • 框架设计:如Spring的依赖注入(@Autowired)、MyBatis的Mapper动态实现。
  • 序列化/反序列化:JSON库(如Jackson)通过反射读取对象字段。

二、注解(Annotation)

注解是代码中的元数据,用于标记类、方法或字段,提供额外信息供编译时或运行时处理。Java内置注解(如 @Override)和自定义注解均可通过反射读取。


1. 注解的定义

1.1 元注解(定义注解的注解)

  • @Target:指定注解可应用的目标(类、方法、字段等)。
@Target(ElementType.METHOD) // 只能修饰方法
  • @Retention:指定注解保留策略。
@Retention(RetentionPolicy.RUNTIME) // 运行时保留(反射可读取)
  • @Documented:注解是否出现在Javadoc中。
  • @Inherited:注解是否可被子类继承。

1.2 自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "default value"; // 属性
    int priority() default 1;
}

2. 注解的使用

public class MyClass {
    @MyAnnotation(value = "test", priority = 2)
    public void myMethod() { ... }
}

3. 注解的解析(通过反射)

Method method = MyClass.class.getMethod("myMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
    String value = annotation.value(); // "test"
    int priority = annotation.priority(); // 2
}

三、反射与注解结合应用

1. 模拟 Spring 的依赖注入

public class MyContainer {
    private Map<String, Object> beans = new HashMap<>();

    public void init() throws Exception {
        // 扫描包下的类
        Class<?> clazz = Class.forName("com.example.MyService");
        if (clazz.isAnnotationPresent(Component.class)) {
            Object instance = clazz.newInstance();
            beans.put(clazz.getSimpleName(), instance);
        }
    }

    public Object getBean(String name) {
        return beans.get(name);
    }
}

2. 自定义 ORM 框架(字段映射)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
    String name();
}

public class User {
    @Column(name = "user_id")
    private int id;
    
    @Column(name = "user_name")
    private String name;
}

// 反射解析注解生成SQL
public String buildInsertSQL(Object obj) {
    StringBuilder sql = new StringBuilder("INSERT INTO ");
    Class<?> clazz = obj.getClass();
    sql.append(clazz.getSimpleName()).append(" (");
    
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        if (field.isAnnotationPresent(Column.class)) {
            Column column = field.getAnnotation(Column.class);
            sql.append(column.name()).append(", ");
        }
    }
    sql.delete(sql.length()-2, sql.length()).append(") VALUES (...)");
    return sql.toString();
}

3. 自定义单元测试框架

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
    String description() default "";
}

public class TestRunner {
    public static void main(String[] args) throws Exception {
        Class<?> testClass = Class.forName("com.example.MyTestClass");
        for (Method method : testClass.getDeclaredMethods()) {
            if (method.isAnnotationPresent(MyTest.class)) {
                method.invoke(testClass.newInstance());
            }
        }
    }
}

四、注意事项

  1. 反射的性能
  2. 反射操作比直接调用慢,频繁调用需缓存 Method、Field 对象。
  3. 避免在性能敏感场景过度使用反射。
  4. 安全性
  5. 反射可以绕过访问权限(setAccessible(true)),需谨慎使用。
  6. 安全管理器(SecurityManager)可限制反射操作。
  7. 注解的保留策略
  8. 若注解的 @Retention 设置为 SOURCE 或 CLASS,则运行时无法通过反射读取。

总结

  • 反射是动态操作类的工具,适用于框架、动态代理等场景,但需注意性能和安全。
  • 注解为代码添加元数据,结合反射可实现灵活的逻辑控制(如依赖注入、ORM映射)。

深入学习方向

  • 动态代理(Proxy 和 InvocationHandler)
  • 注解处理器(APT):在编译时处理注解(如Lombok)。
  • ASM 字节码操作:直接修改字节码实现高级功能。

相关推荐

一篇文章带你了解SVG 渐变知识(svg动画效果)

渐变是一种从一种颜色到另一种颜色的平滑过渡。另外,可以把多个颜色的过渡应用到同一个元素上。SVG渐变主要有两种类型:(Linear,Radial)。一、SVG线性渐变<linearGradie...

Vue3 实战指南:15 个高效组件开发技巧解析

Vue.js作为一款流行的JavaScript框架,在前端开发领域占据着重要地位。Vue3的发布,更是带来了诸多令人兴奋的新特性和改进,让开发者能够更高效地构建应用程序。今天,我们就来深入探讨...

CSS渲染性能优化(低阻抗喷油器阻值一般为多少欧)

在当今快节奏的互联网环境中,网页加载速度直接影响用户体验和业务转化率。页面加载时间每增加100毫秒,就会导致显著的流量和收入损失。作为前端开发的重要组成部分,CSS的渲染性能优化不容忽视。为什么CSS...

前端面试题-Vue 项目中,你做过哪些性能优化?

在Vue项目中,以下是我在生产环境中实践过且用户反馈较好的性能优化方案,整理为分类要点:一、代码层面优化1.代码分割与懒加载路由懒加载:使用`()=>import()`动态导入组件,结...

如何通过JavaScript判断Web页面按钮是否置灰?

在JavaScript语言中判断Web页面按钮是否置灰(禁用状态),可以通过以下几种方式实现,其具体情形取决于按钮的禁用方式(原生disabled属性或CSS样式控制):一、检查原生dis...

「图片显示移植-1」 尝试用opengl/GLFW显示图片

GLFW【https://www.glfw.org】调用了opengl来做图形的显示。我最近需要用opengl来显示图像,不能使用opencv等库。看了一个glfw的官网,里面有github:http...

大模型实战:Flask+H5三件套实现大模型基础聊天界面

本文使用Flask和H5三件套(HTML+JS+CSS)实现大模型聊天应用的基本方式话不多说,先贴上实现效果:流式输出:思考输出:聊天界面模型设置:模型设置会话切换:前言大模型的聊天应用从功能...

ae基础知识(二)(ae必学知识)

hi,大家好,我今天要给大家继续分享的还是ae的基础知识,今天主要分享的就是关于ae的路径文字制作步骤(时间关系没有截图)、动态文字的制作知识、以及ae特效的扭曲的一些基本操作。最后再次复习一下ae的...

YSLOW性能测试前端调优23大规则(二十一)---避免过滤器

AlphalmageLoader过滤器是IE浏览器专有的一个关于图片的属性,主要是为了解决半透明真彩色的PNG显示问题。AlphalmageLoader的语法如下:filter:progid:DX...

Chrome浏览器的渲染流程详解(chrome预览)

我们来详细介绍一下浏览器的**渲染流程**。渲染流程是浏览器将从网络获取到的HTML、CSS和JavaScript文件,最终转化为用户屏幕上可见的、可交互的像素画面的过程。它是一个复杂但高度优...

在 WordPress 中如何设置背景色透明度?

最近开始写一些WordPress专业的知识,阅读数奇低,然后我发一些微信昵称技巧,又说我天天发这些小学生爱玩的玩意,写点文章真不容易。那我两天发点专业的东西,两天发点小学生的东西,剩下三天我看着办...

manim 数学动画之旅--图形样式(数学图形绘制)

manim绘制图形时,除了上一节提到的那些必需的参数,还有一些可选的参数,这些参数可以控制图形显示的样式。绘制各类基本图形(点,线,圆,多边形等)时,每个图形都有自己的默认的样式,比如上一节的图形,...

Web页面如此耗电!到了某种程度,会是大损失

现在用户上网大多使用移动设备或者笔记本电脑。对这两者来说,电池寿命都很重要。在这篇文章里,我们将讨论影响电池寿命的因素,以及作为一个web开发者,我们如何让网页耗电更少,以便用户有更多时间来关注我们的...

11.mxGraph的mxCell和Styles样式(graph style)

3.1.3mxCell[翻译]mxCell是顶点和边的单元对象。mxCell复制了模型中可用的许多功能。使用上的关键区别是,使用模型方法会创建适当的事件通知和撤销,而使用单元进行更改时没有更改记...

按钮重复点击:这“简单”问题,为何难住大半面试者与开发者?

在前端开发中,按钮重复点击是一个看似不起眼,实则非常普遍且容易引发线上事故的问题。想象一下:提交表单时,因为网络卡顿或手抖,重复点击导致后端创建了多条冗余数据…这些场景不仅影响用户体验,更可能造成实...