Java面试——设计模式(一)

设计模式

1、设计模式简介

设计模式是人们经过长期编程经验总结出来的一种编程思想。随着软件工程的不断演进,针对不同的需求,新的设计模式不断被提出(比如大数据领域中这些年不断被大家认可的数据分片思想)​,但设计模式的原则不会变。基于设计模式的原则,我们可以使用已有的设计模式,也可以根据产品或项目的开发需求在现有的设计模式基础上组合、改造或重新设计自身的设计模式。

设计模式有7个原则:单一职责原则、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、合成/聚合复用原则、迪米特法则,接下来对这些原则一一进行讲解。

1.1、单一职责原则

单一职责原则又称单一功能原则,它规定一个类只有一个职责。如果有多个职责(功能)被设计在一个类中,这个类就违反了单一职责原则。

1.2、开闭原则

开闭原则规定软件中的对象(类、模块、函数等)对扩展开放,对修改封闭,这意味着一个实体允许在不改变其源代码的前提下改变其行为,该特性在产品化的环境下是特别有价值的,在这种环境下,改变源代码需要经过代码审查、单元测试等过程,以确保产品的使用质量。遵循这个原则的代码在扩展时并不发生改变,因此不需要经历上述过程。

1.3、里氏代换原则

里氏代换原则是对开闭原则的补充,规定了在任意父类可以出现的地方,子类都一定可以出现。实现开闭原则的关键就是抽象化,父类与子类的继承关系就是抽象化的具体表现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

1.4、依赖倒转原则

依赖倒转原则指程序要依赖于抽象(Java中的抽象类和接口)​,而不依赖于具体的实现(Java中的实现类)​。简单地说,就是要求对抽象进行编程,不要求对实现进行编程,这就降低了用户与实现模块之间的耦合度。

1.5、接口隔离原则

接口隔离原则指通过将不同的功能定义在不同的接口中来实现接口的隔离,这样就避免了其他类在依赖该接口(接口上定义的功能)时依赖其不需要的接口,可减少接口之间依赖的冗余性和复杂性。

1.6、合成/聚合复用原则

合成/聚合复用原则指通过在一个新的对象中引入(注入)已有的对象以达到类的功能复用和扩展的目的。它的设计原则是要尽量使用合成或聚合而不要使用继承来扩展类的功能。

1.7、迪米特法则

迪米特法则指一个对象尽可能少地与其他对象发生相互作用,即一个对象对其他对象应该有尽可能少的了解或依赖。其核心思想在于降低模块之间的耦合度,提高模块的内聚性。迪米特法则规定每个模块对其他模块都要有尽可能少的了解和依赖,因此很容易使系统模块之间功能独立,这使得各个模块的独立运行变得更简单,同时使得各个模块之间的组合变得更容易。

设计模式按照其功能和使用场景可以分为三大类:创建型模式(Creational Pattern)​、结构型模式(StructuralPattern)和行为型模式(Behavioral Pattern)​,如表所示。

2、工厂模式的概念及Java实现

工厂模式(Factory Pattern)是最常见的设计模式,该模式设属于创建型模式,它提供了一种简单、快速、高效而安全地创建对象的方式。工厂模式在接口中定义了创建对象的方法,而将具体的创建对象的过程在子类中实现,用户只需通过接口创建需要的对象即可,不用关注对象的具体创建过程。同时,不同的子类可根据需求灵活实现创建对象的不同方法。

通俗地讲,工厂模式的本质就是用工厂方法代替new操作创建一种实例化对象的方式,以提供一种方便地创建有同种类型接口的产品的复杂对象。

如下代码通过new关键字实例化类Class的一个实例class,但如果Class类在实例化时需要一些初始化参数,而这些参数需要其他类的信息,则直接通过new关键字实例化对象会增加代码的耦合度,不利于维护,因此需要通过工厂模式将创建实例和使用实例分开。将创建实例化对象的过程封装到工厂方法中,我们在使用时直接通过调用工厂来获取,不需要关心具体的负载实现过程:

java 复制代码
Class class = new Class()

