一、概念

《Design Patterns: Elements of Reusable Object-Oriented Software》(即后述《设计模式》一书),由 Erich Gamma、Richard Helm、Ralph Johnson

和 John Vlissides 合著(Addison-Wesley,1995)。这几位作者常被称为"四人组(Group of Four)"。

创建型模式(5个):单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式;

结构型模式(7个):适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式;

行为模式(11个):模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

1、单例模式

核心:保证一个类有且只有一个对象(实例),并且提供一个访问该实例的全局访问点。

应用场景:Windows 的任务管理器(Task Manager);

Windows的回收站(Recycle Bin),在整个系统运行中,回收站一直维护着仅有的一个实例;

项目中,读取配置文件类,一般也只有一个类,没必要每次使用配置文件的数据,每次new一个对象去读取;

网站的计数器,一般也是采用单例模式,否则难以同步;

应用程序的日志,也是采用单例模式,这是由于共享文件的日志文件一直处于打开状态,因为只有一个实例去操作,否则内容不好追加;

数据库的连接池也是采用单例模式,因为数据库的连接的是一种数据库资源;

操作系统的文件系统,也是采用单例模式,因为一个操作系统只有一个文件系统;

Application 也是典型的单例模式;

在Spring中,每个Bean默认就是单例,优点是Spring容器都可以管理;

在servlet(Server Applet:小服务程序或服务连接器,Java编写的服务器端程序),每个servlet也是单例

在Spring MVC框架/struts1框架中,控制对象也是单例。

优点: 由于单例只生成一个示例,减少了系统的性能开销,当一个对象产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接

产生一个单例对象,然后永久驻留内存的方式来解决。单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

5种实现方式:

主要有2种:

(1) 饿汉式(线程安全、调用效率,但是不能延时加载)

(2) 懒汉式(线程安全、调用效率不高,但是可以延时加载)

次要有3种:

(3)双重检测模式(由于JVM底层内部模型的原因,偶尔会出现问题,不建议使用)

(4)静态内部类式(线程安全、调用效率高,可以延时加载)

(5)枚举单例(线程安全、调用效率高,不能延时加载,但是可以防止反射和反序列化漏洞)

如何选用?

单例对象占用资源少,不需要延时加载: 枚举式 优于 饿汉式

单例对象占用资源大,需要延时加载:静态内部类式 优于 懒汉式

【初步认识】

