百日筑基第二十三天-23种设计模式-创建型总汇
百日筑基第二十三天-23种设计模式-创建型总汇
前言
设计模式可以说是对于七大设计原则的实现。
总体来说设计模式分为三大类:
- 创建型模式,共五种:单例模式、简单工厂模式、抽象工厂模式、建造者模式、原型模式。
- 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
- 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
23种设计模式的关系图:
单例模式
简介
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建(私有的构造器),这个类对外提供了唯一一种访问其实例的方式,对外只能够直接访问,不能实例化此类对象。例如,一台计算机上可以连接多台打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印作业同时输出到打印机中,即在整个的打印过程中只有一个打印程序的实例。
简单点说,单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,任何时刻,单例类的实例都最多只存在一个。单例模式确保某一个类只有一个实例,而且自行实例化,并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”需求时才可以使用。
单例的类图如下:其中 uniqueInstance 持有唯一的单例实例,类方法 getInstance() 用来获取唯一的实例化对象。
Singleton —————————————————————— -static uniqueInstance -other Attribute —————————————————————— +static genInstance()() +otherMethods()
8 种单例实现方式(有点多但有必要都看)
【1】饿汉式(静态常量)优缺点:
● 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。没有加锁,执行效率会提高。
● 缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存浪费,容易产生垃圾对象。
● 这种方式基于 classload 机制避免了多线程同步问题,instance 在类装载时就实例化,在单例模式中大多数都是调用 getInstance 方法,但是导致类装载的原因有很多种,因此通过其他方式(或者其他静态方法)导致类装载,此时初始化 instance就没有达到 Lazy Loading 的效果。
public class SingleTon {//将构造器私有化,防止直接 Newprivate SingleTon(){}//创建好一个私有的 SingleTon 实例private static SingleTon instance = new SingleTon();//提供一个 public 的静态方法, 可以返回 instancepublic static SingleTon getInstance() {return instance;}
}
【2】饿汉式(静态代码块)优缺点:
● 这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面一样。
● 结论:这种单例模式可用,但可能造成内存浪费。
public class SingleTon {//将构造器私有化,防止直接 Newprivate SingleTon(){}//创建好一个私有的 SingleTon 实例private static SingleTon instance;//静态块static {instance = new SingleTon();}//提供一个 public 的静态方法, 可以返回 instancepublic static SingleTon getInstance() {return instance;}
}
【3】懒汉式(线程不安全)优缺点:
● 起到了 Lazy Loading 的效果,但是只能在单线程下使用。
● 如果在多线程下,一个线程进入if(singleton == null)判断语句块,还未来得及创建,另一个线程也通过了上述判断语句,这时便产生了多个实例。所以在多线程环境下不可使用这种方式。
● 结论:在实际开发中,不要使用这种方法。
public class SingleTon {//将构造器私有化,防止直接 Newprivate SingleTon(){}//创建好一个私有的 SingleTon 实例private static SingleTon instance;//提供一个 public 的静态方法, 可以返回 instancepublic static SingleTon getInstance() {if(instance == null) {instance = new SingleTon();}return instance;}
}
【4】懒汉式(线程安全,同步方法)优缺点:
● 解决了线程不安全问题。
● 效率太低了,每个线程在想获得类的实例的时候,执行 getInstance() 方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得实例,直接 return 就够了。方法进行同步效率太低。
● 结论:在实际开发中不推荐使用。
public class SingleTon {//将构造器私有化,防止直接 Newprivate SingleTon(){}//创建好一个私有的 SingleTon 实例private static SingleTon instance;//提供一个 public 的静态方法, 可以返回 instancepublic static synchronized SingleTon getInstance() {if(instance == null) {instance = new SingleTon();}return instance;}
}
【5】懒汉式(线程安全,同步代码块)优缺点:
● 这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低, 改为同步产生实例化的的代码块。
● 但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了 if (singleton == null) 判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
● 结论:在实际开发中,不能使用这种方式。
public class SingleTon {//将构造器私有化,防止直接 Newprivate SingleTon(){}//创建好一个私有的 SingleTon 实例private static SingleTon instance;//提供一个 public 的静态方法, 可以返回 instancepublic static SingleTon getInstance() {if(instance == null) {//添加同步代码块,提高了效率,多线程时存在创建的对象不一致风险synchronized(SingleTon.class) {instance = new SingleTon();}}return instance;}
}
【6】双重检查(Double-Check)优缺点:
● 双重检查概念是多线程开发中常使用到的,如代码所示,我们进行了两次 if(instance == null) 检查,这样就确保了线程的安全,同时也提高了效率。
● 这样,实例化代码只用执行一次,后面再次访问时,判断 if(instance == null) , 直接 return 实例化对象,也避免的反复进行方法同步。
● 线程安全;延迟加载;效率较高。
● 结论:在实际开发中,推荐使用这种单例设计模式。
public class SingleTon {//将构造器私有化,防止直接 Newprivate SingleTon(){}//创建好一个私有的 SingleTon 实例private static volatile SingleTon instance;//提供一个 public 的静态方法, 可以返回 instancepublic static SingleTon getInstance() {//添加同步代码块,提高了效率.if(instance == null) {synchronized(SingleTon.class) {//解决多线程可能创建多个实例的情况if(instance == null) {instance = new SingleTon();}}}return instance;}
}
【7】静态内部类优缺点:
● 这种方式采用了类加载器的机制来保证初始化实例时只有一个线程。
● 静态内部类方式在 SingleTon 类(父类)被装载时,不会导致内部类被装载,也就不会立即实例化,属于懒加载类型。当调用 getInstance() 方法时,才会装载 SingleTonInstance 类,从而完成 SingleTon 的实例化。
● 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮我们保证了线程的安全,在类初始化时,别的线程无法进入。
● 避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。
● 结论:推荐使用。
public class SingleTon {//将构造器私有化,防止直接 Newprivate SingleTon(){}//在内部内中创建一个对象的实例,当父类 SingleTon 加载时,内部类 SingleTonInstance 无需加载private static class SingleTonInstance{private static final SingleTon INSTANCE = new SingleTon();}//提供一个 public 的静态方法, 可以返回 SingleTon实例public static SingleTon getInstance() {return SingleTonInstance.INSTANCE;}
}
【8】枚举优缺点:
● 这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
● 这种方式是Effective Java作者Josh Bloch 提倡的方式
● 结论:推荐使用
enum SingleTon {//当只有一个对象时,就是单例INSTANCE;
}
单例模式注意事项和使用场景
【1】单例模式保证了系统内存中该内只存在一个对象,节省了系统资源,对于一些需要频繁创建和销毁的对象,使用单例模式可以提高系统性能。
【2】当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new。
【3】单例模式使用场景:
- 需要频繁的进行创建和销毁对象
- 创建对象时耗时过多或消耗过多资源(既重量级对象)但又常使用的对象
- 工具类对象
- 频繁访问数据库或文件的对象(比如数据源、session工厂等)
工厂模式
简介
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单使用的模式。简单工厂模式定义了一个创建对象的类,由这个类来封装实例化对象的行为。在软件开发中,当我们会用到大量的创建某种、某类或者某批量对象时,就会使用到工厂模式。
简单工厂模式案例
【1】定义一个接口 Pizza
package com.yintong.principle.singleresponsibility;
//将Pizza 做成一个接口
public interface Pizza {//准备原材料, 不同的披萨不一样,因此,我们做成抽象方法public void prepare();
}
【2】定义披萨的种类,实现 Pizza 接口,如下定义了 GREEK、CHEESE 两种披萨。如果后续有新的种类可以实现 Pizza 接口,继续添加新的品种。
//希腊披萨
public class GreekPizza implements Pizza{@Overridepublic void prepare() {System.out.println(" 给希腊披萨 准备原材料 ");}
}
//奶酪披萨
public class CheesePizza implements Pizza{@Overridepublic void prepare() {System.out.println(" 给制作奶酪披萨 准备原材料 ");}
}
【3】 创建一个简单的工厂类对象 SimpleFactory
public class SimpleFactory {//传入 orderType(种类) 返回对应的 Pizza 对象public Pizza createPizza(String orderType) {Pizza pizza = null;//使用简单工厂模式if (orderType.equals("greek")) {//希腊披萨pizza = new GreekPizza();} else if (orderType.equals("cheese")) {//奶酪披萨pizza = new CheesePizza();}return pizza;}
}
【4】在应用代码 OrderPizza 中调用工厂类,获取用户需要的披萨对象。
public class OrderPizza {//定义用户要获取的披萨类型private static final String pizza = "greek";public static void main(String[] args) {//通过工厂获取希腊披萨SimpleFactory simpleFactory = new SimpleFactory();//greekPizza 就是我们需要获取的目标披萨Pizza greekPizza = simpleFactory.createPizza(pizza);//调用目标披萨的方法greekPizza.prepare();}
}
简单工厂模式的优缺点
优点:
1)一个调用者想创建一个对象,只要知道其名称就可以了。
2)扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3)屏蔽产品的具体实现,调用者只关心产品接口。
缺点: 每增加一个产品时,都需要增加一个具体实现类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
注意事项: 作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别只需要通过 new 就可以完成创建的对象,无需工厂模式。如果使用工厂模式就需要使用工厂类,会增加系统的复杂度。
抽象工厂方法模式
需求: 在上述的基础上,添加了地区。例如:用户可以点北京的奶酪披萨,北京的胡椒披萨,或者伦敦的奶酪披萨,伦敦的胡椒披萨。
工厂方法设计模式:
1)、定义一个 interface(工厂的接口) 用于创建相关或者依赖关系的对象簇,而无需指明具体对象类。
2)、从设计层面来看,抽象工厂模式就是简单工厂模式的改进(或者称为进一步抽象)。
3)、将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更有利于代码的维护和扩展。
类图: 将简单工厂模式中的工厂类抽象成了一个接口,易于后期扩展。
【1】定义工厂类的接口(AbsFactory)
//一个抽象工厂模式的抽象层(接口)
public interface AbsFactory {//让下面的工厂子类来 具体实现public Pizza createPizza(String orderType);
}
【2】让北京和伦敦的工厂类实现此接口(与简单工厂类的区别就是实现了 AbsFactory 接口,伦敦略)
public class BJFactory implements AbsFactory{//更加orderType 返回对应的Pizza 对象public Pizza createPizza(String orderType) {Pizza pizza = null;//使用简单工厂模式if (orderType.equals("greek")) {//希腊披萨pizza = new GreekPizza();} else if (orderType.equals("cheese")) {//奶酪披萨pizza = new CheesePizza();}return pizza;}
}
【3】在 Order 订单应用中,与接口 AbsFactory 进行组合。
public class OrderPizza {AbsFactory factory;//定义构造器public OrderPizza(AbsFactory factory){this.factory = factory;}//定义用户要获取的披萨类型,可以定义一个变量从客户端传入private static final String pizza = "greek";public void main(String[] args) {//greekPizza 就是我们需要获取的目标披萨Pizza greekPizza = factory.createPizza(pizza);//调用目标披萨的方法greekPizza.prepare();}
}
工厂模式在 JDK-Calendar 类中的应用
//1、调用 Calendar 对象
Calendar instance = Calendar.getInstance();
//2、进入 Calendar.getInstance() 方法
public static Calendar getInstance()
{return createCalendar(TimeZone.getDefault(),Locale.getDefault(Locale.Category.FORMAT));
}
//3、进入 createCalendar 方法
private static Calendar createCalendar(TimeZone zone,Locale aLocale){CalendarProvider provider =LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();if (provider != null) {try {return provider.getInstance(zone, aLocale);} catch (IllegalArgumentException iae) {// fall back to the default instantiation}}Calendar cal = null;if (aLocale.hasExtensions()) {String caltype = aLocale.getUnicodeLocaleType("ca");if (caltype != null) {//这里就是我们要重点查看的部分,根据用户需求,创建Calendar 对象switch (caltype) {case "buddhist":cal = new BuddhistCalendar(zone, aLocale);break;case "japanese":cal = new JapaneseImperialCalendar(zone, aLocale);break;case "gregory":cal = new GregorianCalendar(zone, aLocale);break;}}}return cal;
}
工厂模式小结
1)工厂模式的意义: 将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
2)设计模式依赖抽象原则: 创建对象实例时,不要直接 new 类,而是把这个 new 类的动作放在一个工厂的方法中,并返回。不要让类继承具体类,而是继承抽象类或者实现 interface(接口)。不要覆盖基类中已经实现的方法。
建造者模式
简介
建造者模式(Builder Pattern): 使用简单的对象一步一步构建成一个复杂的对象。这种设计模式属于创建者模式,它提供了一种创建对象的最佳方式。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。例如,计算机是由 CPU、主板、内存、硬盘、显卡、机箱、显示器、键盘、鼠标等部件组装而成的,采购员不可能自己去组装计算机,而是将计算机的配置要求告诉计算机销售公司,计算机销售公司安排技术人员去组装计算机,然后再交给要买计算机的采购员。
以上所有这些产品都是由多个部件构成的,各个部件可以灵活选择,但其创建步骤都大同小异。这类产品的创建无法用前面介绍的工厂模式描述,只有建造者模式可以很好地描述该类产品的创建。
- 建造者模式: 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
- 建造者模式,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
- 建造者模式主要解决在软件系统中,当面临一个复杂对象的创建工作时,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临剧烈的变化,但是将它们组合在一起的算法却相对稳定。
- 主要适用于:一些基本部件不会变,而其组合经常变化的情况。主要是将变与不变进行分离(去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐")。
建造者模式的四个角色
1)Product(产品角色): 包含多个组成部件的复杂对象(产品对象)。
2)Builder(抽象建造者): 创建一个包含 Product 各个子部件的抽象方法或接口,通常还包含了一个返回复杂产品的方法。
3)ConcreteBuilder(具体建造者): 实现 Builder 接口,构建和装备各个子部件的具体实现。
4)Director(指挥者): 构建一个使用 Builder 接口的对象。它主要作用是用于创建一个复杂的对象。在指挥者中不涉及具体产品的信息。主要分两个作用,一是隔离客户与对象的生产过程,二是负责控制对象的生产过程。
建造者模式的注意事项和细节
1)客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
2)每一个具体建造者都相对独立,因此可以方便的替换或者新增具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
3)可以更加精准的控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
4)增加新的具体建筑者无需修改原有类库代码,指挥者类针对抽象建造者类编程,系统扩展方便。符合 OCP 原则。
5)建造者模式创建的产品具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合建造者模式,因此其使用范围受到一定的限制。
6)如果产品的内部变化复杂,可能会导致需要定义很多具体建造者内来实现这种变化,导致系统变得庞大,因此这种情况下,考虑是否选择建造者模式。
7)抽象工厂模式 VS 建造者模式:抽象工厂模式实现对产品家族的创建,一个产品家族就是一系列产品。具有不同分类维度的产品组合,采用抽象工厂模式不需要关系构建过程,只关系产品由什么工厂生成即可。而建造者模式则是要求按照指定的蓝图建造产品,它主要目的是通过组装零配件而产生一个新产品。
建造者模式原理类图
建造者模式案例
【1】创建 Product 产品角色:
public class Product {private String CPU;//主板 mainboardprivate String mainboard;//内存 memoryprivate String memory;//.....略//生成get set 方法 略
}
【2】抽象建造者对象:
public abstract class Builder {//组合 产品对象protected Product product = new Product();//抽象方法public abstract void buildCpu();public abstract void buildMainboard();public abstract void buildMemory();//返回一个产品对象public Product getResult() {return product;}
}
【3】具体建造者对象:一般会有多个这种对象,都实现 Builder 抽象类。
public class ConcreteBuilder_huawei extends Builder{@Overridepublic void buildCpu() {product.setCPU("华为CPU");}@Overridepublic void buildMainboard() {product.setMainboard("华为主板");}@Overridepublic void buildMemory() {product.setMemory("华为内存");}}
【4】指挥者类,主要构建组装的流程:返回的是产品对象
public class Director {//需要将抽象类聚合进来Builder builder = null;//创建一个构造器 调用时传入具体的实现类public Director(Builder builder) {this.builder = builder;}//添加set 方法 用户可以通过此方法修改已有的 建造者对象public void setBuilder(Builder builder) {this.builder = builder;}//笔记本的组成流程 返回的是产品类public Product pack_pc() {builder.buildMainboard();builder.buildCpu();builder.buildMemory();return builder.getResult();}
}
【5】客户端通过指挥者类调用需要的品牌电脑:
public class Client {public static void main(String[] args) {//创建需要的电脑子部件——例如我们要组装的是 华为ConcreteBuilder_huawei huawei = new ConcreteBuilder_huawei();//调用指挥者类,将需要的品牌传入,根据控制着中的流程进行组装Director director = new Director(huawei);//调用组装方法,返回产品Product pack_pc = director.pack_pc();//查看输入的结果System.out.println(pack_pc.toString());//结果为:Product [CPU=华为CPU, mainboard=华为主板, memory=华为内存]}
}
源码分析(StringBuilder)
【1】java.lang.StringBuilder
中的建造者模式:
public class OrginBuilder {public static void main(String[] args) {StringBuilder builder = new StringBuilder("hello");builder.append("world");}
}
【2】进入 append 方法,会发现 StringBuilder 类即就是我们所说的指挥者。建造方法的具体实现是由 AbstractStringBuilder 实现。
@Override
public StringBuilder append(String str) {super.append(str);return this;
}
【3】进入 AbstractStringBuilder 类,实现了 Appendable 接口方法,此类已是建造者,只是不能实例化。
public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this;
}
【4】Appendable 接口定义了多个 append 方法(抽象方法),既 Appendle 为抽象建造者,定义了抽象方法。
public interface Appendable {Appendable append(CharSequence csq) throws IOException; ......
}
原型模式
简介
原型模式(Prototype Pattern): 是用于创建重复对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
思想: Java 中 Object 类是所有类的根类,Object 类提供了一个 clone() 方法,该方法可以将一个 Java 对象复制一份,但是需要实现 clone 的 Java 类必须要实现一个接口 Cloneable,该接口表示该类能够复制且具有复制的能力(原型模式)。
- 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。
- 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
- 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对像拷贝它们自己来实现创建,及对象的clone()。
原理结构图说明:
1)Prototype:原型类,声明一个克隆自己的接口 clone。
2)ConcretePrototype:具体的原型类,实现一个克隆自己的操作。
3)Client 让一个原型对象克隆自己,从而创建一个新的对象(相当于属性)。
原型模式案例分析
【1】克隆类需要实现 Cloneable 重写 clone 方法。
package com.yintong.principle.singleresponsibility;
//写一个手机的克隆类
public class ConcretePrototype implements Cloneable{//名称private String name;//号码private Long number;//构造器public ConcretePrototype(String name, Long number) {super();this.name = name;this.number = number;}@Overridepublic String toString() {return "ConcretePrototype [name=" + name + ", number=" + number + "]";}// 克隆用到的主要部分@Overrideprotected Object clone() throws CloneNotSupportedException {ConcretePrototype ConcretePrototype = null;try {ConcretePrototype = (ConcretePrototype) super.clone();}catch (Exception e) {System.out.println(e.getMessage());}return ConcretePrototype;}
}
【2】客户端调用 clone 方法,实现原型模式。
public class Client {public static void main(String[] args) throws CloneNotSupportedException {//创建一个对象ConcretePrototype prototype = new ConcretePrototype("华为", new Long(1568889932));//通过原型模式完成对象的创建 克隆ConcretePrototype p2 = (ConcretePrototype)prototype.clone();}
}
Spring 中的应用
【1】当 scope 配置为 prototype 时,表示原型模式。
<!-- 这里我们的 scope="prototype" 即 原型模式来创建 --><bean id="id01" class="com.atguigu.spring.bean.Monster" scope="prototype"/>
【2】查看底层调用:
else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}// *** 创建一个代理类 ***bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
浅拷贝
● 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新对象。
● 对于数据类型是引用类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传值,也就是只是将成员变量的引用值(内存地址)复制一份给新对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
● 浅拷贝是使用默认的 clone() 方法来实现。
深拷贝
● 复制对象的所有基本数据类型的成员变量值。
● 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
● 深拷贝的实现方式有两种,第一种是重写 clone 方法来实现深拷贝,第二种是通过序列化实现深拷贝,也是推荐的一种。
深拷贝的两种实现方法
【1】通过调用引用类型的克隆方法,实现深拷贝。缺点就是当引用类型多时,不建议采用。
public class DeepClone implements Cloneable{//基本数据类型private String name;//引用数据类型private Spare spare;//重写clone方法,调用引用类型的克隆方法@Overrideprotected Object clone() throws CloneNotSupportedException {DeepClone deepClone = null;deepClone = (DeepClone)super.clone();//克隆引用数据类型deepClone.spare = (Spare) spare.clone();return deepClone;}
}
【2】通过序列化的方式,实现深拷贝:也是建议使用的方法。
public class DeepClone implements Serializable{/*** 序列化 ID*/private static final long serialVersionUID = 1L;//数据类型 略。。。。//重写clone方法,调用引用类型的克隆方法protected Object deepClone(){ByteArrayOutputStream BOStream = null;ObjectOutputStream OOSream = null;ByteArrayInputStream BIStream = null;ObjectInputStream OIStream =null;try {//序列化BOStream = new ByteArrayOutputStream();OOSream = new ObjectOutputStream(BOStream);//将当前对象写入流中OOSream.writeObject(this);//反序列化BIStream = new ByteArrayInputStream(BOStream.toByteArray());OIStream = new ObjectInputStream(BIStream);DeepClone deepClone = (DeepClone) OIStream.readObject();return deepClone;} catch (Exception e) {e.printStackTrace();return null;}finally {try {BOStream.close();OOSream.close();BIStream.close();OIStream.close();} catch (Exception e2) {e2.printStackTrace();}}}
}
原型模式的注意事项和细节
1)创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
2)不用重新初始化对象,而是动态地获得对象运行时的状态。
3)如果原始对象发生变化(增加或者减少属性),其他克隆对象也会发生变化,无需修改代码。
4)在实现深克隆的时候可能需要比较复杂的代码。
5)缺点:需要为每一个配置类配置一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则。
相关文章:
百日筑基第二十三天-23种设计模式-创建型总汇
百日筑基第二十三天-23种设计模式-创建型总汇 前言 设计模式可以说是对于七大设计原则的实现。 总体来说设计模式分为三大类: 创建型模式,共五种:单例模式、简单工厂模式、抽象工厂模式、建造者模式、原型模式。结构型模式,共…...
张量的基本使用
目录 1.张量的定义 2.张量的分类 3.张量的创建 3.1 根据已有数据创建张量 3.2 根据形状创建张量 3.3 创建指定类型的张量 1.张量的定义 张量(Tensor)是机器学习的基本构建模块,是以数字方式表示数据的形式。PyTorch就是将数据封装成张量…...
Oracle(14)什么是唯一键(Unique Key)?
唯一键(Unique Key)是数据库表中的一个或多个列,它们的值必须在整个表中唯一,但允许包含NULL值。唯一键的主要目的是确保表中每一行的数据在指定的列(或列组合)中是唯一的,以防止重复数据的出现…...
PostgreSQL的引号、数据类型转换和数据类型
一、单引号和双引号(重要): 1、在mysql没啥区别 2、在pgsql中,实际字符串用单引号,双引号相当于mysql的,用来包含关键字; -- 单引号,表示user_name的字符串实际值 insert into t_user(user_nam…...
Mad MAD Sum-Codeforces Round 960 (Div. 2)
题目在这里 大意: MAD函数返回出现次数 ≥ 2 \geq2 ≥2的最大整数 b i b_i bi M A D ( a [ 1 , 2 , . . . i ] ) MAD(a[1,2,...i]) MAD(a[1,2,...i]) 每次操作把 a i a_i ai进行上述操作,直到全变为0为止,对每次操作的数组进行求和,记…...
Flutter 插件之 package_info_plus
当使用Flutter开发应用时,通常需要获取应用程序的基本信息,例如包名、版本号和构建号。Flutter提供了一个名为 package_info_plus 的插件,它能方便地帮助我们获取这些信息。 1. 添加依赖 首先,需要在项目的 pubspec.yaml 文件中添加 package_info_plus 的依赖。打开 pubs…...
如何实现布隆过滤器?
1.布隆过滤器的场景 在Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?中我们说到可以使用布隆过滤器避免「缓存穿透」。 你会说我们只要记录了每个用户看过的历史记录,每次推荐的时候去查询数据库过滤存在的数据实现去重。 …...
运维团队如何高效监控容器化环境中的PID及其他关键指标
随着云计算和容器化技术的快速发展,越来越多的企业开始采用容器化技术来部署和管理应用程序。然而,容器化环境的复杂性和动态性给运维团队带来了前所未有的挑战。本文将从PID(进程标识符)监控入手,探讨运维团队如何高效…...
通过vue3 + TypeScript + uniapp + uni-ui 实现下拉刷新和加载更多的功能
效果图: 核心代码: <script lang="ts" setup>import { ref, reactive } from vue;import api from @/request/api.jsimport empty from @/component/empty.vueimport { onLoad,onShow, onPullDownRefresh, onReachBottom } from @dcloudio/uni-applet form …...
Pointnet++改进即插即用系列:全网首发WTConv2d大接受域的小波卷积|即插即用,提升特征提取模块性能
简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入WTConv2d,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤三 1.理…...
4核16G服务器支持多少人?4C16G服务器性能测评
租赁4核16G服务器费用,目前4核16G服务器10M带宽配置70元1个月、210元3个月,那么能如何呢?配置为ECS经济型e实例4核16G、按固定带宽10Mbs、100GB ESSD Entry系统盘。 那么问题来了,4C16G10M带宽的云服务器可以支持多少人同时在线&…...
塔子哥的平均数-美团2023笔试(codefun2000)
题目链接 塔子哥的平均数-美团2023笔试(codefun2000) 题目内容 给定一个正整数数组a1 ,a2 ,…,an,求平均数正好等于k的最长连续子数组的长度 输入描述 输出描述 输出一个整数,表示最长满足题目条件的长度。 样例1 输入 5 2 1 3 2 4 1 输出 3 样例1解释…...
故障诊断 | 基于小波包能量谱对滚动轴承的故障诊断Matlab代码
故障诊断 | 基于小波包能量谱对滚动轴承的故障诊断Matlab代码 目录 故障诊断 | 基于小波包能量谱对滚动轴承的故障诊断Matlab代码效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于小波包能量谱对滚动轴承的故障诊断 matlab代码 数据采用的是凯斯西储大学数据 首先利用…...
E14.【C语言】练习:有关短路运算
#include <stdio.h> int main() {int i 0,a0,b2,c 3,d4;i a && b && d;printf("a %d\nb %d\nc %d\nd %d\n", a, b, c, d);return 0; } 求输出结果 分析: a:先使用后 ,a(见第15篇http://…...
python BeautifulSoup库安装与使用(anaconda、pip)
BeautifulSoup 是一个可以从HTML或XML文件中提取数据的Python库。Beautiful Soup 已成为和 lxml、html5lib 一样出色的Python解释器,为用户灵活地提供不同的解析策略或强劲的速度。 Requests 获取html BeautifulSoup 解析html、xml,BeautifulSoup4库也称bs4库 安装B…...
基于Matlab的数据可视化
基于Matlab的数据可视化 一、二维图形的绘制(一)基本图形函数(1)plot函数(2)fplot函数(3)其他坐标系的二维曲线 (二)图形属性设置(1)线…...
深入理解Linux网络(二):UDP接收内核探究
深入理解Linux网络(二):UDP接收内核探究 一、UDP 协议处理二、recvfrom 系统调⽤实现 一、UDP 协议处理 udp 协议的处理函数是 udp_rcv。 //file: net/ipv4/udp.c int udp_rcv(struct sk_buff *skb) {return __udp4_lib_rcv(skb, &udp_…...
linux内核中list的基本用法
内核链表 1 list_head 结构 为了使用链表机制,驱动程序需要包含<linux/types.h>头文件,该文件定义了如下结构体实现双向链: struct list_head {struct list_head *next, *prev; };2 链表的初始化 2.1 链表宏定义和初始化 可使用以…...
项目中无关痛痒的词句背后深层含义
项目中听上去无关痛痒的词句背后,深层含义有的时候并不友善。 他们说的:进度表有些激进 真正的意思:我们有麻烦了 他们说的:我们将在接下来的几个迭代里面弥补延误 真正的意思:我们还是有麻烦 他们说的࿱…...
DLMS协议中的高级安全(HLS)身份验证
1.四步身份验证协议 在IEC 62056-53中已说明,ACSE提供部分高级身份安全(HLS)验证服务。高级身份安全验证适用于通信通道不能提供内部安全,应采取防范措施以防止偷听和信息(密码)重现的情况。这时ÿ…...
2024“钉耙编程”杭电多校1006 序列立方(思维+前缀和优化dp)
来源 题目 Problem Description 给定长度为 N 的序列 a。 一个序列有很多个子序列,每个子序列在序列中出现了若干次。 小马想请你输出序列 a 每个非空子序列出现次数的立方值的和,答案对 998244353 取模。 你可以通过样例解释来辅助理解题意。 Input 第…...
钡铼分布式I/O系统边缘计算Modbus,MQTT,OPC UA耦合器BL206
BL206系列耦合器是一个数据采集和控制系统,基于强大的32 位微处理器设计,采用Linux操作系统,支持Modbus,MQTT,OPC UA协议,可以快速接入现场PLC、DCS、PAS、MES、Ignition和SCADA以及ERP系统,同时…...
防火墙--双机热备
目录 双击热备作用 防火墙和路由器备份不同之处 如何连线 双机 热备 冷备 VRRP VGMP(华为私有协议) 场景解释 VGMP作用过程 主备的形成场景 接口故障的切换场景 整机故障 原主设备故障恢复的场景 如果没有开启抢占 如果开启了抢占 负载分…...
机器学习 -逻辑回归的似然函数
公式解释 公式如下: L ( θ ) ∏ i 1 m P ( y i ∣ x i ; θ ) ∏ i 1 m ( h θ ( x i ) ) y i ( 1 − h θ ( x i ) ) 1 − y i L(\theta) \prod_{i1}^m P(y_i | x_i; \theta) \prod_{i1}^m (h_\theta(x_i))^{y_i} (1 - h_\theta(x_i))^{1 - y_i} L(θ)i1∏…...
go 实现websocket以及详细设计流程过程,确保通俗易懂
websocket简介: WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层。WebSocket 协议在 2011 年由 IETF 标准化为 RFC 6455,后由 RFC 7936 补充规范。 WebSocket 使得客户端和服务器之间的数…...
记录工作中遇到的关于更新丢失商品超开的一个坑
场景: 工作中使用MybatisPlus以及Oracle进行数据库操作,收到RocketMQ消息开始并发分摊不同清货单的商品的批次,并对商品更新冻结数量。 上线后频繁出现商品超库存开票问题。(还好是内部业务,人工替换批次记账即可&…...
形状之美:WebKit中CSS形状的实现与创新
形状之美:WebKit中CSS形状的实现与创新 在网页设计的世界里,CSS形状(Shapes)是一种革命性的特性,它允许开发者使用几何形状来创建复杂的布局结构。WebKit,作为现代浏览器的核心引擎之一,对CSS形…...
项目管理进阶之RACI矩阵
前言 项目管理进阶系列续新篇。 RACI?这个是什么矩阵,有什么用途? 在项目管理过程中,如Team规模超5以上时,则有必要采用科学的管理方式,满足工作需要。否则可能事倍功半。 Q:什么是RACI矩阵 …...
docker: No space left on device处理与迁移目录
简介:工作中当遇到Docker容器内部的磁盘空间已满。可能的原因包括日志文件过大、临时文件过多或者是Docker容器的存储卷已满,需要我们及时清理相关文件,并对docker的路径进行迁移。 历史攻略: centos:清理磁盘空间 …...
设计模式使用场景实现示例及优缺点(结构型模式——外观模式)
在一个繁忙而复杂的城市中,有一座名为“技术森林”的巨大图书馆。这座图书馆里藏着各种各样的知识宝典,从古老的卷轴到现的电子书籍,无所不包。但是,图书馆之所以得名“技术森林”,是因为它的结构异常复杂,…...
做分销网站系统/国外推广网站有什么
也就是说,拉勾网岗位数据请求的网址是不变的,改变的是表单数据,表单数据随着页数改变,请求方式为POST,这里没办法在Pyspider里用循环遍历来获取每一页的数据。也许是我对Pyspider框架了解的不够,还达不到得…...
网页微博怎么用qq登录/太原百度网站快速优化
JavaScript中本地对象、内置对象和宿主对象 1、本地对象 ECMA-262 把本地对象(native object)定义为“独立于宿主环境的 ECMAScript 实现提供的对象”。 再来看一下,“本地对象”包含哪些内容: Object、Function、Array、String、…...
专做程序员招聘的网站/软文平台发布
1.第一个Bug的故事 有人把软件产品和药品并称为世界上两种无法根除自身的缺陷却被允许公开合法销售的产品! 随着软件各种Bug的增多,严重的质量事故也随之增多,人们对抗Bug的态度日益强硬。 1945年9月某天,Hopper 正在埋头工作在一…...
网站建设及优化教程/搜狗站长管理平台
1、什么是函数: 函数是一个功能、是一个行为、是一个动作。 函数就是一个具有名称的功能代码的集合。 一段用来解决某一个问题的代码或者指令,具有名称的集合。 2、 为什么使用函数: 减少代码重复!!! 所…...
公司做网站都咨询哪些问题/百度游戏中心
因为如果函数是一个新概念,那么我之前的评论可能有点难以理解。在我个人认为解决这个问题最好的方法是将相关代码包装在一个对象中。在Python在很大程度上基于对象的概念,可以将对象看作是使用对数据进行操作的函数对数据进行分组。一个对象可以表示一个…...
哪个网站做海南二手房/渠道网络
【链接】:CF982C 【题意】:有一颗树,你需要切掉一些边,使这颗树分拆成若干个节点为偶数的联通分量,最多能切掉几条边。若不能切,输出-1。 【分析】: 1.若点数n为奇数,因为奇数不可能分为偶数&am…...