以创建手机为例,假设手机的品牌有华为和苹果两种类型,我们要实现的是根据不同的传入参数实例化不同的手机,则其具体的UML设计如图所示。

其具体实现如下。

(1)定义接口:

java 复制代码
public  interface   Phone  {
      String  brand();
}

以上代码定义了一个Phone接口,并在接口中定义了brand(),用来返回手机的品牌。

(2)定义实现类:

java 复制代码
public  class  Iphone  implements  Phone  {
    @Override
    public  String  brand()  {
      return  "this  is  a  Apple  phone";
    }
}
public  class  HuaWei  implements  Phone  {
    @Override
    public  String  brand()  {
      return  "this  is  a  huawei  phone";
    }
}

以上代码定义了两个Phone的实现类Iphone和HuaWei来表示两个品牌的手机,两个品牌的手机通过实现brand()打印自己的商标。

(3)定义工厂类:

java 复制代码
public  class  Factory  {
  public  Phone  createPhone(String  phoneName){
      if  ("HuaWei".equals(phoneName)){
          return  new  HuaWei();
      }else  if("Apple".equals(phoneName)){
          return  new  Iphone();
      }else{
          return   null;
      }
  };
}

以上代码定义了名为Factory的工厂类,工厂类有一个方法createPhone(),用来根据不同的参数实例化不同品牌的手机类并返回。在createPhone()的参数为"HuaWei"时,工厂类为我们实例化一个HuaWei类的实例并返回;在createPhone()的参数为"Apple"时,工厂类为我们实例化一个Iphone类的实例并返回。这样便实现了工厂类根据不同的参数创建不同的实例,对调用者来说屏蔽了实例化的细节。

(4)使用工厂模式:

java 复制代码
public  static  void  main(String[]  args)  {
      Factory  factory  =  new  Factory();
      Phone  huawei  =   factory.createPhone("HuaWei");
      Phone  iphone  =   factory.createPhone("Apple");
      logger.info(huawei.brand());
      logger.info(iphone.brand());
    }

以上代码定义了一个Factory的实例,并调用createPhone()根据不同的参数创建了名为huawei的实例和名为iphone的实例,并分别调用其brand()打印不同的品牌信息,运行结果如下:

java 复制代码
[INFO]  FactoryDemo  -  this  is  a  huawei  phone
[INFO]  FactoryDemo  -  this  is  a  Apple  phone

3、抽象工厂模式的概念及Java实现

抽象工厂模式(Abstract Factory Pattern)在工厂模式上添加了一个创建不同工厂的抽象接口(抽象类或接口实现)​,该接口可叫作超级工厂。在使用过程中,我们首先通过抽象接口创建出不同的工厂对象,然后根据不同的工厂对象创建不同的对象。

我们可以将工厂模式理解为针对一个产品维度进行分类,比如上述工厂模式下的苹果手机和华为手机;而抽象工厂模式针对的是多个产品维度分类,比如苹果公司既制造苹果手机也制造苹果笔记本电脑,同样,华为公司既制造华为手机也制造华为笔记本电脑。

在同一个厂商有多个维度的产品时,如果使用工厂模式,则势必会存在多个独立的工厂,这样的话,设计和物理世在同一个厂商有多个维度的产品时,如果使用工厂模式,则势必会存在多个独立的工厂,这样的话,设计和物理世

工厂模式定义了工厂方法来实现不同厂商手机的制造。可是问题来了,我们知道苹果公司和华为公司不仅制造手机,还制造电脑。如果使用工厂模式,就需要实现两个工厂类,并且这两个工厂类没有多大关系,这样的设计显然不够优雅,那么如何实现呢?使用抽象工厂就能很好地解决上述问题。我们定义一个抽象工厂,在抽象工厂中定义好要生产的产品(手机或者电脑)​,然后在抽象工厂的实现类中根据不同类型的产品和产品规格生产不同的产品返回给用户。UML的设计如图所示。

UML的具体实现如下。

(1)第1类产品的手机接口及实现类的定义如下:

