zoukankan      html  css  js  c++  java
  • 设计模式之单例模式

    一、单例模式(singleton)

    是Java设计模式中最常用的模式之一,是面试过程中经常考察的点,需要手写单例。单是唯一,例是实例,即某个类在系统中只能存在唯一的一个实例对象可供使用。

    二、要点

    1、 构造器私有化,由于类的实例唯一,所以外界不能通过new关键字创建实例对象

    2、 在类的内部创建实例,并定义一个静态成员变量保存类的实例

    3、 需要向外界提供获得实例的方式

    a) 直接暴露

    b) 通过get方法获取

    三、几种常见的单例模式,一般分为两类:饿汉式和懒汉式

    饿汉式

    随着类的加载不管需不需要都会直接创建实例对象,不存在线程安全问题。

    a. 直接实例化 (简单直观)
    /**
    * 单例模式之直接实例方式
    * @author lux81
    *
    */
    public class Singleton1 {
       //静态常量保存实例对象
    public static final Singleton1 INSTANCE = new Singleton1();
    //构造器私有化
    private Singleton1() {
    System.out.println("直接实例化");
    }
    }
    //调用测试
    public class TestSingleton1 {
    public static void main(String[] args) {
           //直接通过类名.属性 调用
    Singleton1 instance = Singleton1.INSTANCE;
    System.out.println(instance);
    }
    }
    b. 枚举式 (最简洁)
    /**
    * 单例模式之枚举方式
    * 枚举类型 表示该类型的对象是有限的几个
    * 我们可以限定为一个 就成了单例
    * @author lux81
    *
    */
    public enum Singleton2 {
    INSTANCE;
    //添加一个测试方法
    public void sayHello() {
    System.out.println("hello");
    }
    }

    //测试类
    public class TestSingleton2 {
    public static void main(String[] args) {
    Singleton2 instance2 = Singleton2.INSTANCE;
    //调用测试方法 输出 hello
    instance2.sayHello();
    //直接输出实例对象名
    System.out.println(instance2);
    }
    }

     

    c. 静态代码块 (适合复杂实例化)

    在实例化对象的时候可能会初始化一些数据

    /**
    * 单例模式之静态代码块方式
    * 加入在实例化对象时需要初始化一些配置信息可以使用该方式
    * @author lux81
    *
    */
    public class Singleton3 {
    public static final Singleton3 INSTANCE;
    //测试从配置文件中获取初始化数据
    private String info;

    static {
    //加载配置文件
    Properties prop = new Properties();
    try {
    prop.load(Singleton3.class.getClassLoader().getResourceAsStream("test.properties"));
    } catch (IOException e) {
    throw new RuntimeException(e);
    }

    //实例化对象 并获取初始化数据
    INSTANCE = new Singleton3(prop.getProperty("name"));
    }

    private Singleton3(String info) {
    this.info = info;
    }
    //setter和 getter方法略
    }

    //测试
    public class TestSingleton3 {
    public static void main(String[] args) {
    Singleton3 instance3 = Singleton3.INSTANCE;
           //调用方法获取配置文件 name属性值
    System.out.println(instance3.getInfo());
    }
    }

     

    懒汉式

    延迟创建或者是需要的时候才创建

    a. 线程不安全(适用于单线程)
    /**
    * 单例模式之懒汉式(线程不安全,适合单线程)
    * @author lux81
    *
    */
    public class Singleton4 {
    private static Singleton4 instance;

    private Singleton4() {
    }

    //提供给外面获取实例对象的get方法
    public static Singleton4 getInstance() {
    //如果对象不存在则创建,已经存在直接返回
    if (instance == null) {
               //此处如果在多线程的情况下,会出现创建多个不同对象的情况
               try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    instance = new Singleton4();
    }
    return instance;
    }
    }

    //测试
    public class TestSingleton4 {
    public static void main(String[] args) {
    Singleton4 s1 = Singleton4.getInstance();
    Singleton4 s2 = Singleton4.getInstance();
    System.out.println(s1 == s2); //返回结果 true
           
           //使用多线程方式测试
           Callable<Singleton4> c = new Callable<Singleton4>() {
    @Override
    public Singleton4 call() throws Exception {
    return Singleton4.getInstance();
    }
    };
           //定义一个线程池 数量设置为2
    ExecutorService es = Executors.newFixedThreadPool(2);
    Future<Singleton4> f3 = es.submit(c);
    Future<Singleton4> f4 = es.submit(c);

    Singleton4 s3 = f3.get();
    Singleton4 s4 = f4.get();

    System.out.println(s3 == s4); //结果为false

    es.shutdown();
    }
    }

     

    b. 线程安全(适用于多线程)

    在方式a基础上进行优化

    /**
    * 单例模式之懒汉式(线程安全)
    * @author lux81
    *
    */
    public class Singleton5 {
    private static Singleton5 instance;

    private Singleton5() {
    }

    //提供给外面获取实例对象的get方法
    public static Singleton5 getInstance() {
           //如果已经存在对象 就不需要再加同步锁
    if (instance == null) {
    //加上同步代码块 保证线程安全
    synchronized (Singleton5.class) {
    if (instance == null) {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    instance = new Singleton5();
    }
    }
    }
    return instance;
    }
    }
    //测试
    public class TestSingleton5 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    Callable<Singleton4> c = new Callable<Singleton4>() {
    @Override
    public Singleton4 call() throws Exception {
    return Singleton4.getInstance();
    }
    };
    ExecutorService es = Executors.newFixedThreadPool(2);
    Future<Singleton4> f1 = es.submit(c);
    Future<Singleton4> f2 = es.submit(c);

    Singleton4 s1 = f1.get();
    Singleton4 s2 = f2.get();

    System.out.println(s1 == s2); //返回结果为 true

    es.shutdown();
    }
    }

     

    c. 静态内部类方式(适用于多线程)

    方式b看起来有点复杂,而静态内部类方式既能实现延迟加载,也能保证线程安全

    /**
    * 单例模式之懒汉式(静态内部类)
    * 只有在调用getInstance()方法时才会 加载和初始化内部类,进而才会创建实例对象
    * 静态内部类不会随着外部类的加载和初始化而初始化,是独立的,单独加载和初始化
    * @author lux81
    *
    */
    public class Singleton6 {
    private Singleton6() {
    }
    //定义静态内部类 创建实例化对象
    private static class Inner{
    private static final Singleton6 INSTANCE = new Singleton6();
    }
    //还是通过get方法获取
    public static Singleton6 getInstance() {
    return Inner.INSTANCE;
    }
    }

     

  • 相关阅读:
    随机选择
    Creating Apps With Material Design —— Defining Shadows and Clipping Views
    HDU 1853Cyclic Tour(网络流之最小费用流)
    053第502题
    【ThinkingInC++】64、重载new和delete,来模仿内存的分配
    Android设置里面默认存储器选项(default write disk)的实现
    Transparency Tutorial with C#
    ssh远程登录linux live系统
    JAVA把字符串当作表达式执行
    [Head First设计模式]生活中学设计模式——组合模式
  • 原文地址:https://www.cnblogs.com/coderLeo/p/13150054.html
Copyright © 2011-2022 走看看