A.java
public class A {
void show(){
}
}
B.java
@SuppressWarnings(value = {"unused","rawtypes","unchecked"})
public class B extends A{
// unused
int age = 18;
// rawtypes
List list = new ArrayList<>();
@Override
public void show(){
// unchecked
list.add("hello");
}
@Deprecated
public void print(){
}
}
从Java7开始,额外增加了3个注解:
@SafeVarargs - Java7开始支持,忽略任何使用参数为泛型遍历的方法或构造函数调用产生的警告
public class D {
public static void main(String[] args) {
List<String> list1 = Arrays.asList("one","two");
List<String> list2 = Arrays.asList("three","four");
show(list1,list2);
}
@SafeVarargs // 其实并不安全!
static void show(List<String>...stringLists){
Object[] array = stringLists;
List<Integer> tmList = Arrays.asList(42,56);
// tmList是一个List对象(类型已经擦除),赋值给Object类型
// 的对象是允许的(向上塑性),能够编译通过
array[0] = tmList;
String s = stringLists[0].get(0); // 运行时抛出ClassCastException!
}
}
@Functionallnterface - java8开始支持,标识一个匿名函数或函数式式接口。
@FunctionalInterface
public interface C {
void pf();
}
内置注解描述
1、@Deprecated -- @Deprecated 所标注内容,不再被建议使用。
2、@Override -- @Override 只能标注方法,表示该方法覆盖父类中的方法。
3、@SuppressWarnings -- @SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。
4、@FunctionalInterface
5、@SafeVarargs
元注解
我们自定义注解类时,使用的注解,声明注解类时使用的注解。
1、@Documented -- @Documented 所标注内容,恶意出现在javadoc中。
2、@Inherited -- @Inherited 只能被用来标注“Annotation类型”,它所标注的
Annotation具有继承性。
3、@Retention -- @Retention 只能被用来标注“Annatation类型”,而且它
被用来指定Annotation的RetentionPolicy属性。
4、@Target -- @Target 只能被用来标注“Annotation类型”,而且它被用
来指定Annotation的ElementType属性。
元注解:注解的注解,用来定义注解类时使用的注解。
1、@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以
通过反射访问。
2、@Documented - 标记这些注解是否包含再用户文档中出现(javadoc)。
3、@Target - 标记这个注解应该是哪种Java成员。
4、@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)。
5、@Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
Documented
该注解是一个标注注解,主要是为了进行javadoc生成代码的时候,显示注解内容。
注解的定义:
1、@Documented
2、@Retention(RetentionPolicy.RUNTIME)
3、@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
案例
public @interface E {
/*
* test测试方法
* */
int test();
}
使用javadoc编译java文档
1 javadoc -encoding uft-8 -d jc E.java #生成的文档有注解显示
2
3 javadoc -encoding utf-8 -d jc E.java #将@Documented注释,没有使用注解的
Retention
@Retention注解接口,可以理解该接口的生命周期,也就是在运行时保留该注解多少时间。
该注解接口定义为:
1 @Documented
2 @Retention(RetentionPolicy.RUNTIME)
3 @Target(ElementType.ANNOTATION_TYPE)
public @interface Retention
对于 java.lang.annotation.RetentionPolicy 枚举类的定义
也就是说对于Retention 注解的接口参数就三个,分别是:RetentionPolicy.Class,RetentionPolicy.RUNTIME
以及 RetentionPolicy.SOURCE。如果注解接口不包含该注解,则默认的策略是:RetentionPolicy.Class
RetentionPolicy.Class:编译器将把注释记录在class文件中。当运行java程序时,JVM不在保留注释,这是默认值。
RetentionPolicy.RUNTIME:表一起将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。
RetentionPolicy.SOURCE:注释仅存在于源码中,在class字节码文件中不包含。
1 import static java.lang.annotation.RetentionPolicy.RUNTIME;
2 /* RUNTIME 运行时,可以反射调用>CLASS类上>SOURCE 源码上 */
3 @Retention(RUNTIME)
Target
对于@Target注解接口,是注解接口的上下文,注解对象所在的位置
1 @Documented
2 @Retention(RetentionPolicy.RUNTIME)
3 @Target(ElementType.ANNOTATION_TYPE)
public @interface Target
1 @Target 也是用于修饰一个Annotation定义,它用于指定被修饰Annotation能用于修饰那些程序元素。
@Target Annotation也包含一个名为value的成员变量,该成员变量只能是如下几个:
2
3 ElementType.ANNOTATION_TYPE:指定该策略的Annotation只能修饰Annotation。
4 ElementType.CONSTRUCTOR:指定该策略的Annotation能修饰的构造器。
5 ElementType.FIELD:指定该策略的Annotation只能修饰成员变量。
6 ElementType.LOCAL_VARIABLE:指定该策略的Annotation只能修饰局部变量。
7 ElementType.METHOD:指定该策略的Annotation只能修饰方法。
8 ElementType.PACKAGE:指定该策略的Annotation只能修饰包定义。
9 ElementType.PARAMETER:指定该策略的Annotation可以修饰参数。
10 ElementType.TYPE:指定该策略的Annotation可以修饰类、接口(包括注释类型)或枚举定义。
对于ElementType枚举类型,主要提供了Java程序中可能出现的注解的语法位置的简单分类,这些
常量主要是在@Target注解中使用,用来指定在代码的那个位置添加注解。根据@Target的定义,可
以看到该值是数组,所以可以设置多个值,例如上面代码中的:
Target(ElementType.ANNOTATION_TYPE)
举个Java中的例子 @Override 其中它的定义是:
1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.SOURCE)
3 public @interface Override {
4 }
根据上面的解释,就很容易理解该注解,该注解的注解位置是方法,并且
注解的生命周期也就是保留时间是源码,也就是只有在源码中存在。如果该
注解注解到类上,会有如下的错误信息。
Inherited
对于@Inherited注解接口,表示带有该注解的带有继承功能,如果在注解接口的生命中存在继承的
元注解,并且用户在类生命上查询注解接口,而类声明没有针对该接口的注解,则该类的超类将自动
查询该注解接口。此过程将向上重复进行,直到找到此接口的注解,或者达到了类层次接口的顶部。
如果没有超类具有次接口的注释,则查询将指示所讨论的类没有此类注解。
也就是说此类注解主要是为了让子类进行继承。
需要注意此注解只能用于类上,而不能用于方法和属性上。还需要注意的是,这个元注解只会导致
从超类继承注解,已经实现的接口上对于注解是无效的。
1 Documented
2 @Retention(RetentionPolicy.RUNTIME)
3 @Target(ElementType.ANNOTATION_TYPE)
4 public @interface Inherited {
5 }
对于 @Native 和 @Repeatable 是从 Java 1.8 新增加的元注解。
Repeatable
注释接口 java.lang.annotation.Repeatable 用于指示它(元)注释其声明的注释接口时可重复的。@Repeatable的值表示可重复注解接口的包含注解接口。定义为:
1 @Documented
2 @Retention(RetentionPolicy.RUNTIME)
3 @Target(ElementTye.ANNOTATION_TYPE)
4 public @interface Repeatable
Native
表示可以从本机代码引用定义常量值的字段。注解可以用作生成本机头文件的工具的提示,
以确定是否需要头文件,如果需要,应包含哪些声明。
该注解的定义为:
1 @Documented
2 @Target(ElementType.FIELD)
3 @Retention(RetnetionPolicy.SOURCE)
4 public @interface Native {
5 }
自定义注解
public class AnnDemo {
public static void main(String[] args) throws Exception{
// 正射
Lisi li = new Lisi();
// 反射
// Class<Lisi> c1 = Lisi.class;
// Class<?> c2 = Lisi.class;
// Class c3 = li.getClass();
// Class<?> c4 = Class.forName("com.nnzb.day02.Lisi");
// System.out.println(c1);
// System.out.println(c2);
// System.out.println(c2 == c1); // true
// System.out.println(c3 == c2);// true
// System.out.println(c4);
// System.out.println(c4 == c3);// true
// 利用反射机制,获取注解信息
Class<?> c = Lisi.class;
User ua = c.getAnnotation(User.class);
System.out.println(ua.name());
System.out.println(ua.age());
System.out.println(ua.sex());
// 方法上的注解
Method m = c.getMethod("show");
User mu = m.getAnnotation(User.class);
System.out.println(mu.name());
System.out.println(mu.age());
System.out.println(mu.sex());
// 属性上的注解
Field name = c.getField("name"); // 读取非private
System.out.println(name.getAnnotation(Name.class).value());
Field sname = c.getDeclaredField("sname"); // 可以读取private属性
System.out.println(sname.getAnnotation(Name.class).value());
}
}
@User(name = "李强",age = 25)
public class Lisi {
@Name("张三丰")
public String name;
@Name
private String sname;
@User(name = "李三")
public void show(){
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Name {
String value() default "佚名";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD})
public @interface User {
String name();
int age() default 18;
String sex() default "男";
}
注解的使用
注解接口定义好了,如何使用呢?对于注解的使用,现在来说就只有一种方法,就是基于反射机制。
如果有其他的方法,可以评论区告诉我。感谢。
直接上代码。这里用到了反射,不过这些都是简单的使用,加载类,加载类的方法,加载类的属性。
注解使用测试类
public class AnnDemo {
public static void main(String[] args) throws Exception{
// 正射
Lisi li = new Lisi();
// 反射
// Class<Lisi> c1 = Lisi.class;
// Class<?> c2 = Lisi.class;
// Class c3 = li.getClass();
// Class<?> c4 = Class.forName("com.nnzb.day02.Lisi");
// System.out.println(c1);
// System.out.println(c2);
// System.out.println(c2 == c1); // true
// System.out.println(c3 == c2);// true
// System.out.println(c4);
// System.out.println(c4 == c3);// true
// 利用反射机制,获取注解信息
Class<?> c = Lisi.class;
User ua = c.getAnnotation(User.class);
System.out.println(ua.name());
System.out.println(ua.age());
System.out.println(ua.sex());
// 方法上的注解
Method m = c.getMethod("show");
User mu = m.getAnnotation(User.class);
System.out.println(mu.name());
System.out.println(mu.age());
System.out.println(mu.sex());
// 属性上的注解
Field name = c.getField("name"); // 读取非private
System.out.println(name.getAnnotation(Name.class).value());
Field sname = c.getDeclaredField("sname"); // 可以读取private属性
System.out.println(sname.getAnnotation(Name.class).value());
}
}
@User(name = "李强",age = 25)
public class Lisi {
@Name("张三丰")
public String name;
@Name
private String sname;
@User(name = "李三")
public void show(){
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Name {
String value() default "佚名";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,METHOD})
public @interface User {
String name();
int age() default 18;
String sex() default "男";
}
正射 反射
// 正射
Lisi li = new Lisi();
// 反射
// Class<Lisi> c1 = Lisi.class;
// Class<?> c2 = Lisi.class;
// Class c3 = li.getClass();
// Class<?> c4 = Class.forName("com.nnzb.day02.Lisi");
// System.out.println(c1);
// System.out.println(c2);
// System.out.println(c2 == c1); // true
// System.out.println(c3 == c2);// true
// System.out.println(c4);
// System.out.println(c4 == c3);// true
利用反射机制,获取注解信息
// 利用反射机制,获取注解信息
Class<?> c = Lisi.class;
User ua = c.getAnnotation(User.class);
System.out.println(ua.name());
System.out.println(ua.age());
System.out.println(ua.sex());
方法上的注解
// 方法上的注解
Method m = c.getMethod("show");
User mu = m.getAnnotation(User.class);
System.out.println(mu.name());
System.out.println(mu.age());
System.out.println(mu.sex());
属性上的注解
// 属性上的注解
Field name = c.getField("name"); // 读取非private
System.out.println(name.getAnnotation(Name.class).value());
Field sname = c.getDeclaredField("sname"); // 可以读取private属性
System.out.println(sname.getAnnotation(Name.class).value());
总结
注释程序人员查看源码时看,注解JVM运行字节码文件时查看信息
1.如果注解难于理解,你就把它类同于标签,标签为了解释事物,注解为了解释代码。
2.注解的基本语法,创建如同接口,但是多了个@符号。public @interface Ann{}
3.注解的元注解是@Documented @Retention @Target
4.注解的属性 String value(); String name(); defauly "james" int age() default 18。
5.注解主要给编译器及工具类型(框架)的软件用的。
6.注解的提取需要借助于java的反射技术,反射比较慢(但功能强大,是开发框架人员必须
掌握,也是标志升级为动态语句的方式),所以注解使用时也需要谨慎计较时间成本。