java 复制代码
public  interface  Phone  {
    String  call();
}
public  class  PhoneApple  implements  Phone  {
    @Override
    public  String  call()  {
      return  "call  somebody  by  apple  phone";
    }
}
public  class  PhoneHwaiWei  implements  Phone  {
              @Override
    public  String  call()  {
        return  "call  somebody  by  huaiwei  phone";
    }
}

以上代码定义了Phone的接口及其实现类PhoneApple和PhoneHwaiWei。在该接口中定义了一个打电话的方法call(),实现类根据其品牌打印相关信息。

(2)第1类产品的手机工厂类的定义如下:

java 复制代码
public  class  PhoneFactory  extends  AbstractFactory{
    @Override
    public  Phone  createPhone(String  brand)  {
      if  ("HuaWei".equals(brand)){
          return  new  PhoneHwaiWei();
      }else  if("Apple".equals(brand)){
          return  new  PhoneApple();
      }else{
          return   null;
      }
    }
    @Override
    public  Computer  createComputer(String  brand){
          return   null;
    }
}

以上代码定义了PhoneFactory的手机工厂类,该类继承了AbstractFactory并实现了方法createPhone(),createPhone()根据不同的参数实例化不同品牌的手机类并返回。在createPhone()的参数为"HuaWei"时,工厂类为我们实例化一个PhoneHwaiWei类的实例并返回;在createPhone()的参数为"Apple"时,工厂类为我们实例化一个PhoneApple类的实例并返回,这样便满足了工厂根据不同参数生产不同产品的需求。

(3)第2类产品的电脑接口及实现类的定义如下:

java 复制代码
public  interface  Computer  {
    String  internet();
}
public  class  ComputerApple  implements  Computer  {
    @Override
    public  String  internet()  {
      return  "surf  the  internet  by  apple  computer";
  }
}
public  class  ComputerHwaiWei  implements  Computer  {
  @Override
  public  String  internet()  {
      return  "  surf  the  internet  by  huaiwei  computer";
  }
}

以上代码定义了Computer的电脑接口及其实现类ComputerApple和ComputerHwaiWei。在该接口中定义了一个上网的方法internet(),实现类根据其品牌打印相关信息。

(4)第2类产品的电脑工厂类的定义如下:

java 复制代码
public  class  ComputerFactory  extends  AbstractFactory{
    @Override
    public  Phone  createPhone(String  brand)  {
          return   null;
    }
    @Override
    public  Computer  createComputer(String  brand){
      if  ("HuaWei".equals(brand)){
          return  new  ComputerHwaiWei();
      }else  if("Apple".equals(brand)){
          return  new  ComputerApple();
      }else{
          return   null;
      }
    }
}

以上代码定义了ComputerFactory的电脑工厂类,该类继承了AbstractFactory并实现了方法createComputer(),createComputer()根据不同的参数实例化不同品牌的电脑类并返回。在createComputer()的参数为"HuaWei"时,工厂类为我们实例化一个ComputerHwaiWei类的实例并返回;在createComputer()的参数为"Apple"时,工厂类为我们实例化一个ComputerApple类的实例并返回,这样便实现了工厂根据不同参数生产不同产品的需求。

(5)抽象工厂的定义如下:

java 复制代码
public   abstract  class  AbstractFactory  {
    public  abstract  Phone  createPhone(String  brand);
    public  abstract  Computer  createComputer(String  brand);
}

以上代码定义了抽象类AbstractFactory,这个类便是抽象工厂的核心类,它定义了两个方法createPhone()和createComputer(),用户在需要手机时调用其createPhone()构造一个手机(华为或者苹果品牌)即可,用户在需要电脑时调用其createComputer()构造一个电脑(华为或者苹果品牌)即可。

(6)使用抽象工厂:

java 复制代码
AbstractFactory  phoneFactory  =  new  PhoneFactory();
 Phone  phoneHuawei  =    phoneFactory.createPhone("HuaWei");
 Phone  phoneApple  =  phoneFactory.createPhone("Apple");
 logger.info(phoneHuawei.call());
 logger.info(phoneApple.call());
 AbstractFactory  computerFactory  =  new  ComputerFactory();
 Computer  computerHuawei  =    computerFactory.createComputer("HuaWei");
 Computer  computerApple  =  computerFactory.createComputer("Apple");
 logger.info(computerApple.internet());
 logger.info(computerApple.internet());

