设计模式分为三大类: 创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式: 适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式: 策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
单例模式定义: 单例模式确保某个类只有一个实例,并提供一个访问它的全局访问点。主要解决一个全局使用的类频繁地创建与销毁。控制实例数目,节省系统资源。
单例模式的特点: 单例类只能有一个实例。 单例类必须自己创建自己的唯一实例。 单例类必须给所有其他对象提供这一实例。 单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。
单例的四大原则: 构造私有 以静态方法或者枚举返回实例 确保实例只有一个,尤其是多线程环境 确保反序列换时不会重新构建对象
实现单例模式的方式: (1)饿汉式(立即加载): 饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这个对象不会改变,所以本身就是线程安全的。 Singleton 通过将构造方法限定为 private 避免了类在外部被实例化,在同一个虚拟机范围内,Singleton 的唯一实例只能通过 getInstance()方法访问。(事实上,通过 Java 反射机制是能够实例化构造方法为 private 的类的,会使 Java单例实现失效)
public class SingletonTest01 {
public static void main(String[] args) {
Hungry instance = Hungry.getInstance();
Hungry instance1 = Hungry.getInstance();
Hungry instance2 = Hungry.getInstance();
System.out.println(instance.getClass());
System.out.println(instance1.getClass());
System.out.println(instance2.getClass());
}
}
class Hungry {
private Hungry() {
}
private final static Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
(2)懒汉式(延迟加载): 该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个 Singleton 对象
public class SingletonTest03 implements Runnable {
@Override
public void run(){
for (int i=1;i<1000;i++){
LazyMan lazyMan=LazyMan.getInstance();
System.out.println(lazyMan.hashCode());
}
}
public static void main(String[] args) {
new Thread(new SingletonTest03()).start();
new Thread(new SingletonTest03()).start();
new Thread(new SingletonTest03()).start();
}
}
class LazyMan{
private LazyMan(){
}
private static LazyMan lazyMan;
public static synchronized LazyMan getInstance(){
if (lazyMan==null){
lazyMan = new LazyMan();
}
return lazyMan;
}
}
(3)双重检查锁(提高同步锁的效率): 使用双重检查锁进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率。
public class SingletonTest05 {
public static void main(String[] args) {
DoubleLock instance1 = DoubleLock.getInstance();
DoubleLock instance2 = DoubleLock.getInstance();
System.out.println(instance1.hashCode()==instance2.hashCode());
}
}
class DoubleLock{
private static volatile DoubleLock doubleLock;
private DoubleLock(){}
public static DoubleLock getInstance(){
if (doubleLock==null){
synchronized (DoubleLock.class){
if (doubleLock==null){
doubleLock = new DoubleLock();
}
}
}
return doubleLock;
}
}
(4) 静态内部类: 这种方式引入了一个内部静态类(static class),静态内部类只有在调用时才会加载,它保证了 Singleton 实例的延迟初始化,又保证了实例的唯一性。它把 singleton 的实例化操作放到一个静态内部类中,在第一次调用 getInstance() 方法时,JVM 才会去加载 InnerObject 类,同时初始hsingleton 实例,所以能让 getInstance() 方法线程安全。特点是:即能延迟加载,也能保证线程安全。静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。
public class SingletonTest06 {
public static void main(String[] args) {
StaticInClass instance1 = StaticInClass.getInstance();
StaticInClass instance2= StaticInClass.getInstance();
System.out.println(instance1.hashCode()==instance2.hashCode());
}
}
class StaticInClass{
private static volatile StaticInClass staticInClass;
private StaticInClass(){}
private static class Inner{
private static final StaticInClass INSTANCE = new StaticInClass();
}
public static synchronized StaticInClass getInstance(){
return Inner.INSTANCE;
}
}
(5)枚举实现(防止反射攻击): 事实上,通过 Java 反射机制是能够实例化构造方法为 private 的类的。这也就是我们现在需要引入的枚举单例模式。
public class SingletonTest07 {
public static void main(String[] args) {
Singleton instance1 = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
System.out.println(instance2.hashCode()==instance1.hashCode());
System.out.println(instance1.getClass());
System.out.println(instance2.getClass());
System.out.println(instance1.getDeclaringClass());
}
}
enum Singleton{
INSTANCE;
public void say(){
System.out.println("ok!");
}
}
|