单例的目的:仅允许程序中存在有且仅有一个实例
单例的好处:
1、对于频繁使用的对象,可以省略创建对象所需的时间。
2、减少了new对象的操作次数,降低了系统内存的使用频率,减轻了GC的压力,缩短了DC的停顿时间
一、反射对单例模式的破坏
public class Singleton{
private static volatile Singleton mInstance;//声明私有属性的对象
private Singleton(){}//构造方法私有化
public static Singleton getInstance(){//创建一个外部可以访问的公开的方法返回当前对象的实例
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
调试:
public static void getReflect() {
Singleton singleton = Singleton.getInstance();
try {
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);//允许访问私有构造器
Singleton reflectSingleton = constructor.newInstance();
System.out.println(reflectSingleton == singleton);//判断当前两个实例是否是同一个对象
} catch (Exception e) {
e.printStackTrace();
}
}
输出:false
原理解析:通过反射获得私有的构造器,通过把这个构造器的setAccessible属性设置为true,这样直接无视了构造器的私有性,我们先通过正常的getInstance()方法创建一个实例,再通过反射得到的构造器创建一个实例,
解决方案: 其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第二次实例化的时候,抛出异常。
public class Singleton{
private static volatile Singleton mInstance;
private static volatile boolean mIsInstantiated = false;
private Singleton(){
if (mIsInstantiated){
throw new RuntimeException("休想反射破坏我的单例");
}
mIsInstantiated = true;
}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
二、 clone()对单例模式的破坏
public class Singleton implements Cloneable {
private static volatile Singleton mInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (mInstance == null) {
synchronized (Singleton.class) {
if (mInstance == null) {
mInstance = new Singleton();
}
}
}
return mInstance;
}
@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
注意点:若要具有克隆能力,实现Cloneable接口的类必须重写从Object继承来的clone方法,并调用Object的clone方法。
protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException("Class " + getClass().getName() +
" doesn't implement Cloneable");
}
return internalClone();
}
clone方法首先会判对象是否实现了Cloneable接口,若无则抛出CloneNotSupportedException, 最后会调用internalClone. intervalClone是一个native方法。
调试:
public static void getClone() {
try {
Singleton singleton = Singleton.getInstance();
Singleton cloneSingleton;
cloneSingleton = (Singleton) Singleton.getInstance().clone();
System.out.println(cloneSingleton == singleton);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
输出:false
原理解析:java.lang.Obeject#clone() 方法不会调用构造方法,而是直接从内存中拷贝内存区域。
解决方案:重写clone()方法,调clone()时直接返回已经实例的对象
public class Singleton implements Cloneable {
private static volatile Singleton mInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (mInstance == null) {
synchronized (Singleton.class) {
if (mInstance == null) {
mInstance = new Singleton();
}
}
}
return mInstance;
}
@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
// return super.clone();
return mInstance;//调clone()时直接返回已经实例的对象
}
}
三、 序列化对单例模式的破坏
public class Singleton implements Serializable {
private static volatile Singleton mInstance;
private Singleton(){
}
public static Singleton getInstance(){
if(mInstance == null){
synchronized (Singleton.class) {
if(mInstance == null){
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
很简单 实现Serializable接口就行了。
调试:
public static void getSerializable(){
try {
Singleton singleton = Singleton.getInstance();
FileOutputStream fos = new FileOutputStream("singleton.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(singleton);
oos.close();
fos.close();
FileInputStream fis = new FileInputStream("singleton.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Singleton serializedSingleton = (Singleton) ois.readObject();
fis.close();
ois.close();
System.out.println(serializedSingleton==singleton);
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出:false
原理解析:在反序列化时,ObjectInputStream 因为利用反射机制调用了 readObject --> readObject0 --> readOrdinary --> CheckResolve。在readOrdinady中调用了invokeReadResolve(),该方法使用反射机制创建新的对象,从而破坏了单例唯一性。
解决方案:在反序列化时的回调方法 readResolve()中返回单例对象
public class Singleton implements Serializable {
private static volatile Singleton mInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (mInstance == null) {
synchronized (Singleton.class) {
if (mInstance == null) {
mInstance = new Singleton();
}
}
}
return mInstance;
}
protected Object readResolve() throws ObjectStreamException {
return mInstance;
}
}