以上代码使用了我们定义好的抽象工厂,在需要生产产品时,首先需要定义一个抽象的工厂类AbstractFactory,然后使用抽象的工厂类生产不同的工厂类,最终根据不同的工厂生产不同的产品。运行结果如下:

java 复制代码
[INFO]  AbstractFactoryDemo  -  call  somebody  by  huaiwei  phone
[INFO]  AbstractFactoryDemo  -  call  somebody  by  apple  phone
[INFO]  AbstractFactoryDemo  -  surf  the  internet  by  apple  computer
[INFO]  AbstractFactoryDemo  -  surf  the  internet  by  apple  computer

4、单例模式的概念及Java实现

单例模式是保证系统实例唯一性的重要手段。单例模式首先通过将类的实例化方法私有化来防止程序通过其他方式创建该类的实例,然后通过提供一个全局唯一获取该类实例的方法帮助用户获取类的实例,用户只需也只能通过调用该方法获取类的实例。

单例模式的设计保证了一个类在整个系统中同一时刻只有一个实例存在,主要被用于一个全局类的对象在多个地方被使用并且对象的状态是全局变化的场景下。同时,单例模式为系统资源的优化提供了很好的思路,频繁创建和销毁对象都会增加系统的资源消耗,而单例模式保障了整个系统只有一个对象能被使用,很好地节约了资源。

单例模式的实现很简单,每次在获取对象前都先判断系统是否已经有这个单例对象,有则返回,没有则创建。需要注意的是,单例模型的类构造函数是私有的,只能由自身创建和销毁对象,不允许除了该类的其他程序使用new关键字创建对象及破坏单例模式。

单例模式的常见写法有懒汉模式(线程安全)​、饿汉模式、静态内部类、双重校验锁,下面一一解释这些写法。

4.1、懒汉模式(线程安全)

懒汉模式很简单:定义一个私有的静态对象instance,之所以定义instance为静态,是因为静态属性或方法是属于类的,能够很好地保障单例对象的唯一性;然后定义一个加锁的静态方法获取该对象,如果该对象为null,则定义一个对象实例并将其赋值给instance,这样下次再获取该对象时便能够直接获取了。

懒汉模式在获取对象实例时做了加锁操作,因此是线程安全的,代码如下:

java 复制代码
public  class  LazySingleton  {
    private  static  LazySingleton  instance;
    private  LazySingleton(){}
    public  static  synchronized  LazySingleton  getInstance()  {
      if  (instance  ==  null)  {
          instance  =  new  LazySingleton();
      }
      return  instance;
    }
}

4.2、饿汉模式

饿汉模式指在类中直接定义全局的静态对象的实例并初始化,然后提供一个方法获取该实例对象。懒汉模式和饿汉模式的最大不同在于,懒汉模式在类中定义了单例但是并未实例化,实例化的过程是在获取单例对象的方法中实现的,也就是说,在第一次调用懒汉模式时,该对象一定为空,然后去实例化对象并赋值,这样下次就能直接获取对象了;而饿汉模式是在定义单例对象的同时将其实例化的,直接使用便可。也就是说,在饿汉模式下,在ClassLoader完成后该类的实例便已经存在于JVM中了,代码如下:

java 复制代码
public  class  HungrySingleton  {
    private  static  HungrySingleton  instance  =  new  HungrySingleton();
    private  HungrySingleton(){}
    public  static  HungrySingleton  getInstance()  {
      return  instance;
    }
}

4.3、静态内部类

静态内部类通过在类中定义一个静态内部类,将对象实例的定义和初始化放在内部类中完成,我们在获取对象时要通过静态内部类调用其单例对象。之所以这样设计,是因为类的静态内部类在JVM中是唯一的,这很好地保障了单例对象的唯一性,代码如下:

java 复制代码
public  class  Singleton  {
    private  static  class  SingletonHolder  {
      private  static  final  Singleton  INSTANCE  =  new  Singleton();
    }
    private  Singleton()  {
    }
    public  static  final  Singleton  getInstance()  {
      return  SingletonHolder.INSTANCE;
    }
}

