Java中的设计类-单例模式

文章介绍了单例模式在确保资源管理和数据库连接一致性中的应用,强调了线程安全问题及其解决方法,如同步锁、双重检查锁定和静态内部类。此外,还提及了序列化和反序列化的注意事项以及单一职责原则的讨论。

1.为什么要用单例化

确保只有一个实例:单例模式确保在整个应用程序中只存在一个实例。这对于某些对象来说是非常重要的,例如数据库连接、线程池、日志记录器等。通过限制实例数量,可以避免资源浪费和冲突。

比如数据库连接的建立非常耗时,多次的数据库连接实例化会严重拖慢程序运行速度。在斗地主大作业中,每次数据库的操作都会建立一次连接并关闭一次链接,造成了严重的延迟。

当涉及到数据库连接时,单例模式可以确保只有一个数据库连接实例存在,以便在整个应用程序中共享和重复使用该连接。下面是一个简单的数据库连接使用单例模式的例子:


public class DatabaseConnection {
    private static DatabaseConnection instance;
    private Connection connection;

    private DatabaseConnection() {
        // 私有化构造函数,防止外部实例化
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }

    public Connection getConnection() {
        return connection;
    }

    // 其他数据库操作方法
    // ...
}


在上述示例中,DatabaseConnection类使用单例模式来提供数据库连接实例。通过私有化构造函数,防止外部直接实例化对象。getInstance()方法负责创建或返回单例实例,确保在整个应用程序中只有一个实例存在。

通过调用getConnection()方法,可以获取数据库连接实例,从而进行数据库操作。由于单例模式的特性,多个类可以通过DatabaseConnection.getInstance().getConnection()来获取相同的数据库连接实例。

这样做的好处是避免了多个地方创建和管理数据库连接,确保了连接的唯一性和共享性。这对于需要频繁进行数据库操作的应用程序来说,可以提高性能并减少资源消耗。

需要注意的是,上述示例并没有考虑多线程环境下的线程安全性。在实际应用中,可能需要采取额外的措施来保证在多线程环境下的线程安全访问,例如使用双重检查锁定或静态内部类方式。

提供全局访问点:

全局访问点:单例模式提供了一个全局访问点,使得其他类可以方便地访问该实例。这消除了对全局变量的需要,避免了在各处传递对象的麻烦。

资源共享和数据共享:

资源共享和数据共享:由于单例模式只有一个实例,可以更有效地管理共享的资源和数据。多个对象可以共享相同的实例,避免了重复创建和维护多个相同的对象。

延迟实例化:

延迟实例化:单例模式允许在需要时才创建实例,延迟了对象的实例化过程。这对于某些资源密集型或初始化耗时的对象来说是非常有用的,可以提高应用程序的性能和启动速度。

实现单一职责原则:

实现单一职责原则:单例模式对于某些类来说,确保只有一个实例存在有助于遵循单一职责原则。它将创建和管理实例的责任集中在一个地方,使得对象的职责更加清晰明确。

尽管单例模式具有这些优点,但也需要谨慎使用。过度使用单例模式可能导致代码的耦合性增加,增加了对象之间的依赖关系。因此,应根据具体的需求和场景判断是否使用单例模式,并权衡其利弊。

2.怎么使用单例化模式

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // 私有化构造函数,防止外部实例化
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    // 其他方法
    public void doSomething() {
        // 执行操作
    }
}

在上面的示例中,Singleton类的构造函数被私有化,这意味着其他类无法直接实例化Singleton对象。然后,定义了一个静态方法getInstance(),该方法检查类的静态变量instance是否为null。如果instance为null,则创建一个新的Singleton实例;如果instance已经存在,则直接返回现有的实例。通过这种方式,无论在何处调用getInstance()方法,都将始终返回相同的Singleton实例,从而确保只有一个实例存在。

客户端可以通过以下方式来使用单例模式:

Singleton singleton = Singleton.getInstance();
singleton.doSomething();

单例模式存在的问题及解决办法

线程不安全

多线程安全性:在多线程环境下使用单例模式时,需要注意实现线程安全,当多个线程同时调用getInstance()方法并且instance为null时,可能会创建多个实例,违背了单例模式的初衷。

解决方式

1.使用同步锁

使用同步锁(synchronized):在getInstance()方法上使用synchronized关键字,确保在同一时间只有一个线程可以访问该方法。这样可以避免多个线程同时创建实例,但会引入性能的开销。

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}
2.使用双重检查锁定

双重检查锁定(Double-Checked Locking):在synchronized块内部进行双重检查。

public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        }
    }
    return instance;
}

只有当instancenull时才进行同步操作创建实例。避免了每次访问实例都进行同步的开销,因此在实例已经存在的环境下,后续的线程可以直接返回实例,而无需进入同步块。

3.使用静态内部类

使用静态内部类延迟加载实例的特性,并保证线程安全。当第一次调用getInstance()方法时,才会加载内部类并创建实例。

public class Singleton {
    private Singleton() {
        // 私有化构造函数
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

序列化和反序列化

序列化和反序列化:如果单例类需要支持序列化和反序列化,需要特殊处理,以确保反序列化后仍然是单例实例。(暂未学习)

单一职责原则

单一职责原则:单例模式常常被滥用,过度使用单例模式可能导致代码的耦合性增加,违反单一职责原则。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值