注解【java全端课23】

32 阅读5分钟

注解

1.1 自定义注解

用过的注解:@Override,@Test,@Data,@NoArgsConstructor,@AllArgsConstructor,@Deprecated等。

这些注解是别人定义好的,要么是核心类库中,要么是第三方,例如:JUnit,lombok等。

1.1.1 自定义注解的格式

public @interface 注解名{
    
}

示例:

package com.mytest.annotation;

public @interface OneAnnotation {
}

1.1.2 使用注解

使用注解的语法格式:在类或方法或属性上加@注解名

例如:

@OneAnnotation  //在类上面加注解
public class MyDemo {

    @OneAnnotation   //在属性上面加注解
    private int num;

    @OneAnnotation   //在方法上面加注解
    public void print(){
        System.out.println("我是一个方法而已");
    }
}

1.1.3 读取注解

如果只有声明和使用,注解没有任何意义,也不会对代码产生任何影响。

注解必须有一段程序来读取它,然后赋予它意义。

@Override注解是由 Java编译器来读取的。当编译器读取到某个方法上加了 @Override,注解,编译器就会执行一段代码,这段代码是检查该方法是不是严格按照重写的要求来编写的。如果不符合重写的要求,就会报错。

@Deprecated 注解是由 Java编译器和 javadoc.exe 来读取。 Java编译器如果到程序员使用了@Deprecated 标记的类或方法,就会弹出警告。 javadoc.exe 读取到@Deprecated 标记的类或方法,就会在API文档中标记 已过时。

@Test注解是由JUnit组件来读取的,就会让这个@Test标记的方法称为一个测试方法,可以独立运行。

下面,编写一段代码读取MyDemo类上面的@OneAnnotation:

package com.mytest.annotation;

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

@Retention(RetentionPolicy.RUNTIME)  //元注解
@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD}) //元注解
public @interface OneAnnotation {
}
package com.mytest.annotation;

@OneAnnotation  //在类上面加注解
public class MyDemo {

    @OneAnnotation   //在属性上面加注解
    private int num;

    @OneAnnotation   //在方法上面加注解
    public void print(){
        System.out.println("我是一个方法而已");
    }
}
package com.mytest.annotation;

import org.junit.Test;
import java.lang.annotation.Annotation;

public class TestReadOneAnnotation {
    @Test
    public void test1()throws Exception{
        //想要读取MyDemo类上面的@OneAnnotation
        //如果读取到了,我们就打印一句话:MyDemo类也有注解了
        Class clazz = Class.forName("com.mytest.annotation.MyDemo");
        Annotation[] annotations = clazz.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            if(annotation instanceof  OneAnnotation){
                System.out.println("MyDemo类也有注解了");
            }
        }

    }
}

1.1.4 元注解

当我们定义一个新注解时,加在注解上面的注解,称为元注解。它们是用于对这个新注解进行说明用的。

1、@Target

用于描述新注解可以使用的位置。这个位置由ElementType枚举类的常量对象决定,常见的常量对象有:METHOD,FIELD等。

2、@Retention

用于描述新注解可以保留到哪个阶段,即生命周期。这个生命周期由RetentionPolicy枚举类的3个常量对象之一决定:SOURCE,CLASS,RUNTIME。

  • SOURCE:源代码阶段,从java文件到.class文件,这个注解就没用了。
  • CLASS:字节码阶段,从字节码被加载到JVM,这个注解就没用了。
  • RUNTIME:运行时阶段,可以保留到JVM中。只有这个阶段的注解,才可以被反射读取。

3、@Documented

用于描述新注解要不要在生产API文档时,加上它的信息。

4、@Inherited

用于描述新注解能不能被子类继承

1.1.5 注解中的抽象方法

注解被看成是接口,但是它不是真的接口。

注解中也可以定义抽象方法,也只能定义抽象方法。而且这个抽象方法与普通接口的抽象方法有区别:

  • 这个抽象方法的返回值类型只能是8种基本数据类型及其包装类,String,枚举,Class类型以及它们的数组。
  • 这个抽象方法不能有形参列表,必须是空参
  • 这个抽象方法可以指定默认返回值,通过default关键字来指定
  • 这个抽象方法在使用注解的时候“重写”,它是在@注解名(抽象方法名=返回值)
  • 如果抽象方法名是value,且只有一个抽象方法必须重写的话,那么重写时,可以省略value=
package com.mytest.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
public @interface TwoAnnotation {
    String method() default "test";
    String show();
}
package com.mytest.annotation;

@OneAnnotation
@TwoAnnotation(show = "测试")
public class Example {
}
package com.mytest.annotation;

import org.junit.Test;
import java.lang.annotation.Annotation;

public class TestReadTwoAnnotation {
    @Test
    public void test1()throws Exception{
        Class clazz = Example.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            if(annotation instanceof TwoAnnotation){
                TwoAnnotation t = (TwoAnnotation) annotation;
                System.out.println(t.method());//调用注解的抽象方法
                System.out.println(t.show());//调用注解的抽象方法
            }
        }
    }
}

1.2 总结

Java中一切皆对象。

  • Class:Java中把所有数据类型,都看成是Class类的对象。你的程序中有几个类型(包括基本数据类型,类、接口等),相当于JVM中有几个Class类的对象。
  • Constructor:Java中把所有构造器都看成Constructor类的对象。你在程序中写了几个构造器,相当于底层就有几个Constructor类的对象。
    • 所有类的构造器都有修饰符、构造名、形参列表、都能new对象
  • Field:Java中把所有类的属性/成员变量都看成Field类的对象。
    • 所有类的属性都有修饰符、数据类型、属性名、属性值、都可以get/set值
  • Method:Java中把所有类的每一个方法都看成Method类的对象。
    • 所有类的方法都有修饰符、返回值类型、方法名、形参列表、都可以被执行方法体
  • Modifier:Java中所有每一个修饰符都是Modifier类中的一个常量值。

Java类的概念:一类具有相同特性的事物的抽象描述。

到底什么是反射?

反射机制是Java语言提供的一种 powerful 机制,允许程序在“运行时`”获取类的信息以及操作类的内部属性和方法。以下是反射机制的主要特点:
(1)动态获取类信息:可以通过Class对象获取类的构造函数、方法、字段等信息。
(2)创建实例:即使没有类的引用,也可以通过反射创建类的实例。
(3)调用方法:可以调用类中的方法,包括私有方法。
(4)设置和获取字段值:能够访问并修改类的私有字段。

需要注意的是,反射虽然强大但也有其缺点,比如性能开销较大,破坏了封装性,并且可能导致代码难以理解和维护。因此,在实际开发中应谨慎使用反射。