为了账号安全,请及时绑定邮箱和手机立即绑定

创建型设计模式

标签:
Java


创建型设计模式成员

工厂方法模式

抽象工厂模式

建造者模式

原型模式

单例模式

1. 简单工厂模式

1.1 定义

简单工厂模式(Simple Factory Pattern):它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类

1.2 角色组成

Factory:工厂角色

Product:抽象产品角色

ConcreteProduct:具体产品角色

1.2.1 工厂角色Factory

SimpleFactory提供getMilk(String name)方法, 只需要告诉简单工厂牛奶品牌名称, 就能获得对应的牛奶

package com.zhunongyun.spring.springpattern.factory.simple;

import com.zhunongyun.spring.springpattern.factory.Mengniu;

import com.zhunongyun.spring.springpattern.factory.Milk;

import com.zhunongyun.spring.springpattern.factory.Telunsu;

import com.zhunongyun.spring.springpattern.factory.Yili;

public class SimpleFactory {

public Milk getMilk(String name){

    Milk milk = null;

    switch (name) {

        case "特仑苏":

            milk = new Telunsu();

            break;

        case "伊利":

            milk = new Yili();

            break;

        case "蒙牛":

            milk = new Mengniu();

            break;

        default:

            System.out.println("不能生产您所需的产品");

            break;

    }

    return milk;

}

}

### 1.2.2 抽象产品角色Product

> 抽象产品类Milk, 定义产品的属性, 具体产品都需要实现其属性

package com.zhunongyun.spring.springpattern.factory;

public interface Milk {

/**

获取一个标准产品

@return

*/

public String getName();

}

1.2.3 具体产品角色ConcreteProduct

具体产品类SanLu, YiLi, Telunsu是具体的产品, 需要实现抽象产品类Milk

YiLi:

package com.zhunongyun.spring.springpattern.factory;

public class Yili implements Milk {br/>@Override

public String getName() {

return "伊利";

}

}

Telunsu:

package com.zhunongyun.spring.springpattern.factory;

public class Telunsu implements Milk {br/>@Override

public String getName() {

return "特仑苏";

}

}

SanLu:

package com.zhunongyun.spring.springpattern.factory;

public class Sanlu implements Milk{br/>@Override

public String getName() {

return "三鹿";

}

}

 

### 1.2.4 测试

package com.zhunongyun.spring.springpattern.factory.simple;

public class SimpleFactoryTest {

public static void main(String[] args) {

    SimpleFactory factory = new SimpleFactory();

    //把用户的需求告诉工厂,需要用户提供具体产品的名称等信息,用户把产品创建交给工厂处理

    System.out.println(factory.getMilk("AAA"));

    System.out.println(factory.getMilk("蒙牛"));

}

}

输出:

不能生产您所需的产品

null

com.zhunongyun.spring.springpattern.factory.Mengniu@129a8472

## 1.3 总结

由工厂, 抽象产品, 具体产品三个角色组成,用户需要向工厂提供产品名称信息,创建其中的一款产品, 新增产品需要增加新的具体产品,工厂也需要修改  

# 2. 工厂方法模式

## 2.1 定义

