Java注解基础概念:初学者完全指南
·
📚 系列文章:这是Java注解深度学习系列的第一篇,专注于基础概念的讲解
📖 目录
🎯 什么是注解?
形象比喻
想象你在整理书籍,需要给每本书贴上不同颜色的标签:
- 🔴 红色标签:重要的书,需要经常翻阅
- 🟡 黄色标签:参考书,偶尔查阅
- 🟢 绿色标签:已读完的书,可以借给朋友
Java注解就像这些标签,它们:
- 不改变书的内容(不改变代码逻辑)
- 告诉你如何处理这本书(告诉编译器、框架如何处理代码)
- 提供额外信息(元数据)
技术定义
注解(Annotation)是Java 5.0引入的一种元数据机制,用于:
- 为代码元素提供额外信息
- 不直接影响程序的执行逻辑
- 可以在编译期、类加载期或运行期被读取和处理
// 最简单的注解示例
@Override // 这就是一个注解!
public String toString() {
return "Hello Annotation";
}
🔬 注解的本质
从字节码角度理解
让我们看看注解到底是什么:
// 源代码
public class User {
@Deprecated
public void oldMethod() {
System.out.println("这是一个过时的方法");
}
}
编译后,注解信息会被存储在class文件中:
// 简化的字节码表示
class User {
@Deprecated // 注解信息保存在这里
public void oldMethod() { ... }
}
注解的实现原理
- 编译时:编译器将注解信息写入class文件
- 运行时:JVM通过反射机制读取注解信息
- 框架处理:Spring等框架基于注解信息进行相应处理
// 运行时读取注解的示例
Method method = User.class.getMethod("oldMethod");
if (method.isAnnotationPresent(Deprecated.class)) {
System.out.println("这个方法被标记为过时");
}
📝 注解的语法规则
基本语法
@注解名称
@注解名称(参数)
@注解名称(参数1 = 值1, 参数2 = 值2)
使用位置
注解可以应用在多种代码元素上:
@Entity // 类级别注解
public class User {
@Id // 字段级别注解
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name", length = 50) // 字段注解(带参数)
private String name;
@Override // 方法级别注解
@Transactional(readOnly = true) // 方法注解(带参数)
public String toString() {
return "User{" + "name='" + name + '\'' + '}';
}
public void setName(@NotNull String name) { // 参数级别注解
this.name = name;
}
}
注解的参数类型
public @interface MyAnnotation {
String value(); // 字符串参数
int count() default 1; // 整数参数(带默认值)
Class<?> type() default Object.class; // 类参数
String[] tags() default {}; // 数组参数
RetentionPolicy policy() default RetentionPolicy.RUNTIME; // 枚举参数
}
// 使用示例
@MyAnnotation(
value = "示例",
count = 5,
type = String.class,
tags = {"tag1", "tag2"},
policy = RetentionPolicy.RUNTIME
)
public class Example { }
🏷️ 注解的分类
1. 按来源分类
内置注解(JDK提供)
@Override // 重写方法检查
@Deprecated // 标记过时
@SuppressWarnings("unused") // 抑制警告
@FunctionalInterface // 函数式接口标记(Java 8+)
第三方注解(框架提供)
// Spring框架
@Component, @Service, @Repository, @Controller
@Autowired, @Value, @Transactional
// Lombok框架
@Data, @Getter, @Setter, @Builder
// MyBatis框架
@Mapper, @Select, @Insert, @Update, @Delete
自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyCustomAnnotation {
String value();
}
2. 按作用时机分类
编译期注解
- 在编译时发挥作用
- 例:
@Override、@Deprecated
class Parent {
public void method() { }
}
class Child extends Parent {
@Override // 编译器检查是否正确重写
public void method() { } // 编译时验证
}
运行期注解
- 在程序运行时通过反射处理
- 例:Spring的
@Autowired、@Transactional
@Service // 运行时Spring容器处理
public class UserService {
@Autowired // 运行时依赖注入
private UserRepository userRepository;
}
⏰ 注解的生命周期
保留策略(RetentionPolicy)
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE) // 源码期:编译后丢弃
public @interface SourceAnnotation { }
@Retention(RetentionPolicy.CLASS) // 字节码期:运行时不可见(默认)
public @interface ClassAnnotation { }
@Retention(RetentionPolicy.RUNTIME) // 运行期:运行时可通过反射获取
public @interface RuntimeAnnotation { }
生命周期对比
| 阶段 | SOURCE | CLASS | RUNTIME |
|---|---|---|---|
| 源码中 | ✅ | ✅ | ✅ |
| 字节码中 | ❌ | ✅ | ✅ |
| 运行时 | ❌ | ❌ | ✅ |
| 典型应用 | Lombok | 字节码增强 | Spring框架 |
实际示例
// SOURCE示例:Lombok的@Data在编译后就消失了
@Data // 编译时生成代码,运行时注解消失
public class User {
private String name;
// 编译后会有getName()、setName()等方法
}
// RUNTIME示例:Spring的注解需要运行时处理
@Service // 运行时Spring会扫描并创建Bean
public class UserService {
// Spring在运行时通过反射处理这个注解
}
🔧 内置注解详解
@Override - 重写方法验证
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
public void eat(String food) {
System.out.println("动物吃" + food);
}
}
class Dog extends Animal {
@Override
public void makeSound() { // ✅ 正确重写
System.out.println("汪汪汪");
}
// @Override
// public void makSound() { // ❌ 编译错误:方法名拼写错误
// System.out.println("汪汪汪");
// }
@Override
public void eat(String food) { // ✅ 正确重写
System.out.println("狗狗吃" + food);
}
// @Override
// public void eat(int amount) { // ❌ 编译错误:参数类型不匹配
// System.out.println("吃了" + amount + "份食物");
// }
}
@Override的好处:
- 防止方法名拼写错误
- 确保方法签名正确
- 代码更安全可靠
@Deprecated - 过时标记
public class Calculator {
@Deprecated // 标记为过时方法
public int add(int a, int b) {
return a + b;
}
// 推荐使用的新方法
public long addLong(long a, long b) {
return a + b;
}
}
// 使用效果
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
// IDE会显示删除线和警告
int result = calc.add(1, 2); // 过时方法调用警告
long result2 = calc.addLong(1L, 2L); // 推荐方法
}
}
@SuppressWarnings - 抑制警告
public class WarningExample {
@SuppressWarnings("unused") // 抑制"未使用变量"警告
public void method1() {
String unusedVariable = "这个变量没有被使用";
// 正常情况下IDE会警告,但@SuppressWarnings会抑制
}
@SuppressWarnings({"unchecked", "rawtypes"}) // 抑制多种警告
public void method2() {
List list = new ArrayList(); // 原始类型警告被抑制
list.add("item");
}
@SuppressWarnings("all") // 抑制所有警告(不推荐)
public void method3() {
// 所有警告都会被抑制
}
}
常见警告类型:
"unused"- 未使用的变量"unchecked"- 未检查的类型转换"rawtypes"- 使用原始类型"deprecation"- 使用过时的API"all"- 所有警告
🛠️ 自定义注解入门
定义注解的基本步骤
import java.lang.annotation.*;
// 1. 使用@interface定义注解
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Target(ElementType.METHOD) // 只能用在方法上
@Documented // 包含在JavaDoc中
public @interface LogExecutionTime {
// 2. 定义注解参数
String value() default ""; // 默认参数
boolean enable() default true; // 是否启用
int timeout() default 5000; // 超时时间(毫秒)
}
元注解详解
// @Retention:注解的保留策略
@Retention(RetentionPolicy.RUNTIME) // 运行时保留
@Retention(RetentionPolicy.CLASS) // 编译时保留(默认)
@Retention(RetentionPolicy.SOURCE) // 源码时保留
// @Target:注解的使用位置
@Target(ElementType.TYPE) // 类、接口、枚举
@Target(ElementType.METHOD) // 方法
@Target(ElementType.FIELD) // 字段
@Target(ElementType.PARAMETER) // 方法参数
@Target(ElementType.CONSTRUCTOR) // 构造函数
@Target({ElementType.METHOD, ElementType.FIELD}) // 多个位置
// @Documented:是否包含在JavaDoc中
@Documented
public @interface MyAnnotation { }
// @Inherited:是否可被子类继承
@Inherited
public @interface InheritedAnnotation { }
使用自定义注解
public class UserService {
@LogExecutionTime("用户查询") // 使用默认参数
public User findUser(Long id) {
// 查询用户逻辑
return new User();
}
@LogExecutionTime(
value = "用户创建",
enable = true,
timeout = 10000
) // 使用自定义参数
public void createUser(User user) {
// 创建用户逻辑
}
}
通过反射处理注解
import java.lang.reflect.Method;
public class AnnotationProcessor {
public static void processAnnotations(Class<?> clazz) {
// 获取类的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
// 检查方法是否有LogExecutionTime注解
if (method.isAnnotationPresent(LogExecutionTime.class)) {
// 获取注解实例
LogExecutionTime annotation = method.getAnnotation(LogExecutionTime.class);
// 读取注解参数
String methodName = annotation.value();
boolean enabled = annotation.enable();
int timeout = annotation.timeout();
System.out.println("方法: " + method.getName());
System.out.println("描述: " + methodName);
System.out.println("启用: " + enabled);
System.out.println("超时: " + timeout + "ms");
System.out.println("---");
}
}
}
public static void main(String[] args) {
processAnnotations(UserService.class);
}
}
📚 总结
关键要点
- 注解是元数据:为代码提供额外信息,不改变执行逻辑
- 三个生命周期:SOURCE(源码期)、CLASS(字节码期)、RUNTIME(运行期)
- 四种应用场景:编译检查、运行处理、代码生成、配置简化
- 内置注解要掌握:
@Override、@Deprecated、@SuppressWarnings - 自定义注解:使用
@interface定义,配合元注解使用
学习路径
- ✅ 掌握基础概念(本篇)
- 📖 学习四大作用(下一篇:Java注解四大作用详解)
- 🚀 Spring注解实战(第三篇:Spring注解实战指南)
实践建议
- 多看开源项目中的注解使用
- 动手编写简单的自定义注解
- 理解注解背后的反射机制
- 结合具体框架学习注解应用
🔗 下一篇预告:Java注解四大作用详解 - 深入理解编译时检查、运行时处理、代码生成和配置简化四大核心功能
更多推荐

所有评论(0)