/***
* 23-1:单例模式
* 主要有2种:
(1) 饿汉式(线程安全、调用效率,但是不能延时加载) (2) 懒汉式(线程安全、调用效率不高,但是可以延时加载) 次要有3种: (3)双重检测模式(由于JVM底层内部模型的原因,偶尔会出现问题,不建议使用) (4)静态内部类式(线程安全、调用效率高,可以延时加载) (5)枚举单例(线程安全、调用效率高,不能延时加载)
*/
package cn.sxt.pattern; /* 1)饿汉式(单例对象立即加载)
* static变量会在类加载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生
* 并发访问的问题。因此synchronized关键字可以省略。
* 问题:如果只是加载本类,而不是调用getInstance(),甚至永远没有调用,则会造成资源浪费。
* */
class Singleton{
//第2步:私有化一个静态对象。类初始化时立即加载这个对象instance
private static Singleton instance=new Singleton();//instance没什么特殊含义,用s也行,实例化对象的名字 private Singleton() { //第1步:私有构造器,只有自己可以用 }
//由于加载类时,天然的线程安全,方法不用同步,调用效率高
public static Singleton getInstance() {
return instance;
}
} /* 2)懒汉式(单例对象延时加载,不立即加载,中间用的时候才去new一个新的对象)
* 延时加载,真正用的时候才去加载!
* 问题:资源利用率高,但是,每次调用getInstance()方法时,要使用并发,效率低
* */
class Singleton02{
private static Singleton02 s;
private Singleton02() {//私有化构造器 }
//为啥加同步?因为当线程A执行到s==null后,睡觉去啦,当线程B进来后发现s也是null,会去创建一个对象,当线程A醒来之后也去创建
//一个新的对象,这样就2个对象,违反单例的定义(即一个单例类有且只有一个示例(对象))
public static synchronized Singleton02 getInstance() {
if (s==null) {
s=new Singleton02();
}
return s;
}
} /*3)双重检测锁的实现(实际工作很少用),综合了懒汉和饿汉的模式
* 这个模式将同步的内容下放到if内部,提高了执行效率,不必每次获取对象时都进行同步,只有第一次才同步,创建对象后就没必要同步了
* 问题:由于编译器优化的原因和JVM底层内部模型原因,偶尔会出问题
* */ class Singleton03{
private static Singleton03 s=null;
private Singleton03() {
} public static Singleton03 getInstance() { if (s==null) {
Singleton03 sc;
synchronized (Singleton03.class) {
sc=s;
if (sc==null) {
synchronized (Singleton03.class) {
if (sc==null) {
sc=new Singleton03();
}
}
s=sc;
}
}
}
return s;
}
} /*4) 静态内部类的实现(也是一种懒加载)
* 外部类没有static属性,则不会像饿汉式那样立即加载对象
* 只有真正调用getInstance方法时才会加载静态内部类,加载类时线程是安全的,对象s是static final类型的,保证了内存中
* 只有一个实例存在,而且只能赋值一次,从而保证了线程的安全性
* 好处:兼备了并发高效调用和延迟加载的优势
*
* */ class Singleton04{
private static class SingletonClassInstance {
private static final Singleton04 s=new Singleton04();
}
//初始化Singleton04类时不会立即初始化静态内部类SingletonClassInstance,只能用到时才会通过SingletonClassInstance.s
//去调用对象s(这里也可以看作是它的属性),延时加载
public static Singleton04 getInstance() {
return SingletonClassInstance.s;//
}
private Singleton04() { }
} /*5)枚举方式(没有懒加载)
* 优点:实现简单,枚举本身就是单例模式。由JVM从根本上提供保障,避免通过反射和反序列的漏洞
* 缺点:无法延时加载
* */
enum Singleton05{//注意定义与class的区别 //定义一个枚举元素INSTANCE,它就代表Singleton05的一个单例对象
INSTANCE; //添加自己需要的操作
public void singletonOperation() { } } public class Test_0422_Singleton {
public static void main(String[] args) {
/*getInstance是一个函数,在java中,可以用这种方式使用单例模式创建类的实例,所谓单例模式就是一个类有且只有一个实例,
不像object ob=new object();的这种方式去实例化后去使用 */
/*Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println(s1==s2); //输出为true*/ Singleton02 s1=Singleton02.getInstance();
Singleton02 s2=Singleton02.getInstance();
System.out.println(s1==s2); //输出为true Singleton05 s5=Singleton05.INSTANCE;
Singleton05 s6=Singleton05.INSTANCE;
System.out.println(s5==s6); //输出为true }
}

【反射和序列化破解】

/***
* 使用反射和反序列破解4种单例模式(不包含枚举,枚举基于JVM底层无法破解,最安全)
* 以懒汉式为例
*/
package cn.sxt.pattern; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor; class Singleton06 implements Serializable{
private static Singleton06 s;
private Singleton06() {//加了抛出异常的私有化构造器,可以防止破解。当线程试图创建多个对象时会抛出异常。一般不用考虑
if (s!=null) {
throw new RuntimeException();
} }
/*private Singleton06() {//没加抛出异常的私有化构造器 }*/ //为啥加同步?因为当线程A执行到s==null后,睡觉去啦,当线程B进来后发现s也是null,会去创建一个对象,当线程A醒来之后也去创建
//一个新的对象,这样就2个对象,违反单例的定义(即一个单例类有且只有一个示例(对象))
public static synchronized Singleton06 getInstance() {
if (s==null) {
s=new Singleton06();
}
return s;
} //防止通过反序列破解单例。意思是在反序列化时直接调用这个方法,通过这个方法去返回我们指导的对象s,而不是创建一个新的对象
private Object readResolve() throws Exception {//这个方法自己不用调,实现反序列化时自动调用
return s;
}
} public class Test_0422_Singleton2 {
public static void main(String[] args) throws Exception {
Singleton06 s1=Singleton06.getInstance();
Singleton06 s2=Singleton06.getInstance();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2); //输出为true,s1和s2是同一对象 /*//通过反射破解单例。通过反射直接调用私有构造器
Class<Singleton06> clz = (Class<Singleton06>)Class.forName("cn.sxt.pattern.Singleton06");
Constructor<Singleton06> constructor=clz.getDeclaredConstructor(null);
constructor.setAccessible(true);//跳过权限检测,访问私有对象
Singleton06 s3=constructor.newInstance();
Singleton06 s4=constructor.newInstance();
System.out.println(s3==s4);//输出为false,显然s3和s4不是同一对象。证明跳过了单例
*/ /**序列化:将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序;
* 反序列化:从字节流创建对象的相反的过程称为反序列化。而创建的字节流是与平台无关的,在一个平台上序列化的对象可以在
* 不同的平台上反序列化。
* */
//通过反序列化破解单例
FileOutputStream fos =new FileOutputStream("D:/a.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(s1);//把对象s1经由ObjectOutputStream写出到文件 D:/a.txt中
oos.close();
fos.close(); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("D:/a.txt"));
Singleton06 s5=(Singleton06)ois.readObject();
System.out.println(s5);//输出结果与s1和s2不同,证明破解了单例,若加了readResolve()方法,输出结果相同 }
}

【看看效率】

/***
* 测试5种单例模式的执行效率
* 懒汉式:效率最慢
*/
package cn.sxt.pattern; import java.util.concurrent.CountDownLatch; public class Test_0422_Singleton3 {
public static void main(String[] args) throws InterruptedException {
test(); } public static void test() throws InterruptedException {
int threadNum=5;
long start=System.currentTimeMillis(); /***解决时间不准的问题:主线程(与5个线程独立)可能已经执行完毕到end处了,输出主线程的时间。但是5个线程还有没有
* 执行完毕的,达不到效果。
* CountDownLatch类:同步辅助类,是一个统计是否线程结束计数器。 latch:插销 Down:向下
* -countDown() 当前线程调用此方法,则计数器减一
* -await() 调用此方法会一直阻塞线程,直至计数器为0
* */
final CountDownLatch count=new CountDownLatch(threadNum); for (int i = 0; i < threadNum; i++) {//创建5个独立线程,让每个线程去执行调用100次单例模式的getInstance()方法
new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 10000; j++) {
//Object object=Singleton04.getInstance();
Object object2=Singleton05.INSTANCE;
}
count.countDown();//执行完一个线程 ,调用countDown()方法自动将threadNum(正在执行的线程总数)减一
}
}).start();
}
count.await();//阻塞main线程,一直等待,循环检测,看其他线程是否执行完才会继续往下 long end=System.currentTimeMillis();
System.out.println("总耗时:"+(end-start)+"毫秒");
}
}

