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;
}
}
那如今已经准备了一个女友类。
接下来,是不是就要产生一个女朋友了。
那咱们就在client new一个呗。
public class Client {
public static void main(String[] args) {
GirlFriend gf = new GirlFriend();
}
}
问题
大家试想一下。假设我们在client里,再去”找”一个女友,是不是也是能够的。
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:client调用
那这个时候,我们再调用。得到的就是一个唯一的女友了。
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 上绝对的防止了多次实例化。