java单例模式代码实现方式_java单例模式实现方式

java单例模式代码实现方式_java单例模式实现方式JAVA常见的设计模式之单例模式 懒汉模式 懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间(搬运工)。标准的懒汉模式classLazySingleton{//私有成员属性privateLazySingletonlazySingleton;//私有构造方法privateLazySingleto…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

JAVA常见的设计模式之单例模式

  • 懒汉模式

             懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间(搬运工)。

标准的懒汉模式

class LazySingleton {
    // 私有成员属性
    private LazySingleton lazySingleton;
    
    // 私有构造方法
    private LazySingleton() {
    }
    
    // 公共的获取实例方法
    public LazySingleton getLazySingleton() {
        // 如果成员属性为空,则创建实例
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

单线程环境下,该单例模式只会有一个实例

public class TestDemo
{
    public static void main(String[] args) {
        LazySingleton lazySingleton = LazySingleton.getLazySingleton();
        LazySingleton lazySingleton2 = LazySingleton.getLazySingleton();
        System.out.println(lazySingleton == lazySingleton2);
    }
}

运行结果:

java单例模式代码实现方式_java单例模式实现方式

多线程模式下,可能会产生多个实例

public class TestDemo
{
    public static void main(String[] args) {
        new Thread(() -> {
            LazySingleton lazySingleton = LazySingleton.getLazySingleton();
            System.out.println(lazySingleton);
        }).start();
        new Thread(() -> {
            LazySingleton lazySingleton = LazySingleton.getLazySingleton();
            System.out.println(lazySingleton);
        }).start();
    }
}

运行结果:

java单例模式代码实现方式_java单例模式实现方式

初步改进

class LazySingleton {
    // 私有成员属性
    private static LazySingleton lazySingleton;

    // 私有构造方法
    private LazySingleton() {
    }

    // 公共的获取实例方法
    public synchronized static LazySingleton getLazySingleton() {
        // 如果成员属性为空,则创建实例
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

缺点,每次调用方法都会加锁,效率低

再次改进

class LazySingleton {
    // 私有成员属性,使用volatile可以保证代码的有序性,防止指令重排
    private volatile static LazySingleton lazySingleton;

    // 私有构造方法
    private LazySingleton() {
    }

    // 公共的获取实例方法
    // 使用synchronized + 双重确认机制可以保证线程安全,但有可能存在指令重排,会导致创建多个实例
    public static LazySingleton getLazySingleton() {
        if (lazySingleton == null) {
            synchronized (LazySingleton.class) {
                if (lazySingleton == null) {
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }
}

静态类部类单例

/**
 * 由静态内部类持有单例对象,并调用外部类的私有构造器初始化,由外部类调用静态内部类的属性
 * 本质是一个懒汉模式,在类加载时才会初始化对象
 */
class InnerSingleton implements Serializable {

    private static class InnerSingletonHolder {
        private static InnerSingleton innerSingleton = new InnerSingleton();
    }

    private InnerSingleton() {
    }

    public static InnerSingleton getInnerSingleton() {
        return InnerSingletonHolder.innerSingleton;
    }

}

 静态类不类单例不会有线程安全问题,线程安全由类加载机制担保

恶汉模式

             饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间(搬运工)。 

// 利用类加载机制保证线程安全
class HungrySingleton {
    private static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getHungrySingleton() {
        return hungrySingleton;
    }
}

 枚举单例模式

package com.hy.test.singletonDemo;

public enum EnumSingletonDemo {
    INSTANCE;

}

class EnumTest {
    public static void main(String[] args) {
        EnumSingletonDemo instance = EnumSingletonDemo.INSTANCE;
        EnumSingletonDemo instance2 = EnumSingletonDemo.INSTANCE;
        System.out.println(instance == instance2);
    }
}

运行结果:

java单例模式代码实现方式_java单例模式实现方式

单例模式可能出现的问题(都会用静态类不类单例举例)

反射攻击

/**
 * 测试demo
 *
 * @auther Hy
 * @date 2020/8/25
 */
public class TestDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        InnerSingleton innerSingleton = InnerSingleton.getInnerSingleton();
        Class clazz = InnerSingleton.class;
        Constructor<InnerSingleton> declaredConstructor = clazz.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerSingleton innerSingleton1 = (InnerSingleton) declaredConstructor.newInstance();
        System.out.println(innerSingleton == innerSingleton1);
    }
}

/**
 * 由静态内部类持有单例对象,并调用外部类的私有构造器初始化,由外部类调用静态内部类的属性
 * 本质是一个懒汉模式,在类加载时才会初始化对象
 */
class InnerSingleton implements Serializable {

    private static class InnerSingletonHolder {
        private static InnerSingleton innerSingleton = new InnerSingleton();
    }

    private InnerSingleton() {
    }

    public static InnerSingleton getInnerSingleton() {
        return InnerSingletonHolder.innerSingleton;
    }

}

运行结果:

java单例模式代码实现方式_java单例模式实现方式

由此可见,反射生成了一个新的对象,不符合单例模式的定义

解决方法:在私有构造器中添加判断,如果已存在实例对象,抛出异常(也可进行其他操作,根据需求决定)

优化后的代码如下

/**
 * 测试demo
 *
 * @auther Hy
 * @date 2020/8/25
 */
public class TestDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        InnerSingleton innerSingleton = InnerSingleton.getInnerSingleton();
        Class clazz = InnerSingleton.class;
        Constructor<InnerSingleton> declaredConstructor = clazz.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerSingleton innerSingleton1 = (InnerSingleton) declaredConstructor.newInstance();
        System.out.println(innerSingleton == innerSingleton1);
    }
}

/**
 * 由静态内部类持有单例对象,并调用外部类的私有构造器初始化,由外部类调用静态内部类的属性
 * 本质是一个懒汉模式,在类加载时才会初始化对象
 */
class InnerSingleton implements Serializable {

    private static class InnerSingletonHolder {
        private static InnerSingleton innerSingleton = new InnerSingleton();
    }

    private InnerSingleton() {
        // 防止反射攻击,只有恶汉与静态类部类能防止反射攻击
        if (InnerSingletonHolder.innerSingleton != null) {
            throw new RuntimeException("单例模式已存在一个实例");
        }
    }

    public static InnerSingleton getInnerSingleton() {
        return InnerSingletonHolder.innerSingleton;
    }

}

 运行结果:

java单例模式代码实现方式_java单例模式实现方式

注意:只有恶汉模式与静态类部类能防止反射攻击

序列化相关问题

 首先,我们对创建的实例进行序列化,代码如下:

/**
 * 测试demo
 *
 * @auther Hy
 * @date 2020/8/25
 */
public class TestDemo {
    public static void main(String[] args) throws IOException {
        InnerSingleton innerSingleton = InnerSingleton.getInnerSingleton();
        // 序列化测试
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("innerTest"));
        oos.writeObject(innerSingleton);
        oos.close();
        // 反序列化
/*        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("innerTest"));
        InnerSingleton innerSingleton1 = (InnerSingleton) ois.readObject();
        System.out.println(innerSingleton == innerSingleton1);*/
    }
}

/**
 * 由静态内部类持有单例对象,并调用外部类的私有构造器初始化,由外部类调用静态内部类的属性
 * 本质是一个懒汉模式,在类加载时才会初始化对象
 */
class InnerSingleton implements Serializable {

    // 需要固定序列化版本号id,如果不固定,JVM会根据字段、方法等生成一个序列化ID,并存入对应的序列化文件,反序列化时,
    // 会按照相同规则生成一个序列化版本号进行对比,如果类已经发生了改变,反序列化的版本号会对应不上,反序列化会失败
    private static final long serialVersionUID = 7822769557659839582L;

    private static class InnerSingletonHolder {
        private static InnerSingleton innerSingleton = new InnerSingleton();
    }

    private InnerSingleton() {
        // 防止反射攻击,只有恶汉与静态类不类能防止反射攻击
        if (InnerSingletonHolder.innerSingleton != null) {
            throw new RuntimeException("单例已存在一个实例");
        }
    }

    public static InnerSingleton getInnerSingleton() {
        return InnerSingletonHolder.innerSingleton;
    }
    
}

然后,我们进行反序列化,查看反序列化生成的实例跟单例的实例是否是同一个

/**
 * 测试demo
 *
 * @auther Hy
 * @date 2020/8/25
 */
public class TestDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        InnerSingleton innerSingleton = InnerSingleton.getInnerSingleton();
        // 序列化测试
/*        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("innerTest"));
        oos.writeObject(innerSingleton);
        oos.close();*/
        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("innerTest"));
        InnerSingleton innerSingleton1 = (InnerSingleton) ois.readObject();
        System.out.println(innerSingleton == innerSingleton1);
    }
}

