创建型模式

一、单例模式

1、定义分析

所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就够,此时就会使用到单例模式。

2、常见写法

饿汉式(静态常量)

public class SingletonTest01 {
    public static void main(String[] args) {
        System.out.println("饿汉式   静态变量法");
        Singleton instance = Singleton.getInstance();
        Singleton singleton = Singleton.getInstance();

        System.out.println(instance == singleton);

        System.out.println("instance.hashCode = " + instance.hashCode());
        System.out.println("singleton.hashCode = " + singleton.hashCode());
    }
}

//饿汉式(静态变量)
class Singleton{
    //1.构造器私有化,外部无法new
    private Singleton(){

    }
    //2.本类内部创建对象实例
    private final static Singleton instance = new Singleton();
    //3.提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance(){
        return instance;
    }
}

步骤:

  1. 构造器私有化。

  1. 类的内部创建对象实例。

  1. 对外提供一个静态的公共方法。

分析:

写法简单,在类装载的时候就完成实例化,避免了线程同步问题。

没有达到懒加载的效果,如果从始至终从未使用过这个实例,则会造成内存的浪费。

可用,但可能造成内存浪费。

饿汉式(静态代码块)

public class SingletonTest02 {
    public static void main(String[] args) {
        System.out.println("饿汉式   静态代码块法");
        Singleton instance = Singleton.getInstance();
        Singleton singleton = Singleton.getInstance();

        System.out.println(instance == singleton);

        System.out.println("instance.hashCode = " + instance.hashCode());
        System.out.println("singleton.hashCode = " + singleton.hashCode());
    }
}

//饿汉式(静态代码块)
class Singleton{
    //1.构造器私有化,外面无法new
    private Singleton(){

    }

    //2.本类内部创建对象实例
    private static Singleton instance;

    static {
        instance = new Singleton();
    }
    //3.对外提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance(){
        return instance;
    }
}

分析:

与静态常量法类似,只不过将实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。

可用,但也会造成内存浪费。

懒汉式(线程不安全)

public class SingletonTest03 {
    public static void main(String[] args) {
        System.out.println("懒汉式   线程不安全");
        Singleton instance = Singleton.getInstance();
        Singleton singleton = Singleton.getInstance();

        System.out.println(instance == singleton);

        System.out.println("instance.hashCode = " + instance.hashCode());
        System.out.println("singleton.hashCode = " + singleton.hashCode());
    }
}

//懒汉式,线程不安全
class Singleton{
    private static Singleton instance;

