首页 > 学院 > 开发设计 > 正文

设计模式读书笔记一 单例模式

2019-11-09 15:14:30
字体:
来源:转载
供稿:网友

1、单例模式介绍 单例模式是应用最广泛的模式之一,也是可以说是初级工程师唯一会用的设计模式。在应用这一模式的时候,单例对象的类必须保证只有一个实例存在。许多时候整个系统只要一个全局对象,这样有利于我们协调系统整体行为。如在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader中又包含有线程池、缓存系统、网络请求等,很消耗资源,因此,没有理由让它有多个实例。这种情况就是单例模式的使用场景。 2、单例模式的定义 确保类只有一个实例,而且自行实例化并向整个系统提供这个实例 3、使用场景 确保类只有一个对象的场景 4、UML类图 略 5、单例模式的多种实现 (1)饿汉模式 这是单例模式的最简单的一种写法,在具体介绍之前我们写一个简单的例子,这样也方便后面介绍单例模式的其他写发。 例如:一个公司只有一个CEO、几个VP、无数个员工,例子很简单。下面我们一点点的实现

普通员工类:Staff.java

package com.example.singleton;public class Staff { public void work() { //干活 }}

副总裁类:VP.java

package com.example.singleton;public class VP extends Staff { @Override public void work() { // TODO Auto-generated method stub super .work(); //管理下面的经理 }}

CEO类:CEO.java

package com.example.singleton;public class CEO extends Staff { PRivate static final CEO mCeo= new CEO(); // 构造函数私有化(构造方法的私有化是单例模式的核心) private CEO() { } /** * 方法一 * 饿汉单例模式 * * @author HP * */ public static CEO getCeo () { return mCeo ; } @Override public void work() { // TODO Auto-generated method stub super .work(); // 管理 vp }}

公司类:Company.java

package com.example.singleton;import java.util.ArrayList;import java.util.List;/** * 公司类 * @author HP * */public class Company { private List<Staff> allStaffs=new ArrayList<>(); public void addStaff(Staff per) { allStaffs.add(per); } public void showAllStaffs() { for(Staff per:allStaffs) { System.out.println("Obj:"+per.toString()); } }}

最后是Main.java

package com.example.singleton;public class Main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Company cp = new Company(); //方法一 :饿汉模式 Staff ceo1 = CEO. getCeo(); Staff ceo2 = CEO. getCeo(); cp.addStaff(ceo1); cp.addStaff(ceo2); cp.showAllStaffs(); System. out .println("--------------------------------------" ); //测试发现有两条数据是一样的,说明单例类只能提供一个对象 Staff vp1= new VP(); Staff vp2= new VP(); Staff staff1= new Staff(); Staff staff2= new Staff(); Staff staff3= new Staff(); cp.addStaff(vp1); cp.addStaff(vp2); cp.addStaff(staff1); cp.addStaff(staff2); cp.addStaff(staff3); cp.showAllStaffs(); }}

输出结果:

Obj:com.example.singleton.CEO@1cacd5d4Obj:com.example.singleton.CEO@1cacd5d4--------------------------------------Obj:com.example.singleton.CEO@1cacd5d4Obj:com.example.singleton.CEO@1cacd5d4Obj:com.example.singleton.VP@170a6001Obj:com.example.singleton.VP@2a24ed78Obj:com.example.singleton.Staff@5e6276e5Obj:com.example.singleton.Staff@126be4ccObj:com.example.singleton.Staff@697a1686

以上就是这个例子的全部内容,其中CEO.java就是一个单例类,因为之前说了一个公司就只有一个CEO。从代码中可以看到,CEO类不能通过new关键字来构造对象,因为构造方法已经被私有化。只能通过CEO类对外开放的getCeo方法来获取对象,而这个对象是在申明的时候就已经被初始化,这就保证的对象的唯一性。以上单例模式的写法就是所谓的饿汉模式。 (2)懒汉模式 懒汉模式与饿汉模式的区别在于,懒汉模式的对象是在调用了getInstance方法的时候初始化的。懒汉模式的实现方式如下,将CEO类修改。

package com.example.singleton;public class CEO extends Staff { // private static final CEO mCeo=new CEO(); private static CEO mCeo; // 构造函数私有化(构造方法的私有化是单例模式的核心) private CEO() { } // /** // * 方法一 // * 饿汉单例模式 // * // * @author HP // * // */ // public static CEO getCeo() { // return mCeo; // } /** * 方法二 * 懒汉模式 * @return */ public static synchronized CEO getInstance() { if (mCeo != null) { mCeo = new CEO(); } return mCeo ; } @Override public void work() { // TODO Auto-generated method stub super .work(); // 管理 vp }}

对应的修改Main.java

package com.example.singleton;public class Main { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Company cp = new Company();// //方法一 :饿汉模式// Staff ceo1 = CEO.getCeo();// Staff ceo2 = CEO.getCeo(); //方法二:懒汉模式 Staff ceo1 = CEO. getInstance(); Staff ceo2 = CEO. getInstance(); cp.addStaff(ceo1); cp.addStaff(ceo2); cp.showAllStaffs(); System. out .println("--------------------------------------" ); //测试发现有两条数据是一样的,说明单例类只能提供一个对象 Staff vp1= new VP(); Staff vp2= new VP(); Staff staff1= new Staff(); Staff staff2= new Staff(); Staff staff3= new Staff(); cp.addStaff(vp1); cp.addStaff(vp2); cp.addStaff(staff1); cp.addStaff(staff2); cp.addStaff(staff3); cp.showAllStaffs(); }}

懒汉模式中的getInstance方法中添加了synchronized关键字,也就是说getInstance是一个同步方法,这就是在多线程情况下保证对象唯一性的手段。但是有一个问题,那就是即使mCeo已经被初始化,每次调用getInstance方法的时候还是会同步,这样就造成了不必要的资源浪费。这也是懒汉模式的最大问题所在。

△ 懒汉模式的优缺点 懒汉模式的优点是单例只有使用的时候才会被初始化,在一定程度上节约了资源;缺点是第一次加载的时候反应稍慢,最大了问题是每次调用getInstance方法的时候都要同步,会造成不必要的开销。

(3)DCL实现方式 这种方式既可以实现在需要的时候在初始化实例,又保证线程安全,并且在调用getInstance方法的时候不同不同,其实现如下:

package com.example.singleton;public class CEO extends Staff {// private static final CEO mCeo=new CEO(); private static CEO mCeo; // 构造函数私有化(构造方法的私有化是单例模式的核心) private CEO() { }// /**// * 方法一// * 饿汉单例模式// *// * @author HP// *// */// public static CEO getCeo() {// return mCeo;// }// /**// * 方法二// * 懒汉模式// * @return// */// public static synchronized CEO getInstance() {// if (mCeo == null) {// mCeo = new CEO();// }// return mCeo;// } /** * 方法三 * DCL */ public static CEO getInstance() { if (mCeo == null) { synchronized (CEO.class ) { if (mCeo == null) { mCeo =new CEO(); } } } return mCeo ; } @Override public void work() { // TODO Auto-generated method stub super .work(); // 管理 vp }}

这不是一种被推荐使用的单例模式写法 (4)静态内部类实现单例模式 其实现如下:

package com.example.singleton;public class CEO extends Staff { // private static final CEO mCeo=new CEO(); // private static CEO mCeo; // 构造函数私有化(构造方法的私有化是单例模式的核心) private CEO() { } // /** // * 方法一 // * 饿汉单例模式 // * // * @author HP // * // */ // public static CEO getCeo() { // return mCeo; // } // /** // * 方法二 // * 懒汉模式 // * @return // */ // public static synchronized CEO getInstance() { // if (mCeo == null) { // mCeo = new CEO(); // } // return mCeo; // } // /** // * 方法三 // * DCL // */ // public static CEO getInstance() { // if (mCeo==null) { // synchronized (CEO.class) { // if (mCeo==null) { // mCeo=new CEO(); // } // } // } // return mCeo; // } /** * 方法四 静态内部类单例模式(推荐使用的单例模式) * * @return */ public static CEO getInstance() { return SinletonCEO. mCeo; } private static class SinletonCEO { private static final CEO mCeo = new CEO(); } @Override public void work() { // TODO Auto-generated method stub super .work(); // 管理 vp }}

当第一次加载CEO类的时候并不会初始化mCeo,只有在第一次调用CEO的getInstance方法的时候mCeo才会被初始化。因此,第一次调用getInstance方法会导致虚拟机加载SinletonCEO类,这种方式不仅能够确保线程安全,同时也能够保证单例对象的唯一性,同时也延迟了单例的实例化。所以这是一种推荐使用的单例实现方式。

(5)其他单例实现方式 其中还有两种单例的实现方式,他们是枚举单例和使用容器实现单例模式。这里就不再具体介绍了,想了解的同学可以翻阅《Android源码设计模式解析与实战》艺术。

6 总结 不管是哪种方式实现的单例模式,它们的核心就是构造函数的私有化,并且通过静态方法来获取一个唯一的实例,在这个过程中我们必须要保证线程安全、防止反序列化导致重生成实例等问题!


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表