一、创建型模式(Factory Method)
1.工厂模式
1.1普通工厂模式
就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:
举例如下:(我们举一个发送邮件和短信的例子)
首先创建二者的共同接口
public interface Sender {
public void Send();
}
其次,创建实现类:
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
最后,建工厂类:
public class SendFactory {
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
} else if ("sms".equals(type)) {
return new SmsSender();
} else {
System.out.println("请输入正确的类型!");
return null;
}
}
}
我们来测试下:
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produce("sms");
sender.Send();
}
}
1.2 多工厂方法模式
是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:
将上面的代码做下修改,改动下SendFactory类就行,如下:
public class SendFactory {
public Sender produceMail(){
return new MailSender();
}
public Sender produceSms(){
return new SmsSender();
}
}
测试类如下:
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}
输出:this is mailsender!
1.3 静态工厂方法模式
将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。
public class SendFactory {
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.Send();
}
}
输出:this is mailsender!
总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
2. 抽象工厂模式
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。因为抽象工厂不太好理解,我们先看看图,然后就和代码,就比较容易理解。
请看例子:
public interface Sender {
public void Send();
}
两个实现类:
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
两个工厂类:
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
在提供一个接口:
public interface Provider {
public Sender produce();
}
public class Test {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!
3. 单例模式
通过私有构造方法,不允许外面去创建对象,实现只有一个实例。
3.1 饿汉式单例
缺点:一开始就把所有的对象都加载,容易浪费空间。
public class Hungry {
//核心是构造,不允许外面去创建对象
private Hungry() {
}
//创建静态变量
private final static Hungry HUNGRY = new Hungry();
//返回对象
public static Hungry getInstance() {
return HUNGRY;
}
}
3.2 懒汉式单例
(1)只有在对象为空时才去new对象------单线程下没有问题
public class LazyMan {
private LazyMan() {
}
private static LazyMan lazyMan;
//只有在对象为空时才去new对象------单线程下没有问题
public static LazyMan getInstance() {
if(lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
(2)通过多线程去测试,发现单例被破坏。
private LazyMan() {
System.out.println(Thread.currentThread().getName()+"ok");
}
//多线程的情况下就会出现多个对象
public static void main(String[] args) {
for(int i=0;i<10;i++) {
new Thread(()->{
LazyMan.getInstance();
}).start();
}
}
去实现双重检测锁模式的懒汉式单例--DCL懒汉式,并且在lazyMan = new LazyMan();位置,由于还不是原子操作,容易造成指令重排,去加上volatile属性,他是可见的不是原子的,来保证安全。
private volatile static LazyMan lazyMan;
public static LazyMan getInstance() {
if(lazyMan==null) {
synchronized (LazyMan.class) { //锁住类就会只有一个对象
if(lazyMan == null) {
lazyMan = new LazyMan(); //不是一个原子操作
/**
* 1. 分配内存空间
* 2. 执行构造方法,初始化对象
* 3. 把对象指向这个空间
* 假如又A,B两个线程,A执行了132是没有问题的,但是当B在执行时就 直接return了,但是此时还没有完成构造,是不安全的。
* volatile只能保证可见性,不证原子性。
* */
}
}
}
return lazyMan;
}
(3)通过反射机制去破环构造方法的私有性,从而破坏单例模式---一个反射对象。
public static void main(String[] args) throws Exception{
LazyMan instance = LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); //用反射使其私有构造无效
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
去实现三重检测,也就是在构造方法上再次进行加锁判断,避免某一种反射的破坏,其余不变。
private LazyMan() {
synchronized (LazyMan.class) {
if(lazyMan!=null) {
throw new RuntimeException("不要试图使用反射破坏单例");
}
}
}
(4)上面的先正常创建对象,再用反射,如果我们直接建立多个反射对象就可以破坏单例。
public static void main(String[] args) throws Exception{
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); //用反射使其私有构造无效
LazyMan instance = declaredConstructor.newInstance();
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
通过增加一个标志位,可以防止多个反射对象的破坏。
private static boolean flag = false;
private LazyMan() {
synchronized (LazyMan.class) {
if(flag==false) {
flag = true;
}else {
throw new RuntimeException("不要试图使用反射破坏单例");
}
}
}
(5)如果我们能够破解这个标志位,仍然是可以去破坏单例。
public static void main(String[] args) throws Exception{
Field flag = LazyMan.class.getDeclaredField("flag");//获得标志位
flag.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true); //用反射使其私有构造无效
LazyMan instance = declaredConstructor.newInstance();
flag.set(instance, false); //破坏标志位
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
(6)通过使用枚举去保证单例和安全,因为枚举是不可以被反射的。
public enum EnumSingle {
INSTANCE;
//枚举本身也是一个类,只是继承了枚举
public EnumSingle getInstance() {
return INSTANCE;
}
}
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
EnumSingle instance2 = EnumSingle.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
}
我们可以通过我们的反编译class字节文件,发现有一个带参构造,如果这个时候再去用反射破坏,发现就会报menu不能被反射的错了。
public static void main(String[] args) throws Exception {
EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> de = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
de.setAccessible(true);
EnumSingle instance2 = de.newInstance();
}
3.3 静态内部类实现单例
public class Holder {
private Holder() {
}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
4.建造者模式
将一个复杂对象的构建与他的表示层分离,实现了解耦,使得同样的构造过程可以构造不同的表示。具体的建造者类之间是相互独立的,这有利于系统的扩展,符合“开闭原则”。
缺点:共性、内部变化复杂,导致多个建造者类,系统庞大。
(1)举例工人盖房子,有四个步骤地基、钢筋、水泥、粉刷。工人有指挥官,负责指挥工作的顺序,如果需要建不同的楼,只需要换不同的工人,因为工人的技能是不同的。
//抽象的建造者:方法
public abstract class Builder {
abstract void buildA(); //地基
abstract void buildB(); //钢筋
abstract void buildC(); //铺电线
abstract void buildD(); //粉刷
//完工:得到产品
abstract Product getProduct();
}
//产品:房子
public class Product {
private String builderA;
private String builderB;
private String builderC;
private String builderD;
public String getBuilderA() {
return builderA;
}
public void setBuilderA(String builderA) {
this.builderA = builderA;
}
public String getBuilderB() {
return builderB;
}
public void setBuilderB(String builderB) {
this.builderB = builderB;
}
public String getBuilderC() {
return builderC;
}
public void setBuilderC(String builderC) {
this.builderC = builderC;
}
public String getBuilderD() {
return builderD;
}
public void setBuilderD(String builderD) {
this.builderD = builderD;
}
@Override
public String toString() {
return "Product [builderA=" + builderA + ", builderB=" + builderB + ", builderC=" + builderC + ", builderD="
+ builderD + "]";
}
}
public class Worker extends Builder{
private Product product;
public Worker() {
product = new Product();
}
//工人把工作,实现建造的没有步骤
public void buildA() {
product.setBuilderA("地基");
System.out.println("地基");
}
public void buildB() {
product.setBuilderB("钢筋");
System.out.println("钢筋");
}
public void buildC() {
product.setBuilderC("铺电线");
System.out.println("铺电线");
}
public void buildD() {
product.setBuilderD("粉刷");
System.out.println("粉刷");
}
public Product getProduct() {
return product;
}
}
//指挥:核心,负责指挥构建一个工程,工程如何构建由他决定
public class Director {
//指挥工人按照顺序建房子
public Product build(Builder builder) {
builder.buildA();
builder.buildB();
builder.buildC();
builder.buildD();
return builder.getProduct();
}
}
public class Test {
public static void main(String[] args) {
//需要一个指挥者
Director director = new Director();
//指挥工人去完成具体的产品
Product product = director.build(new Worker());
System.out.println(product.toString());
}
}
(2)上述是有指挥官的构造,如果没有指挥官,就不需要顺序,也就是实现无序的搭配。
举例:麦当劳的套餐,服务员(建造者)随意搭配几种食物形成套餐销售给客户。
//抽象的建造者:方法
public abstract class Builder {
abstract Builder buildA(String msg); //汉堡
abstract Builder buildB(String msg); //可乐
abstract Builder buildC(String msg); //薯条
abstract Builder buildD(String msg); //甜品
//完工:得到产品套餐
abstract Product getProduct();
}
//产品:套餐
public class Product {
//默认值,也就是有默认的套餐
private String builderA = "汉堡";
private String builderB = "可乐";
private String builderC = "薯条";
private String builderD = "甜点";
public String getBuilderA() {
return builderA;
}
public void setBuilderA(String builderA) {
this.builderA = builderA;
}
public String getBuilderB() {
return builderB;
}
public void setBuilderB(String builderB) {
this.builderB = builderB;
}
public String getBuilderC() {
return builderC;
}
public void setBuilderC(String builderC) {
this.builderC = builderC;
}
public String getBuilderD() {
return builderD;
}
public void setBuilderD(String builderD) {
this.builderD = builderD;
}
@Override
public String toString() {
return "Product [builderA=" + builderA + ", builderB=" + builderB + ", builderC=" + builderC + ", builderD="
+ builderD + "]";
}
}
public class Worker extends Builder{
private Product product;
public Worker() {
product = new Product();
}
public Builder buildA(String msg) {
product.setBuilderA(msg);
return this;
}
public Builder buildB(String msg) {
product.setBuilderB(msg);
return this;
}
public Builder buildC(String msg) {
product.setBuilderC(msg);
return this;
}
public Builder buildD(String msg) {
product.setBuilderD(msg);
return this;
}
public Product getProduct() {
return product;
}
}
public class Test {
public static void main(String[] args) {
//服务员
Worker worker = new Worker();
//不改变参数,默认套餐
//Product product = worker.getProduct();
//链式编程:在原来的基础上可以自由组合了,如果不组合也有默认的套餐。
Product product = worker.buildA("披萨").buildB("美年达").getProduct();
System.out.println(product.toString());
}
}
5.原型模式
原型模式是以一个为原型,在使用的时候不用再去new ,而是通过clone来提升效率。
第一步,实现一个接口 Cloneable;第二步,重写一个方法 clone();
(1)浅克隆:通过克隆出来的对象和原对象是一样的,但是属性的引用也是一样的,指向同一块内存空间。
public class Video implements Cloneable{
private String name;
private Date createTime;
//浅克隆
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video() {}
public Video(String name,Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String toString() {
return "Video [name=" + name + ", createTime=" + createTime + "]";
}
}
测试如下:
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象
Date date = new Date();
Video v1 = new Video("狂神说",date);
System.out.println("v1:"+v1);
System.out.println("v1.hashcode():"+v1.hashCode());
//clone一个对象
Video v2 = (Video)v1.clone(); //克隆出来的对象和原来的对象是一摸一样的
System.out.println("v2:"+v2);
System.out.println("v2.hashcode():"+v2.hashCode());
v2.setName("clone:狂神说");
System.out.println(v2);
System.out.println("=====但是这样是浅克隆,date会随着原型值的改变而改变=====");
date.setTime(123135);
System.out.println("v1:"+v1);
System.out.println("v2:"+v2);
}
}
(2)深克隆:克隆对象额同时也会把属性一同克隆,使克隆的属性拥有自己独立的内存空间。还有序列化,反序列化。
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Video v = (Video)obj;
//克隆属性
v.createTime = (Date)this.createTime.clone();
return obj;
}