    private Singleton(){

    }

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    //即懒汉式
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

分析:

起到了懒加载的效果,但是只能在单线程下使用。

如果在多线程下,一个线程进入了if(instance == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例,故而多线程环境下并未达到单例效果。

不可用,线程不安全。

懒汉式(线程安全,同步方法)

public class SingletonTest04 {
    public static void main(String[] args) {
        System.out.println("懒汉式   线程安全,同步方法");
        Singleton instance = Singleton.getInstance();
        Singleton singleton = Singleton.getInstance();

        System.out.println(instance == singleton);

        System.out.println("instance.hashCode = " + instance.hashCode());
        System.out.println("singleton.hashCode = " + singleton.hashCode());
    }
}

//懒汉式,线程安全,同步方法
class Singleton{
    private static Singleton instance;

    private Singleton(){}

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    //即懒汉式
    public static synchronized Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

分析:

解决了线程安全问题。

效率很低,每个线程在想获得实例的时候,执行getInstance()方法都要进行同步,其实该方法只需要一次实例化代码就够了,后面的线程想获取实例直接return即可,方法进行同步效率太低。

安全,但不推荐使用。

懒汉式(线程安全,同步代码块)

class Singleton{
    private static Singleton instance;

    private Singleton(){}

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    //即懒汉式
    public static Singleton getInstance(){
        if (instance == null){
            synchronized(Singleton.class){
                instance = new Singleton();
            }
        }
        return instance;
    }
}

分析:

并没有解决线程安全问题,这种写法是错的,不可用。

双重检查

public class SingletonTest06 {
    public static void main(String[] args) {
        System.out.println("Double-Check   双重检查方式   推荐使用");
        Singleton instance = Singleton.getInstance();
        Singleton singleton = Singleton.getInstance();

        System.out.println(instance == singleton);

        System.out.println("instance.hashCode = " + instance.hashCode());
        System.out.println("singleton.hashCode = " + singleton.hashCode());
    }
}

//Double-Check
class Singleton{
    private static volatile Singleton instance;

    private Singleton(){}

    //提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
    //同时保证了效率,推荐使用
    public static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class){
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

分析:

Double-Check概念是多线程开发中常常使用到的,进行两次实例空检查,加上同步代码块,既保证了线程安全,又提高了执行效率,而且还是懒加载。

推荐使用这种方式。

静态内部类

public class SingletonTest07 {
    public static void main(String[] args) {
        System.out.println("静态内部类方式");
        Singleton instance = Singleton.getInstance();
        Singleton singleton = Singleton.getInstance();

        System.out.println(instance == singleton);

        System.out.println("instance.hashCode = " + instance.hashCode());
        System.out.println("singleton.hashCode = " + singleton.hashCode());
    }
}

//静态内部类实现单例,推荐使用
class Singleton{
    //构造器私有化
    private Singleton(){}

    //静态内部类
    private static class SingletonInnerClass{
        private static Singleton INSTANCE = new Singleton();
    }

    //提供一个公有的静态方法
    public static Singleton getInstance(){
        return SingletonInnerClass.INSTANCE;
    }
}

分析:

采用类装载机制来保证初始化实例时只有一个线程。

静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInnerClass类,从而完成Singleton的实例化。

类的静态属性只会在第一次加载类的时候初始化,故而,JVM帮助我们保证了现成的安全性,在类进行初始化时,别的线程是无法进入的。

避免了线程不安全,利用静态内部类特点实现延迟加载,效率高,推荐使用。

枚举

public class SingletonTest08 {
    public static void main(String[] args) {
        System.out.println("枚举类实现单例");
        Singleton instance = Singleton.INSTANCE;
        Singleton singleton = Singleton.INSTANCE;

        System.out.println(instance == singleton);

        System.out.println("instance.hashCode = " + instance.hashCode());
        System.out.println("singleton.hashCode = " + singleton.hashCode());
    }
}

//枚举
enum Singleton{
    INSTANCE;
}

分析:

不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

3、JDK源码中的单例模式

4、单例模式注意事项和细节说明

  1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

  1. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new。

  1. 单例模式使用的场景:需要频繁进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。

二、工厂模式

1、简单工厂模式

1.1 了解需求

看一个披萨店铺的项目,要便于披萨种类的扩展和维护:

  • 披萨的种类很多,如GreekPizz、CheesePizz等;

  • 披萨的制作又prepare、bake、cut、box;

  • 完成披萨店订购功能;

1.2 使用传统方式来完成

public class Tools {
    //写一个方法,可以获取客户希望订购的披萨种类
    public String getType(){
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        }
        catch (IOException e){
            e.printStackTrace();
            return "";
        }
    }
}
public abstract class Pizza {
    public String name;
    //准备原材料,不同的披萨不一样,因此做成抽象方法
    public abstract void prepare();

    public void bake(){
        System.out.println(name + "Pizza is baking");
    }

    public void cut(){
        System.out.println(name + "Pizza is cutting");
    }

    public void box(){
        System.out.println(name + "Pizza is boxing");
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class CheessPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("CheessPizza is preparing");
    }
}
public class GreekPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("GreekPizza is preparing");
    }
}
public class OrderPizza {
    //构造器
    public OrderPizza(){
        Pizza pizza = null;
        String orderType;//订购披萨的类型
        do{
            orderType = new Tools().getType();
            if (orderType.equals("greek")){
                pizza = new GreekPizza();
                pizza.setName(" 希腊披萨 ");
            }
            else if(orderType.equals("cheese")){
                pizza = new CheessPizza();
                pizza.setName(" 奶酪披萨 ");
            }
            else {
                break;
            }

            //输出Pizza制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }while (true);
    }
}

分析优缺点:

  • 优点是易于理解,简单易操作。

  • 缺点是违反了设计模式的ocp原则,即没有对扩展开放,对修改关闭。我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码。

