Java如何创建型设计模式
原创-
单例(Singleton)
Intent
确保一个类只有一个实例,并提供该实例的全局访问点。
Class Diagram
这是使用私有构造函数、私有静态变量和公共静态函数来完成的。
私有构造函数确保不能通过构造函数创建对象实例、实例和只能通过公共静态函数返回唯一的私有静态变量。
Implementation
Ⅰ 懒汉式-线程不安全不安全线程不安全
在下面的实现中,私有静态变量 uniqueInstance 是延迟的,这样做的好处是如果不使用该类,那么它将不会实例化 uniqueInstance这是为了节省资源。这是一种很好的节省资源的方法。这是一种节约资源的好方法。这是为了节省资源。
此实现在多线程环境中不安全,并且如果多个线程可以访问 if (uniqueInstance == null)
此时此刻,此时此刻 uniqueInstance 为 null则将有多个线程在执行 uniqueInstance = new Singleton();
语句,这将导致实例化多个 uniqueInstance。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
Ⅱ 饿汉式-线程安全
线程不安全不安全线程不安全问题主要是由于 uniqueInstance 被多次实例化,直接实例化 uniqueInstance 的方式就不会产生线程不安全不安全线程不安全问题。
但是,直接实例化方法也失去了延迟实例化节省资源的好处。
public class Singleton { //线程不安全不安全线程不安全问题主要是由于 uniqueIntance被实例化了多次,被实例化了多次。被多次实例化,IS被多次实例化,被实例化了多次。被多次实例化,IS被多次实例化, //如果uniqueInstance采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全不安全线程不安全的问题。 private static Singleton uniqueInstance=new Singleton2();
private Singleton(){
}
public static Singleton getUniqueInstance(){
return uniqueInstance;
}
}
Ⅲ 懒汉式-线程安全
只需要对 getUniqueInstance() 方法被锁定,则在某个时间点只有一个线程可以访问该方法。 从而避免实例化多个,从而避免实例化多个,从而避免实例化多个 uniqueInstance。
但是,在线程进入该方法之后,尝试进入该方法的所有其他线程必须等待,并且 即使 uniqueInstance 已被实例化。这可能会导致线程阻塞太长时间,因此此方法存在性能问题,不推荐使用。
public class Singleton{ //线程不安全不安全线程不安全问题主要是由于 uniqueIntance被实例化了多次,被实例化了多次。被多次实例化,IS被多次实例化,被实例化了多次。被多次实例化,IS被多次实例化, //如果uniqueInstance采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全不安全线程不安全的问题。 private static Singleton uniqueInstance;
private Singleton(){
}
//线程进入该方法后,尝试进入该方法的所有其他线程都必须等待
public synchronized static Singleton getUniqueInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
- 延迟装货的想法延迟装货的想法延迟装货的想法 也就是说,一开始不要加载资源或数据,等到资源或数据立即使用时才加载。 只有当您无法避免时才会加载它,因此它也被称为Lazy Load, 不懒啊,就是“延时加载”,这是实际开发中很常见的想法,尽量节省资源。
- 缓存的想法缓存缓存的想法 将这些数据缓存到内存中,每次操作时,首先查看内存中是否有这样的数据。 如果是,则直接使用它,如果不是,则获取它并将其设置到缓存中,以便下次访问它时可以直接从内存中获取。 因此,缓存当然是一种典型的空间换时间解决方案,从而节省了大量时间。
Ⅳ 双重检查锁双重检查锁双重检查锁-线程安全
uniqueInstance 它只需要实例化一次,之后就可以直接使用。锁定操作只需要在实例化它的那部分代码上执行,并且仅当 uniqueInstance 只有在未实例化时才需要锁定。
双重检查锁双重检查锁双重检查锁先判断 uniqueInstance 无论它是否已被实例化,并且只有在它尚未被实例化的情况下,才会将锁应用于该实例化语句。
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
请考虑下面的实现,它只使用 if 语句。 在 uniqueInstance == null 如果两个线程都执行案例,如果两个线程都在两个线程都执行的情况下执行,则返回 if 语句,则两个线程都将进入该语句,然后两个线程都将进入语句,然后两个线程都将进入 if 在一个语句块中。尽管在中不可用 if 语句块中有一个锁定操作,但两个线程都将执行 uniqueInstance = new Singleton();
这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重检查锁双重检查锁双重检查锁,也就是需要使用两个 if 语句。
if (uniqueInstance == null) { synchronized (Singleton.class) { uniqueInstance = new Singleton(); } }
uniqueInstance 采用 volatile 关键字修改也是必要的关键字修改也是必要的,关键字修改也是必要的,以及 uniqueInstance = new Singleton();
这段代码实际上分三步执行。代码实际上分三个步骤执行。
- 为 uniqueInstance 分配内存空间分配内存空间
- 初始化 uniqueInstance
- 将 uniqueInstance 指向分配的内存地址指向分配的内存地址
但是由于 JVM 有了指令重排功能,执行顺序有可能变成 1>3>2。指令重新排序在单线程环境中不是问题,但在多线程环境中可能会导致线程获取尚未初始化的实例。 例如线程,例如线程,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回的值不为空,因此返回的值不为空,因此返回 uniqueInstance,但此时 uniqueInstance 尚未初始化。尚未初始化。尚未初始化。尚未初始化。
使用 volatile 可以禁止 JVM 指令重新排列,以确保即使在多线程环境中也能正常运行。
Ⅴ 静态内部类实现静态内部类实现静态内部类实现
当 Singleton 类加载时的静态内部类 SingletonHolder 不会加载到内存中。仅当调用 getUniqueInstance()
方法来触发触发该方法的方法,从而触发 SingletonHolder.INSTANCE
时 SingletonHolder 只有在加载初始化时才会加载初始化时才会加载它,当初始化时才加载它,当初始化时才加载它 INSTANCE 实例、实例和 JVM 能确保 INSTANCE 仅实例化一次。仅实例化一次。它只实例化一次。仅实例化一次。
这种方法不仅具有延迟初始化的好处,而且还受到 JVM 提供对线程安全的支持。提供对线程安全的支持。提供了线程安全支持。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
Ⅵ 枚举实现
public enum Singleton {
INSTANCE;
private String objName;
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
public static void main(String[] args) {
// 单例测试
Singleton firstSingleton = Singleton.INSTANCE;
firstSingleton.setObjName("firstName");
System.out.println(firstSingleton.getObjName());
Singleton secondSingleton = Singleton.INSTANCE;
secondSingleton.setObjName("secondName");
System.out.println(firstSingleton.getObjName());
System.out.println(secondSingleton.getObjName());
// 反射获取示例测试反射获取示例测试
try {
Singleton[] enumConstants = Singleton.class.getEnumConstants();
for (Singleton enumConstant : enumConstants) {
System.out.println(enumConstant.getObjName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
firstName secondName secondName secondName
此实现在多次序列化然后反序列化后不会获得多个实例。而其他实现需要使用 transient 正在修改所有字段。修改修改所有字段的所有字段,修改所有 并实现序列化和反序列化方法。
此实现可防止反射攻击。在其他实现中,可以通过以下方式防止反射攻击 setAccessible() 方法可以将私有构造函数的访问级别设置为 public然后调用构造函数,从而实例化对象。为了防止这种攻击,您需要向构造函数添加代码以防止多个实例化。该实现由以下人员提供 JVM 保证只实例化一次,所以不会出现如上所述的反射攻击。
枚举实现(最推荐使用最推荐的用法)
public class Singleton { private Singleton(){}
public static Singleton getUniqueInstance(){
return Singleton.INSTANCE.getSingleton();
}
private enum Singleton{
INSTANCE;
//如果您打算自定义自己的方法,则必须将它们添加到enum在实例序列的末尾添加分号。
//而且 Java 必须首先定义需求必须首先定义需求必须首先定义需求 enum 实例
private Singleton singleton;
//JVM确保绝对只调用此方法一次
Singleton(){
singleton=new Singleton();
}
public Singleton getSingleton() {
return singleton;
}
}
}
Examples
- Logger Classes
- Configuration Classes
- Accesing resources in shared mode
- Factories implemented as Singletons
JDK
-
简单工厂(简单工厂(Simple Factory)
Intent
在不向客户端公开内部细节的情况下创建对象,并为创建对象提供通用接口。
Class Diagram
简单工厂将实例化操作放在一个单独的类中,该类成为简单工厂类。 让简单的工厂类决定应该使用哪个具体的子类来实例化。
这将客户端类的实现从具体的子类中分离出来。 客户端类不再需要知道它有哪些子类以及应该实例化哪些子类。 通常有多个客户端类,如果不使用简单工厂,则所有客户端类都必须知道其所有子类的详细信息。 一旦更改了子类,例如通过添加子类,则必须修改所有客户端类。
Implementation
public interface Product { }
public class ConcreteProduct implements Product { }
public class ConcreteProduct1 implements Product { }
public class ConcreteProduct2 implements Product { }
以下的 Client 类包含实例化的代码,这是错误的实现。如果客户端类中存在这样的实例化代码,则需要考虑将代码放入简单的工厂中。
public class Client {
public static void main(String[] args) {
int type = 1;
Product product;
if (type == 1) {
product = new ConcreteProduct1();
} else if (type == 2) {
product = new ConcreteProduct2();
} else {
product = new ConcreteProduct();
}
// do something with the product
}
}
以下的 SimpleFactory 是一个简单的工厂实现,由需要实例化的所有客户端类调用。
public class SimpleFactory {
public Product createProduct(int type) {
if (type == 1) {
return new ConcreteProduct1();
} else if (type == 2) {
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
public class Client {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
// do something with the product
}
}
- 单纯工厂的优势和劣势
帮助封装:简单的工厂很简单,但非常友好,可以帮助我们实现组件封装,然后在组件之外实现真正的面向接口的编程。
分离:通过简单的工厂分离客户端类和具体子类的实现。
可能会增加客户端的复杂性。可能会增加客户端的复杂性。客户端复杂性可能增加:。可能会增加客户端的复杂性。 如果通过客户端参数选择特定的实现类,则 然后客户端必须能够理解每个参数的具体功能和含义,这将增加客户端的使用难度。 它还部分公开了内部实现,该实现可以以可配置的方式实现
不方便扩展子工厂:将简单工厂的构造函数私有化,并使用静态方法创建 也不可能通过编写简单工厂类的子类来更改创建接口的方法的行为。但是,通常没有必要为简单工厂创建子类。
-
工厂方法(工厂方法(Factory Method)
Intent
定义用于创建对象的接口,但实例化哪个类由子类决定。工厂方法将实例化操作推迟到子类。
Class Diagram
在简单的工厂中,是另一个类创建对象,而在工厂方法中,是子类创建对象。
下图中,Factory 有一个 doSomething() 方法时,此方法需要使用产品对象,该对象由 factoryMethod() 方法已创建。该方法是抽象的,需要由子类实现。
Implementation
public abstract class Factory { abstract public Product factoryMethod(); public void doSomething() { Product product = factoryMethod(); // do something with the product } }
public class ConcreteFactory extends Factory { public Product factoryMethod() { return new ConcreteProduct(); } }
public class ConcreteFactory1 extends Factory { public Product factoryMethod() { return new ConcreteProduct1(); } }
public class ConcreteFactory2 extends Factory { public Product factoryMethod() { return new ConcreteProduct2(); } }
JDK
- java.util.Calendar
- java.util.ResourceBundle
- java.text.NumberFormat
- java.nio.charset.Charset
- java.net.URLStreamHandlerFactory
- java.util.EnumSet
- javax.xml.bind.JAXBContext
-
抽象工厂(抽象工厂(抽象工厂(Abstract Factory)
Intent
提供用于创建的接口提供用于创建的接口提供用于创建的接口 相关对象族相关对象族 。
Class Diagram
抽象工厂模式创建对象族,即多个对象而不是一个对象,并且这些对象是相关的,也就是说,它们必须一起创建。另一方面,工厂方法模式仅用于创建单个对象,这与抽象工厂模式非常不同。
抽象工厂模式使用工厂方法模式来创建单个对象。AbstractFactory 中的 createProductA() 和 createProductB() 方法是由子类实现的,这两个方法单独创建一个对象,这符合工厂方法模式的定义。
至于创建一系列对象的概念,它在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建两个对象,其中两个对象具有很大的相关性。Client 这两个对象需要同时创建。这两个对象都需要创建。这两个对象都需要创建。
在高层次上,抽象工厂使用组合,即 Cilent 组合了 AbstractFactory工厂方法模式使用继承。
Implementation
public class AbstractProductA { }
public class AbstractProductB { }
public class ProductA1 extends AbstractProductA { }
public class ProductA2 extends AbstractProductA { }
public class ProductB1 extends AbstractProductB { }
public class ProductB2 extends AbstractProductB { }
public abstract class AbstractFactory { abstract AbstractProductA createProductA(); abstract AbstractProductB createProductB(); }
public class ConcreteFactory1 extends AbstractFactory { AbstractProductA createProductA() { return new ProductA1(); }
AbstractProductB createProductB() {
return new ProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory { AbstractProductA createProductA() { return new ProductA2(); }
AbstractProductB createProductB() {
return new ProductB2();
}
}
public class Client { public static void main(String[] args) { AbstractFactory abstractFactory = new ConcreteFactory1(); AbstractProductA productA = abstractFactory.createProductA(); AbstractProductB productB = abstractFactory.createProductB(); // do something with productA and productB } }
JDK
- javax.xml.parsers.DocumentBuilderFactory
- javax.xml.transform.TransformerFactory
- javax.xml.xpath.XPathFactory
-
生成器(Builder)
Intent
封装对象的构造过程,并允许逐步构造。 (将复杂对象的构造与其表示分开允许相同的构造过程创建不同的表示。)
Class Diagram
要认识到相同的构建过程可以创建不同的表示,那么自然的思维方式是 首先分离构建过程,并将其称为生成器模式中的导师。 它指导组装过程,但不负责每一步的具体实施。 当然,仅有指南是不够的;还必须有能够具体实现每个步骤的对象,并且在生成器模式中,这些实现对象称为生成器。 通过这种方式,指导者是可以重用的构建过程,生成器是可以切换的具体实现。
Implementation1
**
-
导师负责指导组装过程,但不负责每一步的具体实施。 */ public class Director {
private AbstractComputerBuilder computerBuilder;
public void setComputerBuilder(AbstractComputerBuilder computerBuilder) { this.computerBuilder = computerBuilder; }
public Product getProduct() { return computerBuilder.getProduct(); }
public void constructComputer() { computerBuilder.buildProduct(); computerBuilder.buildMaster(); computerBuilder.buildScreen(); computerBuilder.buildKeyboard(); computerBuilder.buildMouse(); computerBuilder.buildAudio(); } }
/**
-
定义产品类别定义产品类别 */ public class Product { private String master; private String screen; private String keyboard; private String mouse; private String audio;
public void setMaster(String master) { this.master = master; }
public void setScreen(String screen) { this.screen = screen; }
public String getMaster() { return master; }
public String getScreen() { return screen; }
public String getKeyboard() { return keyboard; }
public void setKeyboard(String keyboard) { this.keyboard = keyboard; }
public String getMouse() { return mouse; }
public void setMouse(String mouse) { this.mouse = mouse; }
public String getAudio() { return audio; }
public void setAudio(String audio) { this.audio = audio; } }
/**
- 生成器抽象类生成器抽象类生成器抽象类
-
负责每一步的具体实施的对象 */ public abstract class AbstractComputerBuilder { protected Product product;
public Product getProduct() { return product; }
public void buildProduct(){ product=new Product(); System.out.println("生产计算机生产计算机"); }
public abstract void buildMaster(); public abstract void buildScreen(); public abstract void buildKeyboard(); public abstract void buildMouse(); public abstract void buildAudio(); } public class HPComputerBuilder extends AbstractComputerBuilder{ @Override public void buildMaster() { // TODO Auto-generated method stub product.setMaster("i7,16g,512SSD,1060"); System.out.println("(i7,16g,512SSD,1060)惠普大型机"); }
@Override public void buildScreen() { // TODO Auto-generated method stub product.setScreen("4K"); System.out.println("(4K)HP Display HP显示器"); }
@Override public void buildKeyboard() { // TODO Auto-generated method stub product.setKeyboard("cherry 绿轴机械键盘绿轴机械键盘"); System.out.println("(cherry 绿轴机械键盘绿轴机械键盘)的键盘"); }
@Override public void buildMouse() { // TODO Auto-generated method stub product.setMouse("MI 鼠标"); System.out.println("(MI 鼠标)的鼠标"); }
@Override public void buildAudio() { // TODO Auto-generated method stub product.setAudio("飞利浦 音响"); System.out.println("(飞利浦 音响)的音响"); } } public class DELLComputerBuilder extends AbstractComputerBuilder{ @Override public void buildMaster() { // TODO Auto-generated method stub product.setMaster("i7,32g,1TSSD,1060"); System.out.println("(i7,32g,1TSSD,1060)戴尔大型机戴尔托管戴尔大型机"); }
@Override public void buildScreen() { // TODO Auto-generated method stub product.setScreen("4k"); System.out.println("(4k)的dell显示屏"); }
@Override public void buildKeyboard() { // TODO Auto-generated method stub product.setKeyboard("cherry 黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘"); System.out.println("(cherry 黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘黑轴机械键盘)的键盘"); }
@Override public void buildMouse() { // TODO Auto-generated method stub product.setMouse("MI 鼠标"); System.out.println("(MI 鼠标)的鼠标"); }
@Override public void buildAudio() { // TODO Auto-generated method stub product.setAudio("飞利浦 音响"); System.out.println("(飞利浦 音响)的音响"); } }
/**
- Mentor是一个可重用的构建过程,而Mentor是一个可重用的构建过程,
-
生成器是可以切换的特定实现 */ public class Client { public static void main(String[] args) { AbstractComputerBuilder computerBuilder=new HPComputerBuilder(); AbstractComputerBuilder computerBuilder2=new DELLComputerBuilder(); Director director=new Director();
director.setComputerBuilder(computerBuilder); director.constructComputer(); //获取PC Product pc=director.getProduct(); director.setComputerBuilder(computerBuilder2); director.constructComputer(); Product pc2=director.getProduct();
} }
生成器按以下顺序调用。生成器调用的顺序。
Implementation2
这里是一个简单的例子下面是一个简单的例子下面是一个简单的例子 StringBuilder 实现,参考实现,参考 JDK 1.8 源码。
public class AbstractStringBuilder { protected char[] value;
protected int count;
public AbstractStringBuilder(int capacity) {
count = 0;
value = new char[capacity];
}
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX\_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
} public class StringBuilder extends AbstractStringBuilder { public StringBuilder() { super(16); }
@Override
public String toString() {
// Create a copy, dont share the array
return new String(value, 0, count);
}
} public class Client { public static void main(String[] args) { StringBuilder sb = new StringBuilder(); final int count = 26; for (int i = 0; i < count; i++) { sb.append((char) (a + i)); } System.out.println(sb.toString()); } }
abcdefghijklmnopqrstuvwxyz
JDK
- java.lang.StringBuilder
- java.nio.ByteBuffer
- java.lang.StringBuffer
- java.lang.Appendable
- Apache Camel builders
-
原型模式(原型模型(Prototype)
Intent
使用原型实例指定要创建的对象的类型,并通过复制此原型来创建新对象。
Class Diagram
Implementation
public abstract class Prototype { abstract Prototype myClone(); } public class ConcretePrototype extends Prototype {
private String filed;
public ConcretePrototype(String filed) {
this.filed = filed;
}
@Override
Prototype myClone() {
return new ConcretePrototype(filed);
}
@Override
public String toString() {
return filed;
}
} public class Client { public static void main(String[] args) { Prototype prototype = new ConcretePrototype("abc"); Prototype clone = prototype.myClone(); System.out.println(clone.toString()); } }
abc
免费Java先进的材料需要自己拿起并盖好Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo总共是高度并发的分布式教程和其他教程30G。
传送门: https:// mp.weixin.qq.com/s/Jzdd fH-7yNudmkjT0IRL8Q
转载于:https://www.cnblogs.com/yuxiang1/p/10968822.html
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除