栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

在Java中,是否可以更改或修改枚举本身,从而破坏枚举单例?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

在Java中,是否可以更改或修改枚举本身,从而破坏枚举单例?

我开始

Color
使用分解枚举,开始进行分析
javap -c
。以下是摘录:

 static {};    Code:       0: new#1       // class playground/Color       3: dup       4: ldc#14      // String RED       6: iconst_0  7: invokespecial #15      // Method "<init>":(Ljava/lang/String;I)V      10: putstatic     #19      // Field RED:Lplayground/Color;      13: new#1       // class playground/Color      16: dup      17: ldc#21      // String GREEN      19: iconst_1 20: invokespecial #15      // Method "<init>":(Ljava/lang/String;I)V      23: putstatic     #22      // Field GREEN:Lplayground/Color;      26: new#1       // class playground/Color      29: dup      30: ldc#24      // String BLUE      32: iconst_2 33: invokespecial #15      // Method "<init>":(Ljava/lang/String;I)V      36: putstatic     #25      // Field BLUE:Lplayground/Color;      39: iconst_3 40: anewarray     #1       // class playground/Color      43: dup      44: iconst_0 45: getstatic     #19      // Field RED:Lplayground/Color;      48: aastore  49: dup      50: iconst_1 51: getstatic     #22      // Field GREEN:Lplayground/Color;      54: aastore  55: dup      56: iconst_2 57: getstatic     #25      // Field BLUE:Lplayground/Color;      60: aastore  61: putstatic     #27      // Field ENUM$VALUES:[Lplayground/Color;      64: return

在索引61处,我们看到将三个枚举常量分配给名称为的静态数组

ENUM$VALUES

通过反射列出所有静态字段…

Field[] declaredFields = Color.class.getDeclaredFields();for (Field field : declaredFields) {  if (Modifier.isStatic(field.getModifiers())) {    System.out.println(field.getName() + ": " + field.getType());  }}

显示枚举常量并显示数组:

RED: class playground.ReflectEnum$ColorGREEN: class playground.ReflectEnum$ColorBLUE: class playground.ReflectEnum$ColorENUM$VALUES: class [Lplayground.ReflectEnum$Color;

我定义了以下方法来获取枚举数组:

  protected static <E extends Enum<E>> E[] getEnumsArray(Class<E> ec) throws Exception {    Field field = ec.getDeclaredField("ENUM$VALUES");    field.setAccessible(true);    return (E[]) field.get(ec);  }

使用它可以更改枚举常量的顺序:

Color[] colors = getEnumsArray(Color.class);colors[0] = Color.GREEN;colors[1] = Color.RED;colors[2] = Color.BLUE;

列出枚举常量

for (Color color : Color.values()) {  System.out.println(action + ":" + color.ordinal());}

显示:

GREEN:1RED:0BLUE:2

显然,顺序已更改。

由于可以为数组分配值,因此分配也是有效的

null

Color[] colors = getEnumsArray(Color.class);colors[0] = Color.GREEN;colors[1] = Color.RED;colors[2] = null;

列出枚举常量显示:

GREEN:1RED:0Exception in thread "main" java.lang.NullPointerException    at playground.ReflectEnum.main(ReflectEnum.java:57)

如果我们尝试按名称查询枚举常量

System.out.println(Color.valueOf("GREEN"));System.out.println(Color.valueOf("RED"));System.out.println(Color.valueOf("BLUE"));

我们看到最后一次更改甚至打破了:

Exception in thread "main" java.lang.NullPointerException    at java.lang.Class.enumConstantDirectory(Class.java:3236)    at java.lang.Enum.valueOf(Enum.java:232)    at playground.Color.valueOf(Color.java:1)    at playground.ReflectEnum.main(ReflectEnum.java:48)

该行

ReflectEnum.java:48
包含的上述打印语句
Color.valueOf("GREEN")
。此枚举常量未设置为
null
。因此,它完全打破了
valueOf
方法
Color

但是

Enum.valueOf(Color.class, "BLUE")
仍然解析枚举常量
Color.BLUE

由于枚举数组被声明为

staticfinal
我遵循的,因此使用Java反射来更改private static
final字段
来创建和设置新的枚举数组。

  protected static <E extends Enum<E>> void setEnumsArray(Class<E> ec, E... e) throws Exception {    Field field = ec.getDeclaredField("ENUM$VALUES");    Field modifiersField = Field.class.getDeclaredField("modifiers");    field.setAccessible(true);    modifiersField.setAccessible(true);    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);    field.set(ec, e);  }

但是执行

setEnumsArray(Color.class, Color.BLUE, Color.GREEN, Color.RED,Color.BLUE)
失败,

