设计模式之单例模式

本文深入解析单例模式的核心概念及其实现方式,包括饿汉式、懒汉式、双重检测锁、静态内部类和枚举单例等五种常见模式。探讨了每种模式的特点、优缺点以及应用场景,如配置文件管理、计数器、文件系统等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式

核心:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常用场景:
1. windows 系统 任务管理器、回收站

2. 网站计数器

3. 配置文件

4. 操作系统的文件系统

5. spring 中的 bean 默认是单例

...
优点:
减少系统开销
常见的五种单例模式
  1. 主要

    • 饿汉式(线程安全、调用效率高。但是,不能延时加载)
    • 懒汉式(线程安全、调用效率不高。但是可以延时加载)
  2. 其它

    • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题。不建议使用)
    • 静态内部类式(线程安全、调用效率高。可以延时加载)
    • 枚举单例(线程安全、调用效率不高。但是可以延时加载,天然防止反射、反序列化漏洞)
问题
  • 反射破解(不包含枚举类)以上几种方式(可以在构造方法中手动抛出异常空值)
1. 饿汉式
/*
 * 饿汉式实现单例模式
 * 
 *  1. 构造方法私有化 
 *  2. 类初始化时,立即加载(希望延时加载,即懒汉式)
 */
public class Singleton {

    // 天然的线程安全,调用效率高。
    private static Singleton singleton = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return singleton;
    }

}
2. 懒汉式
/*
 * 懒汉式 单例模式
 * 
 *  延迟加载,真正用的时候才加载
 */
public class SingletonLazy implements Serializable {

    private static SingletonLazy singleton;

    private SingletonLazy() {
        // 防止反射破解单例模式
        // if (singleton != null) {
        // throw new RuntimeException();
        // }
    }

    // 每次调用都要同步
    // 资源利用率高,并发效率低
    public static synchronized SingletonLazy getInstance() {
        if (singleton == null)
            singleton = new SingletonLazy();
        return singleton;
    }

    // 反序列化时,如果定义了 readResolve 方法则直接返回此方法返回的对象,不需要单独再创建对象
    // private Object readResolve() throws ObjectStreamException {
    // return singleton;
    // }

}
3. 双重检验锁
/*
 * 双重检测锁模式
 */
public class SingletonLock {

    private static SingletonLock singleton;

    private SingletonLock() {

    }

    public static SingletonLock getInstance() {
        if (singleton == null) {
            SingletonLock sl;
            synchronized (SingletonLock.class) {
                sl = singleton;
                if (sl == null) {
                    synchronized (SingletonLock.class) {
                        if (sl == null) {
                            sl = new SingletonLock();
                        }
                    }
                    singleton = sl;
                }
            }
        }
        return singleton;
    }
}

4. 静态内部类
/*
 * 静态内部类实现单例模式
 * 
 *   线程安全并且懒加载
 */
public class SingletonInner {

    private SingletonInner() {

    }

    private static class SingletonInnerClass {
        private static final SingletonInner instance = new SingletonInner();
    }

    public static SingletonInner getInstance() {
        return SingletonInnerClass.instance;
    }
}
5. 枚举类
/*
 * 枚举实现单例
 */
public enum SingletonEnum {

    // 枚举元素,本身就是单例对象,没有懒加载
    INSTANCE;

    public void getInstance() {

    }
}

测试反射、反序列化

/**
 * 测试反射、反序列化破解单例
 * 
 * @author apple
 */
public class Main {

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

        SingletonLazy instance1 = SingletonLazy.getInstance();
        // SingletonLazy instance2 = SingletonLazy.getInstance();
        //
        System.out.println(instance1);
        // System.out.println(instance2);
        //
        // // 通过反射获取私有构造器
        // Class<SingletonLazy> clz = (Class<SingletonLazy>)
        // Class.forName("com.beng.design.singleton.SingletonLazy");
        // Constructor<SingletonLazy> cons = clz.getDeclaredConstructor(null);
        // cons.setAccessible(true);
        // SingletonLazy instance3 = cons.newInstance();
        // SingletonLazy instance4 = cons.newInstance();
        //
        // System.out.println(instance3);
        // System.out.println(instance4);

