一、上午 3h 注解入门 + Java 内置注解
1. 注解核心概念详细讲解
1.1 什么是注解
注解(Annotation):Java 中的一种标记 ,写在类、方法、变量、参数上方,用来给程序附加额外信息。
1.2 注解三大核心作用
- 做标记:标记方法重写、类过时、忽略警告
- 简化代码:替代 XML 配置文件,框架主流用法
- 程序识别 :运行时通过反射读取注解信息,自动执行逻辑
1.3 注解 vs 注释 本质区别(必背)
表格
| 类型 | 作用范围 | 程序能否读取 | 用途 |
|---|---|---|---|
| 单行 // 多行 /* */ 注释 | 仅给程序员看 | 不能被 JVM / 代码解析 | 代码说明、笔记 |
| 注解 @XXX | 标记程序元素 | 可以被反射读取 | 框架配置、业务标记、校验 |
1.4 注解使用位置
可标注:类、接口、成员变量、方法、形参、构造方法、局部变量等
2. Java 三大内置常用注解(重点 + 代码案例)
2.1 @Override 重写校验
作用 :强制校验当前方法是否真的重写了父类方法,写错方法名直接编译报错使用场景:子类重写父类 / 接口方法
java
运行
class Animal{
public void eat(){
System.out.println("动物吃饭");
}
}
class Dog extends Animal{
// 加上@Override 严格校验重写格式
@Override
public void eat() {
System.out.println("小狗啃骨头");
}
}
解释 :不加也能重写,加上是语法校验,防止手写错误。
2.2 @Deprecated 标记过时 / 废弃
作用:标记类、方法、变量已经过时,不建议继续使用,调用时出现删除线提示
java
运行
public class TestDeprecated {
// 标记该方法已过时
@Deprecated
public void oldMethod(){
System.out.println("老旧方法");
}
public static void main(String[] args) {
TestDeprecated t = new TestDeprecated();
t.oldMethod(); // 代码出现删除线,提示废弃
}
}
拓展:源码中大量旧 API 全部使用此注解标记。
2.3 @SuppressWarnings 压制警告
作用:消除代码中黄色警告,不影响程序运行,仅美化代码提示常用参数:
@SuppressWarnings("all"):压制所有警告@SuppressWarnings("unused"):压制未使用变量警告@SuppressWarnings("rawtypes"):压制泛型未指定警告
java
运行
import java.util.ArrayList;
public class TestWarning {
// 压制所有警告
@SuppressWarnings("all")
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 无泛型警告直接消失
int a; // 未使用变量警告消失
}
}
3. 注解初识定义格式
简易格式:
java
运行
public @interface 注解名{
}
@interface 是定义注解专用关键字,和 class、interface 同级。
二、下午 2.5h 四大元注解 + 自定义注解(核心重难点)
1. 四大元注解(用来修饰自定义注解,控制注解规则)
元注解:注解的注解,专门用来规定自定义注解能怎么用
1.1 @Target 【重中之重】
作用 :限定自定义注解只能写在哪些位置常用取值(ElementType 枚举):
ElementType.TYPE:作用在 类、接口、枚举上ElementType.FIELD:作用在 成员变量上ElementType.METHOD:作用在 成员方法上ElementType.PARAMETER:作用在 方法参数上ElementType.CONSTRUCTOR:作用在构造方法上
1.2 @Retention 【最核心,决定能否被反射读取】
作用 :设置注解保留生命周期,三个级别优先级由低到高
RetentionPolicy.SOURCE:仅存在源码阶段,编译后消失 → 不能反射读取RetentionPolicy.CLASS:保留到class 字节码,运行时丢失 → 默认级别,无法反射解析RetentionPolicy.RUNTIME:保留到程序运行时 → 只有这个能被反射读取解析
实战开发、框架注解 必须写 RUNTIME
1.3 @Documented
作用:使用 javadoc 生成 API 文档时,将注解信息一同写入文档,纯文档作用。
1.4 @Inherited
作用 :父类上加了该注解,子类自动继承父类身上的这个注解。
2. 自定义注解完整语法 + 三类案例
规则
- 使用
@interface定义 - 内部可以定义属性 ,格式:
数据类型 属性名() default 默认值; - 属性支持类型:基本类型、String、枚举、注解、以上类型数组
2.1 无属性自定义注解(标记注解)
java
运行
import java.lang.annotation.*;
// 限定只能作用在类上
@Target(ElementType.TYPE)
// 运行时有效,支持反射读取
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFlagAnno {
// 无任何属性,纯做标记
}
使用:
java
运行
@MyFlagAnno
public class User {
}
2.2 单属性自定义注解 + value 简写
规则 :注解中只有一个属性,且名字为value,使用时可省略value=直接赋值
java
运行
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValueAnno {
// 唯一属性value
String value();
}
正常写法:
java
运行
@MyValueAnno(value = "学生管理类")
简写写法(最常用):
java
运行
@MyValueAnno("学生管理类")
2.3 多属性自定义注解 + 设置默认值
java
运行
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInfoAnno {
// 注解属性,带默认值
String author() default "匿名开发者";
int version() default 1;
String desc(); // 无默认值,使用时必须赋值
}
使用方式:
java
运行
public class Book {
@MyInfoAnno(desc = "查询书籍信息",author = "张三",version = 2)
public void selectBook(){
}
}
三、晚上 1.5h 反射解析注解 + 实战练习(注解最终用途)
核心原理
只有被@Retention(RetentionPolicy.RUNTIME)修饰的注解,才能通过反射获取
1. 反射常用注解解析 API
java
运行
// 1. 判断类上是否存在指定注解
clazz.isAnnotationPresent(注解名.class)
// 2. 获取类上的注解对象
注解类型 变量 = clazz.getAnnotation(注解名.class)
// 3. 获取方法/变量上注解同理
method.getAnnotation()
field.getAnnotation()
2. 完整综合案例:反射读取自定义注解属性
步骤 1:定义运行时注解
java
运行
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassInfo {
String className();
String type() default "业务类";
}
步骤 2:实体类使用注解
java
运行
@ClassInfo(className = "用户实体类",type = "数据实体")
public class UserEntity {
}
步骤 3:反射解析注解(核心代码)
java
运行
import java.lang.annotation.Annotation;
public class AnnoParseTest {
public static void main(String[] args) {
// 1. 获取Class对象
Class<UserEntity> clazz = UserEntity.class;
// 2. 判断是否标注了ClassInfo注解
boolean hasAnno = clazz.isAnnotationPresent(ClassInfo.class);
if(hasAnno){
// 3. 获取注解对象
ClassInfo classInfo = clazz.getAnnotation(ClassInfo.class);
// 4. 读取注解里面的属性值
String name = classInfo.className();
String type = classInfo.type();
System.out.println("注解类名:"+name);
System.out.println("注解类型:"+type);
}
}
}
逐行解释
isAnnotationPresent:判断标记是否存在getAnnotation:拿到注解实例- 直接调用注解内部属性名 () 即可取值
3. 简易实战:模拟框架注解配置
需求:自定义注解配置端口号,通过注解读取启动端口
1. 定义配置注解
java
运行
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServerConfig {
int port() default 8080;
String host() default "127.0.0.1";
}
2. 服务类使用注解
java
运行
@ServerConfig(port = 8888,host = "localhost")
public class WebServer {
}
3. 工具类读取注解自动启动
java
运行
public class ServerStart {
public static void main(String[] args) {
Class<WebServer> clazz = WebServer.class;
ServerConfig config = clazz.getAnnotation(ServerConfig.class);
System.out.println("服务器IP:"+config.host());
System.out.println("启动端口:"+config.port());
System.out.println("模拟服务器启动成功");
}
}
实战意义:SpringBoot 配置端口、MyBatis 映射路径底层全是这套逻辑。
4. 注解在框架中的底层用途总结
- Spring:
@Component @Controller扫描注解,反射创建对象存入容器 - SpringMVC:
@RequestMapping解析注解路径,映射请求地址 - MyBatis:注解配置 SQL 语句,运行时反射读取执行
- JUnit:
@Test标记测试方法,框架识别自动运行
四、计划遗漏必补重要知识点(你原计划没有,必考)
1. 注解属性限制
- 不能定义 void、自定义实体类等非常规类型
- 数组属性用法:
String[] value() default {};
2. 重复注解(JDK8 + 新特性)
同一位置可以多次使用同一个注解,框架高频使用
3. 注解不支持继承方法属性
自定义注解不能继承类,只能被元注解 @Inherited 让子类继承标记
4. 编译期注解与运行期注解使用场景区分
- SOURCE:仅编译校验(@Override 本质就是源码级别)
- RUNTIME:业务框架配置、反射解析必备
5. 注解没有执行逻辑
注解本身只是标记,无任何代码逻辑 ,所有功能全靠反射代码读取注解后实现
五、Day30 最终达标验收完整版
- 分清注解与注释本质区别,说出三大内置注解用法
- 熟记四大元注解作用、@Target 位置、@Retention 三级生命周期
- 独立手写:无属性、单属性 value 简写、多属性带默认值三种自定义注解
- 熟练使用反射 API:判断注解存在、获取注解、读取注解属性值
- 能独立完成「配置注解 + 反射读取」简易实战案例
- 理解主流框架底层依靠注解 + 反射实现自动化配置原理
六、统一规范(JDK17 无报错)
- 所有自定义注解必须加上
@Retention(RetentionPolicy.RUNTIME)才能解析 - 一个 java 文件仅一个 public 类,练习代码全部整合无冲突
- 所有案例均可直接复制运行,无需额外导包
整体流程(先看懂大框架)
- 定义注解 → 相当于做一张自定义标签
- 使用注解 → 把标签贴在类上
- 反射解析注解 → 把标签撕下来读内容