工厂模式方法(factory method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类  

## 2.2 角色组成

* 抽象工厂

* 具体工厂

* 抽象产品

* 具体产品

### 2.2.1 抽象工厂

package com.zhunongyun.spring.springpattern.factory.func;

import com.zhunongyun.spring.springpattern.factory.Milk;

public interface Factory {

//工厂必然具有生产产品技能,统一的产品出口

Milk getMilk();

}

### 2.2.2 具体工厂

MengniuFactory:

package com.zhunongyun.spring.springpattern.factory.func;

import com.zhunongyun.spring.springpattern.factory.Mengniu;

import com.zhunongyun.spring.springpattern.factory.Milk;

public class MengniuFactory implements Factory {br/>@Override

public Milk getMilk() {

return new Mengniu();

}

}

SanluFactory:

package com.zhunongyun.spring.springpattern.factory.func;

import com.zhunongyun.spring.springpattern.factory.Milk;

import com.zhunongyun.spring.springpattern.factory.Sanlu;

public class SanluFactory implements Factory {br/>@Override

public Milk getMilk() {

return new Sanlu();

}

}

TelunsuFactory:

package com.zhunongyun.spring.springpattern.factory.func;

import com.zhunongyun.spring.springpattern.factory.Milk;

import com.zhunongyun.spring.springpattern.factory.Telunsu;

public class TelunsuFactory implements Factory {

@Override

public Milk getMilk() {

    return new Telunsu();

}

}

### 2.2.3 抽象产品

package com.zhunongyun.spring.springpattern.factory;

public interface Milk {

/**

获取一个标准产品

@return

*/

public String getName();

}

2.2.4 具体产品

YiLi:

package com.zhunongyun.spring.springpattern.factory;

public class Yili implements Milk {

    @Override

    public String getName() {

        return "伊利";

    }

}

Telunsu:

package com.zhunongyun.spring.springpattern.factory;

public class Telunsu implements Milk {

    @Override

    public String getName() {

        return "特仑苏";

    }

}

SanLu:

package com.zhunongyun.spring.springpattern.factory;

public class Sanlu implements  Milk{

    @Override

    public String getName() {

        return "三鹿";

    }

}

2.2.5 测试

package com.zhunongyun.spring.springpattern.factory.func;

public class FactoryTest {

    public static void main(String[] args) {

        Factory factory = new SanluFactory();

        System.out.println(factory.getMilk());

    }

}

输出:

com.zhunongyun.spring.springpattern.factory.Sanlu@1b0375b3

2.3 总结

在工厂方法模式下,如果要增加产品,只需要扩展对应的具体工厂(ConcreteCreator)和具体产品(ConcreteProduct)即可,原有源码无需改变,遵循了“开闭原则” 

用户需要某款产品只需要实例化对应的产品,此时没有创建产品,只要当需要使用时才会创建产品,用户在使用时都是以方法的事情操作产品

3. 抽象工厂模式

3.1 定义

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式

3.2 角色组成

抽象工厂

具体工厂

抽象产品

具体产品

3.2.1 抽象工厂

package com.zhunongyun.spring.springpattern.factory.abstr;

import com.zhunongyun.spring.springpattern.factory.Milk;

public abstract class AbstractFactory {

    //公共的逻辑

    //方便于统一管理

    /**

     * 获得一个蒙牛品牌的牛奶

     * @return

     */

    public  abstract Milk getMengniu();

    /**

     * 获得一个伊利品牌的牛奶

     * @return

     */

    public abstract  Milk getYili();

    /**

     * 获得一个特仑苏品牌的牛奶

     * @return

     */

    public  abstract  Milk getTelunsu();

    public abstract Milk getSanlu();

}

3.2.2 具体工厂

package com.zhunongyun.spring.springpattern.factory.abstr;

import com.zhunongyun.spring.springpattern.factory.*;

public class MilkFactory extends  AbstractFactory {

    @Override

    public Milk getMengniu() {

        return new Mengniu();

    }

    @Override

    public Milk getYili() {

        return new Yili();

    }

    @Override

    public Milk getTelunsu() {

        return new Telunsu();

    }

    @Override

    public Milk getSanlu() {

        return new Sanlu();

    }

}

3.3.3 测试

package com.zhunongyun.spring.springpattern.factory.abstr;

public class AbstractFactoryTest {

    public static void main(String[] args) {

        MilkFactory factory = new MilkFactory();

        //对于用户而言,更加简单了

        //用户只有选择的权利了,保证了程序的健壮性

        System.out.println(factory.getSanlu());

    }

}

输出:

com.zhunongyun.spring.springpattern.factory.Sanlu@1b0375b3

3.4 总结

抽象工厂模式,定义了一个抽象工厂,抽象工厂中定义了创建对象的接口,由实现类实现对象创建,用户在使用时,只需要实例化抽象工厂,就能通过接口获取对象

4. 单例模式

4.1 定义

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。保证一个类仅有一个实例,并提供一个访问它的全局访问点

4.2 单例模式的几种实现方式

4.2.1 懒汉式

这种方式是lazy初始化,第一次调用才初始化,避免内存浪费, 多线程时为了保证线程安全需要加锁

线程不安全:

package com.zhunongyun.spring.springpattern.singleton;

public class Lazy {

    private static Lazy instance;

    private Lazy (){}

    public static Lazy getInstance() {

        if (instance == null) {

            instance = new Lazy();

        }

        return instance;

    }

}

线程安全:

package com.zhunongyun.spring.springpattern.singleton;

public class Lazy {

    private static Lazy instance;

    private Lazy (){}

    public static synchronized Lazy getInstance() {

        if (instance == null) {

            instance = new Lazy();

        }

        return instance;

    }

}

4.2.2 饿汉式

这种方式,类加载时就初始化,浪费内存,基于classloader机制避免了多线程的同步问题, 多线程下是安全的

package com.zhunongyun.spring.springpattern.singleton;

public class Hungry {

    private static Hungry instance = new Hungry();

    private Hungry(){}

    public static Hungry getInstance() {

        return instance;

    }  

}

4.2.3 双重校验锁

这种方式采用双锁机制,安全且在多线程情况下能保持高性能

package com.zhunongyun.spring.springpattern.singleton;

public class DoubleCheckedLocking {

    private volatile static DoubleCheckedLocking singleton;

    private DoubleCheckedLocking() {

    }

    public static DoubleCheckedLocking getSingleton() {

        if (singleton == null) {

            synchronized (DoubleCheckedLocking.class) {

                if (singleton == null) {

                    singleton = new DoubleCheckedLocking();

                }

            }

        }

        return singleton;

    }

}

4.2.4 登记式/静态内部类

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用 

这种方式同样利用了classloader机制来保证初始化instance时只有一个线程,SingletonHolder类没有被主动使用,只有通过显式调用getInstance方法时,才会显式装载SingletonHolder类,从而实例化instance

package com.zhunongyun.spring.springpattern.singleton;

public class Register {

    private static class SingletonHolder {

        private static final Register INSTANCE = new Register();

    }

    private Register() {

    }

    public static final Register getInstance() {

        return SingletonHolder.INSTANCE;

    }

}

4.2.5 枚举

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化

package com.zhunongyun.spring.springpattern.singleton;

/**

 * 数据库连接

 */

public class DBConnection {

}

package com.zhunongyun.spring.springpattern.singleton;

public enum DataSourceEnum {

    DATASOURCE;

    private DBConnection connection = null;

    private DataSourceEnum() {

        connection = new DBConnection();

    }

    public DBConnection getConnection() {

        return connection;

    }

}

package com.zhunongyun.spring.springpattern.singleton;

public class DataSourceEnumTest {

    public static void main(String[] args) {

        DBConnection con1 = DataSourceEnum.DATASOURCE.getConnection();

        DBConnection con2 = DataSourceEnum.DATASOURCE.getConnection();

        System.out.println(con1 == con2);

    }

}

输出:

true

结果表明两次获取返回了相同的实例

Java规范中规定,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定 

在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过 java.lang.Enum 的valueOf()方法来根据名字查找枚举对象 

也就是说,以下面枚举为例,序列化的时候只将DATASOURCE这个名称输出,反序列化的时候再通过这个名称,查找对于的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同

枚举会是线程安全、序列化与反序列化是相同的实例

4.3 总结

一般情况下,不建议使用4.2.1懒汉方式,建议使用4.2.2饿汉方式。只有在要明确实现lazy loading效果时,才会使用4.2.4登记方式。如果涉及到反序列化创建对象时,可以尝试使用4.2.5枚举方式。如果有其他特殊的需求,可以考虑使用4.2.3双检锁方式

5. 原型模式

5.1 定义

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用

5.2 序列化和反序列化

5.2.1 序列化和反序列化的概念

序列化: 把对象转换为字节序列的过程称为对象的序列化

反序列化: 把字节序列恢复为对象的过程称为对象的反序列化

对象的序列化主要有两种用途:

1 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;

2 在网络上传送对象的字节序列。

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象

5.2.2 Serializable接口类

在JDK库中只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式

package com.zhunongyun.spring.prototype;

import lombok.Data;

import java.io.Serializable;

/**

 * 测试对象序列化和反序列化

 */

@Data

public class Person implements Serializable {

    /**

     * 序列化ID

     */

    private static final long serialVersionUID = -5809782578272943999L;

    private int age;

    private String name;

    private String sex;

}

5.2.3 serialVersionUID的作用

serialVersionUID字面意思上是序列化的版本号,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量

private static final long serialVersionUID = -5809782578272943999L;

两种生成方式:

1 default serial version ID

2 generated serial version ID

// default serial version ID

private static final long serialVersionUID = 1L;

// generated serial version ID

private static final long serialVersionUID = 4603642343377807741L;

那么serialVersionUID(序列化版本号)到底有什么用呢,我们用如下的例子来说明一下serialVersionUID的作用,看下面的代码:

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

public class TestSerialversionUID {

    public static void main(String[] args) throws Exception {

        SerializeCustomer();// 序列化Customer对象

        Customer customer = DeserializeCustomer();// 反序列Customer对象

        System.out.println(customer);

    }

    /**

     * MethodName: SerializeCustomer 

     * Description: 序列化Customer对象

     * @author xudp

     * @throws FileNotFoundException

     * @throws IOException

     */

    private static void SerializeCustomer() throws FileNotFoundException,

            IOException {

        Customer customer = new Customer("gacl",25);

        // ObjectOutputStream 对象输出流

        ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(

                new File("E:/Customer.txt")));

        oo.writeObject(customer);

        System.out.println("Customer对象序列化成功!");

        oo.close();

    }

    /**

     * MethodName: DeserializeCustomer 

     * Description: 反序列Customer对象

     * @author xudp

     * @return

     * @throws Exception

     * @throws IOException

     */

    private static Customer DeserializeCustomer() throws Exception, IOException {

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(

                new File("E:/Customer.txt")));

        Customer customer = (Customer) ois.readObject();

        System.out.println("Customer对象反序列化成功!");

        return customer;

    }

}

