*好记性不如烂笔头,注解入门笔记
————————————————————————————————————————————————————
一、引言
注解是什么?百度百科:“用文字来解释字句”。如果翻译成编程语言来说,那么注解就是用代码解释代码,只是对代码进行一个简单的描述。最常见的@Override就是Java中自带的一种注解,常常出现在实现接口的类中。注解的使用,不仅可以有利于代码的阅读和提示编译器及时报错,还广泛的应用于一些框架中。
二、注解
在Java中,注解可分为三种类型:自带注解,元注解,自定义注解。
1.自带注解
Java的自带注解只有三个,分别是@Override、@Deprecated、@SuppressWarnings
@Override:重写、覆盖。常出现在实现接口的类中,标注于重写方法之前,用于标记该方法是一个重写方法。一般建议在重写方法之前表注@Override,这不仅有利于代码阅读,而且编写代码时也能够及时报错。
@Deprecated:废弃,不宜用。一般标注于字段或方法之前,用于标记该字段或方法已过时,新版本中已经有其他的字段或方法来代替。
@SuppressWarnings:压制、屏蔽警告。一般标注于会被编译器警告的代码之前,可以屏蔽警告,例如强制类型转换时可以加注解@SuppressWarning("unchechked"),屏蔽编译器的类型转换警告。
2.元注解
(1)什么是元注解?
元注解是注解的注解,用来对注解的一个简单描述。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
打开@Override的源码,我们会发现,除了@Override的声明部分,还多出了2个注解@Target、@Retention,这两个注解就是所谓的元注解,常标注于自定义注解之前,对该注解的一个简单描述。
(2)元注解类型
元注解一共有四个,它们分别是@Target、@Retention、@Inherited、@Documented。
类型 | 说明 |
ElementType.ANNOTATION_TYPE | 该注解可以作用于注解 |
ElementType.PACKAGE | 该注解可以作用于包 |
ElementType.TYPE | 该注解可以作用于类、接口等 |
ElementType.FILED | 该注解可以作用于字段 |
ElementType.CONSTRUCTOR | 该注解可以作用于构造方法 |
ElementType.METHOD | 该注解可以作用于方法 |
ElementType.PARAMETER | 该注解可以作用于方法参数 |
ElementType.LOCAL_VARIABLE | 该注解可以作于局部变量 |
@Retention:(必选)表示该注解的保留策略
类型 | 说明 |
RetentionPolicy.SOURCE | 该注解只在源码时作用 |
RetentionPolicy.CLASS | 该注解在源码和编译时作用 |
RetentionPolicy.RUNTIME | 该注解在源码、编译和运行时都起作用 |
@Inherited:(可选)表示该注解可以被继承,即某个类中使用了该注解,其子类也将该注解继承下来。
@Documented:(可选)表示生成javadoc时也包括该注解在内。
3.自定义注解
自定义注解就是开发者可根据自身的需求来自己声明一个注解。
(1)声明语法:
@Target(ElementType.……)
@Retention(RetentionPolicy.……)
权限修饰符 @interface 注解名称(){
//注解体
}
(2)查看Spring框架的一些jar包,可以找到一些注解相关代码参考,例如:
//Spring框架注解之Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component{
String value() default "";
}
//Spring框架注解之Service
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service{
String value() default "";
}
//Spring框架注解之Bean
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean{
String[] name() default {};
Autowire autowire() default
String initMethod() default "";
String destroyMethod() default "(inferred)";
}
与@Override不同,这些注解里面添加了属性的定义。
属性声明格式:属性类型 属性名() default 默认值;
属性的类型:基本数据类型(byte、short、int、long、float、double、boolean、char)、Class、String、enum、Annotation、以及上述类型的数组形式。
属性名():根据个人需求声明属性名。当属性只有一个时侯,一般属性名都写成value()。
default:设置属性默认值。
默认值:默认值数据类型应当与属性类型保持一致。
三、AnnotaionElement接口与Annotaion类
1.AnnotaionElement接口
所有的反射类型,如Class、Field、Constructor、Method都已经实现了接口AnnotaionElement。
返回值类型 | 方法 | 说明 |
<T extends Annotation> T | getAnnotaion(Class<T> annotationClass) | 通过指定注释的Class对象获取在该元素上的注释 |
Annotation[] | getAnnotations() | 获取该元素的所有注释 |
boolean | isAnnotaionPresent(Class<? extend Annotaion> annotationClass) | 判断是否存在指定类型注解 |
2.Annotation类
通过getAnnotaion()方法获得的Annotation类型对象可以通过对象.注解属性名()的方式获取注解对应的属性值。
四、Anntation类的简单使用
在实际应用中,注解大多与反射结合使用。一般情况下,注解的使用是不影响代码的正常执行,它只是对代码做了个简单的描述。但是通过注解+反射,就能够在编译时或者是运行时对代码执行产生影响。这里尝试设计了一个运行时的注解,并通过反射获取注解属性参与到代码的执行当中。
由于类的设计较为简单,就略去UML图了。
定义一个保留策略是运行时的注解@Biological
@Target(ElementType.TYPE)//ElementType.Type表示注解作用于类、接口、枚举
@Retention(RetentionPolicy.RUNTIME)//注解保留至运行时
public @interface Biological {
//Biological属性:种类
String kind() default "";
//Biological属性:手
int hands() default 0;
//Biological属性:腿
int legs() default 0;
}
声明Biology接口及其实现类Human和类Fish
interface Biology{}
@Biological(kind="人类",hands=2,legs=2)
class Human implements Biology{}
@Biological(kind="鱼类",hands=0,legs=0)
class Fish implements Biology{}
编写测试类
/*需求:编写一个测试类,测试类中维护一个ArrayList的集合,
然后随机的将Human类和Fish类的实例放置入ArrayList集合中,
再通过注解判断这个未知实例是哪种类型,并将结果返回控制台。*/
public class AnnotationTest {
ArrayList<Biology> ls=new ArrayList<Biology>();
/*这里通过2个不同线程来争抢时间片满足随机性。
* 2个线程都执行向ArrayList集合插入Human实例和Fish实例的代码*/
Runnable r0=new Runnable(){
@Override
public void run() {
for(int i=0;i<5;i++){
addObject(new Human());
//线程让步:每插入一次实例,便放弃时间片,进入就绪状态,重新争抢时间片
Thread.yield();
}
}
};
Runnable r1=new Runnable(){
@Override
public void run() {
for(int i=0;i<5;i++){
addObject(new Fish());
//线程让步:每插入一次实例,便放弃时间片,进入就绪状态,重新争抢时间片
Thread.yield();
}
}
};
//同步方法,解决ArrayList的临界资源问题,防止漏插实例对象
synchronized public void addObject(Biology biology){
this.ls.add(biology);
}
public static void main(String[] args) {
AnnotationTest test=new AnnotationTest();
Thread t0=new Thread(test.r0);
Thread t1=new Thread(test.r1);
//启动线程
t0.start();
t1.start();
try {
/*合并线程。防止主线程争抢到时间片开始正常执行时,新建的线程没有完成相应的插入操作。
*过早执行主线程会导致主线程因为空指针异常而突然死亡 */
t0.join();
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<test.ls.size();i++){
/*由于Class对象已经实现了Annotation接口,因此可以通过Biological.class为参数,
* 从getAnnotation中获取Annotation对象,并通过.属性名()的方式获取其值*/
String str=test.ls.get(i).getClass().getAnnotation(Biological.class).kind();
System.out.println("第"+(i+1)+"个是:"+str);
}
}
}
运行结果(运行结果不唯一,仅供参考):
第1个是:鱼类
第2个是:人类
第3个是:人类
第4个是:鱼类
第5个是:人类
第6个是:鱼类
第7个是:人类
第8个是:鱼类
第9个是:人类
第10个是:鱼类
注:这个需求之所以通过线程来实现随机性是因为很久没有温习过线程的知识点了。还有其他的方式实现随机插入实例,就不一一写了。
五、结论
1.注解只提供对代码的描述,不影响代码的正常执行。影响代码执行的是通过反射获取注解中的属性。
2.Java自带注解:@Override、@Deprecated、@SupperessWarning;Java元注解:@Target、@Retention、@Inherited、@Documented。
3.自定义注解以及自定义注解属性的方法。
4.注解+反射的简单应用