Java设计模式之单例模式

场景问题

业内都有一个不朽的传说,就是程序员是找不到女朋友的。没有女朋友怎么行。今天咱就带着大家用Java的知识,来”追”一个女朋友。
那在追女友之间阿,咱先定一个女友的标准。不过,这个标准不能乱定是吧。不能像网上流传的一样,”女的,活的”。做为一个有理想的程序员,我觉得我的女朋友,要有身高吧,然后罩杯也不对低,低重也不好太胖阿。那么用 Java 语言来表述,就是这个”女友”得有三个属性。身高,体重,与胸围。所以,咱就建这么个类。叫 GirlFriend.

public class GirlFriend {
    private String cup;
    private int height;
    private int weight;

    public String getcup() {
        return cup;
    }

    public void setcup(String cup) {
        this.cup = cup;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}

那现在已经准备了一个女友类。接下来,是不是就要产生一个女朋友了。那咱们就在客户端 new一个呗。

public class Client {
    public static void main(String[] args) {
        GirlFriend gf = new GirlFriend();
    }
}

问题
大家试想一下,如果我们在客户端里,再去”找”一个女友,是不是也是可以的。

public class Client {
    public static void main(String[] args) {
        GirlFriend gf = new GirlFriend();
        GirlFriend gf1 = new GirlFriend();
        System.out.println(gf==gf1);
    }
}

而且,上述的代码,最终的结果也是 false。
那,做为一个有原则的男人,是不是要限制这种情况发现呢。

单例模式

单例模式定义

解决上面问题的一种方式,就是用单例模式。那么,何谓单例模式?我们先来看一下定义:

保证一个类仅有一个实例,并提供一个它的全局访问点。

实现方法

在 Java 中,单例模式实现分为两种。一种称为懒汉式,一种又称为饿汉式。我们分别来看一下这两种方式是怎么实现的。
1:懒汉式

public class GirlFriend {
    private String cup;
    private int height;
    private int weight;
    //定义一个变更来储存实例
    private static GirlFriend  instance =null;
    private GirlFriend(){

    }
    public static GirlFriend getInstance(){
        //判断实例是否为空
        if(null==instance){
            //如果当前实例还没有创建,那就生成一个,并赋值给储存实例
            instance = new GirlFriend();
        }
        return instance;
    }
    //以下都是示例方法

    public String getcup() {
        return cup;
    }

    public void setcup(String cup) {
        this.cup = cup;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}

2:饿汉式:

public class GirlFriend {
    private String cup;
    private int height;
    private int weight;
    //定义一个变更来储存创建好的实例
    private static GirlFriend  instance =new GirlFriend();
    private GirlFriend(){

    }
    public static  GirlFriend getInstance(){

        return instance;
    }
    //以下都是示例方法

    public String getcup() {
        return cup;
    }

    public void setcup(String cup) {
        this.cup = cup;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}

3:客户端调用
那这个时候,我们再调用,得到的就是一个唯一的女友了。

public class Client {
    public static void main(String[] args) {
        GirlFriend gf = GirlFriend.getInstance();
        GirlFriend gf1 =GirlFriend.getInstance();
        System.out.println(gf==gf1);
    }
}

由于我们在女友类里,把构造方法给私有化了,所以,在客端端调用的时候,得到的就是唯一的一个女友对象了。(在这里,我们暂时不考虑反射。)
4:关于命名
其实,所谓饿汉与懒汉也是一种比较形象的说法吧。
所谓饿汉,也就是说你比较饥渴,饿,于是就在装载类的时候就已经创建好”女友了”。那懒汉,也就是懒,等你需要女友的时候再去创建。

延迟加载与缓存

延迟加载

懒汉式的单例模式体现了延迟加载的思想。那什么是延迟加载。
简单一点来主,延迟加载就是一开始不去加载数据或者资源,等到要用到的时候,才去加载,也就是 Lazy Load。这在实际开发中也是一种常见的思想,尽可能的节约资源。

缓存

懒汉式还体现了缓存的思想,缓存在开发中也是常见的功能。
简单的来说,就是当某些资源需要被反复使用的时候,而这些资源储存在系统外部,比如数据库,硬盘等,那如果每次操作都要重新读取一次,显然很浪费资源。所以,懒汉式加载中,在一开始,就定义了一个变量,来储存要生成的实例,也就是:

private static GirlFriend  instance =null;

之后,再生成实例后,赋值给变量。

线程安全

从线程安全性上来讲,不加同步的懒汉式是线程不安全的。也就是说,如果多个线程同时调用getInstance方法,就会导制并发问题。那该如何解决。其实只要加上 synchronized就可以了。也就是:

  public static synchronized GirlFriend getInstance(){}

但是这样一来,就会降低访问速度,而且每次都要判断,那该如何实现,这里就要用到双重加锁。
所谓双重加锁,指的是,不是每一次 getInstance都要同步。而是先不同步,进入方法之后,先检查实例是否存在。如果不存在,再进入同步块,这是第一重检查。进入同步块之后,再次检查实例是否存在,如果不存在,就在同步情况下创建一个实例。这是第二重检查。这样一来,就可以减少多次在同步情况下进行判断所浪费的时间了。
实现代码如下:

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

注:双重加载需要在 java5以上的版本。

枚举

其实,还有一种更高效的单例实现,也就是单元素的枚举。关于枚举的介绍,我这里就不多做介绍了。我们看一下如何用枚举实现单例。

public enum  GirlFriend {
    instance;
    private String cup;
    private int height;
    private int weight;
    //以下都是示例方法

    public String getcup() {
        return cup;
    }

    public void setcup(String cup) {
        this.cup = cup;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}

使用枚举实现单例会使代码更加的简洁。而且,从 JVM 上绝对的防止了多次实例化。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值