4.4、双重校验锁

双锁模式指在懒汉模式的基础上做进一步优化,给静态对象的定义加上volatile锁来保障初始化时对象的唯一性,在获取对象时通过synchronized (Singleton.class)给单例类加锁来保障操作的唯一性。代码如下:

java 复制代码
public  class  Lock2Singleton  {
    private volatile static Lock2Singleton singleton; //1:对象锁
    private  Lock2Singleton(){}
    public  static  Lock2Singleton  getSingleton()  {
      if  (singleton  ==  null)  {
          synchronized (Singleton.class) {//2:synchronized方法锁
              if  (singleton  ==  null)  {
                  singleton  =  new  Lock2Singleton();
              }
            }
        }
        return  singleton;
    }
}

5、建造者模式的概念及Java实现

建造者模式(Builder Pattern)使用多个简单的对象创建一个复杂的对象,用于将一个复杂的构建与其表示分离,使得同样的构建过程可以创建不同的表示,然后通过一个Builder类(该Builder类是独立于其他对象的)创建最终的对象。

建造者模式主要用于解决软件系统中复杂对象的创建问题,比如有些复杂对象的创建需要通过各部分的子对象用一定的算法构成,在需求变化时这些复杂对象将面临很大的改变,这十分不利于系统的稳定。但是,使用建造者模式能将它们各部分的算法包装起来,在需求变化后只需调整各个算法的组合方式和顺序,能极大提高系统的稳定性。建造者模式常被用于一些基本部件不会变而其组合经常变化的应用场景下。

注意,建造者模式与工厂模式的最大区别是,建造者模式更关注产品的组合方式和装配顺序,而工厂模式关注产品的生产本身。

建造者模式在设计时有以下几种角色。

  • Builder:创建一个复杂产品对象的抽象接口。
  • ConcreteBuilder:Builder接口的实现类,用于定义复杂产品各个部件的装配流程。
  • Director:构造一个使用Builder接口的对象。
  • Product:表示被构造的复杂对象。ConcreteBuilder定义了该复杂对象的装配流程,而Product定义了该复杂对象的结构和内部表示。

以生产一个电脑为例,电脑的生产包括CPU、Memory、Disk等生产过程,这些生产过程对顺序不敏感,这里的Product角色就是电脑。我们还需要定义生产电脑的Builder、ConcreteBuilder和Director。UML的设计如图所示。

具体实现如下。

(1)定义需要生产的产品Computer:

java 复制代码
public  class  Computer  {
    private  String  cpu;
    private  String  memory;
    private  String  disk;
    //省去getter setter方法
} 

以上代码定义了一个Computer类来描述我们要生产的产品,具体的一个Computer包括CPU、内存(memory)和磁盘(disk)​,当然,还包括显示器、键鼠等,这里作为demo,为简单起见就不一一列举了。

(2)定义抽象接口ComputerBuilder来描述产品构造和装配的过程:

java 复制代码
public  interface  ComputerBuilder  {
    void  buildcpu();
    void  buildemory();
    void  buildDisk();
    Computer  buildComputer();
}

以上代码定义了ComputerBuilder接口来描述电脑的组装过程,具体包括组装CPU的方法buildcpu()、组装内存的方法buildemory()和组装磁盘的方法buildDisk(),等这些都生产和组装完成后,就可以调用buildComputer()组装一台完整的电脑了。

(3)定义ComputerBuilder接口实现类ComputerConcreteBuilder以实现构造和装配该产品的各个组件:

java 复制代码
public  class  ComputerConcreteBuilder  implements  ComputerBuilder  {
    Computer  computer;
    private  final  static  Log  logger  =
                          LogFactory.getLog(ComputerConcreteBuilder.class);
    public  ComputerConcreteBuilder()  {
      computer  =new   Computer();
    }
    @Override
    public  void  buildcpu()  {
      logger.info("buildcpu......");
      computer.setCpu("8core");
    }
    @Override
    public  void  buildemory()  {
      logger.info("buildemory......");
      computer.setMemory("16G");
    }
    @Override
    public  void  buildDisk()  {
      logger.info("buildDisk......");
      computer.setDisk("1TG");
    }
    @Override
    public  Computer  buildComputer()  {
      return  computer;
    }
}