Exception in thread "main" java.lang.IllegalAccessException: Can not set static final [Lplayground.Color; field playground.Color.ENUM$VALUES to [Lplayground.Color;    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)    at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)    at java.lang.reflect.Field.set(Field.java:758)    at playground.ReflectEnum.setEnumsArray(ReflectEnum.java:76)    at playground.ReflectEnum.main(ReflectEnum.java:37)

编辑1(在Radiodef的评论之后):

添加以下两种方法后…

  protected static Field getEnumsArrayField(Class<?> ec) throws Exception {    Field field = ec.getDeclaredField("ENUM$VALUES");    field.setAccessible(true);    return field;  }  protected static void clearFieldAccessors(Field field) throws ReflectiveOperationException {    Field fa = Field.class.getDeclaredField("fieldAccessor");    fa.setAccessible(true);    fa.set(field, null);    Field ofa = Field.class.getDeclaredField("overrideFieldAccessor");    ofa.setAccessible(true);    ofa.set(field, null);    Field rf = Field.class.getDeclaredField("root");    rf.setAccessible(true);    Field root = (Field) rf.get(field);    if (root != null) {      clearFieldAccessors(root);    }

我尝试了这个:

System.out.println(Arrays.toString((Object[]) getEnumsArrayField(Color.class).get(null)));clearFieldAccessors(getEnumsArrayField(Color.class));setEnumsArray(Color.class, Color.BLUE, Color.GREEN, Color.RED, Color.BLUE);System.out.println(Arrays.toString(Color.values()));

由此可见:

[RED, GREEN, BLUE][BLUE, GREEN, RED, BLUE]

枚举数组已被另一个替换。


编辑2(在GotoFinal的注释之根据GotoFinal的答案,关于如何在Java中使用反射创建枚举实例?有可能在运行时创建更多的枚举实例。然后应该可以用另一个实例代替一个枚举实例。我有以下枚举单例:

  public enum Singleton {    INSTANCE("The one and only");    private String description;    private Singleton(String description) {      this.description = description;    }    @Override    public String toString() {      return description;    }  }

重用代码GotoFinal已显示我定义了以下方法:

  protected static Singleton createEnumValue(String name, int ordinal, String description) throws Exception {    Class<Singleton> monsterClass = Singleton.class;    Constructor<?> constructor = monsterClass.getDeclaredConstructors()[0];    constructor.setAccessible(true);    Field constructorAccessorField = Constructor.class.getDeclaredField("constructorAccessor");    constructorAccessorField.setAccessible(true);    sun.reflect.ConstructorAccessor ca = (sun.reflect.ConstructorAccessor) constructorAccessorField.get(constructor);    if (ca == null) {      Method acquireConstructorAccessorMethod = Constructor.class.getDeclaredMethod("acquireConstructorAccessor");      acquireConstructorAccessorMethod.setAccessible(true);      ca = (sun.reflect.ConstructorAccessor) acquireConstructorAccessorMethod.invoke(constructor);    }    Singleton enumValue = (Singleton) ca.newInstance(new Object[] { name, ordinal, description });    return enumValue;  } protected static <E extends Enum<E>> void setFinalField(Class<E> ec, Field field, E e) throws NoSuchFieldException, IllegalAccessException {    Field modifiersField = Field.class.getDeclaredField("modifiers");    field.setAccessible(true);    modifiersField.setAccessible(true);    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);    field.set(ec, e);  }

现在执行

System.out.println(Singleton.INSTANCE.toString());// setting INSTANCE = theNewoneSingleton theNewOne = createEnumValue(Singleton.INSTANCE.name(), Singleton.INSTANCE.ordinal(), "The new one!");setFinalField(Singleton.class, Singleton.class.getDeclaredField(Singleton.INSTANCE.name()), theNewOne);System.out.println(Singleton.INSTANCE.toString());// setting enum array = [theNewOne]clearFieldAccessors(getEnumsArrayField(Singleton.class));setEnumsArray(Singleton.class, theNewOne);System.out.println(Arrays.toString(Singleton.values()));

显示:

The one and onlyThe new one![The new one!]

总结:

  • 可以在运行时修改一个枚举,并用另一个替换该枚举数组。但是至少设置一个枚举常量会

    null
    破坏VM中定义的枚举的一致性。尽管可以根据GotoFinal的答案解决此问题。

  • 使用实现单例时

    enum
    ,可以用另一个枚举实例替换唯一的实例-至少对于知道其实现的某些JDK / JRE而言。如果枚举构造函数具有参数,则新创建的枚举实例可以利用它们来植入恶​​意行为。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/431250.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号