工厂模式主要用一下几种形态:java
abstract class Fruit {} class Apple extends Fruit {} class Banana extends Fruit {} class FruitFactory { public static Fruit newInstance(Class<? extends Fruit> clz) { try { return clz.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } }
每一个工厂类能够有多于一个的工厂方法,分别负责建立不一样的产品对象。好比java.text.DateFormat
类是其子类的工厂类,而它就提供了多个静态工厂方法。算法
在有些状况下,工厂角色能够由抽象产品角色扮演。典型的应用就是java.text.DateFormat
类,一个抽象产品类同时是子类的工厂。数据库
若是抽象产品角色已经被忽略,而工厂角色就能够与具体产品角色合并。换言之,一个产品类为自身的工厂。编程
class ConcreteProduct { public static ConcreteProduct factory() { return new ConcreteProduct(); } }
abstract class Fruit {} abstract class FruitFactory { public abstract Fruit newInstance(); } class Apple extends Fruit {} class Banana extends Fruit {} class AppleFactory extends FruitFactory { @Override public Fruit newInstance() { return new Apple(); } } class BananaFactory extends FruitFactory { @Override public Fruit newInstance() { return new Banana(); } }
java.util.Collection
接口继承来Iterable
接口,全部其子类都必须实现Iterator<T> iterator()
方法,这个iterator()
方法就是一个工厂方法。缓存
抽象工厂角色和抽象产品角色均可以选择由Java接口或者Java抽象类来实现。
若是具体工厂角色由共同的逻辑,那么这些共同的逻辑就能够向上移动到抽象工厂角色中,这也意味着抽象工厂角色应该由抽象类实现;反之就应当由接口实现。网络
抽象工厂角色能够规定出多于一个的工厂方法,从而使具体工厂角色实现这些不一样的工厂方法。并发
工厂方法模式经常与模版方法模式一块儿联合使用。缘由其实不难理解:第一,两个模式都是基于方法的,工厂方法模式是基于多态性的工厂方法的,而模版方法模式是基于模版方法和基本方法的;第二,两个模式都将具体工做交给子类。工厂方法模式将建立工做推延给子类,模版方法模式将剩余逻辑交给子类。app
工厂方法模式老是涉及到两个等级结构中的对象,而这两个等级结构能够分别是MVC中的控制器和试图。一个MVC模式能够有多个控制器和多个视图。
若是系统内只须要一个控制器,那么能够简化为简单工厂模式。ide
享元模式使用了带有循环逻辑的工厂方法。函数
下面例子中,手机、电脑是抽象产品,苹果、三星等是工厂。
单例模式确保某个类只有一个实例,并且自行实例化并向整个系统提供这个实例。
私有化构造方法!
Context
,那么容易引起内存泄漏,此时须要注意传递给单例对象的context
,最好是Application Context
class EagerSingleton { // 建立单例类对象 private static EagerSingleton instance = new EagerSingleton(); // 构造方法私有化 private EagerSingleton() {} // 获取可用对象 public static EagerSingleton getInstance() { return instance; } }
class LazySingleton { // 声明单例类对象 private static LazySingleton instance; // 构造方法私有化 private LazySingleton() {} // 获取可用对象 public static synchronized LazySingleton getInstance() { if(null == instance) { instance = new LazySingleton(); } return instance; } }
getInstance()
方法进行了同步处理,可能会致使一些性能问题,因而有了下面的改进方法,经过双重检查和同步代码块的形式来处理懒汉式的并发问题。但要注意的是,这在Java语言中多是问题的,之因此是可能有问题,是由于不一样Java版本的内存模型不一样。instance
为null
,两个线程都经过第一次检查instance
仍然为null
,因而执行(5)初始化instance = new Singleton()
,而后线程1执行完毕释放锁。instance
不为null
,因此线程2不会进行初始化,直接退出,返回已经初始化好的instance
。instance = new Singleton()
这一句话并非原子操做!class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() throws Exception { if(null == instance) { // (1)第一次检查 // (2)这里会有多个线程同时到达 synchronized(Singleton.class) { // 同步代码块加锁 // (3)此处只能是单线程 if (null == instance) { // (4)第二次检查 instance = new Singleton(); // (5)初始化instance } } } return instance; } }
为展现问题出现的缘由,假设代码行instance =new Singleton();
执行了下列伪代码:
mem = allocate(); // (1)为单例对象分配内存空间. instance = mem; // (2)注意,instance引用如今已经不是null,但还未初始化 ctorSingleton(instance); // (3)为单例对象经过instance调用构造函数
上述伪代码中,执行的顺序多是(1)(3)(2),此时不会致使上述问题;但若是(1)(2)(3)的执行过程,则可能在线程1执行到(2)时,CPU开始执行线程2,此时刚好线程2执行到第一次检查,获取到的是一个不为null
但还没有初始化的值,此时程序会抛出错误。
在高版本的JDK中,使用volatile
关键字能够保证不会产生上述问题。被volatile
所修饰的变量的值不会被本地线程缓存,全部对该变量的读写都是直接操做共享内存来实现,从而确保多个线程能正确的处理该变量。
该关键字可能会屏蔽掉虚拟机中的一些代码优化,因此其运行效率可能不是很高。
class Singleton { private static volatile Singleton instance; }
class Singleton { private Singleton() {} private static class Holder { static Singleton instance = new Singleton(); } public static Singleton getInstance() { // 外围类能直接访问内部类(无论是不是静态的)的私有变量 return Holder.instance; } }
多例模式实际上就是单例模式的推广,多例类能够有多个实例,多例类必须本身建立、管理本身的实例,并向外界提供本身的实例。
多例模式分为有上限多例类和无上限多例类,无上限多例类要经过集合来实现。
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。它提供了一种建立对象的最佳方式。
一个产品一般有不一样的组成成分做为产品的零件,不一样的产品能够有不一样的零件,建造产品的过程是建造零件的过程。建造者模式将产品的结构和产品的零件建造过程对外隐藏起来,把对建造过程进行指挥的责任和具体建造零件的责任分割开来,达到责任划分和封装的目的。
主要解决在开发过程当中,有时须要建立一个复杂对象,一般由多个部分的子对象构成;因为复杂对象的多样性,这个复杂对象的各个部分常常面临着剧烈的变化,可是将它们组合在一块儿的算法须要保持稳定。
public class Waiter { public static void main(String[] args) { KFCBuilder builder = new MexicanTwisterBuilder(); builder.buildBeverage(); builder.buildHamburger(); builder.buildSnack(); KFCCombo combo = builder.getCombo(); } } // 套餐接口 abstract class KFCCombo { private String hamburger; private String beverage; private String snack; // getters & setters } // 墨西哥鸡肉卷套餐 class MexicanTwisterCombo extends KFCCombo {} // Builder接口 interface KFCBuilder { void buildHamburger(); void buildBeverage(); void buildSnack(); KFCCombo getCombo(); } class MexicanTwisterBuilder implements KFCBuilder { private KFCCombo combo = new MexicanTwisterCombo(); @Override public void buildHamburger() { combo.setHamburger("Mexican Twister"); } @Override public void buildBeverage() { combo.setBeverage("Pepsi Cola"); } @Override public void buildSnack() { combo.setSnack("Hot Wings"); } @Override public KFCCombo getCombo() { return combo; } }
若是一个类有不少属性,此时为此类写一个Builder内部类,来辅助建造该类。
class Phone { private String screen; private String camera; private String cpu; private String battery; public static Builder builder() { return new Builder(); } public static class Builder { private Phone phone = new Phone(); public Builder screen(String screen) { phone.screen = screen; return this; } public Builder camera(String camera) { phone.camera = camera; return this; } public Builder cpu(String cpu) { phone.cpu = cpu; return this; } public Builder battery(String battery) { phone.battery = battery; return this; } public Phone build() { return phone; } } }
原始模型模式经过给一个原型对象来指明所要建立的对象的类型,而后用复制这个原型对象的办法建立出更多同类型的对象。
这种模式涉及到三个角色:
用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。
clone()
方法返回的对象叫作原始对象的克隆体。一个克隆对象的基本特性必须是:
a.clone()!=a
,这也就意味着克隆对象和原始对象在java中是两个不一样的对象。a.clone().getClass == a.getClass()
,克隆对象与原对象类型相同a.clone.equals(a)
,也就是说克隆对象完彻底全是原始对象的一个拷贝。此条件是非必需的。Object
类没有实现该接口,因此用户若是没有主动实现该接口时,调用clone()
方法会报错CloneNotSupportedException
。
Cloneable
接口,这是步骤的关键之处。clone()
方法,并声明为public
,由于Object
的该方法是protected
的。super.clone()
来获取新的克隆对象。在运行时刻,Object
中的clone()
识别出你要复制的是哪个对象,而后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。class A implements Cloneable { @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } }
Object.clone()
是浅复制。transient
,从而将之排除在复制过程以外。public Object deepClone() throws Exception { //将对象写到流里 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); //从流里读出来 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return(oi.readObject()); }