JavaSE : 注解 Annotation

注解
Java中的注解(Annotation)是一种元数据形式,用于向编译器或JVM提供有关程序元素(如类、方法、变量、参数和包)的附加信息。注解不会直接影响程序的行为或结构,但它们可以被编译器、开发工具或运行时环境用于生成代码、进行验证、执行处理或提供信息。以下是关于Java注解的几个关键点:

1. 注解的种类
1.1.内置标准注解:
@Override:指示一个方法覆盖了超类中的方法。
@Deprecated:标记已过时的元素,建议不再使用。
@SuppressWarnings:抑制编译器警告。
1.2.元注解:给注解用的注解
用于创建自定义注解的注解,包括:

1.2.1.@Retention:
指定注解保留策略(解析时间),如RetentionPolicy.SOURCE(只保留在源码中)、RetentionPolicy.CLASS(保留在class文件中)或RetentionPolicy.RUNTIME(运行时可通过反射访问)。

1.2.1.1. SOURCE : 编译时(Compile Time)
编译时解析的注解在源代码被编译成字节码时被处理。这些注解主要用于生成额外的代码、检查代码的正确性或进行其他静态分析。例如,@Override, @Deprecated, 和一些编译器使用的注解如Lombok的@Data就是在编译时期被解析的。编译器或注解处理器(如APT, Annotation Processing Tool)会读取这些注解并据此执行相应的操作。

1.2.1.2.CLASS : 类加载时(Class Load Time)
这类注解在类被加载到Java虚拟机(JVM)时被解析。例如,一些框架或库会在类加载阶段利用这些注解来配置类的行为,如Spring框架使用@Component, @Service, @Autowired等注解来实现依赖注入和bean的装配,这些注解会在应用上下文初始化,即类加载时被解析和处理。

1.2.1.3.RUNTIME : 运行时(Runtime)
运行时解析的注解在应用程序运行过程中可以被读取和处理。这意味着注解信息在程序执行期间始终可用,可以通过反射API来访问这些注解。例如,@Retention(RetentionPolicy.RUNTIME)注解的那些注解可以在程序运行时通过反射被检测到,常用于自定义注解处理、日志记录、权限控制等场景。

1.2.2.@Target:
指定注解可以应用到哪些程序元素上。

它接受一个ElementType类型的枚举值数组作为参数,ElementType枚举定义了一系列可能的目标元素类型。以下是ElementType的一些主要选项:

ElementType.TYPE: 类、接口(包括注解类型)或枚举声明。
ElementType.FIELD: 类或接口中的字段声明(包括枚举常量)。
ElementType.METHOD: 类或接口中的方法声明。
ElementType.PARAMETER: 方法参数声明。
ElementType.CONSTRUCTOR: 类的构造函数声明。
ElementType.LOCAL_VARIABLE: 本地变量声明。
ElementType.ANNOTATION_TYPE: 另一个注解的声明。
ElementType.PACKAGE: 包声明。
ElementType.TYPE_PARAMETER: 类型参数声明(Java 8及以后版本)。
ElementType.TYPE_USE: 任何类型使用的地方,如泛型参数、类型转换、静态成员的类型等(Java 8及以后版本)。
1.2.3.@Documented:
指示该注解应被包含在生成的API文档中。

1.2.4.@Inherited:
允许子类继承父类上的注解。

1.2.5.@Repeatable:
自Java 8起,允许同一注解在同一声明上多次使用。

1.3.自定义注解:
开发者可以定义自己的注解,通过@interface关键字实现,用于满足特定的编程需求或框架集成。

public @interface MyAnnotation {

}

1.3.1.注解的参数
定义注解参数

参数类型:注解参数必须是基本类型、String、Class、枚举类型、注解类型或者这些类型的数组。不能使用其他复杂类型如自定义类作为参数。

默认值:每个注解参数都可以有一个默认值,如果没有提供默认值且该参数又是必需的,则在使用注解时必须显式指定参数值。

参数命名:参数命名遵循Java的标识符规则,通常采用小驼峰命名法。

示例定义

public @interface MyAnnotation {
    String name() default "";      // 默认值为空字符串
    int age();                    // 必须提供值,没有默认值
    Class<?> clazz() default Object.class; // 默认值为Object类
    ElementType[] targets();       // 参数可以是数组类型
}

使用注解参数

在应用注解时,通过参数名指定参数值,如果是数组类型的参数,可以使用花括号 {} 来包裹多个值。