/**

 * <p>ClassName: Customer<p>

 * <p>Description: Customer实现了Serializable接口,可以被序列化<p>

 * @author xudp

 * @version 1.0 V

 * @createTime 2014-6-9 下午04:20:17

 */

class Customer implements Serializable {

    //Customer类中没有定义serialVersionUID

    private String name;

    private int age;

    public Customer(String name, int age) {

        this.name = name;

        this.age = age;

    }

    /*

     * @MethodName toString

     * @Description 重写Object类的toString()方法

     * @author xudp

     * @return string

     * @see java.lang.Object#toString()

     */

    @Override

    public String toString() {

        return "name=" + name + ", age=" + age;

    }

}

运行结果:

image

下面我们修改一下Customer类,添加多一个sex属性,如下:

class Customer implements Serializable {

    //Customer类中没有定义serialVersionUID

    private String name;

    private int age;

    //新添加的sex属性

    private String sex;

    public Customer(String name, int age) {

        this.name = name;

        this.age = age;

    }

    public Customer(String name, int age,String sex) {

        this.name = name;

        this.age = age;

        this.sex = sex;

    }

    /*

     * @MethodName toString

     * @Description 重写Object类的toString()方法

     * @author xudp

     * @return string

     * @see java.lang.Object#toString()

     */

