一、简介
Java注解用于为Java代码提供元数据。元数据是指用来描述数据的数据,简单来说就是描述代码间的关系,或者代码与其他资源之间内在联系的数据。在Java中,元数据以标签的形式存在于Java代码中,元数据标签的存在并不影响程序代码的编译和执行。简而言之,言而总之,注解就是标签的意思。
二、创建注解
注解语法:
package day02;
public @interface 注解名称 {
属性类型 属性值() default "";
}
package day02;
/**
* @author qx
*/
public @interface HelloAnnotation {
String name() default "";
int age() default 0;
}
三、元注解
元注解说可以注解到注解上的注解,或者说元注解是一种基本注解,它能够应用到其他注解上面。元注解有@Retention、@Documented、@Target、@Inherited、@Repeatable 5种。
1.@Retention
这个注解说明了这个注解的生命周期。
RetentionPolicy.SOURCE:只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS:只被保留到编译进行的时候,并不会被加载到JVM中。
RetentionPolicy.RUNTIME:可以保留到程序运行的时候,它会被加载到JVM中。
2.@Documented
这个注解的作用是能够将注解中的元素包含到Javadoc中去。
3.@Target
标明注解运用到的地方。
ElementType.ANNOTATION_TYPE:可以给一个注解进行注解
ElementType.CONSTRUCTOR:可以给构造方法进行注解
ElementType.FIELD:可以给属性进行注解
ElementType.METHOD:可以给方法进行注解
ElementType.TYPE:可以给一个类型进行注解,比如类、接口、枚举
4.@Inherited
Inherited是继承的意思。如果一个超类被@Inherited注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
我们创建父类和子类,并且编写一个带有@Inherited注解的注解。然后我们在父类上标注这个注解。
package day02;
import java.lang.annotation.*;
/**
* @author qx
*/
@Target(ElementType.TYPE)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {
String name() default "";
}
package day02;
/**
* @author qx
* @date 2023/10/26
* @des 父类
*/
@HelloAnnotation(name = "parent")
public class Parent {
}
package day02;
/**
* @author qx
* @date 2023/10/26
* @des 子类
*/
public class Child extends Parent {
}
测试:我们测试父类和子类的注解信息。我们没有在子类上标注任何注解信息。
package day02;
import java.lang.annotation.Annotation;
/**
* @author qx
* @date 2023/10/26
* @des Inherited 测试
*/
public class InheritedTest {
public static void main(String[] args) {
Annotation[] parentAnnotations = Parent.class.getAnnotations();
System.out.println("---父类信息---");
System.out.println("父类注解个数:" + parentAnnotations.length);
for (Annotation parentAnnotation : parentAnnotations) {
System.out.println(parentAnnotation.annotationType().getSimpleName());
}
// 打印子类注解信息
Annotation[] childAnnotations = Child.class.getAnnotations();
System.out.println("---子类信息---");
System.out.println("子类注解个数:" + childAnnotations.length);
for (Annotation childAnnotation : childAnnotations) {
System.out.println(childAnnotation.annotationType().getSimpleName());
}
}
}
输出:
---父类信息---
父类注解个数:1
HelloAnnotation
---子类信息---
子类注解个数:1
HelloAnnotation
从输出结果我们可以看到子类会继承超类的注解。
5.@Repeatable
Repeatable是可以重复的意思,通常注解的值可以同时取多个。
package day02;
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 Persons {
Person[] value();
}
package day02;
import java.lang.annotation.*;
/**
* @author qx
* @date 2023/10/26
* @des
*/
@Repeatable(Persons.class)
public @interface Person {
String role() default "";
}
package day02;
/**
* @author qx
* @date 2023/10/26
* @des
*/
@Person(role = "cto")
@Person(role = "ceo")
public class Man {
}
测试:
package day02;
import java.lang.annotation.Annotation;
/**
* @author qx
* @date 2023/10/26
* @des
*/
public class RepeatableTest {
public static void main(String[] args) {
Annotation[] annotations = Man.class.getAnnotations();
System.out.println(annotations.length);
Persons persons = (Persons) annotations[0];
for (Person person : persons.value()) {
System.out.println(person.role());
}
}
}
输出:
1
cto
ceo
四、注解的属性
package day02;
import java.lang.annotation.*;
/**
* @author qx
*/
@Target(ElementType.TYPE)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface HelloAnnotation {
// default 后面设置注解属性的默认值
String name() default "admin";
}
就是值使用这个注解属性的时候,如果没有指定属性值那么会使用这个默认的属性值。
五、Java预置的注解
1.@Override
子类要重写父类中被@Override修饰的方法。
2.@Deprecated
加上这个注解之后,表示此方法或类不再建议使用,调用时会出现删除线,但不代表不能用,只是说不推荐使用。
package day02;
/**
* @author qx
* @date 2023/10/26
* @des
*/
public class Hello {
@Deprecated
public void show(){
System.out.println("这是一个过时方法");
}
public void test(){
System.out.println("这是一个正常方法");
}
}
package day02;
/**
* @author qx
* @date 2023/10/26
* @des
*/
public class DeprecatedTest {
public static void main(String[] args) {
Hello hello = new Hello();
hello.test();
hello.show();
}
}
输出:
这是一个正常方法
这是一个过时方法
3.@SuppressWarnings
阻止警告的意思。
该注解的作用说给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保存静默。
package day02;
/**
* @author qx
* @date 2023/10/26
* @des
*/
public class DeprecatedTest {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
Hello hello = new Hello();
hello.test();
// @SuppressWarnings使用后警告没有出现了
hello.show();
}
}
4.@SafeVarargs
参赛安全类型注解,它的目的说提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生unchecked这样的警告。在声明具有模糊类型的可变参数的构造函数或方法时,Java编译器会报unchecked警告。鉴于这种情况,如果程序猿断定声明的构造函数和方法的主体no problem,可使用@SafeVarargs进行标记,这样Java编译器就不会报unchecked警告了!
package day02;
import java.util.Arrays;
/**
* @author qx
* @date 2023/10/26
* @des
*/
public class SafeVarargsAnnotation<T> {
private T[] args;
public SafeVarargsAnnotation(T... args) {
this.args = args;
}
public static <T> void loopPrintInfo(T... infos){
for(T info:infos){
System.out.println(info);
}
}
public static void main(String[] args) {
SafeVarargsAnnotation.loopPrintInfo("a","b","c");
}
}
编译的时候会出现使用了未经检查或不安全的操作。
然后我们在构造方法和静态方法上加上@SafeVarargs注解。
package day02;
import java.util.Arrays;
/**
* @author qx
* @date 2023/10/26
* @des
*/
public class SafeVarargsAnnotation<T> {
private T[] args;
@SafeVarargs
public SafeVarargsAnnotation(T... args) {
this.args = args;
}
@SafeVarargs
public static <T> void loopPrintInfo(T... infos) {
for (T info : infos) {
System.out.println(info);
}
}
public static void main(String[] args) {
SafeVarargsAnnotation.loopPrintInfo("a", "b", "c");
}
}
我们再次编译的时候没有出现unchecked的提示。
5.@FunctionalInterface
Java8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,该注解规定接口只有一个接口方法,也被称作函数式接口。当你写的接口不符合函数式接口定义的时候,编译器会报错。
package day02;
/**
* @author qx
* @date 2023/10/26
* @des
*/
@FunctionalInterface
public interface MyInterface {
void say(String msg);
}
测试:
package day02;
/**
* @author qx
* @date 2023/10/26
* @des
*/
public class FunctionalInterfaceTest {
public static void main(String[] args) {
MyInterface myInterface = msg -> System.out.println(msg);
myInterface.say("hello");
}
}
六、注解的使用场景
注解是一系列元数据,它提供数据来解释程序代码,但是注解并非说所解释的代码本身一部分,注解对代码的运行结果没有直接影响。
用处:
编译器可以利用注解来探测错误或警告信息
软件工具可以利用注解信息来生成代码、HTML文档或其它响应处理。
某些注解可以在程序运行时接受代码的提取。
总结:
1、注解就是标签,注解为了解释代码
2、注解的基本语法@interface
3、注解的元注解
4、注解的属性
5、注解主要给编译器及工具类型的软件用的
6、注解的提取要借助于Java的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本