  • OrderPizza类既依赖于Pizza类,又依赖于其各个子类,新增加一个Pizza类时,订购Pizza的类都要修改。

改进思路分析:

分析:修改代码可以接受,但是如果我们在其它的地方也有创建Pizza的代码,就意味着也需要修改,而创建Pizza的代码,往往有多处。

思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类即可,其它有创建Pizza对象的代码就不需要修改了,这就是简单工厂模式。

1.3 基本介绍

简单工厂模式属于创建型模式,是工厂模式的一种,是由一个工厂对象决定创建出哪一种产品类的实例,是工厂模式家族中最简单实用的模式。

定义一个创建对象的类,由这个类来封装实例化对象的行为。

在开发中,当我们用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。

//简单工厂模式
public class SimpleFactory {
    //根据orderType返回对应的Pizza对象
    public Pizza createPizza(String orderType){
        System.out.println("使用简单工厂模式");
        Pizza pizza = null;

        if (orderType.equals("greek")){
            pizza = new GreekPizza();
            pizza.setName(" 希腊披萨 ");
        }
        else if(orderType.equals("cheese")){
            pizza = new CheessPizza();
            pizza.setName(" 奶酪披萨 ");
        }
        else if (orderType.equals("pepper")){
            pizza = new PepperPizza();
            pizza.setName("胡椒披萨");
        }

        return pizza;
    }
}
public class OrderPizza {
    //构造器
    public OrderPizza(SimpleFactory simpleFactory){
        setSimpleFactory(simpleFactory);
    }
    Pizza pizza = null;

    public void setSimpleFactory(SimpleFactory simpleFactory){
        do{
            String orderType = new Tools().getType();
            pizza = simpleFactory.createPizza(orderType);
            //输出pizza信息
            if (pizza != null){ //订购成功
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }
            else {
                System.out.println("Pizza订购失败");
                break;
            }
        }while (true);
    }
}

1.4 静态工厂模式

public class StaticSimpleFactory {
    public static Pizza createPizza(String orderType){
        System.out.println("使用简单静态工厂模式");
        Pizza pizza = null;

        if (orderType.equals("greek")){
            pizza = new GreekPizza();
            pizza.setName(" 希腊披萨 ");
        }
        else if(orderType.equals("cheese")){
            pizza = new CheessPizza();
            pizza.setName(" 奶酪披萨 ");
        }
        else if (orderType.equals("pepper")){
            pizza = new PepperPizza();
            pizza.setName("胡椒披萨");
        }

        return pizza;
    }
}
public class OrderPizza {
    //构造器
    public OrderPizza(){
        setSimpleFactory();
    }
    Pizza pizza = null;

    public void setSimpleFactory(){

        do{
            String orderType = new Tools().getType();
            pizza = StaticSimpleFactory.createPizza(orderType);
            //输出pizza信息
            if (pizza != null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }
            else {
                System.out.println("Pizza订购失败");
                break;
            }
        }while (true);
    }
}

2、工厂方法模式

2.1 了解新需求

不同地区的客户可以点不同区域不同口味的披萨,比如北京的奶酪披萨、北京的胡椒披萨、伦敦的奶酪披萨、伦敦的胡椒披萨等。

2.2 思路分析

思路一:

使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory等,这样做的话,系统的可维护性、可扩展性不是很好。

思路二:

使用工厂方法模式,将披萨项目的实例化功能进行抽象,在不同地区的订购中具体实现。

2.3 基本介绍

工厂方法模式定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

2.4 示例分析及代码

public abstract class Pizza {
    public String name;
    //准备原材料,不同的披萨不一样,因此做成抽象方法
    public abstract void prepare();

    public void bake(){
        System.out.println(name + "Pizza is baking");
    }

    public void cut(){
        System.out.println(name + "Pizza is cutting");
    }

    public void box(){
        System.out.println(name + "Pizza is boxing");
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class BJCheessPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 北京奶酪披萨 原材料……");
    }
}
public class BJPepperPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 北京胡椒披萨 原材料……");
    }
}
public class LDCheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 伦敦奶酪披萨 原材料……");
    }
}
public class LDPepperPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 伦敦胡椒披萨 原材料……");
    }
}
public abstract class OrderPizza {
    public OrderPizza(){
        Pizza pizza = null;
        String orderType;//订购披萨的类型
        do{
            orderType = new Tools().getType();
            pizza = createPizza(orderType);//抽象方法,由工厂子类完成
            if(pizza != null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }
        }while (pizza != null);
    }