    @Override

    public String toString() {

        return "name=" + name + ", age=" + age;

    }

}

然后执行反序列操作,此时就会抛出如下的异常信息:

Exception in thread "main" java.io.InvalidClassException: Customer; 

local class incompatible: 

stream classdesc serialVersionUID = -88175599799432325, 

local class serialVersionUID = -5182532647273106745

意思就是说,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID。在TestSerialversionUID例子中,没有指定Customer类的serialVersionUID的,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显指定serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。因此,只要我们自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象照样可以使用,而且还多了方法或者属性可以用。

下面继续修改Customer类,给Customer指定一个serialVersionUID,修改后的代码如下:

class Customer implements Serializable {

    /**

     * Customer类中定义的serialVersionUID(序列化版本号)

     */

    private static final long serialVersionUID = -5182532647273106745L;

    private String name;

    private int age;

    //新添加的sex属性

    //private String sex;

    public Customer(String name, int age) {

        this.name = name;

        this.age = age;

    }

    /*public Customer(String name, int age,String sex) {

        this.name = name;

        this.age = age;

        this.sex = sex;

    }*/

    /*

     * @MethodName toString

     * @Description 重写Object类的toString()方法

     * @author xudp

     * @return string

     * @see java.lang.Object#toString()

     */

    @Override

    public String toString() {

        return "name=" + name + ", age=" + age;

    }

}