以上代码定义了ComputerConcreteBuilder来完成具体电脑的组装,其中Computer的实例在构造函数中进行了定义。

(4)定义ComputerDirector使用Builder接口实现产品的装配:

java 复制代码
public  class  ComputerDirector  {
    public  Computer  constructComputer(ComputerBuilder  computerBuilder)  {
      computerBuilder.buildemory();
      computerBuilder.buildcpu();
      computerBuilder.buildDisk();
      return  computerBuilder.buildComputer();
    }
}

以上代码定义了ComputerDirector来调用ComputerBuilder接口实现电脑的组装,具体组装顺序为buildemory、buildpu、buildDisk和buildComputer。该类是建造者模式对产品生产过程的封装,在需求发生变化且需要先装配完磁盘再装配CPU时,只需调整Director的执行顺序即可,每个组件的装配都稳定不变。

(5)构建Computer:

java 复制代码
public  static  void  main(String[]  args)  {
    ComputerDirector  computerDirector  =  new  ComputerDirector();
    ComputerBuilder   computerConcreteBuilder  =  new  ComputerConcreteBuilder();
    Computer  computer  =
                  computerDirector.constructComputer(computerConcreteBuilder);
      logger.info(computer.getCpu());
      logger.info(computer.getDisk());
      logger.info(computer.getMemory());
    }

以上代码首先定义了一个ComputerDirector和ComputerBuilder,为构建Computer做好准备,然后通过调用ComputerDirector的constructComputer()实现产品Computer的构建,运行结果如下:

java 复制代码
[INFO]  ComputerConcreteBuilder  -  buildemory......
[INFO]  ComputerConcreteBuilder  -  buildpu......
[INFO]  ComputerConcreteBuilder  -  buildDisk......
[INFO]  BuilderDemo  -  8core
[INFO]  BuilderDemo  -  1TG
[INFO]  BuilderDemo  -  16G

6、原型模式的概念及Java实现

原型模式指通过调用原型实例的Clone方法或其他手段来创建对象。

原型模式属于创建型设计模式,它以当前对象为原型(蓝本)来创建另一个新的对象,而无须知道创建的细节。原型模式在Java中通常使用Clone技术实现,在JavaScript中通常使用对象的原型属性实现。

原型模式的Java实现很简单,只需原型类实现Cloneable接口并覆写clone方法即可。Java中的复制分为浅复制和深复制。

  • 浅复制:Java中的浅复制是通过实现Cloneable接口并覆写其Clone方法实现的。在浅复制的过程中,对象的基本数据类型的变量值会重新被复制和创建,而引用数据类型仍指向原对象的引用。也就是说,浅复制不复制对象的引用类型数据。
  • 深复制:在深复制的过程中,不论是基本数据类型还是引用数据类型,都会被重新复制和创建。简而言之,深复制彻底复制了对象的数据(包括基本数据类型和引用数据类型),浅复制的复制却并不彻底(忽略了引用数据类型)。

(1)浅复制的代码实现如下:

java 复制代码
public  class  Computer  implements  Cloneable  {
    private  String  cpu;
    private  String  memory;
    private  String  disk;
    public  Computer(String  cpu,  String  memory,  String  disk)  {
      this.cpu  =  cpu;
      this.memory  =  memory;
      this.disk  =  disk;
    }
    public Object clone() {//浅复制
      try  {
          return  (Computer)super.clone();
      }  catch  (Exception  e)  {
          e.printStackTrace();
          return  null;
      }
    }
}

以上代码定义了Computer类,要使该类支持浅复制,只需实现Cloneable接口并覆写clone()即可。

(2)深复制的代码实现如下:

