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

java的反射注解_Java中的注解和反射

Java中的注解和反射

注解

Java注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制。

注解定义

通过@interface来声明一个注解

public @interface Anno {

}

元注解

对注解进行注解的类就是元注解(meta-annotation),在自定义时,一般需要指定两个元注解

@Target

限制可以应用注解的Java元素类型,包括以下几种:

public enum ElementType {

/** Class, interface (including annotation type), or enum declaration */

TYPE,

/** Field declaration (includes enum constants) */

FIELD,

/** Method declaration */

METHOD,

/** Formal parameter declaration */

PARAMETER,

/** Constructor declaration */

CONSTRUCTOR,

/** Local variable declaration */

LOCAL_VARIABLE,

/** Annotation type declaration */

ANNOTATION_TYPE,

/** Package declaration */

PACKAGE,

/**

* Type parameter declaration

*

* @since 1.8

*/

TYPE_PARAMETER,

/**

* Use of a type

*

* @since 1.8

*/

TYPE_USE

}

TYPE:作用于类、接口或者枚举

FIELD:作用于字段

METHOD:作用于方法

PARAMETER:作用于方法参数

CONSTRUCTOR:作用于构造方法

LOCAL_VARIABLE:作用于局部变量

ANNOTATION_TYPE:作用于注解

@Retention

指定注解的保留阶段,有以下几种

public enum RetentionPolicy {

/**

* Annotations are to be discarded by the compiler.

*/

SOURCE,

/**

* Annotations are to be recorded in the class file by the compiler

* but need not be retained by the VM at run time. This is the default

* behavior.

*/

CLASS,

/**

* Annotations are to be recorded in the class file by the compiler and

* retained by the VM at run time, so they may be read reflectively.

*

* @see java.lang.reflect.AnnotatedElement

*/

RUNTIME

}

SOURCE:注解只保留在源码中,编译时会被忽略。

CLASS:注解在编译时会保留,但JVM会忽略。

RUNTIME: 注解会被JVM保留,因此运行环境可以使用。

注解类型元素

@Retention(RetentionPolicy.SOURCE)

@Target(ElementType.TYPE)

public @interface Anno {

//无默认值,在应用注解时必须设置

String value();

//有默认值

int age() default 18;

}

注解应用场景

根据Retention的类型,注解的应用场景有以下三种:

SOURCE

作用于源码级别的注解,可提供给IDE语法检查、APT等场景使用。

IDE语法检查

Android中提供了@IntDef注解

@Retention(SOURCE)

@Target({ANNOTATION_TYPE})

public @interface IntDef {

/** Defines the allowed constants for this element */

int[] value() default {};

/** Defines whether the constants can be used as a flag, or just as an enum (the default) */

boolean flag() default false;

}

这个注解的意义在于能够取代枚举,实现如方法入参限制。

如我们要限制参数只能在MONDAY和TUESDAY中的一个,可以先定义常量

public class WeekDay {

public static final int MONDAY = 1;

public static final int TUESDAY = 2;

}

然后定义注解

@IntDef(value = {WeekDay.MONDAY, WeekDay.TUESDAY})

@Target(ElementType.PARAMETER)

@Retention(RetentionPolicy.SOURCE)

public @interface Week {

}

使用注解

public void test(@Week int week) {

}

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

test(WeekDay.MONDAY);

}

APT注解处理器

APT全称为:"Anotation Processor Tools",意为注解处理器。编写好的Java源文

件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码Class文件。注解处理器是 javac 自带的一个工

具,用来在编译时期扫描处理注解信息。

注解处理器是对注解应用最为广泛的场景。在Glide、EventBus、ButterKnife、ARouter等常用框架中都有注解处理器的身影。

CLASS

定义为CLASS的注解,会保留在class文件中,但是会被JVM忽略。应用场景为:字节码增强,通过修改字节码文件来达到修改代码执行逻辑的目的。常用的框架有:AspectJ、热修复Roubust。

RUNTIME

注解保留至运行期,我们可以通过反射获取注解中的所有信息。

反射

反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

Class

Class是一个类,封装了当前对象所对应的类的信息。

获取Class对象

获取Class对象的三种方法

通过类名获取:类名.class

通过对象获取:对象名.getClass()

通过全类名获取:Class.forName(全类名) classLoader.loadClass(全类名)

创建实例

使用Class对象的newInstance()方法来创建Class对象对应类的实例。

Class> c = String.class;

Object str = c.newInstance();

先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这

种方法可以用指定的构造器构造类的实例

//获取String所对应的Class对象

Class> c = String.class;

//获取String类带一个String参数的构造器

Constructor constructor = c.getConstructor(String.class);

//根据构造器创建实例

Object obj = constructor.newInstance("hello");

System.out.println(obj);

获取构造器信息

得到构造器的方法

Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的public构造函数(包括父类)

Constructor[] getConstructors() -- 获得类的所有公共构造函数

Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(包括私有)

Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的

一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

public T newInstance(Object ... initargs)

获取类的成员变量信息

Field getField(String name) -- 获得命名的公共字段

Field[] getFields() -- 获得类的所有公共字段

Field getDeclaredField(String name) -- 获得类声明的命名的字段

Field[] getDeclaredFields() -- 获得类声明的所有字段

调用方法

获得方法信息的方法

Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法

Method[] getMethods() -- 获得类的所有公共方法

Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法

Method[] getDeclaredMethods() -- 获得类声明的所有方法

