保证一个类只允许创建一个对象 ,这个类就是单例类,这中设计模式就叫单例模式。
比如一个打印日志的例子,2个人都new一个日志对象,同时打印一个问题,如果超过一个对象,行为不正确
public class Logger {private FileWriter writer;public Logger() {File file = new File("/Users/wangzheng/log.txt");writer = new FileWriter(file, true); //true表示追加写入}//这里同时调用,会导致日志错乱,可能存在日志信息互相覆盖的情况。public void log(String message) {writer.write(message);}
}// Logger类的应用示例:
public class UserController {private Logger logger = new Logger();public void login(String username, String password) {// ...省略业务逻辑代码...logger.log(username + " logined!");}
}public class OrderController {private Logger logger = new Logger();public void create(OrderVo order) {// ...省略业务逻辑代码...logger.log("Created an order: " + order.toString());}
}
比如,项目中配置文件,一个项目只需要有一份就可以了,不需要每次用到还有创建它。或者 ,唯一递增 ID 号码生成 ,如果有2个对象,还存在重复生成id情况 、或者数据库连接池
import java.util.concurrent.atomic.AtomicLong;
public class IdGenerator {// AtomicLong是一个Java并发库中提供的一个原子变量类型,// 它将一些线程不安全需要加锁的复合操作封装为了线程安全的原子操作,// 比如下面会用到的incrementAndGet().private AtomicLong id = new AtomicLong(0);private static final IdGenerator instance = new IdGenerator();private IdGenerator() {}public static IdGenerator getInstance() {return instance;}public long getId() { return id.incrementAndGet();}
}// IdGenerator使用举例
long id = IdGenerator.getInstance().getId();
饿汉式的实现方式,在类加载的期间,就已经将 instance 静态实例初始化好了,所以,instance 实例的创建是线程安全的。不过,这样的实现方式不支持延迟加载实例。
对于一些创建对象比较耗时,建议使用懒汉式,在程序启动时尽早创建对象,也可以暴露问题
public class IdGenerator { private AtomicLong id = new AtomicLong(0);private static final IdGenerator instance = new IdGenerator();private IdGenerator() {}public static IdGenerator getInstance() {return instance;}public long getId() { return id.incrementAndGet();}
}
public class IdGenerator { private AtomicLong id = new AtomicLong(0);private static IdGenerator instance;private IdGenerator() {}public static synchronized IdGenerator getInstance() {if (instance == null) {instance = new IdGenerator();}return instance;}public long getId() { return id.incrementAndGet();}
}// 修改为双重检测,解决性能问题public class IdGenerator { private AtomicLong id = new AtomicLong(0);private static IdGenerator instance;private IdGenerator() {}public static IdGenerator getInstance() {if (instance == null) {synchronized(IdGenerator.class) { // 此处为类级别的锁if (instance == null) {instance = new IdGenerator();}}}return instance;}public long getId() { return id.incrementAndGet();}
}// instance 成员变量添加 volatile 关键字来禁止指令重排序即
内部SingletonHolder 在使用到的时候,才会进行创建,jvm保证线程安全
public class IdGenerator { private AtomicLong id = new AtomicLong(0);private IdGenerator() {}private static class SingletonHolder{private static final IdGenerator instance = new IdGenerator();}public static IdGenerator getInstance() {return SingletonHolder.instance;}public long getId() { return id.incrementAndGet();}
}
这种方式最简洁,可以保证不能通过反射实例化,枚举模式的单例还可以防止序列化和反序列化生成新的实例
public enum IdGenerator {INSTANCE;private AtomicLong id = new AtomicLong(0);public long getId() { return id.incrementAndGet();}
}
一、对单元测试不是很友好
当我们需要改变是实现的时候
public class Order {public void create(...) {//...long id = IdGenerator.getInstance().getId();// 需要将上面一行代码,替换为下面一行代码long id = OrderIdGenerator.getIntance().getId();//...}
}public class User {public void create(...) {// ...long id = IdGenerator.getInstance().getId();// 需要将上面一行代码,替换为下面一行代码long id = UserIdGenerator.getIntance().getId();}
}
二、隐藏类和类之间的依赖关系
三、对扩展性不好
怎么替换单例模式