设计模式之单例模式
来源:互联网 发布:php 上传工具 编辑:程序博客网 时间:2024/06/06 00:37
转载请声明出处:http://blog.csdn.net/qq_24692041/article/details/61921625
在开发中对于每一个类,一般来说我们要去调用这个类中的非静态方法,我们都需要new一个对象,然后去调用方法,这些实例所拥有的的属性值不一定都相同。比如说现在有一个猫Cat这个类,每一次我们要用到该对象我们都new一个对象,那我们new1000次就有1000个对象,就有1000只猫。分别都是:咖啡猫,懒猫,花猫,卖萌猫等等等等,一共有1000只,名字,毛的颜色,年龄等等属性都不一样。那如果现在在我们的项目中我们只需要一只猫,那咋办呢?有人说如果我们写代码的时候注意点,只new一次以后大家都用这一个对象,那就不会有1000只猫了,这样也可以,但是你确定你能保证只new一次吗?那我们可以用代码强制只能new一次,那多好呢?是不?要达到只有一个对象的目的,我们就得用到今天讲的单例模式。
下面边看代码一边进行细节述说:
首先我们开一个平时使用的代码看看:很简单就是一个简单的Cat类,然后我们在Main类中去new1000只猫。
/** * Created by PICO-USER on 2017/3/13. */public class Cat { public Cat(String name, int age) { this.name = name; this.age = age; }}
public class Main { public static void main(String[] args0) { for (int i = 0; i < 1000; i++) { Cat cat = new Cat(); System.out.print(cat + "\n"); } }}
从运行结果可以看出,我们new出来猫都是不同的,是不同的对象。
看单例模式下的代码:
单例模式一般来说我们广义的说法有两种,分别是所谓的懒汉式,恶汉式。下面这种就是懒汉式,后面我再说恶汉式。
首先我将构造方法私有化,new字段其实就是调用构造方法得到一个Cat类型的实例。现在将构造方法私有化,外面就无法new Cat()了,网友自己可以试试,下面的代码new的时候不会有提示,强行new出来会报错的。然后自己声明了一个Cat类型的私有变量。但是并没有new出来,再写了一个getInstance方法,在这个方法里面有一个判断,如果cat为null才会new一个对象返回。这儿虽然是私有方法,但是内部是可以访问的,这个应该不用说。
/** * Created by PICO-USER on 2017/3/13. */public class Cat { private static Cat cat; private Cat() {System.out.print("Cat()\n");} public static Cat getInstance() {System.out.print("getInstance()\n");if (cat == null) { cat = new Cat(); } return cat; }}
Main类稍作修改,通过getInstance方法获取对象。
public class Main { public static void main(String[] args0) { for (int i = 0; i < 1000; i++) { Cat cat = Cat.getInstance(); System.out.print(cat + "\n"); } }}
这时候,再来看看运行结果,我们调用了1000次getInstance方法,但是得到的都是同一个对象。这就是单例模式的作用了,将自己的构造方法私有化,不让外部无线的去创建对象,达到对象唯一的作用。这儿注意一个细节,构造方法是在getInstance方法之后调用的,也就是说是在第一次调用getInstance方法的时候才去new了cat对象。
现在我们Cat还是不变,来改一下调用的Main类中的调用方式,我们改为启动多个线程,在线程中来调用getInstance方法,看看会有什么状况!
public class Main { public static void main(String[] args0) { for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { @Override public void run() { Cat cat = Cat.getInstance(); System.out.print(cat + "\n"); } }).start(); } }}
看到上面的打印结果,很明显有三只猫,这就是线程并发的问题发生了,线程之间是并发的,也就是说不知道是谁先启动,也可能是同时启动,并且他们有可能同时访问getInstance方法。在1线程还没有new出对象的时候,2线程就调用getInstance方法了,这时候3和4刚好也来调用了,他们都看到cat是null,所以他们都进入了getInstance的if中,所以就创建出了几个不同的对象。具体的线程并发的问题,不是很清楚的网友可以在我之前两篇片专门讲线程的博文中看看。
《Java 线程和进程,并发解决之synchronized》,《多线程之原子性,可见性,有序性,并发问题解决》
好了,说明我们上面的单例模式在同一个线程中去使用没有问题,但是在不同的多个线程中去使用,那就有可能出问题,达不到我们想要的结果,现在就用synchronized字段来解决这个问题,当然也可以不用synchronized字段改用别的方式比如说semaphore。这儿主要是讲设计模式,线程并发的问题可以到我的博文分类《Android多线程》看看。现在我们来更改代码;
/** * 通过这个方法返回给外面Cat的对象,加synchronized修饰,同一时间只允许一个线程访问该方法。 * * @param name * @param age * @return */public static synchronized Cat getInstance() { if (cat == null) { cat = new Cat(); } return cat;}
这时候就能保证只有一个实例了,还有一种更安全的写法,加双重检测:判断了两次cat是否为null,并且将synchronized修饰字段放到里面,用于锁定里面的if判断。
/** * 通过这个方法返回给外面Cat的对象 * * @param name * @param age * @return */public static Cat getInstance() { if (cat == null) { synchronized (Cat.class) {if (cat == null) {cat = new Cat();
}
} } return cat;}
好了,懒汉式单例就讲完了。下面我来讲讲恶汉式单例。这种方式就是直接声明一个Cat类型的变量,并且直接new出来,在调用getInstance方法的时候返回。
/** * Created by PICO-USER on 2017/3/13. */public class Cat { //定义了一个私有化的本身这个类的对象 private static Cat cat = new Cat(); /** * 私有化构造方法,让外面无法进行new操作 */ private Cat() { System.out.print("Cat()\n"); } /** * 通过这个方法返回给外面Cat的对象 * * @return */ public static Cat getInstance() { System.out.print("getInstance\n"); return cat; }}
public class Main { public static void main(String[] args0) { for (int i = 0; i < 1000; i++) { new Thread(new Runnable() { @Override public void run() { Cat cat = Cat.getInstance(); System.out.print(cat+"\n"); } }).start(); } }}
可以看出来构造方法是在getInstance方法之前就调用了,并且对象也都是同一个。我们来看看两者之间的区别,首先看恶汉式,在Cat这个类加载的时候就已经new出来一个对象,每次调用getInstance方法的时候就将这个对象返回。而懒汉式在类加载的时候并没有new出对象,而是在第一次调用getInstance方法的时候才会去new一个出来,返回。
上面懒汉式单例中用到synchronized字段,同一时间只能由一个线程能访问共同代码,其他的就需要排队等待,这在性能上不是很好。所以就有了下面这种另类的单例模式,其实可以算是懒汉式单例吧!因为没有在类加载的时候就直接先new对象而是在第一次调用getInstance方法的时候,用内部类放方式,这种方式一般都用得比较多的。
/** * Created by PICO-USER on 2017/3/13. */public class Cat { private String name; private int age; private Cat() { System.out.print("Cat()\n"); } public static class InstanceHolder { private static Cat cat = new Cat(); } /** * 通过这个方法返回给外面Cat的对象 * * @return */ public static Cat getInstance() { System.out.print("getInstance\n"); return InstanceHolder.cat; } }
好了,到这儿,单例模式我们就算讲完了。
- 设计模式之 单例设计模式
- 设计模式之 单例设计模式
- 设计模式之单例设计模式
- 设计模式之-----------单例设计模式
- 设计模式之:单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之-单例设计模式
- 设计模式之单例设计模式 标签: 设计模式
- 设计模式之单例
- 设计模式之单例
- 设计模式之 单例
- 打开一个已经存在于工作空间的android项目
- 直接将datatable插入数据库中
- Java选择排序算法
- 日常生活 -- 数据结构与算法告一段落
- spring AOP详解之--前置增强 (MethodBeforeAdvice)
- 设计模式之单例模式
- 使用Spring的@Scheduled实现定时任务
- MyEclipse破解版+tomcat安装+jdk
- [Python 爬虫之路4] 使用selenium爬取知乎任意一个问题下,所有回答中的图片
- Android TextView 添加下划线的几种方式
- Spring Web MVC基础的一个小实例
- 详解google Chrome浏览器(理论篇)
- Android框架/系统服务是怎样管理第三方Search数据源的?
- 《趣学Python编程》笔记---第一部分:学习编程(2)