Java 反射与注解

1. Class 类

1.1 Class 对象

在 Java 中,每个已加载的类在内存中都有一份类信息,类信息对应的类是 `java.lang.Class`,每个对象都持有指向它所属类信息的引用。想要获取 Class 对象,有以下三种方法:

1. 通过类名获取:

Class<Date> dateClass = Date.class;
Class<Integer> integerClass = int.class;

2. 通过对象的`getClass()`方法获取:

Date date = new Date();
Class<? extends Date> aClass = date.getClass();

3. 通过 Class 类的静态方法 `forName` 获取:

Class<?> aClass = Class.forName("java.util.Date");

1.2 名称信息

Class 提供了如下三个方法,用于获取名称信息,其作用和输出分别如下:

- getName:返回 Java 内部使用的名称;

- getSimpleName:返回简称;

- getCanonicalName:返回由 Java 语言规范定义的命名。

Class 对象

getName

getSimpleName

getCanonicalName

int.class

int

int

int

int[].class

[I

int[]

int[]

int[][].class

[[I

int[][]

int[][]

String.class

java.lang.String

String

java.lang.String

String[].class

[Ljava.lang.String;

String[]

java.lang.String[]

ArrayList.class

java.util.ArrayList

ArrayList

java.util.ArrayList

对于数组类型的getName返回值,使用前缀`[`表示数组,有几个`[`表示是几维数组,数组类型使用简写表示:`boolean(Z)`、`byte(B)`、`char(C)`、`double(D)`、`float(F)`、`int(I)`、`long(J)`、`short(S)`、`接口和类(L)`。

1.3 构造器

Class 有以下四个获取构造器信息的方法:

//឴ 获取所有的public构造器
public Constructor<?>[] getConstructors()
//឴ 获取所有构造器
public Constructor<?>[] getDeclaredConstructors()
//឴ 获取指定参数的public构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 获取指定参数的构造器឴
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

获取指定的构造器后,可以使用 `newInstance` 方法来创建其实例,示例如下:

class Test {    static class Student {private String name;private int age;        Student() {}        Student(String name, int age) {
            this.name = name;
            this.age = age;
        }        @Overridepublic String toString() {return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';}
    }public static void main(String[] args) throws Exception {
        Constructor<? extends Student> constructor = Student.class.getDeclaredConstructor(String.class, int.class);
        Student student = constructor.newInstance("shangjack", 10);
        System.out.println(student);
    }
}// 输出:Student{name='shangjack', age=10}

如果你只是想调用默认的无参构造器,则可以直接使用 Class 对象的`newInstance`方法:

Student student = Student.class.newInstance();

1.4 字段信息

Class 有以下四个获取字段信息的方法:

// 返回本类或其父类的所有public字段
public Field[] getFields()
// 返回本类声明的所有字段,但不包括父类的
public Field[] getDeclaredFields()
// 返回本类或其父类中指定名称的public字段
public Field getField(String name)
// 返回本类声明的指定名称的字段
public Field getDeclaredField(String name)

字段信息被封装在 Field 类中,Field 对象有以下常用方法:

// 获取字段的名称
public String getName()
// 判断是否有该字段的访问权限
public boolean isAccessible()
// 设置为true表示忽略Java的访问检查机制,从而可以读写非public的字段
public void setAccessible(boolean flag)
// 获取指定对象obj中该字段的值
public Object get(Object obj)
// 将指定对象obj中该字段的值设为value
public void set(Object obj, Object value)

以下给出一个修改字段的使用示例:

class Test {    static class Student {private String name;private int age;
    }public static void main(String[] args) throws Exception {
        Student student = new Student();
        Class<? extends Student> aClass = student.getClass();
        Field field = aClass.getDeclaredField("name");
        field.setAccessible(true);
        field.set(student, "shangjack");
        System.out.println(field.get(student)); 
    }
}
// 输出: shangjack

1.5 方法信息

Class 有以下四个获取方法信息的方法:

// 返回本类或其父类的所有public方法
public Method[] getMethods() 
// 返回本类声明的所有方法,但不包括父类的
public Method[] getDeclaredMethods() 
// 返回本类或其父类中指定名称及其参数类型的public方法
public Method getMethod(String name, Class<?>... parameterTypes)
// 返回本类声明的指定名称及其参数类型的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

方法信息被封装在 Method 类中,Method 对象有以下常用方法:

// 获取方法的名称
public String getName()
// 设置为true表示忽略Java的访问检查机制,从而可以调用非public方法    
public void setAccessible(boolean flag)
// 在指定的obj对象上调用invoke方法,args为传递的参数列表    
public Object invoke(Object obj, Object... args)

基本使用示例如下:

class Test {    static class Student {private String name;public void setName(String name) {this.name = name;}public String getName() { return name;}
    }public static void main(String[] args) throws Exception {
        Student student = new Student();
        Class<? extends Student> aClass = student.getClass();
        Method setMethod = aClass.getDeclaredMethod("setName",String.class);
        setMethod.invoke(student, "shangjack");
        Method geMethod = aClass.getDeclaredMethod("getName");
        System.out.println(geMethod.invoke(student));
    }
}
// 输出: shangjack

1.6  类型检查

想要判断某个对象是否是某个类或其子类的示例,可以使用 `instanceof` 关键字,或者使用 Class 的 `isInstance` 方法,示例如下:

static class Manager {}static class Employee extends Manager {}public static void main(String[] args) {
    Employee employee = new Employee();
    System.out.println(employee instanceof Manager);  // true
    System.out.println(employee instanceof Employee); // true    System.out.println(Manager.class.isInstance(employee));  // true
    System.out.println(Employee.class.isInstance(employee)); // true}

如果想要判断对象的具体类型,则可以使用`equals`方法进行比较,示例如下:

employee.getClass().equals(Manager.class); // false
employee.getClass().equals(Employee.class); // true

在知道对象的类型后,如果你需要对其进行类型转换,则可以使用类型转换语法或者 Class 的 `cast` 方法:

Manager manager01 = Manager.class.cast(employee);
Manager manager02 = (Manager) employee;


2.注解

2.1 注解定义

下面是 `@Override` 注解的定义,所有注解均使用 `@interface` 关键字修饰, `@Target` 定义注解的使用对象,`@Retention` 表示注解信息保留到什么时候:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

`@Target` 有以下可选值,其含义分别如下:

- TYPE:表示类、接口(包括注释类型)或枚举声明;

- FIELD:字段声明(包括枚举常量);

- METHOD:方法声明;

- PARAMETER:参数声明;

- CONSTRUCTOR:构造器声明;

- LOCAL_VARIABLE:本地变量声明;

- ANNOTATION_TYPE:注解声明;

- PACKAGE:包声明;

- TYPE_PARAMETER:类型参数声明;

- TYPE_USE:任何使用类型的语句。

`@Retention` 有以下可选值,其含义分别如下:

- SOURCE:只在源代码中保留,编译器将代码编译为字节码后就会丢掉;

- CLASS:保留在字节码文件中,但虚拟机将 class 文件加载到内存时不一定会在内存中保留,这是默认的行为;

- RUNTIME:一直保留到运行时,可以通过反射获取其信息。

2.2 注解信息

对于 `@Retention` 类型为 `RUNTIME` 的注解,可以利用反射机制查看其信息。因为注解可以运用在不同目标上,所以 Class 、Field、Method、Constructor 都有如下方法:

// 获取所有注解
public Annotation[] getAnnotations()
// 获取本元素上直接声明的所有注解,忽略inherited来的
public Annotation[] getDeclaredAnnotations()
// 获取指定类型的注解,如果不存在则返回null
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 
// 判断是否有指定类型的注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 

这里给出一个使用示例,模仿常用的序列化框架,通过注解定义字段的序列化名称和序列化格式,注解定义如下:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Json {
    String value();
    String format() default "";
}

class Test {    static class Student {/*当只有一个值并且其对应的方法名为value时候,可以省略value = */
        @Json("姓名")private String name;        @Json(value = "出生日期", format = "yyyy-MM-dd HH:mm:ss")private Date birthday;
        ......
    }    static void parse(Object object, Class<?> clazz) throws IllegalAccessException {        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {if (field.isAnnotationPresent(Json.class)) {
                field.setAccessible(true);
                Object value = field.get(object);
                Json json = field.getAnnotation(Json.class);
                String name = json.value();
                String format = json.format();if (!"".equals(format) && field.getType() == Date.class) {
                    SimpleDateFormat formatter = new SimpleDateFormat(format);
                    value = formatter.format((Date) value);
                }
                System.out.println(name + ":" + value);
            }
        }
    }public static void main(String[] args) throws Exception {
        Student student = new Student("shangjack", new Date());
        parse(student, Student.class);
    }
}输出如下: 
姓名:shangjack
出生日期:2019-07-06 08:45:22

2.3 注解继承

一个常用的元注解是 `@Inherited`,它表示某个注解是否能够被继承。示例如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ClassName {
    String value();
}

使用示例如下,此时如果加上 `@Inherited` 注解,则两者的输出都是 true;如果不加上 `@Inherited` ,第二行语句输出 false,代表子类 PrimaryStudent 没有继承到 `@ClassName` 注解:​​​​​​​

class Test {    @ClassName("学生类")
    static class Student {}    static class PrimaryStudent extends Student {}public static void main(String[] args) {
        System.out.println(Student.class.isAnnotationPresent(ClassName.class));
        System.out.println(PrimaryStudent.class.isAnnotationPresent(ClassName.class));
    }
}

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

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

相关文章

docker打包container成image,然后将image上传到docker hub

第一步&#xff1a;停止正在运行的容器 docker stop <container_name> eg: docker stop xuanjie_mlir 第二步&#xff1a;将对应的container打包成image docker commit <container_id> <镜像名&#xff1a;版本> eg&#xff1a;docker commit 005672e6d97a…

ChinaSoft 论坛巡礼 | 安全攸关软件的智能化开发方法论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

京东数据平台:2023年9月京东智能家居行业数据分析

鲸参谋监测的京东平台9月份智能家居市场销售数据已出炉&#xff01; 9月份&#xff0c;智能家居市场销售额有小幅上涨。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年9月&#xff0c;京东平台智能家居的销量为37万&#xff0c;销售额将近8300万&#xff0c;同比增…

XUbuntu22.04之解决桌面突然放大,屏幕跟着鼠标移动问题(一百九十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【基带开发】AD9361 复乘 com_cmpy_a12_b12

IP核 tb_com module tb_com();reg ad9361_l_clk,rst; initial beginad9361_l_clk0;forever #4.545 ad9361_l_clk~ad9361_l_clk; end initial beginrst1;#9.09 rst0; end wire [63 : 0] m_fll_phase_shift_dout; // fll 输出 dout // FLL Phase Shift com_cmpy_a12_b12 FLL_P…

QT在线安装5.15之前的版本(下载速度飞快)

使用最新的QT在线安装器&#xff0c;安装QT版本时只能安装5.15以及之后的版本&#xff0c;安装QT5.15之前的版本只能通过离线安装的方式&#xff0c;离线安装后还要自己去配置QT&#xff0c;离线安装还有个问题的&#xff0c;后续维护比较麻烦&#xff0c;QT的维护工具还要自己…

【动作模式识别】实现复合动作模式识别(离线控制模块)

一、思路 一般来说&#xff0c;要实现摸脸动作&#xff0c;需要采集手臂的以下肌肉的肌电信号&#xff1a; 肱二头肌&#xff1a;控制肘关节的屈曲肱三头肌&#xff1a;控制肘关节的伸展前臂屈肌群&#xff1a;控制手腕和手指的屈曲前臂伸肌群&#xff1a;控制手腕和手指的伸…

记一次并发问题 Synchronized 失效

记一次并发问题 Synchronized 失效 场景&#xff1a;为避免信息提交重复&#xff0c;给事务方法增加了synchronized修饰符&#xff0c;实际场景中仍然无法完全避免重复&#xff0c;原因是因为在第一个线程执行完synchronized代码段后&#xff0c;此时spring还未完成事务提交&a…

JMM讲解

一&#xff1a;为什么要有JMM&#xff0c;它为什么出现&#xff1f; CPU的运行并不是直接操作内存而是先把内存里面的数据读到缓存&#xff0c;而内存的读和写操作的时候会造成不一致的问题。JVM规范中试图定义一种Java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异&…

AndroidAuto PCTS A118解决杂音问题

A118最后播放三段media类型音频数据,中间会有一点beep的杂音,这个是暂停跟播放没有衔接好导致的,解决这个问题的思路是要分离开播放跟暂停,不能还没完全暂停就播放下一段音频数据 修改点在AudioPlayer.java @Overridepublic synchronized void onStart(int sessionId) {if …

WPF布局控件之WrapPanel布局

前言&#xff1a;博主文章仅用于学习、研究和交流目的&#xff0c;不足和错误之处在所难免&#xff0c;希望大家能够批评指出&#xff0c;博主核实后马上更改。 概述&#xff1a; 后续排序按照从上至下或从右至左的顺序进行&#xff0c;具体取决于方向属性的值。WrapPanel 位…

Linux内核分析(一)--内核架构和子系统

目录 一、引言 二、内核架构 ------>2.1、kernel源码获取 ------>2.2、cpuinfo ------>2.3、内核体系结构 ------>2.4、内核主要组件 三、内核源码及子系统 ------>3.1、整体结构与子系统 ------>3.2、cpuinfo ------>3.3、整体结构与子系统 -…

海上风电应急救援vr模拟安全培训提高企业风险防范能力

相比传统的发电厂&#xff0c;海上风电作业积累的经验少&#xff0c;风险高&#xff0c;因此为了规范施工人员的行为和操作&#xff0c;保障生产安全进行&#xff0c;开展海上风电VR安全培训具有重要意义。 有助于提高员工的安全意识 通过模拟真实的海上风电作业环境&#xff0…

怎样做好金融投资翻译

我们知道&#xff0c; 金融投资翻译所需的译文往往是会议文献、年终报表、信贷审批等重要企业金融资料&#xff0c;其准确性事关整个企业在今后一段时期内的发展战略与经营成效。尤其像年报&#xff0c;对于上市公司来说更是至关重要的。那么&#xff0c;怎样做好金融投资翻译&…

英国 AI 安全峰会前瞻:为什么是现在,为什么在英国

撰文&#xff1a;Ingrid Lunden 来源&#xff1a;TechCrunch 图片来源&#xff1a;由无界AI生成 人工智能的前景和危害是如今的热门话题。有人说人工智能将拯救我们&#xff0c;可以帮助诊断一些恶性疾病、弥补教育领域的数字鸿沟等。但也有人担心它在战争、安全、错误信息等方…

微信小程序中使用GIF

前言 最近在微信小程序开发时遇到了一个非常复杂的动画&#xff0c;如果要手搓的话需要用canvas一点点弄&#xff0c;比较麻烦&#xff0c;于是打算做一个gif来实现动画效果 根据需求&#xff0c;动画只需播放一次即可&#xff0c;并且设置了一个重播按钮&#xff0c;点击即可重…

mybatis-plus分页total为0,分页失效,mybatis-plus多租户插件使用

背景&#xff1a;项目使用mybatis分页插件不生效&#xff0c;以及多租户使用时读取配置异常 分页插件不细述&#xff0c;网上很多方法试了还是不生效&#xff0c;最后修改到当前版本解决&#xff0c;直接上代码 多租户插件使用遇到的问题&#xff1a; 最开始在MyTenantLineH…

C++ 服务器软件工程师测试题目(仅为出题记录,不存在实际意义)

C 服务器软件工程师测试题目&#xff0c;本题目列表会持续补充&#xff0c;仅作为面试测试的参考意见题目&#xff0c;不具备任何实际价值&#xff0c;感兴趣的童鞋可以用于面试问答提取题目测试。 注意&#xff1a; 题目可能是多条题目合并为一条&#xff0c;用作面试请不要…

【Unity编辑器扩展】艺术字/自定义图片字体生成工具

艺术字在游戏中很常用&#xff0c;由于普通字体样式过于平淡&#xff0c;制作花里胡哨的文字图片作为游戏字体使用&#xff0c;这就是艺术字。 不依赖第三方工具&#xff0c;仅使用Unity自带的Custom Font 一张艺术字图集就能实现这个功能&#xff0c;但是为了便于使用&#…

#django基本常识01#

#manage.py# 所有子命令的入口&#xff0c;比如&#xff1a; python3 manage.py runserver 启动服务 python3 manage.py startapp 创建应用 python3 manage.py migrate 数据库迁移 直接执行python3 manage.py 可显示所有子命令 #安装django环境# 1:虚拟环境安装 1.1: 安装vir…