框架开发之Java注解的妙用

作者:locality

来源:https://www.jianshu.com/p/b560b30726d4

如果你还不会使用注解,你肯定不好意思对别人说你学过Spring,你学过Mybatis,因为它们用了大量的注解。可见注解在开发领域已经使用的非常广泛了。

注解的好处:

1.能够读懂别人写的代码,特别是框架相关的代码。

2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程更加简洁,代码更加清晰。

3.(重点)刮目相看。
(但是怎么样才能让别人刮目相看呢?会用注解不是目的,最重要的是要使用自定义注解来解决问题。)
举个栗子:
如果面试的时候,你跟老板说你会使用注解,老板觉得你这个人还行;但是如果老板发现你会自定义注解解决问题,老板肯定就会眼前一亮。

注解这一概念是在java1.5版本提出的,说Java提供了一种原程序中的元素关联任何信息和任何元数据的途径的方法。


一、Java中的常见注解

1)JDK注解

JDK注解一共分为三类:

案例:

我们先新建一个接口people,如下:

public interface people {public String name();public int age();public void work();
}

然后再建一个类Child实现类people这个接口,并实现该类的方法:

public class Child implements people {@Overridepublic String name() {return null;}@Overridepublic int age() {return 0;}@Overridepublic void work() {}

看到这里,我们发现这里的所有方法都会加上一个@Override标记,它告诉我们,同时也告诉编译器我们的这些方法肯定覆盖了类people里面的方法的。假如说,我现在把类people里面的某一个方法注释掉:

//public String name();

再看类Child里面的name方法就会报错。这样,以后大家看到@Override的时候就能想到这个方法是覆盖了某个接口的方法的。

然后,我们回过头来看类people里面有一个work的方法。这里我们可以理解为人是要工作的,但是并不是所有的人都在工作,那么怎么办呢?如果说这个接口正在用,我们不能删除这个方法,这个时候我们就可以这样:

@Deprecated
public void work();

@Deprecated标记就表明这个方法已经过时了,在实际中,它又有什么样的应用场景呢?我们在建一个测试类:

public class Test {public void work() {people people=new Child();
!      people.work();}
}

这个时候我们会发现myeclipse会给一个警告,并且在work中间出现一个破折号,意思就是这个方法已经过时了。那么问题来了,虽然这个方法过时了,但是我们就是那么傲娇,一定要用它,怎么办呢?只需要这样:

public class Test {@SuppressWarnings("deprecation")public void work() {people people=new Child();people.work();}
}

这样我们就忽略了这个警告。@SuppressWarnings("deprecation")就表示我们忽略了deprecation这样的一个警告。

2)Java第三方注解




二、注解的分类

1)按照运行机制划分:

【源码注解→编译时注解→运行时注解】

源码注解:只在源码中存在,编译成.class文件就不存在了。

编译时注解:在源码和.class文件中都存在。像前面的@Override、@Deprecated、@SuppressWarnings,他们都属于编译时注解。

运行时注解:在运行阶段还起作用,甚至会影响运行逻辑的注解。像@Autowired自动注入的这样一种注解就属于运行时注解,它会在程序运行的时候把你的成员变量自动的注入进来。

2)按照来源划分:

【来自JDK的注解——来自第三方的注解——自定义注解】

3)元注解:

元注解是给注解进行注解,可以理解为注解的注解就是元注解。


三、自定义注解

我们分四步来解析自定义注解:


自定义注解的语法要求:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {String desc();String author();int age() default 18;
}

首先我们要明确这不是一个接口,它是使用@interface关键字定义的一个注解。

然后我们看下面的几个方法,String desc();虽然它很类似于接口里面的方法,其实它在注解里面只是一个成员变量(成员以无参无异常的方式声明),int age() default 18;(成员变量可以用default指定一个默认值的)。

最后我们要知道:

①.成员类型是受限制的,合法的类型包括基本的数据类型以及String,Class,Annotation,Enumeration等。

②.如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=)。

③.注解类可以没有成员,没有成员的注解称为标识注解。

元注解:

有没有发现上面那段代码有一个没有说呢?没错,它们就是我们所说的元注解:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented

我们先看第一行:@Target是这个注解的作用域,ElementType.METHOD是这个注解的作用域的列表,METHOD是方法声明,除此之外,还有:
CONSTRUCTOR(构造方法声明),FIELD(字段声明),LOCAL VARIABLE(局部变量声明),METHOD(方法声明),PACKAGE(包声明),PARAMETER(参数声明),TYPE(类接口)

第二行:@Retention是它的生命周期,前面不是说注解按照运行机制有一个分类嘛,RUNTIME就是在运行时存在,可以通过反射读取。除此之外,还有:
SOURCE(只在源码显示,编译时丢弃),CLASS(编译时记录到class中,运行时忽略),RUNTIME(运行时存在,可以通过反射读取)

第三行:@Inherited是一个标识性的元注解,它允许子注解继承它。

第四行:@Documented,生成javadoc时会包含注解。

使用自定义注解:

