1525 字
4 分钟
Java 注解与反射
JDBC
Java Database Connectivity
用于连接和操作关系型数据库的 Java API。它提供了一组接口和类,使 Java 应用程序能够与各种数据库进行交互。
访问步骤
-
加载数据库驱动
-
建立连接
DriverManager.getConnection() -
创建连接对象
-
创建语句对象
-
执行sql语句获取执行结果
-
关闭连接(先开后关)
连接池
预先创建好的多个数据库连接对象,节省每次new连接对象的开销。
注解
概念
注解(Annotation):贴在代码上的“标签”,可以被编译器、框架或工具读取的特殊备注。
- 格式:@注解名
- 作用范围:package、class、method、field
- 访问:可以通过反射机制对这些元数据访问
内置注解
包:java.lang
| 注解 | 作用 | 使用位置 | 是否保留到运行时 |
|---|---|---|---|
@Override | 表示当前方法重写了父类的方法。如果父类没有这个方法,编译会报错。 | 方法上 | 否(只在编译期有效) |
@Deprecated | 表示某个类、方法或字段已过时,不推荐使用。使用时编译器会警告你。 | 类 / 方法 / 字段 | 是(保留到运行时) |
@SuppressWarnings | 抑制编译器警告。比如你用了不安全的泛型操作,可以用它关掉警告。 | 类 / 方法 / 字段 / 局部变量 | 否 |
@FunctionalInterface | 表示这是一个函数式接口(只能有一个抽象方法)。如果不是,编译报错。 | 接口上 | 否 |
@SafeVarargs | 用于可变参数方法,告诉编译器“我知道有泛型风险,但我保证安全”,从而消除警告。 | 方法 / 构造器 | 否 |
@SuppressWarnings参数
| 参数值 | 作用说明 |
|---|---|
"unchecked" | 忽略泛型未检查转换警告(如 List list = new ArrayList();) |
"deprecation" | 忽略使用了 @Deprecated 过时代码的警告 |
"rawtypes" | 忽略使用原始类型(如 List 而不是 List<String>) |
"unused" | 忽略未使用的变量、方法、参数等 |
"fallthrough" | 忽略 switch 中 case 穿透(无 break)警告 |
"serial" | 忽略可序列化类缺少 serialVersionUID 的警告 |
"all" | idea自带,不推荐 |
元注解
包:java.lang.annotation
@Retention
作用:指定注解保留到哪个阶段。
参数类型:RetentionPolicy
| 参数值 | 含义 | 是否可通过反射获取 |
|---|---|---|
RetentionPolicy.SOURCE | 只在源代码中存在,编译时丢弃 | 不能 |
RetentionPolicy.CLASS | 编译后保留在 .class 文件中,但 JVM 不加载 | 不能(默认值) |
RetentionPolicy.RUNTIME | 保留在运行时,JVM 加载后可用 | 能(常用) |
@Target
作用:限制注解能标注在哪些程序元素上。
参数类型:ElementType[](数组,可写多个)
| 参数值 | 可标注的位置 |
|---|---|
ElementType.TYPE | 类、接口、注解、枚举 |
ElementType.FIELD | 字段(成员变量) |
ElementType.METHOD | 方法 |
ElementType.PARAMETER | 方法参数 |
ElementType.CONSTRUCTOR | 构造方法 |
ElementType.LOCAL_VARIABLE | 局部变量 |
ElementType.ANNOTATION_TYPE | 注解类型本身(用于元注解) |
ElementType.PACKAGE | 包(需配合 package-info.java 使用) |
ElementType.TYPE_PARAMETER | 类型参数(Java 8+,如 class MyClass<T> 中的 T) |
ElementType.TYPE_USE | 任何类型使用处(Java 8+,如 List<@My String>) |
示例:
@Target({ElementType.METHOD, ElementType.FIELD})
@Documented
作用:是否在生成 JavaDoc 时包含该注解。
参数:无参数(标记型注解)
@Inherited
作用:父类的注解是否被子类继承(仅对类有效)。
参数:无参数(标记型注解)
@Repeatable
作用:允许同一个地方多次使用同一个注解。
参数:value(必须指定一个“容器注解”的 class)
| 参数名 | 类型 | 说明 |
|---|---|---|
value | Class<? extends Annotation> | 指定一个“容器注解”,用于存放重复的注解 |
自定义注解
- 1个参数,名为value,使用注解可以不给参数名
- default参数默认值:不显式声明时使用
@元注解public @interface 注解名{ //注解参数: 类型 参数名() default 默认值;}反射
概念
- 静态语言:运行时代码能改变自身结构(Java、C、C++)
- 动态语言:运行时代码不能改变自身结构(C#、JavaScript、Python)
- 反射:程序运行期间,动态获取类的结构信息(类名、构造器、方法、字段、注解等),并对其进行操作(创建对象、调用方法、访问字段)。
- Java:通过反射机制成为准动态语言
Class
包:java.lang.Class、java.lang.reflect
- 动态获取类的结构信息通过Class类
- Class 本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在 JVM 中只会有一个 Class 实例
- 一个 Class 对象对应的是一个加载到 JVM 中的一个 class 文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过 Class 可以完整地得到一个类中的所有被加载的结构
- Class 类是 Reflection 的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class 对象
四种方式获取:
// 1. 通过类名(最常用,最灵活)Class<?> c1 = Class.forName("com.example.User");
// 2. 通过类.class(编译期已知)Class<User> c2 = User.class;
// 3. 通过对象.getClass()User u = new User();Class<? extends User> c3 = u.getClass();
// 4. 包装类通过TYPE属性Class c4 = Integer.TYPE;| 方式 | 返回类型 | 类型是否已知 | 是否触发类初始化 | 使用场景 | 注意事项 |
|---|---|---|---|---|---|
Class.forName() | Class<?> | 运行时才知道 | 执行 static 块 | 动态加载、配置文件、框架反射 | 必须类在 classpath;要写全限定名;可能抛 ClassNotFoundException |
类名.class | Class<User> | 编译期已知 | 不会触发 | 编译时确定的类;泛型安全 | 最安全高效;不能用于动态类名 |
对象.getClass() | Class<? extends User> | 运行时真实类型 | 类必须已加载 | 多态场景;获取实际类型 | 返回的是实际类型,可能是子类 |
包装类 .TYPE | Class<Integer> | 已知 | 不触发 | 获取基本类型对应的 Class | Integer.TYPE == int.class,不是 Integer.class |
常用方法
获取类信息
c1.getName(); // 全限定名c1.getSimpleName(); // 类名c1.getSuperclass(); // 父类c1.getInterfaces(); // 接口c1.getAnnotations(); // 注解示例
class User { private String name;
private User(String name) { this.name = name; }
private void hello() { System.out.println("Hello " + name); }}
// 反射使用Class<?> clazz = Class.forName("User");
// 创建对象Constructor<?> con = clazz.getDeclaredConstructor(String.class);con.setAccessible(true);Object obj = con.newInstance("Tom");
// 调用方法Method m = clazz.getDeclaredMethod("hello");m.setAccessible(true);m.invoke(obj);优缺点
-
高度灵活,解耦严重依赖
-
支持运行时扩展
-
是大型框架的基础
-
性能较低(绕过编译期优化)
-
破坏封装性
-
编译期无法检查错误
-
代码可读性、可维护性下降
结论: 业务代码慎用,框架代码必用
对比
| 对比项 | new | 反射 |
|---|---|---|
| 编译期检查 | 有 | 无 |
| 性能 | 高 | 低 |
| 灵活性 | 低 | 高 |
| 是否依赖类名字符串 | 否 | 是 |
Java 注解与反射
https://oddpalmer.space/posts/注解与反射/注解与反射/ 部分信息可能已经过时