目录
1.什么是注解
-
jdk5提供了一个新的应用 Annotation, 注解, 注释
-
与之前所学的注释的区别
-
之前的注释:是给程序员看 ,让程序员知道程序(代码)有什么用,实现了什么功能
-
今天的注解:是给编辑器或jvm看的。在编译和运行时提供一些信息,按照信息完成后续的工作
我们在开发中经常使用注解作为配置信息的载体。类似于xml配置文件的作用。
-
2.注解的分类
-
普通注解,在编程中经常用来作为配置的注解,包括jdk自带的注解和自定义注解
-
@Override 告诉编译,当前方法按照重写的规则检查。
-
@Deprecated 告诉编译器,当前说明的内容是过时的,不建议使用的。但可以继续使用
-
@SuppressWarnings 告诉编译,当前说明的内容相关的警告可以忽略
-
-
元注解,用来说明注解的注解 。 未来还有一个概念:元数据
用户信息:dmy , 18 , 男, 30000
元数据:姓名,字符串类型,最长10个字符 , 性别,年龄,工资
元注解有4个
@Target
用来说明当前注解都可以作用在哪些内容上
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
public @interface SuppressWarnings {}
@Retention
用来说明当前注解存活范围,有3个范围, 只能3选1
RetentionPolicy.SOURCE; //表示在原码到字节码编译过程中有效。编译后消失
RetentionPolicy.CLASS; //表示在类加载过程中有效,加载完消失。
RetentionPolicy.RUNTIME;//表示在jvm运行时都有效。
-
@Documented
用来说明当前注解在生成api文档时,会一同出现在文档中。 -
@Inherited
用来说明当前注解在类继承的过程中,可以一同被继承。
3.注解语法结构
注解是一个特殊的类,使用@interface
定义注解
public @interface A{}
使用元注解,在普通注解头上来说明注解特性。至少要使用@Retention 和 @Target
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface A {}
注解里只能写一种内容 (可以称为属性 也 可以称为方法)
public @interface A{
String i();
}
该内容类型只能是以下几种
-
String
-
8种基本类型
-
Class
-
其他注解类型 , 当注解作为类型时,不需要加前面的@
-
枚举类型
-
上述所有类型的数组类型
可以使用default关键字,为注解中的属性设置默认值
public @interface A{
String name() default "dmy" ;
String[] names() default {"dmy","zs","ls"};
}
4.注解的使用
使用注解,提供配置信息
-
根据Target元注解的指定,可以在对应的类的内容上使用注解
-
如果注解中有属性,可以在使用注解时,类似于传参的方式为属性赋值
-
属性赋值时,需要指定属性名和其对应的属性值
-
如果属性是数组类型,多个属性值要使用{}包含
@A(name="zhangsan",names={"zs","ls","ww"})
class B{}
特殊情况说明
如果注解不需要传递属性值 (没有属性,都有默认值),后面的括号可有可无
@A()
@A
class B{}
如果注解中只有一个属性需要赋值,且这个属性名叫value。赋值时可以只写值,不写名
@interface A{
String value();
}
@A(value="zs")
@A("zs")
class B{}
如果注解属性是数组类型,且传参赋值时只赋予一个值,可以省略{}
@A(name="zs",names={"dmy"})
@A(name="zs",names="dmy")
class B{}
使用注解,获得配置信息
-
注解在使用时,也是类的一部分
-
可以通过反射获得注解,进而获得注解中配置的信息
-
反射中常用的对象(Class,Field , Method , Constructor)都提供了获得注解对象的方法
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface A {
String value() ;
}
public class Test3 {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<B> c = B.class;
A a = c.getAnnotation(A.class);
System.out.println(a.value());
Field i = c.getDeclaredField("i");
Annotation[] annotations = i.getAnnotations();
for(Annotation annotation : annotations){
Method value = annotation.getClass().getMethod("value");
Object v = value.invoke(annotation);
System.out.println(v);
}
}
}
@A("dmy")
class B{
@A(value="dongmingyu",i="zs")
@SuppressWarnings("all")
int i ;
}
补充:所有的注解,都默认继承Annotation父注解类型
5.特殊情况的注解使用
注解的重复使用
-
默认情况下,无法使用两个相同的注解,修饰同一个类信息
-
以前解决方案是定义一个新注解,新注解中有一个数组属性,可以包含多个所需要注解
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AS {
A[] value();
}
@AS({
@A("zs") ,
@A("ls") ,
@A("ww")
})
class B{}
现在可以在注解上使用另一个注解@Repeatable(AS.class)
指定当前注解重复出现的时候,会自动的将重复的注解组成指定的那个@AS注解
@AS还需要正常定义
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(AS.class)
public @interface A {}
@Target({ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AS {
A[] value();
}
@A("zs")
@A("ls")
@A("ww")
class C{}
函数式接口
-
这是lambda表达式相关的一个概念
-
lambda表达式针对的必须是接口,而且只能有一个抽象方法。这样的接口就称为函数式接口
-
函数式接口,可以使用
@FunctionalInterface
声明。 在编译就会按照函数式接口的特点进行检测
@FunctionalInterface
interface A{
void t1();
}
-
使用注解声明,必须是函数式接口
-
函数式接口,不一定使用注解声明