java 复制代码
public  class  ComputerDetail  implements  Cloneable  {
    private  String  cpu;
    private  String  memory;
    private  Disk  disk;
    public  ComputerDetail(String  cpu,  String  memory,  Disk  disk)  {
      this.cpu  =  cpu;
      this.memory  =  memory;
      this.disk  =  disk;
  }
  public Object clone() {//深复制
      try  {
          ComputerDetail  computerDetail  =   (ComputerDetail)super.clone();
          computerDetail.disk  =  (Disk)  this.disk.clone();
          return  computerDetail;
      }  catch  (Exception  e)  {
          e.printStackTrace();
          return  null;
      }
  }
}
//应用对象深复制
public  class  Disk  implements  Cloneable  {
  private  String  ssd;
  private  String  hhd;
  public  Disk(String  ssd,  String  hhd)  {
      this.ssd  =  ssd;
      this.hhd  =  hhd;
  }
  public  Object  clone()  {
      try  {
        return    (Disk)super.clone();
      }  catch  (Exception  e)  {
          e.printStackTrace();
          return  null;
      }
  }
}

以上代码定义了ComputerDetail和Disk两个类,其中ComputerDetail的disk属性是一个引用对象,要实现这种对象的复制,就要使用深复制技术,具体操作是引用对象类需要实现Cloneable接口并覆写clone(),然后在复杂对象中声明式地将引用对象复制出来赋值给引用对象的属性,具体代码如下:

java 复制代码
computerDetail.disk = (Disk) this.disk.clone()。

(3)使用原型模型:

java 复制代码
public static void main(String[] args) {
//浅复制
Computer  computer  =  new  Computer("8core", "16G", "1TB");
logger.info("before  simple  clone:"+computer.toString());
Computer  computerClone  =  (Computer)computer.clone();
logger.info("after  simple  clone:"+computerClone.toString());
//深复制
Disk  disk  =  new  Disk("208G", "2TB");
ComputerDetail  computerDetail  =  new
                ComputerDetail("12core", "64G", disk);
logger.info("before  deep  clone:"+computerDetail.toString());
ComputerDetail  computerDetailClone  =
                (ComputerDetail)computerDetail.clone();
logger.info("after  deep  clone:"+computerDetailClone.toString());
}

以上代码先定义了一个简单对象computer,并利用浅复制技术复制出一个新的对象computerClone,然后定义了复制对象computerDetail,并使用深复制技术复制出一个新的对象computerDetailClone,最后分别打印出复制前和复制后的对象。注意,这里调用的toString()鉴于篇幅原因省去了,需要读者补充。运行结果如下:

java 复制代码
before  simple  clone:Computer{cpu='8core',  memory='16G',  disk='1TB'}
after  simple  clone:Computer{cpu='8core',  memory='16G',  disk='1TB'}
before  deep  clone:ComputerDetail{cpu='12core',  memory='64G', disk={ssd='208G',  hhd='2TB'}}
after  deep  clone:ComputerDetail{cpu='12core',  memory='64G',  disk={ssd='208G', hhd='2TB'}}
相关推荐
半亩码田1 小时前
C#转Python第3.6篇:Python 的 @property 比 C# 的 get/set 更灵活
java·python·c#
老郑聊AI业财智造1 小时前
Spring AI 技术架构与源码分析
java·人工智能·后端·spring·架构·软件工程
省长1 小时前
别人绕过我的网关直接调用资源服务怎么办?使用 Sa-Token 解决:网关转发鉴权、RPC调用鉴权
java·后端·开源
MetaLite1 小时前
SpringBoot异常处理-到底该转换还是继续抛-入口层与调用层不能一刀切
java·spring boot·后端
赵广陆2 小时前
Spring AI的聊天模型
java·人工智能·spring
IT 小阿姨(数据库)2 小时前
K8s v1.24.17 完整搭建文档(CentOS7 + containerd1.6.33 + Calico)
java·容器·kubernetes
lhldsg3 小时前
全民健身解决方案:从共享球场到智能运营的技术实践
java·大数据·开发语言·需求分析
vipxieliang3 小时前
ValidX时间注解完全指南:10种时间验证注解详解
java·后端
雨辰AI3 小时前
openGauss 生产运维避坑指南|适配信创项目改造核心难点
java·运维·后端