单例模式的前提:1.有私有的构造方法(为了代码中不可以直接new),2.全局唯一的访问点
懒汉式单例:懒汉的双重加锁机制
package com.java8.java8.singleton;
public class LazySingleton {
public static void main(String[] args) {
System.out.println(LazySingleton.getInstance());
}
// 这里要加上内存屏障,防止getInstance过程中指令重排的发生
private volatile static LazySingleton modle = null;//声明对象,不实例化
//构造函数私有化
private LazySingleton() {
}
//向外提供访问该类对象的方法
// 需要加锁要不会有线程安全问题,自己体会不细说了
public static LazySingleton getInstance(){
if (modle == null)
synchronized (LazySingleton.class){
modle = new LazySingleton();
}
return mo dle;
}
}
饿汉式:这个饿汉式还可以升级下,这个类如果有很多静态变量,或者静态方法,他们被调用的时候,这个单例都会被创建了,粒度太粗了。
package com.java8.java8.singleton;
public class HungrySingleton {
public static void main(String[] args) {
System.out.println(HungrySingleton.NAME);
}
static{
System.out.println("static块创建");
}
private static final String NAME = "张工";
// 这个对象是在类加载过程中创建的,静态代码块创建了这个对象也就创建了
private static HungrySingleton modle = new HungrySingleton();
//构造函数私有化
private HungrySingleton(){
}
//向外声明访问该类对象的方法
public static HungrySingleton getInstance() {
return modle;
}
}
细粒度的懒汉式单例也叫内部类单例:
package com.java8.java8.singleton;
public class HungrySingleton2 {
public static void main(String[] args) {
// 调用外面的静态变量,主要是为了看下内部类的单例会不会打印
System.out.println(HungrySingleton2.NAME);
// 调用内部类的静态变量,开始创建单例对象
//System.out.println(InnerSingleton.getInstance());
}
static{
System.out.println("外面static块创建");
}
private static final String NAME = "张工";
// 创建这个内部类就是为了把粗的单例变细,什么时候第一次调用getInstance什么时候在创建对象
static class InnerSingleton{
static{
System.out.println("内部类static块创建");
}
// 这个对象是在类加载过程中创建的,静态代码块创建了这个对象也就创建了
private static InnerSingleton modle = new InnerSingleton();
//构造函数私有化
private InnerSingleton(){
}
//向外声明访问该类对象的方法
public static InnerSingleton getInstance() {
return modle;
}
}
}
枚举单例
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
完整的枚举单例
package com.java8.java8.singleton;
public class CompleteEnumSingleton {
//私有化构造函数
private CompleteEnumSingleton(){ }
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private CompleteEnumSingleton user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new CompleteEnumSingleton();
}
public CompleteEnumSingleton getInstnce(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static CompleteEnumSingleton getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
class Test {
public static void main(String [] args){
System.out.println(CompleteEnumSingleton.getInstance());
System.out.println(CompleteEnumSingleton.getInstance());
System.out.println(CompleteEnumSingleton.getInstance()==CompleteEnumSingleton.getInstance());
}
}
以上3种方式创建了单例对象,现在讲解下怎么来破坏单例
1.反射
package com.java8.java8.singleton.destory;
import com.java8.java8.singleton.HungrySingleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class DestorySingleton {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 通过反射创建对象
Constructor<HungrySingleton> declareConstructor = HungrySingleton.class.getDeclaredConstructor();
declareConstructor.setAccessible(true);
HungrySingleton hungrySingleton1 = declareConstructor.newInstance();
//正常创建对象
HungrySingleton hungrySingleton2 = HungrySingleton.getInstance();
System.out.println(hungrySingleton1 == hungrySingleton2);
}
}
上面的单例被破坏了,但是我们可以在私有方法上加上检查。这样反射创建对象时候就检查住了。
//构造函数私有化
private HungrySingleton(){
if( modle != null){
throw new RuntimeException("对象已经创建了");
}
}
2.序列化
// 把这个对象序列化
package com.java8.java8.singleton.serizerDestory;
import java.io.Serializable;
public class HungrySingleton implements Serializable {
public static void main(String[] args) {
System.out.println(HungrySingleton.NAME);
}
static{
System.out.println("static块创建");
}
private static final String NAME = "张工";
// 这个对象是在类加载过程中创建的,静态代码块创建了这个对象也就创建了
private static HungrySingleton modle = new HungrySingleton();
//构造函数私有化
private HungrySingleton(){
if( modle != null){
throw new RuntimeException("对象已经创建了");
}
}
//向外声明访问该类对象的方法
public static HungrySingleton getInstance() {
return modle;
}
// 这句是解决序列化反序列化对象破环单例用的
private Object readResolve(){
return modle;
}
}
// 序列化对象和反序列化对象后和原来单例对象比较
package com.java8.java8.singleton.serizerDestory;
import java.io.*;
public class HungrySingletonSerizerTest {
public static void main(String[] args) {
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
// 把对象序列化后保存在文件中
// try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test"))) {
// objectOutputStream.writeObject(hungrySingleton);
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } catch (IOException e) {
// e.printStackTrace();
// }
// 把上面的文件反序列化成对象
HungrySingleton hungrySingleton2 = null;
try (ObjectInputStream objectOutputStream = new ObjectInputStream(new FileInputStream("test"))) {
hungrySingleton2 = (HungrySingleton) objectOutputStream.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(hungrySingleton2.toString());
System.out.println(hungrySingleton.toString());
// 比较单例创建出来的对象和文件饭序列化出来的对象是否相同比较,结果为false,可见反序列化破化了单例
// 解决办法在对象中readResolve() 指定数据源
// private Object readResolve(){
// return modle;
// }
System.out.println(hungrySingleton2 == hungrySingleton);
}
}
解决办法在代码中写了