建一个网站需要什么网站百度
反射与注解
教学目标:
-
理解反射机制的核心原理与应用场景
-
掌握注解的定义与使用方法
-
能够通过反射动态操作类、方法和字段
-
结合反射与注解实现灵活的程序设计
一、课程引入
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
注解的实现原理