前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java编程——单例模式的安全性

Java编程——单例模式的安全性

作者头像
慕容千语
发布2019-06-11 18:57:40
7920
发布2019-06-11 18:57:40
举报

单例模式,我想大家再熟悉不过了,不过本文不是介绍单例模式该怎么写的。

本文来说说怎么破坏一个单例,让你写的单例变成一个假的单例。当然,本文也会给出怎么进行防守的方法。

一个简单的单例

来一个简单的单例模式例子:

public class Singleton { private static final Singleton INSTANCE = new Singleton(); private String name; public String getName() { return this.name;

} private Singleton() { this.name = "Neo";

} public static Singleton getInstance() { return INSTANCE;

}

}

上面是一个比较简单的饿汉写法的单例模式,我们看看客户端调用:

public class APP { // 由于构造方法上加了 private 修饰,所以我们已经不能通过 ‘new’ 来产生实例了

// Singleton intance = new Singleton();

Singleton instance = Singleton.getInstance();

System.out.println(instance.getName());

}

通过反射破坏单例

原理很简单,通过反射获取其构造方法,然后重新生成一个实例。

class APP { public static void main(String[] args) throws Exception {

Singleton instance1 = Singleton.getInstance(); // 下面我们通过反射得到其构造方法,并且修改其构造方法的访问权限,并用这个构造方法构造一个对象

Constructor constructor = Singleton.class.getDeclaredConstructor();

constructor.setAccessible(true);

Singleton instance2 = (Singleton) constructor.newInstance(); // 是不是产生了两个实例了?

System.out.println(instance1 == instance2); // false

}

}

显然,说好的单例已经不单一了,上面的程序运行结果肯定是:false

防止反射方式破坏

如果要避免单例被反射破坏,Java 提供了枚举,举个例子:

public enum Singleton {

INSTANCE;// 这里只有一项

private String name;

Singleton() { this.name = "Neo";

} public static Singleton getInstance() { return INSTANCE;

} public String getName() { return this.name;

}

}

这个时候,如果我们再想通过反射获取类的构造方法:

Constructor constructor = Singleton.class.getDeclaredConstructor();

会抛出 NoSuchMethodException 异常:

Exception in thread "main" java.lang.NoSuchMethodException: com.javadoop.Singleton.() at java.lang.Class.getConstructor0(Class.java:3082)

at java.lang.Class.getDeclaredConstructor(Class.java:2178)

at com.javadoop.singleton.APP.main(APP.java:11)

对于枚举,JVM 会自动进行实例的创建,其构造方法由 JVM 在创建实例的时候进行调用。

我们在代码中是获取不到 enum 类的构造方法的。

通过序列化破坏

下面,我们再说说另一种破解方法:序列化、反序列化。

我们知道,序列化是将 java 对象转换为字节流,反序列化是从字节流转换为 java 对象。

class APP { public static void main(String[] args) throws ... {

Singleton instance1 = Singleton.getInstance();

Constructor constructor = Singleton.class.getDeclaredConstructor();

constructor.setAccessible(true);

Singleton instance2 = (Singleton) constructor.newInstance(); // instance3 将从 instance1 序列化后,反序列化而来

Singleton instance3 = null;

ByteArrayOutputStream bout = null;

ObjectOutputStream out = null; try {

bout = new ByteArrayOutputStream();

out = new ObjectOutputStream(bout);

out.writeObject(instance1);

ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());

ObjectInputStream in = new ObjectInputStream(bin);

instance3 = (Singleton) in.readObject();

} catch (Exception e) {

} finally { // close bout&out

} // 显然,instance3 和 instance1 不是同一个对象了

System.out.println(instance1 == instance3); // false

}

}

毫无疑问,instance1 == instance3 也会返回false。

防止序列化破坏

在序列化之前,我们要在类上面加上implements Serializable。

我们需要做的是,在类中加上 readResolve() 这个方法,返回实例。

public class Singleton implements Serializable { private static final Singleton INSTANCE = new Singleton(); private String name; public String getName() { return this.name;

} private Singleton() { this.name = "Neo";

} public static Singleton getInstance() { return INSTANCE;

} // 看这里

public Object readResolve() throws ObjectStreamException { return INSTANCE;

}

}

你再试一下,会发现变成true了。

因为在反序列化的时候,JVM 会自动调用 readResolve() 这个方法,我们可以在这个方法中替换掉从流中反序列化回来的对象。

这个方法完整的描述是这样的:

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

总结

文中没有示例说反序列化在 enum 类中的表现,我直接说结论吧。enum 类自带这种特殊光环,不用写 readResolve() 方法就可以自动防止反序列化方式对单例的破坏。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.08.28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一个简单的单例
  • 通过反射破坏单例
  • 防止反射方式破坏
  • 通过序列化破坏
  • 防止序列化破坏
  • 总结
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档