本文目录导读:

在Java中获取类的所有属性(字段或方法等),主要依赖于反射机制,以下是几种常见且实用的案例和实现方式。
核心API回顾
反射中用于获取属性的核心类和方法:
Class.getFields():获取所有 public 属性(包含父类继承的)。Class.getDeclaredFields():获取当前类中 所有访问权限 的属性(不包括父类)。Field类:代表一个属性,可获取名称、类型、修饰符等。
完整代码案例
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
public class FieldReflectionDemo {
// 示例父类
static class Animal {
public String species = "unknown";
private int age = 0;
}
// 示例子类
static class Dog extends Animal {
public String name = "旺财";
private String breed;
protected double weight;
String color; // 默认访问权限(包级别)
}
public static void main(String[] args) {
// 获取 Dog 类的所有属性
List<Field> allFields = getAllFields(Dog.class);
System.out.println("=== 获取 Dog 类所有属性(包括私有、父类) ===");
for (Field field : allFields) {
System.out.printf("字段名: %s | 类型: %s | 修饰符: %s%n",
field.getName(),
field.getType().getSimpleName(),
Modifier.toString(field.getModifiers()));
}
System.out.println("\n=== 仅获取当前类声明的所有属性(不包括父类) ===");
Field[] declaredFields = Dog.class.getDeclaredFields();
for (Field field : declaredFields) {
System.out.printf("字段名: %s | 类型: %s | 可访问: %s%n",
field.getName(),
field.getType().getSimpleName(),
field.canAccess(null)); // 默认不可访问私有字段
}
}
/**
* 获取类的所有属性(包括私有和继承的)
* @param clazz 目标类
* @return 属性列表
*/
public static List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
// 递归遍历父类
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
// 排除 Object 类中的字段(一般不需要)
if (!"java.lang.Object".equals(clazz.getName())) {
fields.add(field);
}
}
clazz = clazz.getSuperclass();
}
return fields;
}
}
运行结果示例:
=== 获取 Dog 类所有属性(包括私有、父类) ===
字段名: name | 类型: String | 修饰符: public
字段名: breed | 类型: String | 修饰符: private
字段名: weight | 类型: double | 修饰符: protected
字段名: color | 类型: String | 修饰符:
字段名: species | 类型: String | 修饰符: public
字段名: age | 类型: int | 修饰符: private
注意:getDeclaredFields() 默认不会影响 private 字段的访问权限,若需要操作(读取/修改)私有字段,需调用 field.setAccessible(true)。
不同场景的获取方式对比
| 需求场景 | 推荐方法 | 注意事项 |
|---|---|---|
| 仅获取当前类所有字段(含私有) | getDeclaredFields() |
快速获取,不含父类字段 |
| 获取当前类及父类所有字段(常需要) | 手动递归 + getDeclaredFields() |
注意循环调用 getSuperclass(),避免包含 Object 类 |
| 获取所有 public 字段(含继承) | getFields() |
会包含父类 public 字段,但忽略私有字段 |
| 获取指定名称的字段 | getDeclaredField("fieldName") |
若字段不存在会抛 NoSuchFieldException |
高级用法:获取属性的完整信息
import java.lang.reflect.Field;
import java.lang.reflect.Type;
public class FieldDetailExample {
static class User {
public String username = "admin";
private int age = 25;
}
public static void main(String[] args) throws Exception {
User user = new User();
// 获取所有字段并打印详细信息
for (Field field : User.class.getDeclaredFields()) {
System.out.println("字段名: " + field.getName());
System.out.println("类型: " + field.getType().getSimpleName());
System.out.println("泛型类型: " + field.getGenericType());
System.out.println("是否为枚举常量: " + field.isEnumConstant());
// 动态获取字段值(注意私有字段需要 setAccessible)
field.setAccessible(true);
Object value = field.get(user);
System.out.println("当前值: " + value);
System.out.println("----------------------");
}
}
}
输出示例:
字段名: username
类型: String
泛型类型: class java.lang.String
是否为枚举常量: false
当前值: admin
----------------------
字段名: age
类型: int
泛型类型: int
是否为枚举常量: false
当前值: 25
----------------------
性能与安全建议
- 缓存反射结果:重复获取字段列表时,建议缓存(如用
Map<Class<?>, Field[]>),因为反射操作相对较慢。 - 设置可访问性控制:使用
setAccessible(true)后,记得在不再需要时还原为false,或只在无法避免时使用(某些环境有安全管理器限制)。 - 避免过度使用:在框架或库内部合理使用,业务代码中尽量用具体类的方法和属性,保持类型安全。
示例应用场景
| 应用场景 | 说明 |
|---|---|
| ORM框架(如MyBatis) | 通过字段名映射数据库列名 |
| JSON序列化/反序列化 | 获取所有字段进行动态处理 |
| 对象拷贝/克隆工具 | 遍历字段进行属性值复制 |
| 配置注入工具 | 检测配置类字段并赋值 |
| IDE调试器或日志工具 | 打印对象的完整属性状态 |