@MyAnnotation(name = "Alice", age = 25, clazz = String.class, targets = {ElementType.FIELD, ElementType.METHOD})
public class MyClass {
    // ...
}


特殊参数类型

Class类型参数:允许指定一个类或接口类型。
枚举类型参数:限制参数为预定义的一组值。
注解类型参数:允许注解作为参数,用于嵌套注解的情况。
数组类型参数:支持传递多个相同类型的值,如上面的targets参数。
1.3.2.value参数
在Java注解中,value是一个特殊的参数名。当一个注解只有一个参数,并且该参数名为value时,你可以省略参数名直接指定值。这种设计让注解的使用更加简洁易读。下面是具体说明和示例:

特殊之处

默认识别:如果一个注解仅定义了一个参数,并且这个参数的名字是value,那么在使用该注解时,可以直接写值而不必指定参数名。编译器会自动将这个值赋给value参数。
多参数注解中的value : 即使一个注解定义了多个参数,如果其中包含名为value的参数,你仍然可以在只有一个值需要传递时省略value=。但是,如果有多个值需要指定,则所有参数都必须明确指出参数名。
示例定义

public @interface MySingleValueAnnotation {
    String value(); // 注意这里只有一个名为value的参数
}

使用示例

@MySingleValueAnnotation("Some text") // 直接写值,不需要写value=
public class MyClass {
    // ...
}

// 对于非字符串或基本类型,同样适用
@MySingleValueAnnotation(123) // 假设value参数类型为int
public void myMethod() {
    // ...
}

2.访问注解信息
在Java中,通过反射处理注解是一种常见的做法,主要用于在运行时 ( 即使用@Retention(RUNTIME)定义的注解 ) 动态地检查和操作那些被注解标记的类、方法、字段等元素。以下是一些基本步骤和示例,展示如何使用反射来获取和使用注解信息:

2.1. 获取注解实例
首先,你需要获取到你想要检查的类、方法或字段的 Class 对象。之后,可以使用这个 Class 对象提供的几个方法来检查和获取注解:

isAnnotationPresent(Class<? extends Annotation> annotationClass):检查是否具有指定类型的注解。
getAnnotation(Class<? extends Annotation> annotationClass):获取指定类型的注解实例,如果没有则返回 null。
getAnnotations() 或 getDeclaredAnnotations():获取所有注解或直接声明的注解数组。
示例:获取类上的注解
假设我们有一个自定义的注解 @MyAnnotation,并应用到了某个类 MyClass 上。

@MyAnnotation(value = "Hello, World!")
public class MyClass {
    // ...
}

获取这个类上的 MyAnnotation 实例:

Class<MyClass> clazz = MyClass.class;
if (clazz.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation myAnnotation = clazz.getAnnotation(MyAnnotation.class);
    System.out.println("注解的值: " + myAnnotation.value());
}

示例:获取方法上的注解
同样的,如果 MyAnnotation 应用于某个方法上:

public class MyClass {
    @MyAnnotation(value = "Method Annotation")
    public void myMethod() {
        // ...
    }
}

获取方法上的注解:

Method method = MyClass.class.getMethod("myMethod");
if (method.isAnnotationPresent(MyAnnotation.class)) {
    MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
    System.out.println("方法注解的值: " + myAnnotation.value());
}

示例:基于注解值动态操作
根据注解的信息,你可以进一步决定程序的执行逻辑,例如:

if (myAnnotation != null && "Special Value".equals(myAnnotation.value())) {
    // 执行特定的操作
} else {
    // 执行默认操作
}

注意事项
确保你的注解有运行时保留策略 (@Retention(RetentionPolicy.RUNTIME)),否则它们不会在运行时可见。
当使用反射处理注解时,考虑性能影响,特别是在频繁操作或大型项目中。
利用注解可以极大地增强代码的灵活性和可配置性,但过度使用或设计不当也可能导致代码难以理解和维护。
3. 注解的作用
提供配置信息:注解可以替代XML或其他外部配置文件,直接在代码中提供配置细节。
代码文档化:增强代码的自我解释性,提供方法用途、参数意义等信息。
编译时检查:通过编译时注解处理器进行类型安全检查、格式验证等。
框架集成:许多Java框架(如Spring)使用注解来配置依赖注入、事务管理等。
代码生成:一些工具可以读取注解并据此自动生成代码片段,如序列化/反序列化代码、getter/setter等。
运行时处理:通过反射,应用程序可以在运行时发现和处理注解,实现动态行为。

                         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值