        // 通过序列化的方式构造多个对象
        // FileOutputStream fos = new FileOutputStream("a.txt");
        // ObjectOutputStream oos = new ObjectOutputStream(fos);
        // oos.writeObject(instance1);
        // oos.close();
        // fos.close();
        // System.out.println("end");

        ObjectInputStream oin = new ObjectInputStream(new FileInputStream("a.txt"));

        SingletonLazy instance = (SingletonLazy) oin.readObject();
        System.out.println(instance);
    }

}

测试单例模式效率

/**
 * 测试创建单例模式 效率
 * 
 * @author apple
 */
public class Test {

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

        long start = System.currentTimeMillis();
        int nthreads = 10;
        final CountDownLatch count = new CountDownLatch(nthreads);

        for (int j = 0; j < nthreads; ++j) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 1000000; ++i) {
                        Object obj = SingletonEnum.INSTANCE;
                    }
                    count.countDown();
                }
            }).start();
        }

        long end = System.currentTimeMillis();
        count.await(); // main 线程阻塞,直到count=0
        System.out.println("总耗时:" + (end - start));

    }
}

资源下载链接为: https://pan.quark.cn/s/5c50e6120579 在Android移动应用开发中,定位功能扮演着极为关键的角色,尤其是在提供导航、本地搜索等服务时,它能够帮助应用获取用户的位置信息。以“baiduGPS.rar”为例,这是一个基于百度地图API实现定位功能的示例项目,旨在展示如何在Android应用中集成百度地图的GPS定位服务。以下是对该技术的详细阐述。 百度地图API简介 百度地图API是由百度提供的一系列开放接口,开发者可以利用这些接口将百度地图的功能集成到自己的应用中,涵盖地图展示、定位、路径规划等多个方面。借助它,开发者能够开发出满足不同业务需求的定制化地图应用。 Android定位方式 Android系统支持多种定位方式,包括GPS(全球定位系统)和网络定位(通过Wi-Fi及移动网络)。开发者可以根据应用的具体需求选择合适的定位方法。在本示例中,主要采用GPS实现高精度定位。 权限声明 在Android应用中使用定位功能前,必须在Manifest.xml文件中声明相关权限。例如,添加<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />,以获取用户的精确位置信息。 百度地图SDK初始化 集成百度地图API时,需要在应用启动时初始化地图SDK。通常在Application类或Activity的onCreate()方法中调用BMapManager.init(),并设置回调监听器以处理初始化结果。 MapView的创建 在布局文件中添加MapView组件,它是地图显示的基础。通过设置其属性(如mapType、zoomLevel等),可以控制地图的显示效果。 定位服务的管理 使用百度地图API的LocationClient类来管理定位服务
资源下载链接为: https://pan.quark.cn/s/dab15056c6a5 Oracle Instant Client是一款轻量级的Oracle数据库连接工具,能够在不安装完整Oracle客户端软件的情况下,为用户提供访问Oracle数据库的能力。以“instantclient-basic-nt-12.1.0.1.0.zip”为例,它是针对Windows(NT)平台的Instant Client基本版本,版本号为12.1.0.1.0,包含连接Oracle数据库所需的基本组件。 Oracle Instant Client主要面向开发人员和系统管理员,适用于数据库查询、应用程序调试、数据迁移等工作。它支持运行SQL*Plus、PL/SQL Developer等管理工具,还能作为ODBC和JDBC驱动的基础,让非Oracle应用连接到Oracle数据库。 安装并解压“instantclient_12_1”后,为了使PL/SQL Developer等应用程序能够使用该客户端,需要进行环境变量配置。设置ORACLE_HOME指向Instant Client的安装目录,如“C:\instantclient_12_1”。添加TNS_ADMIN环境变量,用于存放网络配置文件(如tnsnames.ora)。将Instant Client的bin目录添加到PATH环境变量中,以便系统能够找到oci.dll等关键动态链接库。 oci.dll是OCI(Oracle Call Interface)库的重要组成部分。OCI是Oracle提供的C语言接口,允许开发者直接与数据库交互,执行SQL语句、处理结果集和管理事务等功能。确保系统能够找到oci.dll是连接数据库的关键。 tnsnames.ora是Oracle的网络配置文件,用于定义数据库服务名与网络连接参数的映射关系,包括服务器地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值