/**
 * 由静态内部类持有单例对象,并调用外部类的私有构造器初始化,由外部类调用静态内部类的属性
 * 本质是一个懒汉模式,在类加载时才会初始化对象
 */
class InnerSingleton implements Serializable {

    // 需要固定序列化版本号id,如果不固定,JVM会根据字段、方法等生成一个序列化ID,并存入对应的序列化文件,反序列化时,
    // 会按照相同规则生成一个序列化版本号进行对比,如果类已经发生了改变,反序列化的版本号会对应不上,反序列化会失败
    private static final long serialVersionUID = 7822769557659839582L;

    private static class InnerSingletonHolder {
        private static InnerSingleton innerSingleton = new InnerSingleton();
    }

    private InnerSingleton() {
        // 防止反射攻击,只有恶汉与静态类不类能防止反射攻击
        if (InnerSingletonHolder.innerSingleton != null) {
            throw new RuntimeException("单例已存在一个实例");
        }
    }

    public static InnerSingleton getInnerSingleton() {
        return InnerSingletonHolder.innerSingleton;
    }

}

运行结果:

java单例模式代码实现方式_java单例模式实现方式

由此可见,反序列化创建了一个新的实例

解决方法:Serializable的源码中给出了提示

java单例模式代码实现方式_java单例模式实现方式