    public abstract Pizza createPizza(String orderType);
}
public class BJOrderPizza extends OrderPizza{
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")){
            pizza = new BJCheessPizza();
            pizza.name = "北京奶酪";
        }
        else if(orderType.equals("pepper")){
            pizza = new BJPepperPizza();
            pizza.name = "北京胡椒";
        }

        return pizza;
    }
}
public class LDOrderPizza extends OrderPizza{
    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType == "cheese"){
            pizza = new LDCheesePizza();
            pizza.name = "伦敦奶酪";
        }
        else if(orderType == "pepper"){
            pizza = new LDPepperPizza();
            pizza.name = "伦敦胡椒";
        }

        return pizza;
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        String area = "bj";
        if(area == "bj")
            new BJOrderPizza();
        else if(area == "ld")
            new LDOrderPizza();
    }
}

三、抽象工厂模式

1、基本介绍

抽象工厂模式定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。

抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。

从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。

将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

2、类图

3、示例代码

public abstract class Pizza {
    public String name;
    //准备原材料,不同的披萨不一样,因此做成抽象方法
    public abstract void prepare();

    public void bake(){
        System.out.println(name + "Pizza is baking");
    }

    public void cut(){
        System.out.println(name + "Pizza is cutting");
    }

    public void box(){
        System.out.println(name + "Pizza is boxing");
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class BJCheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 北京Cheese 披萨原材料……");
    }
}
public class BJPepperPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 北京胡椒 披萨原材料……");
    }
}
public class LDCheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 伦敦Cheese 披萨原材料……");
    }
}
public class LDPepperPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备 伦敦胡椒 披萨原材料……");
    }
}
public interface AbsFactory {
    public Pizza createPizza(String orderType);
}
public class BJFactory implements AbsFactory{

    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if(orderType.equals("cheese")){
            pizza = new BJCheesePizza();
            pizza.name = "北京芝士";
        }
        else if (orderType.equals("pepper")){
            pizza = new BJPepperPizza();
            pizza.name = "北京胡椒";
        }

        return pizza;
    }
}
public class LDFactory implements AbsFactory{

    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("cheese")){
            pizza = new LDCheesePizza();
            pizza.name = "伦敦芝士";
        }
        else if (orderType.equals("pepper")) {
            pizza = new LDPepperPizza();
            pizza.name = "伦敦胡椒";
        }
        return pizza;
    }
}
public class OrderPizza {
    //AbsFactory factory;
    public OrderPizza(AbsFactory factory){
        setFactory(factory);
    }

    private void setFactory(AbsFactory factory){
        Pizza pizza = null;
        //this.factory = factory;
        do {
            String orderType = new Tools().getType();
            pizza = factory.createPizza(orderType);
            if (pizza != null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }
            else {
                System.out.println("订购失败……");
                break;
            }
        }while (true);
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        AbsFactory factory = null;
        String area = "bj";
        if (area == "bj")
            factory = new BJFactory();
        else if (area == "ld")
            factory = new LDFactory();
        OrderPizza orderPizza = new OrderPizza(factory);
    }
}

四、工厂模式小结

1、工厂模式的意义

将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和著项目的依赖关系的解耦。从而提高项目的扩展性和维护性。

2、三种工厂模式

简单工厂模式、工厂方法模式、抽象工厂模式

3、设计模式的依赖抽象原则

  • 创建对象实例时,不要直接new 类,而是把这个new 类的动作放在一个工厂的方法中并返回,有的书上说,变量不要直接持有具体类的引用。

  • 不要让类继承具体类,而是继承抽象类或者是实现接口。

  • 不要覆盖基类中已经实现的方法。

五、原型模式

1、克隆羊问题

现有一只羊tom,姓名:tom,年龄1,颜色白色,编写程序创建和tom羊属性完全相同的4只羊。

2、传统方式解决克隆羊问题

思路分析(图解)

代码如下

public class Sheep {
    private String name;
    private int age;
    private String color;

