Java基础16-Java注解简介和最佳实践(一)

简介: Java基础16-Java注解简介和最佳实践(一)

Java注解简介

Annotation 中文译过来就是注解、标释的意思,在 Java 中注解是一个很重要的知识点,但经常还是有点让新手不容易理解。

我个人认为,比较糟糕的技术文档主要特征之一就是:用专业名词来介绍专业名词。 比如:

Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。这是大多数网站上对于 Java 注解,解释确实正确,但是说实在话,我第一次学习的时候,头脑一片空白。这什么跟什么啊?听了像没有听一样。因为概念太过于抽象,所以初学者实在是比较吃力才能够理解,然后随着自己开发过程中不断地强化练习,才会慢慢对它形成正确的认识。

我在写这篇文章的时候,我就在思考。如何让自己或者让读者能够比较直观地认识注解这个概念?是要去官方文档上翻译说明吗?我马上否定了这个答案。

后来,我想到了一样东西————墨水,墨水可以挥发、可以有不同的颜色,用来解释注解正好。

不过,我继续发散思维后,想到了一样东西能够更好地代替墨水,那就是印章。印章可以沾上不同的墨水或者印泥,可以定制印章的文字或者图案,如果愿意它也可以被戳到你任何想戳的物体表面。

但是,我再继续发散思维后,又想到一样东西能够更好地代替印章,那就是标签。标签是一张便利纸,标签上的内容可以自由定义。常见的如货架上的商品价格标签、图书馆中的书本编码标签、实验室中化学材料的名称类别标签等等。

并且,往抽象地说,标签并不一定是一张纸,它可以是对人和事物的属性评价。也就是说,标签具备对于抽象事物的解释。

所以,基于如此,我完成了自我的知识认知升级,我决定用标签来解释注解。

注解如同标签

之前某新闻客户端的评论有盖楼的习惯,于是 “乔布斯重新定义了手机、罗永浩重新定义了xX” 就经常极为工整地出现在了评论楼层中,并且广大网友在相当长的一段时间内对于这种行为乐此不疲。这其实就是等同于贴标签的行为。在某些网友眼中,罗永浩就成了xX的代名词。

广大网友给罗永浩贴了一个名为“xx”的标签,他们并不真正了解罗永浩,不知道他当教师、砸冰箱、办博客的壮举,但是因为“xx”这样的标签存在,这有助于他们直接快速地对罗永浩这个人做出评价,然后基于此,罗永浩就可以成为茶余饭后的谈资,这就是标签的力量。

而在网络的另一边,老罗靠他的人格魅力自然收获一大批忠实的拥泵,他们对于老罗贴的又是另一种标签。

老罗还是老罗,但是由于人们对于它贴上的标签不同,所以造成对于他的看法大相径庭,不喜欢他的人整天在网络上评论抨击嘲讽,而崇拜欣赏他的人则会愿意挣钱购买锤子手机的发布会门票。

我无意于评价这两种行为,我再引个例子。

《奇葩说》是近年网络上非常火热的辩论节目,其中辩手陈铭被另外一个辩手马薇薇攻击说是————“站在宇宙中心呼唤爱”,然后贴上了一个大大的标签————“鸡汤男”,自此以后,观众再看到陈铭的时候,首先映入脑海中便是“鸡汤男”三个大字,其实本身而言陈铭非常优秀,为人师表、作风正派、谈吐举止得体,但是在网络中,因为娱乐至上的环境所致,人们更愿意以娱乐的心态来认知一切,于是“鸡汤男”就如陈铭自己所说成了一个撕不了的标签。

我们可以抽象概括一下,标签是对事物行为的某些角度的评价与解释。

到这里,终于可以引出本文的主角注解了。

初学者可以这样理解注解:想像代码具有生命,注解就是对于代码中某些鲜活个体的贴上去的一张标签。简化来讲,注解如同一张标签。

在未开始学习任何注解具体语法而言,你可以把注解看成一张标签。这有助于你快速地理解它的大致作用。如果初学者在学习过程有大脑放空的时候,请不要慌张,对自己说:

注解,标签。注解,标签。