/**
 * 测试demo
 *
 * @auther Hy
 * @date 2020/8/25
 */
public class TestDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        InnerSingleton innerSingleton = InnerSingleton.getInnerSingleton();
        // 序列化测试
/*        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("innerTest"));
        oos.writeObject(innerSingleton);
        oos.close();*/
        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("innerTest"));
        InnerSingleton innerSingleton1 = (InnerSingleton) ois.readObject();
        System.out.println(innerSingleton == innerSingleton1);
    }
}

/**
 * 由静态内部类持有单例对象,并调用外部类的私有构造器初始化,由外部类调用静态内部类的属性
 * 本质是一个懒汉模式,在类加载时才会初始化对象
 */
class InnerSingleton implements Serializable {

    // 需要固定序列化版本号id,如果不固定,JVM会根据字段、方法等生成一个序列化ID,并存入对应的序列化文件,反序列化时,
    // 会按照相同规则生成一个序列化版本号进行对比,如果类已经发生了改变,反序列化的版本号会对应不上,反序列化会失败
    private static final long serialVersionUID = 7822769557659839582L;

    private static class InnerSingletonHolder {
        private static InnerSingleton innerSingleton = new InnerSingleton();
    }

    private InnerSingleton() {
        // 防止反射攻击,只有恶汉与静态类不类能防止反射攻击
        if (InnerSingletonHolder.innerSingleton != null) {
            throw new RuntimeException("单例已存在一个实例");
        }
    }

    public static InnerSingleton getInnerSingleton() {
        return InnerSingletonHolder.innerSingleton;
    }

    // 反序列化时,如果是单例模式,需要重写该方法,返回单例的实例,否则会获取到不同的对象
    Object readResolve() throws ObjectStreamException {
        return InnerSingletonHolder.innerSingleton;
    }
}

运行结果:

java单例模式代码实现方式_java单例模式实现方式

因此,在工作中推荐大家使用静态类部类单例模式,可以有效的防止反射攻击与序列化带来的相关问题

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/169696.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 移动手机app开发

    移动手机app开发App开发,是指专注于手机应用软件开发与服务。App是application的缩写,通常专指手机上的应用软件,或称手机客户端。另外目前有很多在线app开发[1]平台,比如应用之星平台很好用。App开发,是指专注于手机应用软件开发与服务。App是application的缩写,通常专指手机上的应用软件,或称手机客户端。苹果公司的Appstore开创了手机软件业发展的新篇章,使得第三方软

  • 人体检测–热释电传感器开发

    人体检测–热释电传感器开发人体检测–热释电传感器开发人体热释电传感器顾名思义是探测是否有人体通行和通过,由于它的廉价性,使得它的应用范围非常广泛。楼道里的灯,天台的报警设施等,都是利用这个来进行报警和检测。本文章将分为两个板块来介绍传感器的开发和应用。一·热释电传感器的工作原理  某些晶体,例如钽酸锂、硫酸三甘肽等受热时,晶体两端会产生数量相等、符号相反的电荷。1842年布鲁斯特将这种由温度变化引起的电极化现象正式命名为…

  • 游戏智能中的AI——从多角色博弈到平行博弈

    游戏智能中的AI——从多角色博弈到平行博弈“数据猿年度重磅活动预告:2020年度金猿策划活动(金猿榜单发布+金猿奖杯颁发)即将推出,尽情咨询期待!大数据产业创新服务媒体——聚焦数据·改变商业本文作者:沈宇,韩金朋,李灵犀…

  • SQL service基础(四)连接查询、自身连接查询、外连接查询和复合条件连接查询[通俗易懂]

    SQL service基础(四)连接查询、自身连接查询、外连接查询和复合条件连接查询[通俗易懂]实验目标:1.掌握涉及一个以上数据表的查询方法。2.掌握等值连接3.掌握自然连接4.掌握非等值连接5.掌握自身连接、外连接和复合条件连接本次实验sql脚本:INSERT[dbo].[T]([TNO],[TN],[SEX],[AGE],[PROF],[SAL],[COMM],[DEPT])VALUES(N’T1′,N’李力  ‘,N’男’,…

  • 企业微信机器人定时发送信息

    企业微信机器人定时发送信息企业微信可以配置机器人,提升办公效率,还能实现一键群发和定时发送等功能。

  • fiddler和charles哪个好用_windows一分钟重启解决

    fiddler和charles哪个好用_windows一分钟重启解决前言Charles是收费软件,可以免费试用30天。试用期过后,未付费的用户仍然可以继续使用,但是每次使用时间不能超过30分钟,并且启动时将会有10秒种的延时。此时,我们只需网上找一个注册码即可解

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号