    public Sheep(String name, int age, String color){
        this.name = name;
        this.age = age;
        this.color = color;
    }

    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 String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String toString(){
        return "name = " + this.getName() + " age = " + this.getAge() + " color = " + this.getColor();
    }
}
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom",1,"白色");

        Sheep sheep1 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());

        System.out.println(sheep.hashCode() + " --- " + sheep.toString());
        System.out.println(sheep1.hashCode() + " --- " + sheep1.toString());
        System.out.println(sheep2.hashCode() + " --- " + sheep2.toString());
        System.out.println(sheep3.hashCode() + " --- " + sheep3.toString());
    }
}

3、传统方式的优缺点

优点是比较好理解,简单易操作

在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低

总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活

改进的思路分析:

Java中object类是所有类的根类,object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力 ==> 原型模式

4、原型模式基本介绍

原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节

工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝他们自己来实施创建,即对象.clone()

形象的理解:孙大圣拔出猴毛,变出其他孙大圣

5、原型模式实例

代码实现

public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;

    private String address = "鄂尔多斯";

    public Sheep(String name, int age, String color){
        this.name = name;
        this.age = age;
        this.color = color;
    }

    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 String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }


    public String getAddress() {
        return address;
    }

    public String toString(){
        return "name = " + this.getName() + " age = " + this.getAge() + " color = " + this.getColor() + " address = " + this.getAddress();
    }

    //克隆该实例,使用默认的clone方法来完成
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try{
            sheep = (Sheep)super.clone();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        return sheep;
    }
}
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom",1,"白色");

        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();

        System.out.println(sheep.hashCode() + " --- " + sheep.toString());
        System.out.println(sheep1.hashCode() + " --- " + sheep1.toString());
        System.out.println(sheep2.hashCode() + " --- " + sheep2.toString());
        System.out.println(sheep3.hashCode() + " --- " + sheep3.toString());
    }
}

6、深入讨论浅拷贝和深拷贝

浅拷贝

对于数据类型是基本数据类型及String类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。String属于Java中的字符串类型,也是一个引用类型,并不属于基本的数据类型。

对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

浅拷贝时使用默认的clone()方法来实现。

sheep = (Sheep) super.clone();

深拷贝

复制对象的所有基本数据类型的成员变量值

为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象科大的所有对象。即,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝

深拷贝实现方式一:重写clone方法来实现深拷贝

深拷贝实现方式二:通过对象序列化实现深拷贝(推荐)

public class DeepCloneableTarget implements Serializable, Cloneable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String cloneName;
    private String cloneClass;

    //构造器
    public DeepCloneableTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }
    //因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class DeepProtoType implements Serializable, Cloneable{
    
    public String name; //String 属性
    public DeepCloneableTarget deepCloneableTarget;// 引用类型
    public DeepProtoType() {
        super();
    }
    
    
    //深拷贝 - 方式 1 使用clone 方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        
        Object deep = null;
        //这里完成对基本数据类型(属性)和String的克隆
        deep = super.clone(); 
        //对引用类型的属性,进行单独处理
        DeepProtoType deepProtoType = (DeepProtoType)deep;
        deepProtoType.deepCloneableTarget  = (DeepCloneableTarget)deepCloneableTarget.clone();
        
        // TODO Auto-generated method stub
        return deepProtoType;
    }
    
    //深拷贝 - 方式2 通过对象的序列化实现 (推荐)
    
    public Object deepClone() {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this); //当前这个对象以对象流的方式输出
            
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType copyObj = (DeepProtoType)ois.readObject();
            return copyObj;
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        } finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
}
public class Client {
    public static void main(String[] args) throws Exception {
        DeepProtoType p = new DeepProtoType();
        p.name = "张三";
        p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");
        
        //方式1 完成深拷贝
//        DeepProtoType p2 = (DeepProtoType) p.clone();
//        System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
//        System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
    
        //方式2 完成深拷贝
        DeepProtoType p2 = (DeepProtoType) p.deepClone();
        System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
        System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());
    }
}

7、总结

创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率

不用重新初始化对象,而是动态地获得对象运行时的状态

如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码

若成员变量无引用类型,浅拷贝clone即可;若引用类型的成员变量很少,可考虑递归实现clone,否则推荐序列化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值