创建型模式(5种):用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。
一、单例模式(Singleton)
1.1 什么是单例模式
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
单例的实现主要是通过以下两个步骤:
1.将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
2.在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
1.2 单例模式适用场景
1.需要生成唯一序列的环境
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境
1.3 单例模式优缺点
优点:
1.在内存中只有一个对象,节省内存空间;
2.避免频繁的创建销毁对象,可以提高性能;
3.避免对共享资源的多重占用,简化访问;
4.为整个系统提供一个全局访问点。
缺点:
1.不适用于变化频繁的对象;
2.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
3.如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;
1.4 单例模式实现
1.饿汉式:
public class SingleCase {
private int i=0;
private static SingleCase singleCase=new SingleCase();
private SingleCase(){
};
public static SingleCase getSingleCase(){
return singleCase;
}
public static void main(String[] args) {
SingleCase singleCase1=SingleCase.getSingleCase();
singleCase1.i++;
SingleCase singleCase2=SingleCase.getSingleCase();
System.out.println(singleCase2.i);
}
}
定义一个变量来验证是否实现单例模式,定义一个静态私有对象并实例化,之后再将默认构造私有化防止其它类调用构造方法创建单例对象,最后对外提供一个静态get方法来获取创建好的静态实例对象。
优点: 这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点: 在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
2.懒汉式:
public class SingleCase {
int i=0;
private static SingleCase singleCase;
private SingleCase(){
}
public static SingleCase getSingleCase(){
if (singleCase==null){
singleCase=new SingleCase();
}
return singleCase;
}
public static void main(String[] args) {
SingleCase singleCase1=SingleCase.getSingleCase();
singleCase.i++;
SingleCase singleCase2=SingleCase.getSingleCase();
System.out.println(singleCase2.i);
}
}
我们从懒汉式单例可以看到,单例实例被延迟加载,即只有在真正使用的时候才会实例化一个对象并交给自己的引用。
这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
3.双重加锁机制:
public class SingleCase {
private volatile static SingleCase singleCase;
int i=2;
private SingleCase(){
}
private static SingleCase getSingleCase(){
if (singleCase==null){
synchronized (SingleCase.class){
if (singleCase==null){
singleCase=new SingleCase();
}
}
}
return singleCase;
}
public static void main(String[] args) {
SingleCase singleCase1=SingleCase.getSingleCase();
singleCase1.i++;
SingleCase singleCase2=SingleCase.getSingleCase();
System.out.println(singleCase2.i);
}
}
使用synchronized代表锁住的区域,程序第一次判断为null时只允许一个线程进入锁住区域,进去之后进行第二次判断防止重复创建。volatile用于保障变量创建完成后,第二个线程读取变量时还是未创建状态。(volatile:当读取时必须执行完写操作才可以)
使用双重检测同步延迟加载去创建单例的做法是一个非常优秀的做法,其不但保证了单例,而且切实提高了程序运行效率
优点: 线程安全;延迟加载;效率较高。
二、工厂方法模式(Factory Method)
2.1 什么是工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
2.2 工厂模式适用场景
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
2.3 工厂模式优缺点
优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成
倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
2.4 工厂模式代码实现
1. 简单工厂模式(Simple Factory):
该模式对对象创建管理方式最为简单,因为其仅仅简单的对不同类对象的创建进行了一层薄薄的封装。该模式通过向工厂传递类型来指定要创建的对象。
鞋子接口:
为了统一产品具有的共同操作,例如鞋子:展示自己的品牌