框架开发之Java注解的妙用和详解
注解的好处:
1.能够读懂别人写的代码,特别是框架相关的代码。
2.本来可能需要很多配置文件,需要很多逻辑才能实现的内容,就可以使用一个或者多个注解来替代,这样就使得编程更加简洁,代码更加清晰。
3.(重点)刮目相看。
(但是怎么样才能让别人刮目相看呢?会用注解不是目的,最重要的是要使用自定义注解来解决问题。)
举个栗子:
如果面试的时候,你跟老板说你会使用注解,老板觉得你这个人还行;但是如果老板发现你会自定义注解解决问题,老板肯定就会眼前一亮。
注解这一概念是在java1.5版本提出的,说Java提供了一种原程序中的元素关联任何信息和任何元数据的途径的方法。
一、Java中的常见注解
1)JDK注解
JDK注解一共分为三类:
案例:
我们先新建一个接口people,如下:
1 2 3 4 5 | public interface people { public String name(); public int age(); public void work(); } |
然后再建一个类Child实现类people这个接口,并实现该类的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Child implements people { @Override public String name() { return null ; } @Override public int age() { return 0 ; } @Override public void work() { } |
看到这里,我们发现这里的所有方法都会加上一个@Override标记,它告诉我们,同时也告诉编译器我们的这些方法肯定覆盖了类people里面的方法的。假如说,我现在把类people里面的某一个方法注释掉:
1 | //public String name(); |
再看类Child里面的name方法就会报错。这样,以后大家看到@Override的时候就能想到这个方法是覆盖了某个接口的方法的。
然后,我们回过头来看类people里面有一个work的方法。这里我们可以理解为人是要工作的,但是并不是所有的人都在工作,那么怎么办呢?如果说这个接口正在用,我们不能删除这个方法,这个时候我们就可以这样:
1 2 | @Deprecated public void work(); |
@Deprecated标记就表明这个方法已经过时了,在实际中,它又有什么样的应用场景呢?我们在建一个测试类:
1 2 3 4 5 6 | public class Test { public void work() { people people= new Child(); ! people.work(); } } |
这个时候我们会发现myeclipse会给一个警告,并且在work中间出现一个破折号,意思就是这个方法已经过时了。那么问题来了,虽然这个方法过时了,但是我们就是那么傲娇,一定要用它,怎么办呢?只需要这样:
1 2 3 4 5 6 7 | 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)元注解:
元注解是给注解进行注解,可以理解为注解的注解就是元注解。
三、自定义注解
我们分四步来解析自定义注解:
自定义注解的语法要求:
1 2 3 4 5 6 7 8 9 | @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(),在使用时可以忽略成员名和赋值号(=)。
③.注解类可以没有成员,没有成员的注解称为标识注解。
元注解:
有没有发现上面那段代码有一个没有说呢?没错,它们就是我们所说的元注解:
1 2 3 4 | @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>,…)
案例:
1 2 3 4 | @Description (desc= "i am Color" ,author= "boy" ,age= 18 ) public String Color() { return "red" ; } |
这里的Description是我们刚才在自定义注解语法要求里面定义的注解噢,然后我们可以给它的每一个成员变量赋值,注意数据类型。值得注意的是,因为我们前面定义的作用域是在方法和类接口上,所以这个注解在Color()方法上使用是没问题的。
解析注解
概念:
通过反射获取类 、函数或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。
准备工作:
接下来,我们就开始测试了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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类上面的注解。
接下来,我们继续解析方法上的注解:
1 2 3 4 5 6 7 8 9 10 | //获取所有的方法 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
可以看到,我们成功的解析了方法上面的注解。
1 2 3 4 5 6 7 8 9 10 11 12 | //另一种解析方法 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()); } } } |
也可以得到上面的效果。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
【聚能聊】一行代码蒸发64亿,智能合约是否安全
4月22日中午,有黑客利用以太坊ERC-20智能合约中的BatchOverFlow漏洞攻击BEC智能合约,成功将两个地址中转出天量级的BEC代币,导致市场上海量BEC被抛售,此事导致当日BEC的价值几乎为零,64亿人民币瞬间蒸发。智能合约,简单来说,就是利用区块链技术,大家共同约定,当一定条件满足时,可以被自动执行的合约。据了解,这是基于ERC020标准的代币首次出现智能合约漏洞,而导致此漏洞出现的原因,仅仅是程序员自身不严谨,调用函数时写错一行代码所致。一位从事区块链接技术的创业者告诉记者,一个程序员只需要花5分钟,从网上抄一些智能合约的代码,稍加修改,就可以发行一个代币,再写个白皮书,找几个知名顾问,就可以在数字代币交易所里面发行几千万甚至上亿的项目。 关于此事我们提出了以下四个问题,来看看网友们对此事如何看待? 网友普片的观点是: 1:大家几乎都没有购买过数字代币,并且差不多一致性的认为数字代币不安全,没有国家发行的有保障。2:大家肯定都会担心自己持有的数字代币是否也会被黑客攻击,觉得互联网上的东西不会绝对安全。3:几乎都统一的认为是缺乏监管或者监管机构不认可。4:都觉得影响不大...
- 下一篇
Java锁优化
Java锁优化 应用程序在并发环境下会产生很多问题,通常情况下,我们可以通过加锁来解决多线程对临界资源的访问问题。但是加锁往往会成为系统的瓶颈,因为加锁和释放锁会涉及到与操作系统的交互,会有很大的性能问题。那么这个时候基于锁的优化手段就显得很重要了。 一般情况下,可以从两个角度进行锁优化:对单个锁算法的优化和对锁粒度的细分。 1. 单个锁的优化 自旋锁: 非自旋锁在未获取锁的情况会被阻塞,之后再唤醒尝试获得锁。而JDK的阻塞和唤醒是基于操作系统实现的,会有系统资源的开销。自旋锁就是线程不停地循环尝试获得锁,而不会将自己阻塞,这样不会浪费系统的资源开销,但是会浪费CPU的资源。所有现在的JDK大部分都是先自旋等待,如果自旋等待一段时间之后还没有获取到锁,就会将当前线程阻塞。 锁消除: 当JVM分析代码时发现某个方法只被单个线程安全访问,而且这个方法是同步方法,那么JVM就会去掉这个方法的锁。 单个锁优化的瓶颈: 对单个锁优化的效果就像提高单个CPU的处理能力一样,最终会由于各个方面的限制而达到一个平衡点,到达这个点之后优化单个锁的对高并发下面锁的优化效果越来越低。所以将一个锁...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Hadoop3单机部署,实现最简伪集群
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果