重新执行序列化操作,将Customer对象序列化到本地硬盘的Customer.txt文件存储,然后修改Customer类,添加sex属性,修改后的Customer类代码如下:

class Customer implements Serializable {

    /**

     * Customer类中定义的serialVersionUID(序列化版本号)

     */

    private static final long serialVersionUID = -5182532647273106745L;

    private String name;

    private int age;

    //新添加的sex属性

    private String sex;

    public Customer(String name, int age) {

        this.name = name;

        this.age = age;

    }

    public Customer(String name, int age,String sex) {

        this.name = name;

        this.age = age;

        this.sex = sex;

    }

    /*

     * @MethodName toString

     * @Description 重写Object类的toString()方法

     * @author xudp

     * @return string

     * @see java.lang.Object#toString()

     */

    @Override

    public String toString() {

        return "name=" + name + ", age=" + age;

    }

}

执行反序列操作,这次就可以反序列成功了,如下所示: 

image

5.2.4 serialVersionUID的取值

serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。

类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。

显式地定义serialVersionUID有两种用途: 

1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID; 

2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

5.3 深复制与浅复制

5.3.1 拷贝的引入

5.3.1.1 引用拷贝

创建一个指向对象的引用变量的拷贝。

Teacher teacher = new Teacher("Taylor",26);

Teacher otherteacher = teacher;

System.out.println(teacher);

System.out.println(otherteacher);

输出结果:

blog.Teacher@355da254

blog.Teacher@355da254

结果分析:由输出结果可以看出,它们的地址值是相同的,那么它们肯定是同一个对象。teacher和otherteacher的只是引用而已,他们都指向了一个相同的对象Teacher(“Taylor”,26)。 这就叫做引用拷贝。

image

5.3.1.2 对象拷贝

创建对象本身的一个副本。

Teacher teacher = new Teacher("Swift",26);

Teacher otherteacher = (Teacher)teacher.clone();

System.out.println(teacher);

System.out.println(otherteacher);

输出结果:

blog.Teacher@355da254

blog.Teacher@4dc63996

结果分析:由输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。

image

深拷贝和浅拷贝都是对象拷贝

5.3.2 浅拷贝

5.3.2.1 定义:

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。

简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象

5.3.2.2 浅拷贝实例

package blog;

/**

 * Created by 白夜行 on 2017/5/8.

 */

public class ShallowCopy {

    public static void main(String[] args) throws CloneNotSupportedException {

        Teacher teacher = new Teacher();

        teacher.setName("Delacey");

        teacher.setAge(29);

        Student2 student1 = new Student2();

        student1.setName("Dream");

        student1.setAge(18);

        student1.setTeacher(teacher);

        Student2 student2 = (Student2) student1.clone();

        System.out.println("拷贝后");

        System.out.println(student2.getName());

        System.out.println(student2.getAge());

        System.out.println(student2.getTeacher().getName());

        System.out.println(student2.getTeacher().getAge());

        System.out.println("修改老师的信息后-------------");

        // 修改老师的信息

        teacher.setName("Jam");

        System.out.println(student1.getTeacher().getName());

        System.out.println(student2.getTeacher().getName());

    }

}

class Teacher implements Cloneable

{

    private String name;

    private int age;

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name = name;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age = age;

    }

}

class Student2 implements Cloneable

{

    private String name;

    private int age;

    private Teacher teacher;

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name = name;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age = age;

    }

    public Teacher getTeacher()

    {

        return teacher;

    }

    public void setTeacher(Teacher teacher)

    {

        this.teacher = teacher;

    }

    @Override

    public Object clone() throws CloneNotSupportedException

    {

        Object object = super.clone();

        return object;

    }

}

输出结果:

拷贝后

Dream

18

Delacey

29

修改老师的信息后-------------

Jam

Jam

结果分析: 两个引用student1和student2指向不同的两个对象,但是两个引用student1和student2中的两个teacher引用指向的是同一个对象,所以说明是浅拷贝。

image

5.3.3 .深拷贝

5.3.3.1 定义

深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

5.3.3.2 实现深拷贝(实例1)

package blog;

/**

 * Created by 白夜行 on 2017/5/8.

 */

public class DeepCopy {