[19/04/22-星期一] GOF23_创建型模式(单例模式)的更多相关文章

  1. 设计模式01 创建型模式 - 单例模式(Singleton Pattern)

    参考 [1] 设计模式之:创建型设计模式(6种) | 博客园 [2] 单例模式的八种写法比较 | 博客园 单例模式(Singleton  Pattern) 确保一个类有且仅有一个实例,并且为客户提供一 ...

  2. [19/04/24-星期三] GOF23_创建型模式(建造者模式、原型模式)

    一.建造者模式 本质:分离了对象子组件的单独构造(由Builder负责)和装配的分离(由Director负责),从而可以构建出复杂的对象,这个模式适用于:某个对象的构建过程十分复杂 好处:由于构建和装 ...

  3. [19/04/23-星期二] GOF23_创建型模式(工厂模式、抽象工厂模式)

    一.工厂模式(分为:简单工厂模式.工厂方法模式.抽象工厂模式) 实现了创建者和调用者的分离 核心本质:1.实例化对象,用工厂方法代替new操作:2.将选择实现类.创建对象统一管理和控制,从而将调用者跟 ...

  4. [19/04/28-星期日] GOF23_结构型模式(享元模式)

    一.享元模式(FlyWeight,轻量级) [共享类与非共享类] /*** *FlyweightFactory享元工厂类: 创建并管理享元对象,享元池一般设计成键值对 */ package cn.sx ...

  5. java架构之路-(设计模式)五种创建型模式之单例模式

    设计模式自身一直不是很了解,但其实我们时刻都在使用这些设计模式的,java有23种设计模式和6大原则. 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可 ...

  6. 设计模式04: Factory Methord 工厂方法模式(创建型模式)

    Factory Methord 工厂方法模式(创建型模式) 从耦合关系谈起耦合关系直接决定着软件面对变化时的行为 -模块与模块之间的紧耦合使得软件面对变化时,相关的模块都要随之变更 -模块与模块之间的 ...

  7. 设计模式(3)-对象创建型模式-Abstract Factory模式

    1.对象创建型模式 1.3           Abstract Factory模式 1.3.1 需求 在下面情况能够使用Abstract Factory模式: •  一个系统要独立于它的产品的创建. ...

  8. Java设计模式之五大创建型模式(附实例和详解)

    一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥 ...

  9. (转)Java经典设计模式(1):五大创建型模式(附实例和详解)

    原文出处: 小宝鸽 一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代 ...

