Java注解原来如此通俗易懂

目录:
1.注解概述
2.注解的作用分析
3.JDK中预定义的一些注解
4.自定义注解
5.元注解
6.解析注解

1.注解概述

Java注解是一系列元数据,它提供数据用来解释程序代码,它是JDK1.5之后版本引入的新的特性,与类,接口,枚举是同一个层次,它可以声明在包,类,字段,方法,局部变量,方法参数等的前面,用来说明程序的注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响

2.注解的作用分析

简单了解注解的作用:

注解作用:
1.编译检查:通过代码里标识的注解据让编译器实现基本的检查
2.编写文档:通过代码里标识的注解生成文档【doc文档】
3.代码分析:通过代码标识的注解对代码进行分析【使用反射】

1.编译检查
就比如我们接触过的Override注解:
Override注解作用是检查所注解方法是否重写了父类的方法,如果不是注解的正确使用,编译就不会通过,这就是所谓的注解检查:
在这里插入图片描述

2.编写文档
就是我们在文档注释上加一点注解:

package annotation;
/**
 * 计算两数的和
 * @author itcat
 * @version 1.0
 * @since 1.5
 */
public class AnnoDemo1 {
/**
 * 计算两数的和
 * @param a 整数
 * @param b 整数
 */
public int add(int a,int b)
{
    return a+b;
}
}

然后如果我们把上边代码编写成文档那么注解的内容将会被翻译成文档中相应的内容
ps:博主不知道为啥不能生成文档所以不能演示生成文档后,文档里边的内容,mmp这文档肯定看不起我
在这里插入图片描述
当然其实只要了解到它会在生成文档中会被翻译成对应信息就行

3.代码分析:通过代码标识的注解对代码进行分析【使用反射】,在下面详细说明

3.JDK预定义的注解

1.@Override:检查被注解的方法是否是继承自父类的
2.@Deprecated:将该注解标注的内容已经过时
3.@Suppresswarnings:压制警告

1.@Override:这个不多讲,上边的演示有
2.@Deprecated
例子:
在这里插入图片描述

我们可以看到当我们选择方法时,show1上是有一个删除线的,而show2是没有的,这就是本注解的作用,虽然注解内容已经过时但是不影响我们对它的调用,java中标注这个注解说明该方法已经存在更好的方法进行替换

3.@Suppresswarnings
例子:
在这里插入图片描述

乍一看好像没啥啊,但是可以把鼠标放在红色叹号下边的黄色横杠上,就会出现提示信息,上图就有一个Method 'show2()' is never used这个警告信息,如果不想要这些警告,那么就可以在你不想要的警告的方法上添加此注解,就会屏蔽。这个注解一般加在类上边的部分,这样就能屏蔽全部的警告

注意使用此注解一般需要加一个参数all,正确格式为@Suppresswarnings("all")

4.自定义注解

1.我们看一个系统自定义注解Override的注解格式分析:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

格式:

1.元注解:下边会讲
2.public @interface 注解名称{}:一个类似于普通接口的接口,作用和使用规则和普通方法一样

注解本质:

所有注解都会继承得的公共接口,系统默认为你继承java.lang.annotation.Annotation接口

注解属性:

就是注解的接口里边可以定义的成员方法
要求:
1.属性的返回值类型:
(1)基本数据类型
(2)String
(3)注解
(4)以上类型的数组
2.定义属性后:
(1)使用时需要给属性赋值
(2)如果定义属性时使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
(3)数组赋值的时候使用{}包裹,如果数组只有一个元素{}可以省略,如果只有一个属性需要赋值,并且属性的名称是value那么赋值时value可以直接省略

为了方便理解举个例子(对属性返回值类型的要求):
在这里插入图片描述

很明显void不合适(编译不通过),注意我们把注解里边的方法叫做属性

接下来我们看看定义属性后的对属性进行赋值的案例:

package annotation;
public @interface AnnoDemo1 {
   int age();
   String str();
}

@AnnoDemo1(age=12,str="张三")
class person{
    int a;
    void fly()
    {
        System.out.println("我要飞的更高,摔的更惨");
    }

    public static void main(String[] args) {
        new person().fly();
    }
}

很好奇是不是为啥方法会等于一个值,回答是java规定的,懂了吗,你定义的方法必须要在使用的时候为方法赋值,否则编译不通过当然不包括default

