什么是创建型模式
所谓创建型模式,就是提供了一种在创建对象的同时隐藏创建逻辑的方式,不显式的通过new运算符直接实例化对象。
简单粗暴的理解就是,不提高public的构造方法,所有的构造方法都是private,要new谁,new多少个,是通过前端要求的名字来new的。
1、单例模式
1.1 单例模式的实现方式
1.1.1 懒汉式
顾名思义,懒嘛,就会懒得创建,用得上再创建,万一一直用不上,就不需要创建了,完美体现了懒汉这个词。
所以,懒汉式是在第一次调用时才进行初始化,也就是new出相关实例。
1.1.1.1 普通懒汉式
public class Singleton {
private static Singleton instance;
private Singleton() {
System.out.println("实例化懒汉式Singleton对象");
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Singleton.getInstance();
}
}
很明显,只有在第一次调用getInstance()时才会创建对象,将对象的创建推迟到了使用的时候创建,实现了lazy loading.
lazy loading也称懒加载,把对象的创建延迟到使用的时候创建,而不是对象实例化的时候创建,避免了无谓的性能开销
但是这种第一次调用时才去创建的方式,在多线程时很容易出现问题。
多线程就意味着,可能出现同一个时刻,两个线程一起调用,那么对于这两个线程而已,他们在调用时,实例都还是null,那么符合第一次调用时实例化这个概念,就可能new出两个实例:
1.1.1.2 多线程时的懒汉式单例
public class Singleton {
private static Singleton instance;
private Singleton(){
System.out.println("【"+Thread.currentThread().getName()+"】实例化懒汉式Singleton对象");
}
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(()->{
Singleton.getInstance();
},"多线程懒汉式实例化-"+i).start();
}
}
}
两个线程在 if 判断时都为true,从而都进入了方法体,那么整个程序就会得到两个实例。这样就完全不符合单例模式这个概念了。
为了解决这样问题,就要保证所有线程在执行到 if 时,能够存在一种先后关系。在多线程中,就需要加锁实现。
1.1.1.3 线程安全的懒汉式
1.1.1.3.1 在getInstance()方法上加同步锁
public static synchronized Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
这种写法能够在多线程中很好的工作。而且看起来也具备很好的lazy loading,但是,效率很低,99%情况下不需要同步。
1.1.1.3.2 双重检索锁定
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
在getInstce中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,既是线程安全的,同时避免了每次都同步的性能损耗。
线程同步synchronized
又称隐式锁,可重入的、非公平的悲观锁。
- 以前是重量级锁,锁可以让临界区互斥执行,不过1.6对它进行了各种优化,有些情况下,它就并不那么重了;
- 具体锁住的对象:
1、普通同步方法 -> 锁是this
2、static同步方法 -> 锁是 类.class
3、 同步方法块 -> 锁是synchronized括号里配置的对象;整个临界区都具有原子性
- 修饰的位置:
public synchronized void synMethod(){}
粒度太大,比如hashTable是线程安全的,就是这么实现的,相当于每次操作,都是锁整个表。
虽然当一个线程访问Object的一个synchronized(this)同步代码块时,其他线程仍然可以访问Object中所有其他非synchronized(this)同步代码块。但是hashtable里的方法,都是synchronized的;
对于非static方法,同步锁就是this;对于static方法,使用的是当前方法所在类的字节码对象(类名.class)
synchronized(变量或对象){
代码块
}
粒度小很多
- 编译器编译之后,会有两个字节码指令monitorenter和monitorexit,一个进行+1操作,一个进行-1操作,相当于计数器呗,只有计数为0,锁才会被释放;
- 可重入性的悲观锁
所谓可重入,就是为了防止自己锁死自己。因为用的是计数器来判断要不要释放锁,所以在锁释放前,同一个线程是可以再次加锁的,计数直接+1就行,前提是,要在同一个线程里
- 为什么说synchronized是非公平锁
主要是在获取锁的行为上,并非按照申请锁的时间前后顺序给等待的线程分配锁的,而是每当锁释放后,任何一个线程都有机会竞争到锁,这样做的目的是为了提高执行性能,缺点是可能会产生线程饥饿现象(就是有的线程老等不到资源)
- 为什么说synchronized是一个悲观锁
因为他的并发策略很悲观:不管是否产生竞争,先加锁,用户态核心态转换,维护锁计数器和检查是否有被阻塞的线程需要被唤醒等操作
- 什么是锁消除和锁粗化
锁消除:就是编译时,发现有的代码不存在竞争,没必要加锁,就会默默的把这个锁消除掉,正常情况下,这个没用,没人闲着没事乱加锁;
锁粗化:譬如在一个循环里加锁,那就加锁太频繁了,反而消耗性能,直接在循环外,整个循环加锁就好
1.1.1.3.3 静态内部类
public class Singleton {
private Singleton(){
System.out.println("【"+Thread.currentThread().getName()+"】实例化懒汉式Singleton对象");
}
public static Singleton getInstance(){
return Lazy.instance;
}
private static class Lazy{
private static final Singleton instance=new Singleton();
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(()->{
Singleton.getInstance();
},"多线程懒汉式实例化-"+i).start();
}
}
}
利用了classloader的机制来保证初始化instance时只有一个线程,所以是线程安全的,同时没有性能损耗。
1.1.2 饿汉式
饿汉式,可以将其理解为饿死鬼,宁愿先吃撑,也不放过;
所以饿汉式在类初始化时,就将单例给初始化出来,不管后续用不用得到这个单例。
又因为在一开始,还没有任何线程调用前,就将实例初始化出来了,所以确保无论如何,都只有这么一个实例,所以饿汉式是线程安全的。
public class Singleton {
private static final Singleton instance=new Singleton();
private Singleton(){
System.out.println("【"+Thread.currentThread().getName()+"】实例化饿汉式Singleton对象");
}
public static Singleton getInstance(){
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Thread(()->{
Singleton.getInstance();
},"多线程饿汉式实例化-"+i).start();
}
}
}
因为饿汉式在类创建的同时就已经建好一个静态的对象供系统使用,以后也不再改变,避免了多线程的同步问题,所以天生是线程安全的。缺点是instance在类装载时就早早的实例化了。
2、抽象工厂模式
工厂模式:定义一个接口,比如shape,然后多个类继承shape,之后编写一个工厂类,在里面判断是什么形状,然后new出相应的对象并返回;
抽象工厂:在原本工厂的基础上,再来一个工厂,然后编写一个抽象工厂类,在抽象工厂内判断是属于哪个工厂,然后new出对应的工厂
- 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类;
- 抽象工厂是一个父类工厂,可以创建其它工厂类。具体的工厂类负责实现具体的产品实例。所以我们也叫它 “工厂的工厂”。主要用于解决接口选择的问题;
- 类图
2.1 抽象工厂模式的实现方式
2.1.1 创建抽象工厂类,定义具体工厂的公共接口:
public abstract class AbstractFactory {
public abstract Color getColor(String colorName);
public abstract Shape getShape(String shapeName);
}
2.1.2 创建抽象产品类,定义具体产品的公共接口:
public interface Color {
void colorIn();
}public interface Shape {
void selectShape();
}
2.1.3 创建具体产品类,定义生产的具体产品:
public class Square implements Shape {
@Override
public void selectShape() {
System.out.println("\tDraw a square");
}
}
public class Circle implements Shape {
@Override
public void selectShape() {
System.out.println("\tDraw a circle");
}
}
public class Rectangle implements Shape{
@Override
public void selectShape() {
System.out.println("\tDraw a Rectangle");
}
}
public class Yellow implements Color {
@Override
public void colorIn() {
System.out.println("\tColor in yellow");
}
}
public class Green implements Color {
@Override
public void colorIn() {
System.out.println("\tColor in green");
}
}
public class Blue implements Color {
@Override
public void colorIn() {
System.out.println("\tColor in blue");
}
}
2.1.4 创建具体工厂类(继承抽象工厂类),定义创建对应具体产品实例的方法:
public class shapeFactory extends AbstractFactory {
@Override
public Color getColor(String colorName) {
return null;
}
@Override
public Shape getShape(String shapeName) {
if("Rectangle".equalsIgnoreCase(shapeName)){
return new Rectangle();
}
else if("Square".equalsIgnoreCase(shapeName)){
return new Square();
}
else if("Circle".equalsIgnoreCase(shapeName)){
return new Circle();
}
return null;
}
}
public class colorFactory extends AbstractFactory {
@Override
public Color getColor(String colorName) {
if("Yellow".equalsIgnoreCase(colorName)){
return new Yellow();
}
else if ("Green".equalsIgnoreCase(colorName)){
return new Green();
}
else if ("Blue".equalsIgnoreCase(colorName)){
return new Blue();
}
return null;
}
@Override
public Shape getShape(String shapeName) {
return null;
}
}
2.1.5 创建工厂创造器
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
2.1.6 客户端通过实例化具体的工厂类并调用其创建不同目标产品的方法创建不同实例:
public class Client {
public static void main(String[] args) {
AbstractFactory shapeFactory=FactoryProducer.getFactory("SHAPE");
AbstractFactory colorFactory=FactoryProducer.getFactory("COLOR");
System.out.println("Draw a circle in blue");
Shape shape1=shapeFactory.getShape("circle");
Color color1=colorFactory.getColor("blue");
shape1.selectShape();
color1.colorIn();
System.out.println("\r\nDraw a square in yellow");
Shape shape2=shapeFactory.getShape("square");
Color color2=colorFactory.getColor("yellow");
shape2.selectShape();
color2.colorIn();
System.out.println("\r\nDraw a rectangle in green");
Shape shape3=shapeFactory.getShape("rectangle");
Color color3=colorFactory.getColor("green");
shape3.selectShape();
color3.colorIn();
}
}
3、工厂模式
3.1 概念
3.2 何时使用
明确地计划不同条件下创建不同实例时
public class Client {
public static void main(String[] args) {
AbstractFactory shapeFactory=new shapeFactory();
AbstractFactory colorFactory=new colorFactory();
System.out.println("Draw a circle in blue");
Shape shape1=shapeFactory.getShape("circle");
Color color1=colorFactory.getColor("blue");
shape1.selectShape();
color1.colorIn();
System.out.println("\r\nDraw a square in yellow");
Shape shape2=shapeFactory.getShape("square");
Color color2=colorFactory.getColor("yellow");
shape2.selectShape();
color2.colorIn();
System.out.println("\r\nDraw a rectangle in green");
Shape shape3=shapeFactory.getShape("rectangle");
Color color3=colorFactory.getColor("green");
shape3.selectShape();
color3.colorIn();
}
}
3.3 如何解决
让其子类实现工厂接口,返回的也是一个抽象的产品
public class Square implements Shape {
@Override
public void selectShape() {
System.out.println("\tDraw a square");
}
}
public class Circle implements Shape {
@Override
public void selectShape() {
System.out.println("\tDraw a circle");
}
}
public class Rectangle implements Shape{
@Override
public void selectShape() {
System.out.println("\tDraw a Rectangle");
}
}
public class Yellow implements Color {
@Override
public void colorIn() {
System.out.println("\tColor in yellow");
}
}
public class Green implements Color {
@Override
public void colorIn() {
System.out.println("\tColor in green");
}
}
public class Blue implements Color {
@Override
public void colorIn() {
System.out.println("\tColor in blue");
}
}
3.4 关键代码
创建过程在其子类执行
public class shapeFactory extends AbstractFactory {
@Override
public Color getColor(String colorName) {
return null;
}
@Override
public Shape getShape(String shapeName) {
if("Rectangle".equalsIgnoreCase(shapeName)){
return new Rectangle();
}
else if("Square".equalsIgnoreCase(shapeName)){
return new Square();
}
else if("Circle".equalsIgnoreCase(shapeName)){
return new Circle();
}
return null;
}
}
4、建造者模式
建造者模式:使用多个简单的对象一步一步构建成一个复杂的对象。
4.1 建造者模式的实现方式
4.1.1 创建一个表示食物条目和食物包装的接口
public interface Item {
public String name();
public Packing packing();
public float price();
}
public interface Packing {
public String pack();
}
4.1.2 创建实现 Packing 接口的实体类
public class Wrapper implements Packing {
@Override
public String pack() {
return "Wrapper";
}
}
public class Bottle implements Packing {
@Override
public String pack() {
return "Bottle";
}
}
4.1.3 创建实现 Item 接口的抽象类,该类提供了默认的功能
public abstract class Burger implements Item {
@Override
public Packing packing() {
return new Wrapper();
}
@Override
public abstract float price();
@Override
public abstract String name();
}
public abstract class ColdDrink implements Item {
@Override
public Packing packing() {
return new Bottle();
}
@Override
public abstract float price();
@Override
public abstract String name();
}
4.1.4 创建扩展了 Burger 和 ColdDrink 的实体类
public class VegBurger extends Burger {
@Override
public float price() {
return 25.0f;
}
@Override
public String name() {
return "Veg Burger";
}
}
public class ChickenBurger extends Burger {
@Override
public float price() {
return 50.5f;
}
@Override
public String name() {
return "Chicken Burger";
}
}
public class Coke extends ColdDrink {
@Override
public float price() {
return 30.0f;
}
@Override
public String name() {
return "Coke";
}
}
public class Pepsi extends ColdDrink {
@Override
public float price() {
return 35.0f;
}
@Override
public String name() {
return "Pepsi";
}
}
4.1.5 创建一个 Meal 类,带有上面定义的 Item 对象
public class Meal {
private List<Item> items = new ArrayList<Item>();
public void addItem(Item item){
items.add(item);
}
public float getCost(){
float cost = 0.0f;
for (Item item : items) {
cost += item.price();
}
return cost;
}
public void showItems(){
for (Item item : items) {
System.out.print("Item : "+item.name());
System.out.print(", Packing : "+item.packing().pack());
System.out.println(", Price : "+item.price());
}
}
}
4.1.6 创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象
public class MealBuilder {
public Meal prepareVegMeal (){
Meal meal = new Meal();
meal.addItem(new VegBurger());
meal.addItem(new Coke());
return meal;
}
public Meal prepareNonVegMeal (){
Meal meal = new Meal();
meal.addItem(new ChickenBurger());
meal.addItem(new Pepsi());
return meal;
}
}
4.1.7 BuiderPatternDemo 使用 MealBuilder 来演示建造者模式(Builder Pattern)
public class BuilderPatternDemo {
public static void main(String[] args) {
MealBuilder mealBuilder = new MealBuilder();
Meal vegMeal = mealBuilder.prepareVegMeal();
System.out.println("Veg Meal");
vegMeal.showItems();
System.out.println("Total Cost: " +vegMeal.getCost());
Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
System.out.println("\n\nNon-Veg Meal");
nonVegMeal.showItems();
System.out.println("Total Cost: " +nonVegMeal.getCost());
}
}
看起来应该是,一层层细分建造,首先定义了一个祖宗级接口:譬如猴子接口和走路接口,然后经过了若干年,猴子逐渐分化成了会走路的猿人和不会走路的黑猩猩,这个时候就有两个类实现了猴子接口和走路接口,两个接口结合成了一个猿人类,但是进化还在继续,猿人抽象类慢慢的又分化出了黑人,白人和黄种人,不同的人种有了肤色等明显区分,所以继承猿人类后,还可以变换新属性得到新的三个类。最后得到的这三个类就是由不同的接口逐渐对接建造起来的,还能有区分属性,比如三个实现类有肤色区别
建造者模式与工厂模式的不同:
建造者模式
最主要的功能是基本方法的调用顺序安排,这些基本方法已经实现了,
顺序不同产生的对象也不同;
工厂模式
则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的
5、原型模式
其实就是一个个new对象的代价太高,如果只new一个,后面的都通过clone获得,性能会更好,于是就管这种方式叫原型模式,原型就是被new出来的对象。它存在的意义就是用来copy。
5.1 原型模式的实现方式
5.1.1 创建一个实现了 Cloneable 接口的抽象类
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
5.1.2 创建扩展了上面抽象类的实体类
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square extends Shape {
public Square(){
type = "Square";
}
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle extends Shape {
public Circle(){
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
5.1.3 创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// 例如,我们要添加三种形状
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(),circle);
Square square = new Square();
square.setId("2");
shapeMap.put(square.getId(),square);
Rectangle rectangle = new Rectangle();
rectangle.setId("3");
shapeMap.put(rectangle.getId(),rectangle);
}
}
5.1.4 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}