单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。单例模式有以下几种实现方式
将构造方法设置成私有方法,其他类无法new对象,从而保证只有一个实例。类加载到内存后,就实例化一个单例,JVM保证线程安全
public class HungrySingleton {
private final static HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton(){} //私有构造方法只能在本类中访问
public static HungrySingleton getInstance() { //静态方法返回实例
return INSTANCE;
}
public static void main(String[] args) {
HungrySingleton hs1 = HungrySingleton.getInstance();
HungrySingleton hs2 = HungrySingleton.getInstance();
System.out.println(hs1==hs2);//true
}
}
饿汉式的另一种写法
public class HungrySingleton{
private final static HungrySingletonTwo INSTANCE;
static { //只是将对象的初始化加到静态语句块中
INSTANCE = new HungrySingletonTwo();
}
private HungrySingleton(){} //构造方法私有化
public static HungrySingleton getInstance() {
return INSTANCE;
}
}
优点:需要用到实例才初始化实例
缺点:带来了线程不安全问题
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){} //构造函数私有化
public static LazySingleton getInstance() {
if (instance == null) {
//当一个线程走到这时,另一个线程判断为空也会实例化一个对象,就违背了单例模式
try {
Thread.sleep(1);
} catch (InterruptedException e) { e.printStackTrace(); }
instance = new LazySingleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i <50 ; i++) {
new Thread(()->{
System.out.println(LazySingleton.getInstance().hashCode());//输出结果不一致
}).start();
}
}
}
懒汉式改进版(不推荐),通过Synchronized来解决线程不安全问题
缺点:虽然解决了线程安全问题,但是执行效率会降低
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
懒汉式改进版,这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
public static LazySingleton getInstance() {
if (instance == null) {
synchronized(LazySingleton.class){
if (instance == null){
instance = new LazySingleton_4();
}
}
}
return instance;
}
静态内部类方式(完美写法之一)JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载,但是这种单例模式可以被反射机制和反序列化破坏
public class InnerSingleton implements Serializable {
private InnerSingleton(){}
//静态内部类
private static class InnerSingletonHolder{
private final static InnerSingleton instance = new InnerSingleton();
}
public static InnerSingleton getInstance() {
return InnerSingletonHolder.instance;
}
public static void main(String[] args) {
for (int i = 0; i <50 ; i++) {
new Thread(()->{
System.out.println(InnerSingleton.getInstance().hashCode());//输出结果一致
}).start();
}
}
}
反射机制破坏单例模式代码
public class DestroyTest {
public static void main(String[] args) {
//获取字节码对象
try {
Class mgr = Class.forName("InnerSingleton");
Constructor declaredConstructor = mgr.getDeclaredConstructor();//获取无参构造
declaredConstructor.setAccessible(true);//取消访问检查
//创建对象
InnerSingleton m1 = (InnerSingleton)declaredConstructor.newInstance();
InnerSingleton m2 = (InnerSingleton)declaredConstructor.newInstance();
InnerSingleton m3 = (InnerSingleton)declaredConstructor.newInstance();
System.out.println(m1);
System.out.println(m2);
System.out.println(m3);//输出内存地址不一样,说明m1、m2、m3不是同一个对象
} catch (Exception e) {
e.printStackTrace();
}
}
}
在InnerSingleton中判断是否已经存在了一个InnerSingleton对象,如果没有存在就new一个对象,如果已经存在就向外抛出异常
public static boolean isExist = false;
private InnerSingleton(){
synchronized(InnerSingleton.class) {//保证在多线程环境下的线程安全问题
if (isExist) {
throw new RuntimeException("不能创建多个对象");
} else {
isExist = true;
}
}
}
反序列化破坏单例模式代码
public class DestroyTest {
public static void main(String[] args) {
try {
writeToFile();
readObj();
readObj();//两次输出的内存地址不一样,说明破坏了单例模式
//添加readResolve()后输出的结果一样,说明没有破坏单例模式
}catch (Exception e) {
e.printStackTrace();
}
}
public static void writeToFile() throws IOException { //向文件中写入对象
InnerSingleton mgr = InnerSingleton.getInstance();//获取InnerSingleton 对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("C:\\Users\\uuu\\IdeaProjects\\DesignProject\\ch01-Singleton\\file\\a.txt"));
out.writeObject(mgr);//写对象
out.close(); //关闭资源
}
public static void readObj() throws Exception{ //向文件中读取对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("C:\\Users\\uuu\\IdeaProjects\\DesignProject\\ch01-Singleton\\file\\a.txt"));
InnerSingleton mgr = (InnerSingleton)in.readObject();//读取对象
System.out.println(mgr);//读取对象
in.close();//释放资源
}
}
在InnerSingleton中添加一个readResolve()方法,该方法在序列化时被反射机制调用, 如果定义了这一个方法就返回这个方法的返回值,否则就返回新new出来的对象
public Object readResolve(){
return InnerSingletonHolder.instance;
}
枚举单例(完美写法之一)不仅可以保证单例,还可以防止序列化,java的反射机制可以通过Class文件newInstance反序列化一个对象,枚举类不会被反序列化的原因是枚举类没有构造方法
public enum EnumSingleton {
INSTANCE;
public void BusinessMethod(){ /*业务方法*/ }
public static void main(String[] args) {
for (int i = 0; i <50 ; i++) {
new Thread(()->{
System.out.println(EnumSingleton.INSTANCE.hashCode());//输出结果一致
}).start();
}
}
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/m0_60117382/article/details/123527364
内容来源于网络,如有侵权,请联系作者删除!