注解

目录

  • 注解
  • 注解怎么使用,用在什么地方?
  • Java内置的注解
  • 元注解
  • 常见的注解有哪些?
  • 自定义注解
  • 注解中的属性可以是哪些类型?
  • 注解属性是数组时
  • 案例
  • 反射注解
  • 通过反射获取注解的属性值
  • 注解的作用

注解,或者叫注释类型,英文单词是:Annotation

注解Annotation是一种引用数据类型。

语法格式:

[修饰符列表] @interface 注解类型名{}

注解怎么使用,用在什么地方?

第一:注解使用时的语法格式是:

@注解类型名

package cn.xiaokw.java.annotation;

public @interface MyAnnotation {
    
}

第二:注解可以出现在类上、属性上、方法上、变量上等...

默认情况下,注解可以出现在任意位置

package cn.xiaokw.java.annotation;

@MyAnnotation
public class AnnotationTest01 {
    @MyAnnotation
    private String name;

    @MyAnnotation
    AnnotationTest01() {

    }

    @MyAnnotation
    public static void test() {

    }

    @MyAnnotation
    public static int method(@MyAnnotation int a, @MyAnnotation int b) {
        return a + b;
    }

}

Java内置的注解

Deprecated

注解在类/方法上,表示这个类/方法已过时,主要是向其它程序员传达一个信息,告知已过时,有更好的解决方案

FunctionalInterface

用翔实的注释类型来表示一个接口类型声明的目的是空功能接口由java语言规范定义。

Override

表示一个方法声明的目的是覆盖父类方法声明。 只能注解方法,这个注解是给编译器参考的,和运行阶段没有关系。凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器会报错。

SafeVarargs

一个程序员断言体标注的方法或构造函数没有对其参数进行交互可能不安全的操作。

SuppressWarnings

指示在注释元素(和包含在注释元素中的所有程序元素中)应被抑制命名的编译器警告。

元注解

用来标注“注解类型”的“注解”,称为元注解。

常见的注解有哪些?

Target 用来指定被标注的注解可以出现在哪些位置上

Retention 用来标注“注解类型”的“注解”,用来指定“被标注的注解”最终保存在哪里

类似Override注解上标注的:

@Target(ElementType.METHOD)  //表示“被标注的注解”只能出现在方法上
@Retention(RetentionPolicy.SOURCE) //表示该注解只被保存在java源文件中
//@Retention(RetentionPolicy.CLASS) //表示该注解只被保存在class源文件中
//@Retention(RetentionPolicy.RUNTIME) //表示该注解只被保存在java源文件中,并且可以被反射机制所读取
public @interface Override {
}

自定义注解

package cn.xiaokw.java.annotation;

//如果注解里定义了属性,且属性没有添加default默认值,则在注解时必须给属性赋值,否则会报错
//如果注解有多个属性需要赋值,则使用逗号区分
//注解属性有default默认值则可以不用赋值
@MyAnnotation(name="zhangsan" , age = 18)
public class AnnotationTest02 {

}
package cn.xiaokw.java.annotation;

public @interface MyAnnotation {
    //给注解添加属性

    String name();

    int age();

    boolean flag()  default false;
}

如果一个注解的属性名字是value的话,在使用的时候可以,该属性名可以省略,前提是该注解 只有一个属性

public @interface MyAnnotation2 {
    String value();
}
@MyAnnotation2("haha")
public class AnnotationTest03 {
}

注解中的属性可以是哪些类型?

byte short int long float double boolean char String Class 枚举类型

可以是以上每一种的数组形式

注解属性是数组时

package cn.xiaokw.java.annotation;

public @interface MyAnnotation3 {
    String[] value();
}
package cn.xiaokw.java.annotation;

public class AnnotationTest04 {
    @MyAnnotation3({"a","b","c"}) //如果是数组时,使用大括号,因为这里属性名为value所以省略了
    public void method1(){}
    
    @MyAnnotation3("a") //如果数组只给一个值则可以省略大括号
    public void method2(){}
}

案例

实现只允许该注解可以标注类、属性;可以被反射

package cn.xiaokw.java.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE , ElementType.FIELD}) //可以标注类、属性
@Retention(RetentionPolicy.RUNTIME) //可以被反射
public @interface MyAnnotation4 {
    String name() default "zhangsan";
}

反射注解

package cn.xiaokw.java.annotation;

@MyAnnotation4(name = "lisi")
public class AnnotationTest5 {}
package cn.xiaokw.java.annotation;

public class ReflectAnnotationTest {
    public static void main(String[] args) throws Exception{
        Class annotation = Class.forName("cn.xiaokw.java.annotation.AnnotationTest5");

        //判断annotation上是否有可以反射的注解MyAnnotation4
        if(annotation.isAnnotationPresent(MyAnnotation4.class)){
            //获取该注解的对象
            MyAnnotation4 a1 = (MyAnnotation4) annotation.getAnnotation(MyAnnotation4.class);
            //获取到该注解对象的属性值
            System.out.println(a1.name());
        }
    }
}

通过反射获取注解的属性值

package cn.xiaokw.java.annotation;

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) //允许反射
public @interface MyAnnotation5 {
    String username() default "admin";
    String password() default "admin";
}
package cn.xiaokw.java.annotation;

import java.lang.reflect.Method;

public class ReflectAnnotationTest2 {
    @MyAnnotation5(username = "zhangsan", password = "admin123")
    public void method() {
    }
    public static void main(String[] args) throws Exception{
        //先获取类
        Class c = Class.forName("cn.xiaokw.java.annotation.ReflectAnnotationTest2");
        //获取类的方法
        Method method = c.getDeclaredMethod("method");
        //判断方法是否有可反射的注解MyAnnotation5
        if(method.isAnnotationPresent(MyAnnotation5.class)){
            //如果有 则获取注解对象
            MyAnnotation5 annotation = method.getAnnotation(MyAnnotation5.class);
            //使用注解对象获取注解属性值
            System.out.println(annotation.username());
            System.out.println(annotation.password());
        }
    }
}

注解的作用

案例:

假设有这样一个注解,叫做:@Student

这个注解只能出现在类上,当这个类上有这个注解的时候,要求这个类中必须要有一个String类型的name属性,如果没有这个属性就报异常。

package cn.xiaokw.java.annotation;

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 Student {
}
package cn.xiaokw.java.annotation;

import java.lang.reflect.Field;

public class Demo{
    public static void main(String[] args) throws Exception{
        //获取Person类
        Class personClass = Class.forName("cn.xiaokw.java.annotation.Person");
        //判断Person类是否标注了@Student注解
        if (personClass.isAnnotationPresent(Student.class)){
            //获取Person类中的所有属性
            Field[] fields = personClass.getDeclaredFields();
            boolean flag = true;
            //判断所有属性里是否包含String类型的name属性
            for (Field field : fields){
                if(field.getName().equals("name") && field.getType().getSimpleName().equals("String")){
                    flag = false;
                }
            }
            if(flag)
                throw new RuntimeException("被@Student注解标注的类必须有String类型的name属性!");
        }
    }
}
@Student
class Person {
//    String name;
    int age;
}