当我们从类中获取了一个方法后,我们就可以用 invoke() 方法来调用这个方法。 invoke 方法的原型为:

public Object invoke(Object obj, Object... args)

利用反射创建数组

数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference 其中的Array类为

java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:

public static Object newInstance(Class> componentType, int length);

反射获取泛型真实类型

Type genType = object.getClass().getGenericSuperclass();

Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

return (Class>) params[0];

基于注解和反射的简单应用

通常我们获取Intent传过来的extra,是通过这样的形式:

getIntent().getStringExtra("name");

getIntent().getIntExtra("age",18);

现在,我们通过注解和反射实现自动获取Extra,类似这样:

@InjectExtra

private String name;

@InjectExtra("age")

private int age;

@InjectExtra("gender")

private boolean gender;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

InjectHelper.inject(this);

Log.i(TAG, "name:" + name + ",age:" + age + ",gender:" + gender);

}

实现步骤

定义注解

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface InjectExtra {

String value() default "";

}

定义InjectHelper

public class InjectHelper {

/**

* 注入Extra

*

* @param activity

*/

public static void inject(Activity activity) {

try {

Class extends Activity> clz = activity.getClass();

Field[] declaredFields = clz.getDeclaredFields();

for (Field field : declaredFields) {

boolean annotationPresent = field.isAnnotationPresent(InjectExtra.class);

if (annotationPresent) {

InjectExtra annotation = field.getAnnotation(InjectExtra.class);

String name = annotation.value();

if (TextUtils.isEmpty(name)) {

//如果注解没有指定value,就用字段名

name = field.getName();

}

Object object = activity.getIntent().getExtras().get(name);

field.setAccessible(true);

field.set(activity, object);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

使用

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

Intent intent = new Intent(this, TargetActivity.class);

intent.putExtra("name", "zs");

intent.putExtra("age", 18);

intent.putExtra("gender", true);

startActivity(intent);

}

}

public class TargetActivity extends AppCompatActivity {

private static final String TAG = InjectHelper.class.getSimpleName();

@InjectExtra

private String name;

@InjectExtra("age")

private int age;

@InjectExtra("gender")

private boolean gender;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

InjectHelper.inject(this);

Log.i(TAG, "name:" + name + ",age:" + age + ",gender:" + gender);

}

}

输出结果

2020-04-27 10:38:22.495 19687-19687/com.wangyz.annotation I/InjectHelper: name:zs,age:18,gender:true

这里为了演示注解与反射,指定Retentaion为RUNTIME,实际上可以指定为SOURCE级别,通过APT来生成辅助类,来减少手动获取Extra的工作量。

相关文章:

  • mfc将mysql查询结果取出_笔记-mysql 导出查询结果
  • mysql数据库中的各种约束_数据库Mysql的学习(三)-各种约束
  • java linux磁盘_java获取linux的磁盘空间,磁盘利用率
  • eclipse java转maven_关于在eclipse中使用Maven创建项目转换成javaweb步骤
  • uwp连接mysql数据库_[UWP小白日记-11]在UWP中使用Entity Framework Core(Entity Framework 7)操作SQLite数据库(一)...
  • centos apache php mysql zend_Centos6.5+php+apache+mysql+zendguardloader环境安装
  • java =1 ==1_为什么在Java中-1右移1 = -1?
  • java commons-pool_apache commons-pool的配置参数
  • JAVA CP936编码转utf8_Java编码转换 - querychinesesto的个人空间 - OSCHINA - 中文开源技术交流社区...
  • java hssffont_Java HSSFFont.setBoldweight方法代碼示例
  • java怎样创建字符串列表_java第三季lt;6-5gt;:生成随机字符串列表并排序之实现...
  • java多表return语句吗_java – 需要一个return语句吗? – 新编码
  • java网格布局如何为空_Java 网格布局
  • java 反序列化php对象_Java对对象的序列化和反序列化
  • Java如何读写基本数据类型和对象_114.Java操作基本数据类型的流对象
  • Angular 响应式表单 基础例子
  • C++11: atomic 头文件
  • Docker: 容器互访的三种方式
  • download使用浅析
  • Effective Java 笔记(一)
  • HashMap ConcurrentHashMap
  • IDEA 插件开发入门教程
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • js
  • Mithril.js 入门介绍
  • mysql 5.6 原生Online DDL解析
  • rabbitmq延迟消息示例
  • react-native 安卓真机环境搭建
  • vue总结
  • Web设计流程优化:网页效果图设计新思路
  • 关于Flux,Vuex,Redux的思考
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 排序算法之--选择排序
  • 前端_面试
  • 说说动画卡顿的解决方案
  • 正则表达式小结
  • ​ssh免密码登录设置及问题总结
  • # 数论-逆元
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (十八)三元表达式和列表解析
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (十五)使用Nexus创建Maven私服
  • (转)树状数组
  • (状压dp)uva 10817 Headmaster's Headache
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .NET Core中的去虚
  • .sh文件怎么运行_创建优化的Go镜像文件以及踩过的坑
  • @RunWith注解作用
  • @Transactional 详解
  • [51nod1610]路径计数
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯
  • [AR Foundation] 人脸检测的流程
  • [BUUCTF]-PWN:wustctf2020_number_game解析(补码,整数漏洞)
  • [BZOJ1040][P2607][ZJOI2008]骑士[树形DP+基环树]
  • [C/C++]数据结构 栈和队列()