什么是注解?

对于很多初次接触的开发者来说应该都有这个疑问?Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。包含在 java.lang.annotation 包中。

注解的用处:

1、生成文档。这是最常见的,也是java 最早提供的注解。常用的有@param @return 等 2、跟踪代码依赖性,实现替代配置文件功能。比如Dagger 2依赖注入,未来java开发,将大量注解配置,具有很大用处; 3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。

注解的原理:

注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy1。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。

元注解:

java.lang.annotation提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):@Documented –注解是否将包含在JavaDoc中 @Retention –什么时候使用该注解 @Target –注解用于什么地方 @Inherited – 是否允许子类继承该注解

1.)@Retention– 定义该注解的生命周期

●   RetentionPolicy.SOURCE : 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。    ●   RetentionPolicy.CLASS : 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式    ●   RetentionPolicy.RUNTIME : 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

2.)Target – 表示该注解用于什么地方。默认值为任何元素,表示该注解用于什么地方。可用的ElementType参数包括

● ElementType.CONSTRUCTOR:用于描述构造器  ● ElementType.FIELD:成员变量、对象、属性(包括enum实例)  ● ElementType.LOCAL_VARIABLE:用于描述局部变量  ● ElementType.METHOD:用于描述方法  ● ElementType.PACKAGE:用于描述包  ● ElementType.PARAMETER:用于描述参数  ● ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明

3.)@Documented–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。

4.)@Inherited – 定义该注释和子类的关系 @Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

JDK里的注解

JDK 内置注解 先来看几个 Java 内置的注解,让大家热热身。

@Override 演示

class Parent {    public void run() {    }}class Son extends Parent {    /**     * 这个注解是为了检查此方法是否真的是重写父类的方法     * 这时候就不用我们用肉眼去观察到底是不是重写了     */    @Override    public void run() {    }}