    public static void main(String[] args) throws Exception

    {

        Teacher2 teacher = new Teacher2();

        teacher.setName("Delacey");

        teacher.setAge(29);

        Student3 student1 = new Student3();

        student1.setName("Dream");

        student1.setAge(18);

        student1.setTeacher(teacher);

        Student3 student2 = (Student3) student1.clone();

        System.out.println("拷贝后");

        System.out.println(student2.getName());

        System.out.println(student2.getAge());

        System.out.println(student2.getTeacher().getName());

        System.out.println(student2.getTeacher().getAge());

        System.out.println("修改老师的信息后-------------");

        // 修改老师的信息

        teacher.setName("Jam");

        System.out.println(student1.getTeacher().getName());

        System.out.println(student2.getTeacher().getName());

    }

}

class Teacher2 implements Cloneable {

    private String name;

    private int age;

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name = name;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age = age;

    }

    @Override

    public Object clone() throws CloneNotSupportedException

    {

        return super.clone();

    }

}

class Student3 implements Cloneable {

    private String name;

    private int age;

    private Teacher2 teacher;

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name = name;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age = age;

    }

    public Teacher2 getTeacher()

    {

        return teacher;

    }

    public void setTeacher(Teacher2 teacher)

    {

        this.teacher = teacher;

    }

    @Override

    public Object clone() throws CloneNotSupportedException

    {

        // 浅复制时:

        // Object object = super.clone();

        // return object;

        // 改为深复制:

        Student3 student = (Student3) super.clone();

        // 本来是浅复制,现在将Teacher对象复制一份并重新set进来

        student.setTeacher((Teacher2) student.getTeacher().clone());

        return student;

    }

}

输出结果:

拷贝后

Dream

18

Delacey

29

修改老师的信息后-------------

Jam

Delacey

结果分析: 

两个引用student1和student2指向不同的两个对象,两个引用student1和student2中的两个teacher引用指向的是两个对象,但对teacher对象的修改只能影响student1对象,所以说是深拷贝

teacher姓名Delacey更改前:

image

teacher姓名Jam更改后:

image

5.3.3.3 利用序列化实现深拷贝

package blog;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

/**

 * Created by 白夜行 on 2017/5/13.

 */

public class DeepCopyServiable {

    public static void main(String[] args) throws Exception {

        Teacher3 t = new Teacher3();

        t.setName("Taylor");

        t.setAge(28);

        Student3 s1 = new Student3();

        s1.setAge(20);

        s1.setName("blank space");

        s1.setTeacher(t);

        Student3 s2 = (Student3) s1.deepClone();

        System.out.println("拷贝后:");

        System.out.println(s2.getName());

        System.out.println(s2.getAge());

        System.out.println(s2.getTeacher().getName());

        System.out.println(s2.getTeacher().getAge());

        System.out.println("---------------------------");

        t.setName("swift");

        System.out.println("修改后:");

        System.out.println(s1.getTeacher().getName());

        System.out.println(s2.getTeacher().getName());

    }

}

class Teacher3 implements Serializable

{

    private String name;

    private int age;

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name = name;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age = age;

    }

}

class Student3 implements Serializable

{

    private String name;

    private int age;

    private Teacher3 teacher;

    public String getName()

    {

        return name;

    }

    public void setName(String name)

    {

        this.name = name;

    }

    public int getAge()

    {

        return age;

    }

    public void setAge(int age)

    {

        this.age = age;

    }

    public Teacher3 getTeacher()

    {

        return teacher;

    }

    public void setTeacher(Teacher3 teacher)

    {

        this.teacher = teacher;

    }

    public Object deepClone() throws Exception

    {

        // 序列化

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        ObjectOutputStream oos = new ObjectOutputStream(bos);

        oos.writeObject(this);

        // 反序列化

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

        ObjectInputStream ois = new ObjectInputStream(bis);

        return ois.readObject();

    }

}

输出结果:

拷贝后:

blank space

20

Taylor

28

---------------------------

修改后:

swift

Taylor

结果分析:说明用序列化的方式实现了对象的深拷贝

6. 建造者模式

6.1 定义

©著作权归作者所有:来自51CTO博客作者悠娜的奶爸的原创作品,如需转载,请注明出处,否则将追究法律责任


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消