下面是对default和value这个特殊名称的使用,str默认值是李四,不使用默认值的话就必须用注释的部分进行赋值:

package annotation;
public @interface AnnoDemo1 {
   int value();
   String str() default "李四";
}
@AnnoDemo1(12)
//@AnnoDemo1(value = 12,str="王五")
class person{
    int a;
    void fly()
    {
        System.out.println("我要飞的更高,摔的更惨");
    }

    public static void main(String[] args) {
        new person().fly();
    }
}

当我们赋一些特殊的值:

package annotation;
public @interface AnnoDemo1 {
   MyEnum p();
   String[] str() ;
   Override over();
}
@AnnoDemo1(p=MyEnum.p1,str={"123","234"},over = @Override)
class person{
    int a;
    void fly()
    {
        System.out.println("我要飞的更高,摔的更惨");
    }

    public static void main(String[] args) {
        new person().fly();
    }
}

附带枚举的代码:

package annotation;
public enum MyEnum {
    p1,p2;
}

5.元注解

我们再把Override的源码拿来分析:

 @Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

简单的说元注解就是描述注解的注解,比如上边的 @Target和@Retention

元注解是Java中定义好的注解,常用元注解:

注解名 注解作用
@Target 描述注解能够作用的位置
@Retention 描述注解被保留的阶段
@Documented 描述注解能否被抽取到api文档中
@Inherited 描述注解是否被子类继承

1.@Target
源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

我们打开 ElementType源码:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

发现是一个枚举类型的数据,我们了解枚举的三个取值:

(1)TYPE:可以作用于类上
(2)METHOD:可以作用于方法上
(3)FIELD:可以作用于成员变量上
ps:注意value可以省略不写因为只有它一个需要赋值的

在这里插入图片描述
看到那个报错的小红灯泡没,就是因为你用它修饰方法

2.@Retention
源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

打开RetentionPolicy 源码:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

也是枚举类型:

1.SOURCE:源码阶段
2. CLASS:类对象阶段
3. RUNTIME:运行时阶段
一般参数是RUNTIME,意思是当前被描述的注解会保留到class字节码文件中,并被JVM读取到
如果参数是CLASS意思是当前被描述的注解会保留到class字节码文件中,但不会被JVM读取到
如果参数是SOURCE意思是当前被描述的注解不会保留到class文件里

3.@Documented
就是描述此方法或其它的注解会被抽取到api文档里边:
在这里插入图片描述
画红线得到部分就是由于@Documented注解才使得划线所代表注保留到了api文档中

4.@Inherited
很好理解,如果父类的注解包含@Inherited那么子类即使不加注解也会默认含有该注解

6.解析注解

那么什么是解析注解:即获取注解中定义的属性值

package untl2;
import java.lang.reflect.Method;
@pro(MethodName ="fly")
public class MyReflect {
   public int a=12;
    public void fly()
    {
        System.out.println("我要飞的更高");
    }
    public static void main(String[] args) throws Exception {
       Class cls=MyReflect.class;
        //其实就是在内存中生成了一个注解接口的子类实现类对象
       pro an=(pro) cls.getAnnotation(pro.class);
       String method=an.MethodName();
        Object obj=cls.newInstance();
        Method method1=cls.getMethod(method);
        method1.invoke(obj);

    }
}

运行结果:
我要飞的更高

其实以上代码等同:

package untl2;
import java.lang.reflect.Method;
@pro(MethodName ="fly")
public class MyReflect {
   public int a=12;
    public void fly()
    {
        System.out.println("我要飞的更高");
    }
    public static void main(String[] args) throws Exception {
       Class cls=MyReflect.class;
        //其实就是在内存中生成了一个注解接口的子类实现类对象
        Object obj=cls.newInstance();
        Method method1=cls.getMethod("fly");
        method1.invoke(obj);

    }
}
运行结果:
我要飞的更高

pro注解的定义代码:

package untl2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface pro {
    String MethodName();
}

getAnnotation是获得注解的Class对象方法,然后就可以通过.方法名拿到我们需要的注解里边的属性值。

一般来说我们主要替换配置文件的时候用的注解比较多

猜你喜欢

转载自blog.csdn.net/qq_45737068/article/details/105881840