随机推荐

  1. .net mvc 获取acion 返回类型

    1..net core 中获取 public override void OnActionExecuted(ActionExecutedContext context) { var descripto ...

  2. sprintf 格式化字符串

    好久没写博客了,又遇到自己觉得很傻的问题,格式化字符串还要找下 定义和用法 sprintf() 函数把格式化的字符串写入变量中. arg1.arg2.++ 参数将被插入到主字符串中的百分号(%)符号处 ...

  3. java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media/20 from pid=711, uid=10074 requires android.permission.READ_

    java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider ur ...

  4. 记录一次使用terminal进行git管理与提交到Github的过程

    1.环境的构建: 使用Mac系统自带的Git进行版本管理存在,Git是系统的Xcode集成的 查看版本的命令: $ git --version git version (Apple Git-) 查看g ...

  5. LeetCode SQL: Second Highest Salary

    , NULL, salary) as `salary` from ( ,) tmp Write a SQL query to get the second highest salary from th ...

  6. USACO06DEC Milk Patterns——Solution

    题目描述 Farmer John has noticed that the quality of milk given by his cows varies from day to day. On f ...

  7. Ubuntu16.04(Linux)安装JDK

    Ubuntu 安装JDK的两种方式: 1:通过ppa(源) 方式安装. 2:通过官网下载安装包安装. 第1种,因为可以通过 apt-get upgrade 方式方便获得jdk的升级 第一种:使用ppa ...

  8. Tomcat的学习和使用(一)

    一.Tomcat服务器端口的配置 Tomcat的所有配置都放在conf文件夹之中,里面的server.xml文件是配置的核心文件. 如果想修改Tomcat服务器的启动端口,则可以在server.xml ...

  9. css3+javascript实现翻页幻灯片

    先上效果图 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <tit ...

  10. 移动端App开发 - 01 - 开篇

    移动端App开发 - 01 - 开篇 从此笔记之后开启移动端 app 开发学习 该系列笔记去掉所有无关重要的东西,简介干练 我的移动端App开发笔记 1.移动端App开发 - 02 - iPhone/ ...