你能破坏 Scala 单例(对象)吗?
Can you break a Scala singleton (object)?
来自 Java 世界,最受欢迎的编程书籍之一是 Joshua Bloch 的 "Effective Java"。
该书中的许多主题之一指导程序员更喜欢基于枚举的单例而不是标准单例实现,因为大多数标准实现都可以通过序列化、克隆等来破坏。
我的问题
标准的 Scala 单例,似乎是使用 "object" 而不是 "class" 定义的,是否可以通过类似的技巧来破解?或者它是否受到 运行 时间环境的保护,就像 Java 中基于枚举的单例一样?
如果您足够努力,没有什么可以阻止您在 Java 中复制 任何 对象,包括枚举值。但是,由于您无法使用 'normal' 反射创建 enum
的实例,因此您必须更深入地了解黑客工具箱:sun.misc.Unsafe
。这里只用于创建实例,其余的可以通过正常反射完成:
Unsafe unsafe = ...; // Obtain the value of the sun.misc.Unsafe.theUnsafe field, using normal reflection
try
{
Object o = unsafe.allocateInstance(TestEnum.class); // creates a new instance of TestEnum with all fields set to 0 / false / null
System.out.println(o); // prints 'null' because the name field is null
System.out.println(o.getClass()); // prints 'JavaTest$Enum'
Field f = Enum.class.getDeclaredField("name");
f.setAccessible(true); // bypasses the final and access checks
f.set(o, "TEST"); // set the name to 'TEST'
f = Enum.class.getDeclaredField("ordinal");
f.setAccessible(true);
f.set(o, 1); // set the ordinal to 1
System.out.println(o); // prints 'TEST'
System.out.println(((Enum) o).ordinal()); // prints 1
}
catch (Exception ex)
{
ex.printStackTrace();
}
此外,您可能希望将字段从 TestEnum 的实例复制到新的 TestEnum 实例。这可以像上面看到的那样手动完成(通过将参数弄乱 f.set(o, ...)
一点)或者通过遍历所有字段并复制它们,如下所示:
for (Field f : TestEnum.class.getDeclaredFields())
{
if (!Modifiers.isStatic(f.getModifiers())
{
f.setAccessible(true);
f.set(o, f.get(TestEnum.INSTANCE));
}
}
当然这只会复制 TestEnum
class 中的字段,在我的例子中没有字段。您可能还想复制 Enum
中的字段,因为此 for
循环不处理它们。
正确回答您的问题:是的,您可以破坏 Scala object
,也可以破坏 enum
。这完全取决于您愿意付出多少努力以及您的非法代码库有多大。
来自 Java 世界,最受欢迎的编程书籍之一是 Joshua Bloch 的 "Effective Java"。
该书中的许多主题之一指导程序员更喜欢基于枚举的单例而不是标准单例实现,因为大多数标准实现都可以通过序列化、克隆等来破坏。
我的问题
标准的 Scala 单例,似乎是使用 "object" 而不是 "class" 定义的,是否可以通过类似的技巧来破解?或者它是否受到 运行 时间环境的保护,就像 Java 中基于枚举的单例一样?
如果您足够努力,没有什么可以阻止您在 Java 中复制 任何 对象,包括枚举值。但是,由于您无法使用 'normal' 反射创建 enum
的实例,因此您必须更深入地了解黑客工具箱:sun.misc.Unsafe
。这里只用于创建实例,其余的可以通过正常反射完成:
Unsafe unsafe = ...; // Obtain the value of the sun.misc.Unsafe.theUnsafe field, using normal reflection
try
{
Object o = unsafe.allocateInstance(TestEnum.class); // creates a new instance of TestEnum with all fields set to 0 / false / null
System.out.println(o); // prints 'null' because the name field is null
System.out.println(o.getClass()); // prints 'JavaTest$Enum'
Field f = Enum.class.getDeclaredField("name");
f.setAccessible(true); // bypasses the final and access checks
f.set(o, "TEST"); // set the name to 'TEST'
f = Enum.class.getDeclaredField("ordinal");
f.setAccessible(true);
f.set(o, 1); // set the ordinal to 1
System.out.println(o); // prints 'TEST'
System.out.println(((Enum) o).ordinal()); // prints 1
}
catch (Exception ex)
{
ex.printStackTrace();
}
此外,您可能希望将字段从 TestEnum 的实例复制到新的 TestEnum 实例。这可以像上面看到的那样手动完成(通过将参数弄乱 f.set(o, ...)
一点)或者通过遍历所有字段并复制它们,如下所示:
for (Field f : TestEnum.class.getDeclaredFields())
{
if (!Modifiers.isStatic(f.getModifiers())
{
f.setAccessible(true);
f.set(o, f.get(TestEnum.INSTANCE));
}
}
当然这只会复制 TestEnum
class 中的字段,在我的例子中没有字段。您可能还想复制 Enum
中的字段,因为此 for
循环不处理它们。
正确回答您的问题:是的,您可以破坏 Scala object
,也可以破坏 enum
。这完全取决于您愿意付出多少努力以及您的非法代码库有多大。