单例模式
一、概念
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类智能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。
因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法智能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
二、创建与代码实例
1. 饿汉式实现
私有化类的构造器->内部创建静态类的对象->创建静态公用类返回该内部对象
class Hungry{
// 私有化类的构造器
private Hungry(){
}
// 内部创建静态类的对象
private static Hungry hungry = new Hungry();
// 创建静态公用类返回该内部对象
public static Hungry getHungry(){
return hungry;
}
}
2. 懒汉式实现
私有化类的构造器->声明当前类的静态对象,没有初始化->创建静态公用类,在类中初始化(需要用ifelse判断是否已经初始化了),并返回当前类对象的方法
class Lazy{
private Lazy(){
}
private static Lazy lazy;
/*
创建对象分为三个步骤:
分配内存空间
初始化对象
将内存空间的地址赋值给对象的引用
但是JVM可能会对代码进行重排序,所以2和3可能会颠倒
*/
// 这里需要注意的是有可能发生重排序,那么就可能出现return的是空值,为了避免重排序,可以加上volatile关键字
// private static volatile Lazy lazy;
public static Lazy getLazy(){
if(lazy == null){ // 在锁外层再加一个条件判断是为了提高效率,这样在第一次创建了对象之后,不需要每一次都要上锁解锁
synchronized (Lazy.class){
if(lazy == null){
lazy = new Lazy();
}
}
} // 这种方式是线程安全的,首先加锁,再判断静态对象是否为空,避免了重复声明对象
if(lazy == null){
lazy = new Lazy();
} // 这种方式是线程不安全的,因为有可能有多个线程同时判空,然后重复声明对象
return lazy;
}
}
3. 区分饿汉式和懒汉式
饿汉式:
· 坏处是对象加载时间过长
· 好处是饿汉式是线程安全的
懒汉式:
· 好处是延迟对象的创建
· 坏处是目前写法是线程不安全的:因为有可能在进入if语句的时候可能会new多个对象-->可以改成线程安全的
三、 优点与应用
- 优点:由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
- 应用:网站的计数器、应用程序的日志应用、数据库连接池