要了解Java注解要先知道Java的反射,反射是运行时获取类的成员,注解也是类的成员,以此达到动态编码的效
果,多用在框架,或者使用框架时候添加注解让框架调用。
注解定义:注解(Annotation),也叫元数据。Java提供了一种原程序中的元素关联任何信息和任何元数据的途
径和方法,一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可
以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译
时检查。注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注
解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实
现对这些元数据(用来描述数据的数据)的访问。另外,你可以在编译时选择代码里的注解是否只存在于源代码级,
或者它也能在class文件、或者运行时中出现(SOURCE/CLASS/RUNTIME)。
如果要对于注解的作用进行分类,还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类:
编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
1、能够读懂别人写的代码,特别是框架相关的代码,增加代码的阅读性;
2、让编程更加简洁,代码更加清晰,理清自己的思路;
3、生成API文档;
它的作用是对覆盖超类中方法的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发
出错误警告。下面的例子是实现类Child实现了接口Person的方法 使用了@Override。
package com.demo.annotation;
/**
* 测试@Override注解
* @author Administrator
* @date 2016年12月9日
*/
public interface Person {
public String name();
public int age();
public void sing();
}
class Child implements Person {
@Override
public String name() {
// TODO Auto-generated method stub
return null;
}
@Override
public int age() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void sing() {
// TODO Auto-generated method stub
}
}
它的作用是对不应该再使用的方法添加注解,当编程人员使用这些方法时,将会在编译时显示提示信息,它与
javadoc里的@deprecated标记有相同的功能,准确的说,它还不如javadoc @deprecated,因为它不支持参数,
使用@Deprecated的示例代码示例如下:
package com.demo.annotation;
/**
* 测试@Deprecated注解
* @author Administrator
* @date 2016年12月9日
*/
public interface Person {
public String name();
public int age();
@Deprecated
public void sing();
}
class Child implements Person {
@Override
public String name() {
// TODO Auto-generated method stub
return null;
}
@Override
public int age() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void sing() {
// TODO Auto-generated method stub
}
}
class Test {
@SuppressWarnings("deprecation")
public void sing(){
Person person = new Child();
person.sing();//sing()方法已经过时
}
}
Person接口使用了@Deprecated修饰了一个已经过时的方法,此时强行调用超类继承来的方法就会有个代码过
时的横线,并且报一个代码过时的警告,但不会影响正常使用。如果要去掉警告可以使用
@suppressWarings("deprecation") 来忽略这个警告。
其参数有:
deprecation:使用了过时的类或方法时的警告
unchecked:执行了未检查的转换时的警告
fallthrough:当 switch 程序块直接通往下一种情况而没有 break 时的警告
path:在类路径、源文件路径等中有不存在的路径时的警告
serial:当在可序列化的类上缺少serialVersionUID 定义时的警告
finally :任何 finally 子句不能正常完成时的警告
all:关于以上所有情况的警告
这里的演示在上面已经出现过。
在这里只是简单的演示一下,在学习框架的过程中再详细说明。
按照过去的方式就是写一个配置文件:
使用注解方式不再使用配置文件:
源码注解:注解只在源码中存在,编译成.class文件就不存在了;
编译时注解:注解在源码和.class文件中都存在,比如@SuppressWarnings、@Override和@Deprecated只在
编译时刻起作用;
运行时注解:源码、编译后以及运行时都存在的注解,在运行阶段还起作用,甚至会影响运行逻辑的注解,比如
spring框架的@Autowired注解。
来自JDK的注解:@SuppressWarnings、@Override和@Deprecated
来自第三方的注解:比如spring框架的@Autowired注解
自定义注解:我们自己定义的注解。
注解的注解:自定义注解中比较常见。
先来看一个模板:
1)使用@Interface关键字定义注解;
2)成员以无参无异常方式声明;
3)可以用default为成员指定一个默认值;
4)成员类型是受限的,合法的类型包括原始类型及String、Class、Annotation、Enumeration;
5)如果注解只有一个成员,则成员必须取名为value(),在使用时可以忽略成员名和赋值号(=);
6)注解类可以没有成员,没有成员的注解称为标识注解;
CONSTRUCTOR:构造方法声明
FIELD:字段声明
LOACL_VARIABLE:局部变量声明
METHOD:方法声明
PACKAGE:包声明
PARAMETER:参数声明
TYPE:类或者接口声明
SOURCE:只在源码显示,编译时会丢弃
CLASS:编译时会记录到class中,运行时忽略
RUNTIME:运行时存在,可以通过反射读取
允许子类继承
生成javadoc时会包含注解
使用注解的语法:@<注解名>(<成员名1>=<成员值1>,<成员名2>=<成员值2>,...)
一个简单的例子:
定义@Description注解:
package com.demo.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Description {
String desc();
String author();
int age() default 18;
}
使用注解:
package com.demo.annotation;
@Description(desc="I am class annotation",author="Mooc boy",age=18)
public class AnnotationTest {
@Description(desc="I am method annotation",author="Mooc boy",age=18)
public String eyeColor(){
return "red";
}
}
@Description注解在eyeColor()方法上使用,可以在类或者接口上使用
概念:通过反射获取类,函数或成员上的运行时注解信息,从而实现动态控制控制程序运行的逻辑。
对于@RetentionRetention表示作用范围,我们知道:SOURCE表示只在源码显示,编译时会丢弃;
CLASS表示编译时会记录到class中,运行时忽略;RUNTIME表示运行时存在,可以通过反射读取。前两个都不会显
示结果,只有最后一个会显示结果。
一个例子:
package com.demo.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 解析注解
* @author Administrator
* @date 2016年12月9日
*/
public class Demo {
public static void main(String[] args) {
//1.使用类加载器加载类
try {
Class< ?> clazz = Class.forName("com.demo.annotation.AnnotationTest");
//2.找到类上面的注解
boolean flag1 = clazz.isAnnotationPresent(Description.class);
if(flag1){
//3.拿到注解实例
Description description1 = (Description)clazz.getAnnotation(Description.class);
System.out.println(description1.desc());
System.out.println(description1.author());
System.out.println(description1.age());
}
//4.解析找到方法上的注解
Method[] methods = clazz.getMethods();
for (Method method : methods) {
boolean flag2 = method.isAnnotationPresent(Description.class);
if(flag2){
//5.拿到注解实例
Description description2 = (Description)method.getAnnotation(Description.class);
System.out.println(description2.desc());
System.out.println(description2.author());
System.out.println(description2.age());
}
}
//另外一种解析方法
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if(annotation instanceof Description){
Description description3 = (Description)annotation;
System.out.println(description3.desc());
System.out.println(description3.author());
System.out.println(description3.age());
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
关于@Inherited元注解
父子类继承注解这块分两种情况,一个是注解定义了@Inherited,一个是没定义。在每种情况中又分类上的注
解,子类实现父类抽象方法,继承了父类方法,覆盖了父类方法这四种情况,具体继承规则如下:
编写自定义注解时未写@Inherited的运行结果:
子类的类上能否继承到父类的类上的注解? 否
子类方法,实现了父类上的抽象方法,这个方法能否继承到注解? 否
子类方法,继承了父类上的方法,这个方法能否继承到注解? 能
子类方法,覆盖了父类上的方法,这个方法能否继承到注解? 否
编写自定义注解时写了@Inherited的运行结果:
子类的类上能否继承到父类的类上的注解? 能
子类方法,实现了父类上的抽象方法,这个方法能否继承到注解? 否
子类方法,继承了父类上的方法,这个方法能否继承到注解? 能
子类方法,覆盖了父类上的方法,这个方法能否继承到注解? 否
仅供参考!