• 戴哥说Java(三)——Java的反射和注解


    📖 小记:毕设快开题了,本来和清华的老师商量着做 Android 安全的课题,一直在看相关论文。最近又说和 Swinburn 那边做,不过做好了可以去华为交流,也挺好。在看 Android 安全的论文时,曾想到一个idea,利用Java的注解和反射机制,在程序运行时加一些感兴趣的信息。注解和反射我之前也没有写过博客,特此记录。

    一、反射 Reflection ⌛

    动态语言和静态语言

    在讲反射前,我们先谈谈静态语言和动态语言。众所周知,Java是一门静态语言,变量的数据类型在声明时即确定。JavaScript是一种常见的动态语言,其变量的数据类型在运行时才能确定且不唯一,会随着程序而改变。

    动态语言静态语言
    JavaScript、PythonJava、C++、C#

    但在反射机制的帮助下,Java可以视为一种“准动态语言”,通过反射实现类似动态语言的效果,使编程更加灵活。

    所谓反射

    同时,Java又是面向对象的语言,所有功能的实现依靠一个个对象。
    正常的对象调用首先需要 import 对应的包(package),然后通过 new 实例化一个对象,最后调用对象的方法或属性。
    反射则是反其道而行之,通过对象获取对应类的信息,主要有以下方式:

    1. 通过 forName() 方法,参数为完整包名+类名。
    2. 通过对象的 getClass() 方法。
    3. 通过 类型.class 获得。
    4. 通过 getSuperclass() 获取父类。
    // 通过forName获取类的class对象,括号内为完整包名+类名
    Class cls1 = Class.forName("com.bean.User");
    Class cls2 = Class.forName("com.bean.User");
    System.out.println(cls1.hashCode()+"    "+cls2.hashCode()); // 同一个class
    // 通过对象获得
    Class cls3 = usr1.getClass();
    // 通过.class获得
    Class cls4 = User.class;
    // 获得父类类型
    Class cls5 = cls1.getSuperclass();
    System.out.println(cls5);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Class 是从 JDK 1.0 起就实现的一个类,可以理解为“管理类的一个类”。通过反射得到 Class 后,就可以调用 Class 的一些方法,其中很重要的就有如 getAnnotations、getFields、getMethods 等,分别得到类的注解、属性、方法等信息。
    反射搭配注解,大大提高了 Java 语言的灵活性。

    二、注解 Annotation 💉

    注解在 SpringBoot 中有广泛的应用,也是 Java 高级编程的一部分。在详细讲解前,可以将注解认为是标签,它允许开发者在不改变代码逻辑的情况下,为源代码加入一些补充信息。

    如何声明一个注解

    一个例子 MyAnnotation

    @Target(value = {ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
        // 注解的参数格式  参数类型 + 参数名()
        String name() default "";
        int age() default 0;
        int id() default -1; // 若默认值为-1,表示不存在
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以看出,注解本质上是一个 @interface 类型,但和普通类型不同的是,下面声明的是参数名而不是方法名。声明完参数后还可以通过 default 关键字写默认值。在 @interface 前还有两个注解,@Target 和 @Retention,它们被称为元注解,即修饰注解的注解

    四类元注解

    Java 中定义了四类元注解,它们在声明自定义注解时使用,分别是 Target、Retention、Documented 和 Inherited。

    @Target

    该注解用于标识自定义注解适用的代码上下文,可选参数由 java.lang.annotation.ElementType 中的枚举常量决定。

    public enum ElementType {
    	/** 以下从 JDK5 开始支持 */
        TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR,
        LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE,
        /** 以下两个从 JDK8 开始支持 */
        TYPE_PARAMETER, TYPE_USE
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    @MyAnnotation 中声明的适用类型为 TYPE 和 MTTHOD,说明该注解可以用于方法或者类。

    @Retention

    Retention 翻译过来是保留,即注解信息会在什么情况下保留。有三种保留策略,在 java.lang.annotation.RetentionPolicy 中声明。

    public enum RetentionPolicy {
        /** 源码级 注解会被编译器丢弃 */
        SOURCE,
        /** 类文件级 这是默认级别
         * 注解会被记录在编译器编译完的 .class 文件中,但不会被JVM在运行时保留 */
        CLASS,
        /** 运行时级 注解不仅保留在 .class 文件中,还被JVM在运行时保留
         * 可通过反射机制被读到 */
        RUNTIME
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    从源码可以看出,保留程度 RUNTIME > CLASS > SOURCE。如果要和反射机制配合使用,必须声明为 RUNTIME

    🎉彩蛋:Java 内置注解

    Java还有三类内置注解,Override、Deprecated 和 SuppressWarnings。
    Override 是最常见的,表示重写父类或接口的方法。Deprecated 表示该方法或类已被弃用,可能影响功能实现。SuppressWarnings 表示被该注解修饰的类、方法、属性等报出的警告,会被编译器抑制。

  • 相关阅读:
    Python将10G的文件拆分成多个小文件
    数据库-范式
    【golang之路】——govaluate
    使用Optional和直接返回null,哪个更好?
    uniapp编写小程序解决富文本渲染图片空白间隙问题
    接口测试学习第一天
    哈工大李治军老师操作系统笔记【5】:操作系统的历史(Learning OS Concepts By Coding Them !)
    React16、18 使用 Redux
    学会 arthas,让你 3 年经验掌握 5 年功力!
    Unity 场景淡入淡出效果
  • 原文地址:https://blog.csdn.net/weixin_45651194/article/details/127953437