编程自学指南:java程序设计开发,反射与注解,反射机制,注解
反射与注解
教学目标:
-
理解反射机制的核心原理与应用场景
-
掌握注解的定义与使用方法
-
能够通过反射动态操作类、方法和字段
-
结合反射与注解实现灵活的程序设计
一、课程引入
1.1 为什么需要反射与注解?
-
反射:
-
运行时动态获取类信息(如框架中自动装配对象)
-
突破封装访问私有成员(慎用!)
-
-
注解:
-
为代码添加元数据(如
@Override
标记重写方法) -
替代XML配置,简化开发(如Spring的
@Autowired
)
-
1.2 生活类比
-
反射:像“X光透视”,查看程序内部结构
-
注解:像“标签”,为代码添加说明或指令
二、反射机制
2.1 反射核心API
类/接口 | 作用 |
---|---|
Class | 表示类或接口 |
Field | 表示类的成员变量 |
Method | 表示类的方法 |
Constructor | 表示类的构造方法 |
2.2 获取Class对象的三种方式
// 方式1:类名.class
Class<String> clazz1 = String.class;
// 方式2:对象.getClass()
String str = "Java";
Class<?> clazz2 = str.getClass();
// 方式3:Class.forName()
Class<?> clazz3 = Class.forName("java.lang.String");
案例1:动态加载类并创建对象
Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.newInstance(); // 调用无参构造
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object user2 = constructor.newInstance("张三", 25);
2.3 操作私有成员
案例2:访问私有方法
public class Secret {
private void hiddenMethod() {
System.out.println("秘密方法被调用!");
}
}
// 反射调用
Secret obj = new Secret();
Method method = Secret.class.getDeclaredMethod("hiddenMethod");
method.setAccessible(true); // 突破私有限制
method.invoke(obj);
三、注解
3.1 内置注解
-
@Override
:标记方法重写 -
@Deprecated
:标记已过时的方法 -
@SuppressWarnings
:抑制编译器警告
3.2 元注解(注解的注解)
元注解 | 作用 |
---|---|
@Target | 定义注解可应用的目标(如方法、字段) |
@Retention | 定义注解保留策略(SOURCE/CLASS/RUNTIME) |
@Documented | 注解包含在Javadoc中 |
3.3 自定义注解
案例3:定义字段校验注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
String message() default "字段不能为空";
}
// 应用注解
public class User {
@NotNull(message = "姓名不能为空")
private String name;
}
3.4 注解处理器(反射解析注解)
案例4:实现注解校验逻辑
public static void validate(Object obj) throws IllegalAccessException {
for (Field field : obj.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(NotNull.class)) {
field.setAccessible(true);
Object value = field.get(obj);
if (value == null) {
NotNull anno = field.getAnnotation(NotNull.class);
throw new IllegalArgumentException(anno.message());
}
}
}
}
// 测试
User user = new User();
validate(user); // 抛出异常:姓名不能为空
四、综合案例
4.1 案例5:简易ORM框架
// 定义表名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String name();
}
// 定义字段注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String name();
String type();
}
// 应用注解
@Table(name = "users")
public class User {
@Column(name = "user_id", type = "INT")
private int id;
@Column(name = "user_name", type = "VARCHAR(50)")
private String name;
}
// 生成SQL建表语句
public static String generateCreateTable(Class<?> clazz) {
if (!clazz.isAnnotationPresent(Table.class)) return null;
Table table = clazz.getAnnotation(Table.class);
StringBuilder sql = new StringBuilder("CREATE TABLE " + table.name() + " (\n");
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
sql.append(" ").append(column.name()).append(" ").append(column.type()).append(",\n");
}
}
sql.deleteCharAt(sql.length() - 2); // 删除末尾逗号
sql.append(");");
return sql.toString();
}
// 输出结果:
// CREATE TABLE users (
// user_id INT,
// user_name VARCHAR(50)
// );
五、常见错误与最佳实践
5.1 常见错误
-
错误1:忽略注解的保留策略
@Retention(RetentionPolicy.SOURCE) public @interface MyAnnotation {} // 运行时无法通过反射获取该注解
-
错误2:反射修改final字段
Field field = String.class.getDeclaredField("value"); field.setAccessible(true); field.set("Hello", new char[] {'H','i'}); // 抛出IllegalAccessException
5.2 最佳实践
-
反射:
-
优先使用公共API,避免操作私有成员
-
缓存反射结果(如Method对象)提升性能
-
-
注解:
-
明确
@Target
和@Retention
策略 -
结合工具(如APT)生成代码
-
六、总结与练习
6.1 总结
-
反射:动态操作类结构,但破坏封装性,需谨慎使用
-
注解:为代码添加元数据,需配合处理器实现功能
6.2 课后任务
-
实现一个JSON序列化工具(通过反射将对象转为JSON字符串,支持
@JsonIgnore
注解忽略字段) -
自定义一个
@Test
注解,标记测试方法并通过反射自动运行 -
预习下一节课:Lambda表达式与Stream API
6.3 扩展挑战
-
阅读Spring框架源码,分析
@Autowired
注解的实现原理