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-------------------

posted @ 2019-07-06 00:00  LeftBody  阅读(280)  评论(0编辑  收藏  举报