Java拾贝第十九天——注解

发布时间 2023-11-07 16:42:25作者: ocraft

Annotation

注解(Annotation)是一种可以作用于类、属性或方法的,带有特定功能的,特殊的 "注释"。

且注解不影响程序的运行:不论是否使用注解,代码都可以正常执行。

Annotation接口定义如下

public interface Annotation {
	boolean equals(Object obj);
	int hashCode();
	String toString();
	
 	Class<? extends Annotation> annotationType();
}

系统自带的注解 或者 用户自定义的注解都默认实现这个接口。

常用的注解如下:
@Override:确保重写的正确性。
@Deprecated:被荒废(荒废就是不建议使用)
@SuppressWarnings:忽略安全警告

常用的元注解如下: (声明在注解之上的注解,一般是约束自定义注解的功能)
@Target:声明注解可作用范围
@Retention:注解的生命周期
@Inherited:注解可否被子类继承

常用注解

@Override

父类方法被重写时可以使用该注解,主要作用是确保重写的正确性

栗子:

public class Test19 {
    public static void main(String[] args) {
        Son son = new Son();
        son.tell("123");
    }
}

class Father{
    public void tell(String something){
        System.out.println(something);
    }
}
class Son extends Father{
    @Override
    public void tell(String something) {
        super.tell(something);//子类调用父类的tell方法
    }
}

程序运行结果:

123

下面的栗子是故意错误的重写方法:

public class Test19 {
    public static void main(String[] args) {
        Son son = new Son();
        son.tell("123");
    }
}

class Father{
    public void tell(String something){
        System.out.println(something);
    }
}
class Son extends Father{
    @Override
    public void Tell(String something) {//重写错误,方法名不同
        super.tell(something);
    }
}

程序运行结果:

java: 方法不会覆盖或实现超类型的方法

@Deprecated

此注解声明的属性、方法或者类会被视为被荒废(荒废就是不建议使用),使用时会出现编译警告。

栗子:

public class Test19 {
    public static void main(String[] args) {
        Father.say();//方法名会被画横线
    }
}

class Father{
    @Deprecated
    public static void say(){
        System.out.println("该方法已荒废,但依旧可以正常执行");
    }
}

Father.say();

程序运行结果:

该方法已荒废,但依旧可以正常执行

除了方法,@Deprecated还可以作用于类,属性:

@Deprecated
class Father{
    @Deprecated
    private int age;

    @Deprecated
    public static void say(){
        System.out.println("该方法已荒废,但依旧可以正常执行");
    }
}

Father.say();

@SuppressWarnings

忽略安全警告。之前泛型说过如果没有明确具体的泛型类型会出现安全警告。
image

不同于上面两个注解,@SuppressWarnings需要传入value具体到忽略哪种类型的安全警告

常用value如下:
deprecation——忽略使用被荒废的类或方法警告
unchecked——忽略泛型未指定警告
fallthrough——忽略switch的case后未加入break警告
all——忽略所有警告

查看@SuppressWarnings定义

public @interface SuppressWarnings {
    String[] value(); //接收value数组,可多传入。

@interface就相当于实现了Annotation接口

自定义注解

格式如下:

修饰符 @interface 注解名{
	数据类型 属性名() default 默认值;
}

如果注解中定义了属性,则属性名后必须加()

如果属性没有默认值,实现该注解必须要传入属性值。

栗子:

public class Test19 {

    @MyAnnotation2//有默认值不用传入
    @MyAnnotation1("123")//属性没有默认值必须要传入
    public static void main(String[] args) {

    }
}

@interface MyAnnotation1{
    String value();
}
@interface MyAnnotation2{
    String value() default "默认值";
}

元注解

元注解是作用于注解之上的注解,一般是约束自定义注解的功能

@Target

如果一个注解没有显式使用@Target,则该注解可以作用在任何地方。
使用@Target限制注解作用于某处

查看Target元注解定义

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
	
//接收ElementType数组,没有默认值,使用Target元注解必须传入属性值
}

ElementType是枚举,其内部如下:

public enum ElementType {
//属性值				描述
    TYPE,			类,接口,枚举
    FIELD,			属性
    METHOD,			方法
    PARAMETER,			参数
    CONSTRUCTOR,		构造方法
    LOCAL_VARIABLE,		局部属性
    ANNOTATION_TYPE,		注解
    PACKAGE,			包
}

至此,栗子如下:

public class Test19 {
    @MyAnnotation2()
    public int x=1;
    
    @MyAnnotation1("123")
    public static void main(String[] args) {

    }
}

@Target(ElementType.METHOD)//限制只能作用于方法上
@interface MyAnnotation1{
    String value() default "默认值";
}

@Target(ElementType.FIELD)//限制只能作用于属性上
@interface MyAnnotation2{
    String value() default "默认值";
}

@Retention

该元注解声明某个注解的声明周期

查看元注解Retention定义

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

RetentionPolicy是枚举,其内部如下:

public enum ElementType {
//属性值				描述
    SOURCE,			此注解只保留在源文件中,编译后类文件(class文件)不存在
    CLASS,			此注解保留在源文件和类文件中,注解信息不会被加载到JVM中(默认是此属性)
    RUNTIME			此注解保留在源文件、类文件中,注解信息会被加载到JVM中。
}

@Override——@Retention(RetentionPolicy.SOURCE)
@Deprecated——@Retention(RetentionPolicy.RUNTIME)
@SuppressWarnings——@Retention(RetentionPolicy.SOURCE)

@Inherited

注解可以被继承

如果一个注解使用@Inherited,实现该注解的类,其子类也会拥有该注解

栗子:

@Inherited
@interface MyAnnotation1{
    String value() default "默认值";
}

@MyAnnotation1
class Father{
    public void say(){}
}

//子类也拥有MyAnnotation1注解
class Son extends Father{}

使用反射获取注解

栗子:

package moudle2;


import java.lang.annotation.*;

public class Test19 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("moudle2.Son");

            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

@Inherited
@interface MyAnnotation1{
    String value() default "默认值";
}

@MyAnnotation1
class Father{

    public void say(){}
}
class Son extends Father{}

程序运行结果:

//空 因为MyAnnotation1注解的信息没有被加载到JVM中。

至此,修改栗子如下:

package moudle2;


import java.lang.annotation.*;

public class Test19 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("moudle2.Son");

            Annotation[] annotations = clazz.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

@Retention(RetentionPolicy.RUNTIME)//加载到JVM中
@Inherited
@interface MyAnnotation1{
    String value() default "默认值";
}

@MyAnnotation1
class Father{

    public void say(){}
}
class Son extends Father{}

程序运行结果:

@moudle2.MyAnnotation1(value=默认值)

一般注解都是使用系统注解,使用框架时也是,直接使用框架提供的注解即可。