使用注解的语法:

@<注解名>(<成员名1>=<成员值1>,<成员名1>=<成员值1>,...)

案例:

@Description(desc="i am Color",author="boy",age=18)public String Color() {return "red";}

这里的Description是我们刚才在自定义注解语法要求里面定义的注解噢,然后我们可以给它的每一个成员变量赋值,注意数据类型。值得注意的是,因为我们前面定义的作用域是在方法和类接口上,所以这个注解在Color()方法上使用是没问题的。

解析注解

概念:

通过反射获取类 、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。


准备工作:

Description类.png

Child类.png

接下来,我们就开始测试了:

public class ParseAnn {public static void main(String[] args) {try {// 使用类加载器加载类Class c = Class.forName("com.test.Child");// 找到类上面的注解boolean isExist = c.isAnnotationPresent(Description.class);// 上面的这个方法是用这个类来判断这个类是否存在Description这样的一个注解if (isExist) {// 拿到注解实例,解析类上面的注解Description d = (Description) c.getAnnotation(Description.class);System.out.println(d.value());}} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

输出的结果:

i am class annotation

可以看到,我们成功的解析了Child类上面的注解。

接下来,我们继续解析方法上的注解:

//获取所有的方法Method[] ms = c.getMethods();// 遍历所有的方法for (Method m : ms) {boolean isExist1 = m.isAnnotationPresent(Description.class);if (isExist1) {Description d1=m.getAnnotation(Description.class);System.out.println(d1.value());}}

输出的结果:

i am class annotation
i am method annotation

可以看到,我们成功的解析了方法上面的注解。

//另一种解析方法for (Method m : ms) {//拿到方法上的所有的注解Annotation[] as=m.getAnnotations();for (Annotation a : as) {//用二元操作符判断a是否是Description的实例if (a instanceof Description) {Description d=(Description) a;System.out.println(d.value());}}}

也可以得到上面的效果。

此时,如果把Description类里面的元注解改一下,比如:
@Retention(RetentionPolicy.RUNTIME)→@Retention(RetentionPolicy.SOURCE),再运行程序,结果会成怎样呢?如果改成CLASS呢?大家要不要试一试?


【END】

关注下方二维码,订阅更多精彩内容

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

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

相关文章

Win10专业版系统PyCharm专业版使用WSL(ubuntu20.04 LTS)配置Docker解释器配置环境详细教程

提前准备好环境: Win10x64专业版21H2; WSL的ubuntu20.04系统; PyCharm2020专业版; Docker for Windows软件稳定版。 一、Win10系统安装WSL(ubuntu20.04 LTS)子系统 1.按照下图,开启“适用于Linux的Windows子系统”和“虚拟机平台”功能,按照提示重启计算机。 2.在Mic…

移动对meta的定义(转)

以下是meta每个属性详解尤其要注意的是content里多个属性的设置一定要用分号空格来隔开&#xff0c;如果不规范将不会起作用。一、<meta http-equiv"Content-Type" content"text/html; charsetutf-8"> //编码META标签是HTML语言HEAD区的一个辅助性…

Mybatis:颠覆你心中对事务的理解

作者&#xff1a;祖大俊来源&#xff1a;my.oschina.net/zudajun/blog/6667641.说到数据库事务&#xff0c;人们脑海里自然不自然的就会浮现出事务的四大特性、四大隔离级别、七大传播特性。四大还好说&#xff0c;问题是七大传播特性是哪儿来的&#xff1f;是Spring在当前线程…

msbuild构建时用SVN修改版本号代替AssemblyVersion的Revision版本号

持续构建需要标识出每次构建的版本&#xff0c;而每次构建的时候人工去修改版本是不现实的。靠程序去添加版本号&#xff0c;有3种可选&#xff1a;1) 顺序流水号&#xff1b;2) 时间戳&#xff1b;3) SVN检出代码的修订版本号1) 顺序流水号。需要每次构建将上次记录的流水号1&…

Docker镜像和容器常用命令

一、.Docker帮助命令 1.显示docker的版本信息 docker version 2.显示docker的系统信息&#xff0c;包括镜像和容器的数量 docker info3.docker帮助命令 docker 命令 --help二、Docker镜像命令 1.查看所有本地的主机上的镜像 docker images实例测试&#xff1a; 2.搜索镜像…

如何学会阅读源码?

作者 | youzhibing链接 | cnblogs.com/youzhibing/p/9553752.html1.读源码的经历刚参加工作那会&#xff0c;没想过去读源码&#xff0c;更没想过去改框架的源码&#xff1b;总想着别人的框架应该是完美的、万能的&#xff0c;应该不需要改&#xff1b;另外即使我改了源码&…

求模和求余

一直以为求模和求余是一回事&#xff0c;发现这两者是不同的。以下为网上转载的资料&#xff1a; 通常情况下取模运算(mod)和求余(rem)运算被混为一谈&#xff0c;因为在大多数的编程语言里&#xff0c;都用%符号表示取模或者求余运算。在这里要提醒大家要十分注意当前环境下%运…

利用Dockefile将Python的py文件项目代码打包为Docker镜像

1.创建python项目 【备注&#xff1a;一定要将项目python环境依赖存至本项目下&#xff0c;默认依赖本机python环境(会造成依赖包过多)】 2.创建main.py文件&#xff0c;完成程序代码 主要功能就是获取"https://www.hao123.com/"网址页面源代码&#xff0c;并存储…

面试官:如何实现幂等性校验?

作者 | wangzaiplus来源 | https://www.jianshu.com/p/6189275403ed一、概念幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次比如:订单接口, 不能多次创建订单支付接口, 重复支付同一笔订单只能扣一次钱支付宝回调接口, 可能会多次回调, 必须处理…

阿里为什么禁用Executors创建线程池?

作者 | 何甜甜在吗来源 | http://rrd.me/eUh6V看阿里巴巴开发手册并发编程这块有一条&#xff1a;线程池不允许使用Executors去创建&#xff0c;而是通过ThreadPoolExecutor的方式&#xff0c;通过源码分析禁用的原因。写在前面首先感谢大家在盖楼的间隙阅读本篇文章&#xff0…

Debian11镜像更新为阿里巴巴开源镜像站镜像,切换root用户,解决用户名不在sudoers文件中此事将被报告,Debian11 文件夹对话框、火狐浏览器、命令终端等没有最大化和最小化

选择Debian作为编程开发最佳Linux的理由&#xff1a; Debian是面向程序员的最古老&#xff0c;最出色的Linux发行版之一。Debian提供了具有.deb软件包管理兼容性的超稳定发行版。Debian为程序员提供了许多最新功能。因此&#xff0c;它具有一个特殊的编程空间。Debian是开发人员…

SCCM2012R2部署之四:配置客户端发现

前面3个章节我们简单的&#xff0c;介绍了安装配置和相关的组件。接下来我们需要给大家介绍的是如何配置客户端发现&#xff0c;让SCCM能真正管控到AD中的所有终端&#xff0c;来提供IT运维的效率。首先我们打开SCCM控制台&#xff0c;如图4-1&#xff0c;这就是我们安装完SCCM…

Debian11安装VLC Media Player视频播放器

在终端内执行下面命令&#xff1a; sudo apt install vlc

面试官:使用SpringBoot如何开发邮件发送系统?

作者 | yizhiwazi来源 | www.jianshu.com/p/5eb000544dd7SpringBoot 开发邮件发送系统还是比较方便的&#xff0c;在开始之前我们先来了解一下和发送邮件有关的基础知识。基础知识什么是SMTP&#xff1f;SMTP全称为Simple Mail Transfer Protocol&#xff08;简单邮件传输协议&…

Python计算校验文件的MD5、SHA1、SHA256和CRC32

# -*- coding: utf-8 -*- import os from hashlib import md5, sha1, sha256 from zlib import crc32strFilePath os.path.join(os.getcwd() "\\" "“捷创源科技”公众号.jpg")def getMd5(strFilePath): # 计算md5mdfive md5()with open(strFilePath,…

拼多多面试|如何用 Redis 统计独立用户访问量?

作者 | 沙茶敏碎碎念来源 | www.cnblogs.com/xiaoMzjm/p/5223799.html众所周至&#xff0c;拼多多的待遇也是高的可怕&#xff0c;在挖人方面也是不遗余力&#xff0c;对于一些工作3年的开发&#xff0c;稍微优秀一点的&#xff0c;都给到30K的Offer当然&#xff0c;拼多多加班…

Linux 指令篇:文档编辑--col

功能说明&#xff1a;过滤控制字符。语  法&#xff1a;col [-bfx][-l<缓冲区列数>]补充说明&#xff1a;在许多UNIX说明文件里&#xff0c;都有RLF控制字符。当我们运用shell特殊字符">"和">>"&#xff0c;把说明文件的内容输出成纯文本…

被一个熟悉的面试题问懵了:StringBuilder 为什么线程不安全?

作者 | 千山qianshan 来源 | juejin.im/post/5d6228046fb9a06add4e37fe前言周五去面试又被面试的一个问题问哑巴了面试官&#xff1a;StringBuilder和StringBuffer的区别在哪&#xff1f; 我&#xff1a;StringBuilder不是线程安全的&#xff0c;StringBuffer是线程安全的 面试…

Python计算校验文件的MD5、SHA1、SHA256和CRC32,获取文件创建日期、修改日期和文件大小

main.py # -*- coding: utf-8 -*- import os from hashlib import md5, sha1, sha256 from zlib import crc32 import time from math import ceilclass Hash:def __init__(self, strFilePath):self

CC++中的qsort库函数

qsort() 参考&#xff1a;http://www.slyar.com/blog/stdlib-qsort.html qsort包含在<stdlib.h>头文件中&#xff0c;此函数根据你给的比较条件进行快速排序&#xff0c;通过指针移动实现排序。排序之后的结果仍然放在原数组中。使用qsort函数必须自己写一个比较函数。 …