继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

创建型设计模式

holdtom
关注TA
已关注
手记 1703
粉丝 240
获赞 991


创建型设计模式成员

工厂方法模式

抽象工厂模式

建造者模式

原型模式

单例模式

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博客作者悠娜的奶爸的原创作品,如需转载,请注明出处,否则将追究法律责任


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP