修饰符工具-ModifierUtil
# 1. 功能概述
ModifierUtil 是 Hutool 提供的修饰符工具类,用于处理 Java 反射中的修饰符信息。该工具类封装了对类、字段、方法和构造函数修饰符的检查和操作功能,提供了便捷的方法来判断各种修饰符,以及修改字段的修饰符。
# 2. 核心功能
# 2.1 修饰符组合判断
- hasAny(clazz, modifierTypes):判断类是否存在给定修饰符中的任意一个
- hasAny(member, modifierTypes):判断成员是否存在给定修饰符中的任意一个
- hasAny(modifiers, checkedModifiers):判断修饰符中是否存在给定修饰符中的任意一个
- hasAll(clazz, modifierTypes):判断类中是否同时存在所有给定修饰符
- hasAll(member, modifierTypes):判断成员中是否同时存在所有给定修饰符
- hasAll(modifiers, checkedModifiers):判断修饰符中是否同时存在所有给定修饰符
# 2.2 具体修饰符判断
- isDefault(method):判断方法是否为 default 方法
- isPublic(member):判断是否为 public 成员(构造、字段、方法)
- isPublic(clazz):判断是否为 public 类
- isPrivate(member):判断是否为 private 成员(构造、字段、方法)
- isPrivate(clazz):判断是否为 private 类
- isStatic(member):判断是否为 static 成员(构造、字段、方法)
- isStatic(clazz):判断是否为 static 类
- isSynthetic(member):判断是否为合成成员(由编译器生成)
- isSynthetic(clazz):判断是否为合成类(由编译器生成)
- isAbstract(member):判断是否为抽象成员
- isAbstract(clazz):判断是否为抽象类
- isInterface(clazz):判断是否为接口
# 2.3 修饰符操作
- removeFinalModify(field):移除字段的 final 修饰符,使其可修改
# 3. 方法详解
# 3.1 修饰符组合判断
# hasAny(clazz, modifierTypes)
功能:判断类是否存在给定修饰符中的任意一个
参数:
clazz:类,如果为null返回falsemodifierTypes:修饰符枚举,如果为空返回false
返回值:是否有指定修饰符,如果有返回 true,否则 false
示例:
// 判断 String 类是否有 public 或 final 修饰符
boolean hasModifier = ModifierUtil.hasAny(String.class, ModifierType.PUBLIC, ModifierType.FINAL); // 返回 true
# hasAny(member, modifierTypes)
功能:判断成员是否存在给定修饰符中的任意一个
参数:
member:构造、字段或方法,如果为null返回falsemodifierTypes:修饰符枚举,如果为空返回false
返回值:是否有指定修饰符,如果有返回 true,否则 false
示例:
Field field = String.class.getDeclaredField("value");
// 判断字段是否有 private 或 final 修饰符
boolean hasModifier = ModifierUtil.hasAny(field, ModifierType.PRIVATE, ModifierType.FINAL); // 返回 true
# hasAll(clazz, modifierTypes)
功能:判断类中是否同时存在所有给定修饰符
参数:
clazz:类,如果为null返回falsemodifierTypes:修饰符枚举,如果为空返回false
返回值:是否同时存在所有指定修饰符,如果有返回 true,否则 false
示例:
// 判断 String 类是否同时有 public 和 final 修饰符
boolean hasAllModifiers = ModifierUtil.hasAll(String.class, ModifierType.PUBLIC, ModifierType.FINAL); // 返回 true
# hasAll(member, modifierTypes)
功能:判断成员中是否同时存在所有给定修饰符
参数:
member:构造、字段或方法,如果为null返回falsemodifierTypes:修饰符枚举,如果为空返回false
返回值:是否同时存在所有指定修饰符,如果有返回 true,否则 false
示例:
Field field = String.class.getDeclaredField("value");
// 判断字段是否同时有 private 和 final 修饰符
boolean hasAllModifiers = ModifierUtil.hasAll(field, ModifierType.PRIVATE, ModifierType.FINAL); // 返回 true
# 3.2 具体修饰符判断
# isDefault(method)
功能:判断方法是否为 default 方法
参数:
method:方法,如果为null返回false
返回值:是否为 default 方法
示例:
// 定义接口
interface MyInterface {
default void defaultMethod() {
// 默认方法实现
}
}
Method method = MyInterface.class.getDeclaredMethod("defaultMethod");
boolean isDefault = ModifierUtil.isDefault(method); // 返回 true
# isPublic(member)
功能:判断是否为 public 成员
参数:
member:构造、字段或方法,如果为null返回false
返回值:是否是 public
示例:
Method method = String.class.getDeclaredMethod("length");
boolean isPublic = ModifierUtil.isPublic(method); // 返回 true
# isPublic(clazz)
功能:判断是否为 public 类
参数:
clazz:类,如果为null返回false
返回值:是否是 public
示例:
boolean isPublic = ModifierUtil.isPublic(String.class); // 返回 true
boolean isPrivate = ModifierUtil.isPublic(Class.forName("java.lang.StringBuilder$StringBuilderAccessor")); // 返回 false
# isStatic(member)
功能:判断是否为 static 成员
参数:
member:构造、字段或方法,如果为null返回false
返回值:是否是 static
示例:
Field field = Math.class.getDeclaredField("PI");
boolean isStatic = ModifierUtil.isStatic(field); // 返回 true
# isSynthetic(member)
功能:判断是否为合成成员(由 java 编译器生成的)
参数:
member:构造、字段或方法
返回值:是否是合成成员
示例:
// 合成成员通常由编译器生成,如内部类访问外部类的私有成员时生成的桥接方法
# isAbstract(clazz)
功能:判断是否为抽象类
参数:
clazz:类,如果为null返回false
返回值:是否是抽象类
示例:
boolean isAbstract = ModifierUtil.isAbstract(AbstractList.class); // 返回 true
# 3.3 修饰符操作
# removeFinalModify(field)
功能:移除字段的 final 修饰符,使其可修改
参数:
field:被修改的 field,不可以为空
异常:
HutoolException:包装 IllegalAccessException 等异常
说明:
- 并非所有 final 字段都可以修改,以下类型的字段无法通过反射修改(编译器内联优化):
- 基本类型:byte, char, short, int, long, float, double, boolean
- Literal String 类型(直接双引号字符串)
- 以下类型的字段可以通过反射修改:
- 基本类型的包装类:Byte、Character、Short、Long、Float、Double、Boolean
- 字符串,通过 new String("") 实例化
- 自定义 Java 类
示例:
// 定义类
class FinalTest {
public static final List<String> LIST = new ArrayList<>();
public static final String LITERAL_STRING = "literal";
public static final String NEW_STRING = new String("new string");
}
// 修改非字面量字符串
Field newStringField = FinalTest.class.getDeclaredField("NEW_STRING");
ModifierUtil.removeFinalModify(newStringField);
newStringField.set(null, new String("modified"));
Console.log(FinalTest.NEW_STRING); // 输出:modified
// 修改集合
Field listField = FinalTest.class.getDeclaredField("LIST");
ModifierUtil.removeFinalModify(listField);
List<String> newList = new ArrayList<>();
newList.add("item1");
newList.add("item2");
listField.set(null, newList);
Console.log(FinalTest.LIST); // 输出:[item1, item2]
# 4. 示例代码
# 4.1 修饰符判断示例
// 判断类修饰符
Console.log("String 类是否为 public:{}", ModifierUtil.isPublic(String.class)); // true
Console.log("String 类是否为 final:{}", ModifierUtil.hasAny(String.class, ModifierType.FINAL)); // true
Console.log("String 类是否为抽象类:{}", ModifierUtil.isAbstract(String.class)); // false
Console.log("AbstractList 类是否为抽象类:{}", ModifierUtil.isAbstract(AbstractList.class)); // true
// 判断方法修饰符
Method lengthMethod = String.class.getDeclaredMethod("length");
Console.log("String.length() 是否为 public:{}", ModifierUtil.isPublic(lengthMethod)); // true
Console.log("String.length() 是否为 static:{}", ModifierUtil.isStatic(lengthMethod)); // false
// 判断字段修饰符
Field valueField = String.class.getDeclaredField("value");
Console.log("String.value 是否为 private:{}", ModifierUtil.isPrivate(valueField)); // true
Console.log("String.value 是否为 final:{}", ModifierUtil.hasAny(valueField, ModifierType.FINAL)); // true
// 判断构造方法修饰符
Constructor<?> constructor = String.class.getDeclaredConstructor(String.class);
Console.log("String(String) 构造方法是否为 public:{}", ModifierUtil.isPublic(constructor)); // true
# 4.2 组合修饰符判断示例
// 定义测试类
class TestClass {
public static final int PUBLIC_STATIC_FINAL = 1;
private int privateField;
protected static void protectedStaticMethod() {}
public abstract void abstractMethod();
}
// 获取字段和方法
Field publicStaticFinalField = TestClass.class.getDeclaredField("PUBLIC_STATIC_FINAL");
Field privateField = TestClass.class.getDeclaredField("privateField");
Method protectedStaticMethod = TestClass.class.getDeclaredMethod("protectedStaticMethod");
// 组合修饰符判断
Console.log("PUBLIC_STATIC_FINAL 是否同时有 public、static、final:{}",
ModifierUtil.hasAll(publicStaticFinalField, ModifierType.PUBLIC, ModifierType.STATIC, ModifierType.FINAL)); // true
Console.log("PUBLIC_STATIC_FINAL 是否有 private 或 protected:{}",
ModifierUtil.hasAny(publicStaticFinalField, ModifierType.PRIVATE, ModifierType.PROTECTED)); // false
Console.log("privateField 是否有 private 或 static:{}",
ModifierUtil.hasAny(privateField, ModifierType.PRIVATE, ModifierType.STATIC)); // true
Console.log("protectedStaticMethod 是否同时有 protected 和 static:{}",
ModifierUtil.hasAll(protectedStaticMethod, ModifierType.PROTECTED, ModifierType.STATIC)); // true
# 4.3 移除 final 修饰符示例
// 定义包含 final 字段的类
class FinalFieldClass {
// 字面量字符串,无法修改
public static final String LITERAL = "literal";
// 通过 new 创建的字符串,可以修改
public static final String DYNAMIC = new String("dynamic");
// 集合类型,可以修改
public static final Map<String, String> MAP = new HashMap<>();
}
// 尝试修改字面量字符串(无法修改)
Field literalField = FinalFieldClass.class.getDeclaredField("LITERAL");
ModifierUtil.removeFinalModify(literalField);
literalField.set(null, "modified_literal");
Console.log("字面量字符串修改后:{}", FinalFieldClass.LITERAL); // 输出:literal(未被修改)
// 修改动态字符串(可以修改)
Field dynamicField = FinalFieldClass.class.getDeclaredField("DYNAMIC");
ModifierUtil.removeFinalModify(dynamicField);
dynamicField.set(null, new String("modified_dynamic"));
Console.log("动态字符串修改后:{}", FinalFieldClass.DYNAMIC); // 输出:modified_dynamic
// 修改集合(可以修改)
Field mapField = FinalFieldClass.class.getDeclaredField("MAP");
ModifierUtil.removeFinalModify(mapField);
Map<String, String> newMap = new HashMap<>();
newMap.put("key", "value");
mapField.set(null, newMap);
Console.log("集合修改后:{}", FinalFieldClass.MAP); // 输出:{key=value}
# 5. 注意事项
- 空指针安全:所有方法都进行了空指针检查,避免了空指针异常
- 修饰符枚举:使用
ModifierType枚举来表示修饰符,提高了代码的可读性和类型安全性 - 合成成员:合成成员是由编译器生成的,通常用于实现内部类访问外部类私有成员等功能
- final 修饰符移除限制:并非所有 final 字段都可以通过反射修改,字面量基本类型和字符串无法修改
- JDK 版本限制:在 JDK 9+ 中,
removeFinalModify方法可能抛出 NoSuchFieldException 异常,原因是模块外无法访问某些属性 - 性能考虑:修饰符判断方法直接调用 JDK 反射 API,性能较好;而
removeFinalModify方法涉及反射操作,性能开销较大
# 6. 最佳实践
- 修饰符判断:在反射操作中,经常需要判断类、字段或方法的修饰符,使用 ModifierUtil 可以简化代码
- 动态修改 final 字段:在测试或特殊场景下,可能需要修改 final 字段的值,使用
removeFinalModify方法可以实现 - 组合修饰符检查:使用
hasAny和hasAll方法可以方便地检查多个修饰符 - 注意 final 字段修改限制:在修改 final 字段前,了解该字段是否可以被修改,避免无效操作
- 性能优化:对于频繁调用的修饰符判断,考虑缓存结果
- 结合其他反射工具:与 ClassUtil、FieldUtil、ReflectUtil 等工具结合使用,实现更复杂的反射操作
ModifierUtil 工具类简化了 Java 反射中修饰符的处理,提供了便捷的方法来判断和操作修饰符。无论是在框架开发还是日常编程中,该工具类都能帮助开发者更高效地处理反射相关的修饰符操作。