@Deprecated 演示 class Parent {

/** * 此注解代表过时了,但是如果可以调用到,当然也可以正常使用 * 但是,此方法有可能在以后的版本升级中会被慢慢的淘汰 * 可以放在类,变量,方法上面都起作用 */@Deprecatedpublic void run() {}}public class JDKAnnotationDemo {    public static void main(String[] args) {        Parent parent = new Parent();        parent.run(); // 在编译器中此方法会显示过时标志    }}

@SuppressWarnings 演示 class Parent {

// 因为定义的 name 没有使用,那么编译器就会有警告,这时候使用此注解可以屏蔽掉警告// 即任意不想看到的编译时期的警告都可以用此注解屏蔽掉,但是不推荐,有警告的代码最好还是处理一下@SuppressWarnings("all")private String name;}

@FunctionalInterface 演示 /**

  • 此注解是 Java8 提出的函数式接口,接口中只允许有一个抽象方法
  • 加上这个注解之后,类中多一个抽象方法或者少一个抽象方法都会报错 */ @FunctionalInterface interface Func { void run(); }

注解处理器实战

注解处理器 注解处理器才是使用注解整个流程中最重要的一步了。所有在代码中出现的注解,它到底起了什么作用,都是在注解处理器中定义好的。概念:注解本身并不会对程序的编译方式产生影响,而是注解处理器起的作用;注解处理器能够通过在运行时使用反射获取在程序代码中的使用的注解信息,从而实现一些额外功能。前提是我们自定义的注解使用的是 RetentionPolicy.RUNTIME 修饰的。这也是我们在开发中使用频率很高的一种方式。

我们先来了解下如何通过在运行时使用反射获取在程序中的使用的注解信息。如下类注解和方法注解。

类注解 Class aClass = ApiController.class; Annotation[] annotations = aClass.getAnnotations();

// 因为定义的 name 没有使用,那么编译器就会有警告,这时候使用此注解可以屏蔽掉警告// 即任意不想看到的编译时期的警告都可以用此注解屏蔽掉,但是不推荐,有警告的代码最好还是处理一下@SuppressWarnings("all")private String name;}

此部分内容可参考: 通过反射获取注解信息

注解处理器实战 接下来我通过在公司中的一个实战改编来演示一下注解处理器的真实使用场景。需求: 网站后台接口只能是年龄大于 18 岁的才能访问,否则不能访问 前置准备: 定义注解(这里使用上文的完整注解),使用注解(这里使用上文中使用注解的例子) 接下来要做的事情: 写一个切面,拦截浏览器访问带注解的接口,取出注解信息,判断年龄来确定是否可以继续访问。

在 dispatcher-servlet.xml 文件中定义 aop 切面

// 因为定义的 name 没有使用,那么编译器就会有警告,这时候使用此注解可以屏蔽掉警告// 即任意不想看到的编译时期的警告都可以用此注解屏蔽掉,但是不推荐,有警告的代码最好还是处理一下@SuppressWarnings("all")private String name;}

切面类处理逻辑即注解处理器代码如

@Component("apiAuthAspect")public class ApiAuthAspect {    public Object auth(ProceedingJoinPoint pjp) throws Throwable {        Method method = ((MethodSignature) pjp.getSignature()).getMethod();        ApiAuthAnnotation apiAuthAnnotation = method.getAnnotation(ApiAuthAnnotation.class);        Integer age = apiAuthAnnotation.age();        if (age > 18) {            return pjp.proceed();        } else {            throw new RuntimeException("你未满18岁,禁止访问");        }    }}

注解的获取方式

类注解

你可以在运行期访问类,方法或者变量的注解信息,下是一个访问类注解的例子:

 Class aClass = TheClass.class;Annotation[] annotations = aClass.getAnnotations();for(Annotation annotation : annotations){    if(annotation instanceof MyAnnotation){        MyAnnotation myAnnotation = (MyAnnotation) annotation;        System.out.println("name: " + myAnnotation.name());        System.out.println("value: " + myAnnotation.value());    }}

你还可以像下面这样指定访问一个类的注解:

Class aClass = TheClass.class;Annotation annotation = aClass.getAnnotation(MyAnnotation.class);if(annotation instanceof MyAnnotation){    MyAnnotation myAnnotation = (MyAnnotation) annotation;    System.out.println("name: " + myAnnotation.name());    System.out.println("value: " + myAnnotation.value());}

方法注解

下面是一个方法注解的例子:

public class TheClass {  @MyAnnotation(name="someName",  value = "Hello World")  public void doSomething(){}}

你可以像这样访问方法注解:

Method method = ... //获取方法对象Annotation[] annotations = method.getDeclaredAnnotations();for(Annotation annotation : annotations){    if(annotation instanceof MyAnnotation){        MyAnnotation myAnnotation = (MyAnnotation) annotation;        System.out.println("name: " + myAnnotation.name());        System.out.println("value: " + myAnnotation.value());    }}

你可以像这样访问指定的方法注解:

Method method = ... //获取方法对象Annotation[] annotations = method.getDeclaredAnnotations();for(Annotation annotation : annotations){    if(annotation instanceof MyAnnotation){        MyAnnotation myAnnotation = (MyAnnotation) annotation;        System.out.println("name: " + myAnnotation.name());        System.out.println("value: " + myAnnotation.value());    }}

参数注解

方法参数也可以添加注解,就像下面这样:

public class TheClass {  public static void doSomethingElse(        @MyAnnotation(name="aName", value="aValue") String parameter){  }}

你可以通过 Method对象来访问方法参数注解:

Method method = ... //获取方法对象Annotation[][] parameterAnnotations = method.getParameterAnnotations();Class[] parameterTypes = method.getParameterTypes();int i=0;for(Annotation[] annotations : parameterAnnotations){  Class parameterType = parameterTypes[i++];  for(Annotation annotation : annotations){    if(annotation instanceof MyAnnotation){        MyAnnotation myAnnotation = (MyAnnotation) annotation;        System.out.println("param: " + parameterType.getName());        System.out.println("name : " + myAnnotation.name());        System.out.println("value: " + myAnnotation.value());    }  }}

需要注意的是 Method.getParameterAnnotations()方法返回一个注解类型的二维数组,每一个方法的参数包含一个注解数组。


Java基础16-Java注解简介和最佳实践(二):https://developer.aliyun.com/article/1535701

目录
打赏
0
5
5
1
93
分享
相关文章
重学Java基础篇—Java类加载顺序深度解析
本文全面解析Java类的生命周期与加载顺序,涵盖从加载到卸载的七个阶段,并深入探讨初始化阶段的执行规则。通过单类、继承体系的实例分析,明确静态与实例初始化的顺序。同时,列举六种触发初始化的场景及特殊场景处理(如接口初始化)。提供类加载完整流程图与记忆口诀,助于理解复杂初始化逻辑。此外,针对空指针异常等问题提出排查方案,并给出最佳实践建议,帮助开发者优化程序设计、定位BUG及理解框架机制。最后扩展讲解类加载器层次与双亲委派机制,为深入研究奠定基础。
29 0
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
112 29
JVM简介—1.Java内存区域
注解的艺术:Java编程的高级定制
注解是Java编程中的高级特性,通过内置注解、自定义注解及注解处理器,可以实现代码的高度定制和扩展。通过理解和掌握注解的使用方法,开发者可以提高代码的可读性、可维护性和开发效率。在实际应用中,注解广泛用于框架开发、代码生成和配置管理等方面,展示了其强大的功能和灵活性。
62 25
重学Java基础篇—ThreadLocal深度解析与最佳实践
ThreadLocal 是一种实现线程隔离的机制,为每个线程创建独立变量副本,适用于数据库连接管理、用户会话信息存储等场景。
37 5
重学Java基础篇—Java对象创建的7种核心方式详解
本文全面解析了Java中对象的创建方式,涵盖基础到高级技术。包括`new关键字`直接实例化、反射机制动态创建、克隆与反序列化复用对象,以及工厂方法和建造者模式等设计模式的应用。同时探讨了Spring IOC容器等框架级创建方式,并对比各类方法的适用场景与优缺点。此外,还深入分析了动态代理、Unsafe类等扩展知识及注意事项。最后总结最佳实践,建议根据业务需求选择合适方式,在灵活性与性能间取得平衡。
47 3
|
8天前
|
重学Java基础篇—Java泛型深度使用指南
本内容系统介绍了Java泛型的核心价值、用法及高级技巧。首先阐述了泛型在**类型安全**与**代码复用**中的平衡作用,解决强制类型转换错误等问题。接着详细讲解了泛型类定义、方法实现、类型参数约束(如边界限定和多重边界)、通配符应用(PECS原则)以及类型擦除的应对策略。此外,还展示了泛型在通用DAO接口、事件总线等实际场景的应用,并总结了命名规范、边界控制等最佳实践。最后探讨了扩展知识,如通过反射获取泛型参数类型。合理运用泛型可大幅提升代码健壮性和可维护性,建议结合IDE工具和单元测试优化使用。
15 1
|
8天前
|
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
20 1
|
20天前
|
Java静态代码块深度剖析:机制、特性与最佳实践
在Java中,静态代码块(或称静态初始化块)是指类中定义的一个或多个`static { ... }`结构。其主要功能在于初始化类级别的数据,例如静态变量的初始化或执行仅需运行一次的初始化逻辑。
35 4
|
22天前
|
课时3:Java简介(Java主要特点)
本文介绍了Java的主要特点及其运行机制。Java结合了编译型和解释型语言的优点,通过Java虚拟机(JVM)实现跨平台移植,简化了不同操作系统间的开发流程。Java的特点包括可移植性、简单易用、支持多线程编程、自动垃圾收集和面向对象编程。随着硬件技术的发展,Java的性能问题已大大改善,成为行业标准之一,广泛应用于各种商用平台开发。
课时2:Java简介(Java发展概述)
课时2:Java简介(Java发展概述) 摘要: 1. Java基础知识:介绍Java作为编程语言及其思想。 2. Java的发展历史:从1991年GREEN项目到1995年正式推出,历经网景公司、HotJava浏览器等关键节点。 3. Java的版本信息:涵盖从JDK 1.0到JDK 1.8的主要版本及特性,如Lambda表达式和模块化支持。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等