Java注解Annotation
一、什么是注解?有什么作用?
Annotation是1.5之后新加的功能,是一种"元数据",它的存在不会影响程序的逻辑和运行结果,是对类、方法、变量进行的说明。那么这种不会影响程序运行的代码会有什么用呢?注解其实在很多框架中有很多应用,比如Spring等。此外,注解可以进行格式检查。比如@override的方法是覆盖父类的方法,如果子类中程序名称不小心写错,此时父类根本没有这个方法,注解就会提出警告;而当没有注解的时候,JVM认为程序没有问题,是子类一个新的方法而已。
class Father { public void P() { System.out.println("The father class"); } } public class Ann extends Father{ public void Pa() { System.out.println("The son class"); } public static void main(String []args) { Ann a=new Ann(); a.P(); } }
上面的例子极为简单,只是最基本的继承和覆盖,其中子类的方法不小心由P拼写为Pa,但运行出的结果仍然是父类的方法,当添加注解后,这一情况就不会出现。
class Father { public void P() { System.out.println("The father class"); } } public class Ann extends Father{ @Override public void Pa() { System.out.println("The son class"); } public static void main(String []args) { Ann a=new Ann(); a.P(); } }
上面的程序无法运行,因为注解发现子类中的方法并没有覆盖父类的方法。强行运行就会直接报错
二、JDK三大内置注解
@Override上面已经提到过了,还有@SuppressWarnings和@Deprecated两个。
@Deprecated用于类、方法变量的修饰,表示已经不建议使用的,废弃的。一般都有注释说明新的选择,但是并不代表不能使用(只是不建议而已)。
而@SuppressWarnings作用是屏蔽一些警告,例如新定义一个方法但是我没有调用过该方法,就会产生一个未使用的警告。例如下图所示:
但是当我们使用该注解时,警告就消失了。
如果你的参数不对可是没用的哦,@SuppressWarnings的参数有以下类型:
关键字 | 用途 |
---|---|
all | to suppress all warnings (抑制所有警告) |
boxing | to suppress warnings relative to boxing/unboxing operations (抑制装箱、拆箱操作时候的警告) |
cast | to suppress warnings relative to cast operations (抑制映射相关的警告) |
dep-ann | to suppress warnings relative to deprecated annotation (抑制启用注释的警告) |
deprecation | to suppress warnings relative to deprecation (抑制过期方法警告) |
fallthrough | to suppress warnings relative to missing breaks in switch statements (抑制确在switch中缺失breaks的警告) |
finally | to suppress warnings relative to finally block that don’t return (抑制finally模块没有返回的警告) |
hiding | to suppress warnings relative to locals that hide variable(抑制相对于隐藏变量的局部变量的警告) |
incomplete-switch | to suppress warnings relative to missing entries in a switch statement (enum case)(忽略没有完整的switch语句) |
nls | to suppress warnings relative to non-nls string literals( 忽略非nls格式的字符) |
null | to suppress warnings relative to null analysis( 忽略对null的操作) |
rawtypes | to suppress warnings relative to un-specific types when using generics on class params( 使用generics时忽略没有指定相应的类型) |
restriction | to suppress warnings relative to usage of discouraged or forbidden references( 抑制禁止使用劝阻或禁止引用的警告) |
serial | to suppress warnings relative to missing serialVersionUID field for a serializable class( 忽略在serializable类中没有声明serialVersionUID变量) |
static-access | to suppress warnings relative to incorrect static access( 抑制不正确的静态访问方式警告) |
synthetic-access | to suppress warnings relative to unoptimized access from inner classes( 抑制子类没有按最优方法访问内部类的警告) |
unchecked | to suppress warnings relative to unchecked operations( 抑制没有进行类型检查操作的警告) |
unqualified-field-access | to suppress warnings relative to field access unqualified( 抑制没有权限访问的域的警告) |
unused | to suppress warnings relative to unused code( 抑制没被使用过的代码的警告) |
三、自定义注解
其实前面两部分只是铺垫,内容简单且少,注解真正的核心内容就是自定义注解,如何创建一个注解并且去使用它是我们的目标。
创建一个注解的关键字是@interface,下面即为一个无任何内容的注解:
@interface An { }
注解的地位等同于类、接口以及枚举,即互相不可同名,且public修饰时名称必须和文件名称保持一致。
接下来展示一个有成员的注解:
@interface An { String author() default "wu"; int value(); Class type(); }
(1)成员的类型只能是String, int, Class, Annotation以及基本类型,以及上述类型的数组。(据说时Enumeration类型也可以,但是我尝试了一下报错,不知道是版本的原因还是什么?)
(2)如果只有一个成员,它的名字应该命名为value();
(3)设置默认值直接跟default即可,等号可以省略。
此外,注解还有重要的一部分,即元注解。元注解主要是以下五种,分别为@Target,@Retention,@Inherited,@Documented,@Repeatable。接下来我们分别介绍这几个元注解:其中前面两个比较常用,而后面三个相对不太常用。@Target描述注解使用的元素种类,比如如果是针对方法的注解,那@Target的参数就是ElementType.METHOD,所有的Element枚举值列举如下:
ElementType.ANNOTATION_TYPE 可以应用于注释类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注释。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
@Retention设置注解的作用范围,参数也是枚举RetentionPolicy,不过该枚举的常量只有三个,SOURCE作用时间最少,表示注解不编译到类文件中;CLASS表示编译到类文件中,但是在运行时并不加载到JVM中;RUNTIME作用范围最大,会加载到JVM中。在自定义注解中,一般都是用RUNTIME。
@Inherited表示注解可以被继承,@Documented表示生成javadoc会包含注解(不知道怎么生成javadoc,先挖坑再说),@Repeatable是1.8新增的功能,代表可重复,即注解中的值可以被多次赋予不同的值。
接下来展示一个完整的注解以及使用
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface An { String author() default "wu"; int value(); Class type(); } @An(author="wuyiming",value = 10,type = int.class) public void addten(int j) { System.out.println(this.i+j+10); }
四、注解的访问
注解代码:An.java
package zhujie; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @interface An { String author() default "wu"; int value(); String type(); }
主代码:Ann.java
package zhujie; import java.lang.reflect.Method; class Father { protected int i=3; public void P() { System.out.println("The father class"); } } public class Ann extends Father{ @Override public void P() { System.out.println("The son class"); } @Deprecated public int dep() { return 0; } @SuppressWarnings("unused") public int depA() { return i; } @An(author="wuyiming",value = 10,type = "add") public void addten(int j) { System.out.println(j+10); } @An(author="wuyiming",value = 0,type = "divide") public void dividezero() { System.out.println(this.i/0); } public static void main(String []args) { try { Ann a = new Ann(); Class a1 = a.getClass(); Method[] ms = a1.getDeclaredMethods(); for (Method m : ms) { if (m.isAnnotationPresent(An.class)) //参数为注解类对象 { Class[] p = m.getParameterTypes(); if (p.length==0) { //判断数组是否为空,牢记心中 m.invoke(a, null); } else { m.invoke(a,7); } } } } catch (Exception e) { System.out.println(e.getCause().getMessage()); } } }
结果为:
因此,上述代码完成了对Ann类中有@An注解的方法通过反射进行了调用的任务。
当然也能访问注解的参数,通过反射的方法:
(!失败了!)
即时更新,错误已解决:
public static void main(String []args) { try { Ann a = new Ann(); Class a1 = a.getClass(); Method[] ms = a1.getDeclaredMethods(); for (Method m : ms) { if (m.isAnnotationPresent(An.class)) //参数为注解类对象 { /*Fail!*/ An zhu = m.getAnnotation(An.class); System.out.println("The author is " + zhu.author()); System.out.println("The type is " + zhu.type()); System.out.println("The value is " + zhu.value()); System.out.println("--------------------Dividing line-------------------"); } } } catch (Exception e) { System.out.println(e.getCause().getMessage()); } }
之前一致Fail是错误的使用了getParameterAnnotations()方法,而应该使用的方法是getAnnotation(),调试了好久😰
结果如下:
The author is wuyiming
The type is add
The value is 10
--------------------Dividing line-------------------
The author is wuyiming
The type is divide
The value is 0
--------------------Dividing line-------------------