创建型模式【3】——原型模式

 


1 原型模式特点

  • 定义 : 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
  • 特点 : 不需要知道任何创建的细节,不调用构造函数
  • 类型 : 创建型

适用场景

  • 类初始化消耗较多资源
  • new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
  • 构造函数比较复杂
  • 循环体中生产大量对象时
  • 优点 : 原型模式性能比直接new一个对象性能高;简化创建过程
  • 缺点 : 必须配备克隆方法;对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险;深拷贝、浅拷贝要运用得当

2 Coding

现在有一只羊tom(姓名为:tom, 年龄为:1,颜色为:白色),请编写程序创建和tom羊属性完全相同的10只羊

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;
    }
    //省略 get set toString 方法
//public class Client {
//    @Test
//    public void test() {
//        //传统的方法
//        Sheep sheep = new Sheep("tom", 1, "白色");
//        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        //....
//        System.out.println(sheep);
//        System.out.println(sheep2);
//        System.out.println(sheep3);
//        System.out.println(sheep4);
//        System.out.println(sheep5);
//        //...
//    }
//}
//实现Cloneable
public class Sheep implements Cloneable{
	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;
	}
	//重写clone
	@Override
	protected Sheep clone() throws CloneNotSupportedException {
		//使用默认的clone方法来完成
		return (Sheep)super.clone();
	}
	//省略 get set toString 方法
//public class Client {
//    @Test
//    public void test() throws CloneNotSupportedException {
//        System.out.println("原型模式完成对象的创建");
//        Sheep sheep = new Sheep("tom", 1, "白色");
//        Sheep sheep2 = sheep.clone(); //克隆
//        Sheep sheep3 = sheep.clone(); //克隆
//        Sheep sheep4 = sheep.clone(); //克隆
//        Sheep sheep5 = sheep.clone(); //克隆
//        System.out.println("sheep2 =" + sheep2);
//        System.out.println("sheep3 =" + sheep3);
//        System.out.println("sheep4 =" + sheep4);
//        System.out.println("sheep5 =" + sheep5);
//    }
//}

 

//抽象类
public abstract class A implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class B extends A {
    public static void main(String[] args) throws CloneNotSupportedException {
        B b = new B();
        B clone = (B) b.clone();
        System.out.println(b); // B@6ff3c5b5
        System.out.println(clone); // B@3764951d
    }
}

3 原型模式扩展

3.1 浅克隆错误演示

public class Pig implements Cloneable {
    private String name;
    private Date birthday;
    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }
    public String getName() {return name;}
    public void setName(String name) {this.name = name; }
    public Date getBirthday() {return birthday; }
    public void setBirthday(Date birthday) {this.birthday = birthday;}
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Pig{" + "name='" + name + '\'' + ", birthday=" + birthday + '}' + super.toString();
    }
}
public class PigTest {
    @Test
    public void test() throws CloneNotSupportedException {
        Date birthday = new Date(0L);
        Pig pig = new Pig("佩奇", birthday);
        Pig cPig = (Pig) pig.clone();
        System.out.println(pig); //  Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}Pig@66cd51c3
        System.out.println(cPig); // Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}Pig@4dcbadb4
        //本意只是修改pig的生日
        pig.getBirthday().setTime(666666666666L);
        System.out.println(pig); //            Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}Pig@66cd51c3
        //cPig也改了,这个结果不是我想要的,原因出在浅拷贝上
        System.out.println(cPig); // 跟着改了 : Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}Pig@4dcbadb4
    }

3.2 深克隆两种实现方式

public class Pig implements Cloneable, Serializable {
    private static final long serialVersionUID = 779970270042384579L;
    private String name;
    private Date birthday;
    public Pig(String name, Date birthday) {this.name = name;this.birthday = birthday;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public Date getBirthday() {return birthday;}
    public void setBirthday(Date birthday) {this.birthday = birthday;}
    //深拷贝——使用clone方法
    //Remove this "clone" implementation; use a copy constructor or copy factory instead.
    @Override
    protected Pig clone() throws CloneNotSupportedException {
        Pig pig = (Pig) super.clone();
        pig.birthday = (Date) pig.birthday.clone();
        return pig;
    }
    //深拷贝——通过对象的序列化实现 (推荐)
    public Pig 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);
            return (Pig) ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e) {

            }
        }
    }
    @Override
    public String toString() {
        return "Pig{" + "name='" + name + '\'' + ", birthday=" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(birthday) + '}' + " " + super.toString();
    }
}
public class PigTest {
    @Test
    public void test() {
        Date birthday = new Date(0L);
        Pig pig = new Pig("佩奇", birthday);
        //Pig cPig = pig.clone();
        Pig cPig = pig.deepClone();
        System.out.println(pig); //  Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@1b9e1916
        System.out.println(cPig); // Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@61064425
        pig.getBirthday().setTime(666666666666L);
        System.out.println(pig); //  Pig{name='佩奇', birthday=1991/02/16 09:11:06} Pig@1b9e1916
        System.out.println(cPig); // Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@61064425
    }
}


4 原型模式破坏单例模式

单例模式实现了Cloneable一定要小心

//实现Serializable和Cloneable接口
public class Singleton implements Cloneable {
    private final static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
    @Override
    protected Singleton clone() {
        //return (Singleton) super.clone();
        //注意,返回的是静态变量,不要调用父类方法然后返回
        return instance;
    }
}
/**
 * 原型模式里面的克隆可能会破坏单例模式
 */
@Test
public void test() throws Exception {
    Singleton hungrySingleton = Singleton.getInstance();
    Singleton cloneHungrySingleton = hungrySingleton.clone();
    System.out.println(hungrySingleton);//
    System.out.println(cloneHungrySingleton);//
}

5 源码分析 

public class HungrySingleton implements Serializable, Cloneable {
    private final static HungrySingleton hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton() {
        if (hungrySingleton != null) {
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
    private Object readResolve() {
        return hungrySingleton;
    }
    //
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return getInstance();
    }
}
@Test
public void test() throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    HungrySingleton hungrySingleton = HungrySingleton.getInstance();
    Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
    method.setAccessible(true);
    HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
    System.out.println(hungrySingleton);//HungrySingleton@66cd51c3
    System.out.println(cloneHungrySingleton);//HungrySingleton@66cd51c3
}

https://www.jianshu.com/p/54b0711a8ec8 

//java.lang.Cloneable
public interface Cloneable {

//java.util.ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
//<dependency>
//    <groupId>org.mybatis</groupId>
//    <artifactId>mybatis</artifactId>
//    <version>3.5.4</version>
//</dependency>

//org.apache.ibatis.cache.CacheKey
public class CacheKey implements Cloneable, Serializable {
  @Override
  public CacheKey clone() throws CloneNotSupportedException {
    CacheKey clonedCacheKey = (CacheKey) super.clone();
    clonedCacheKey.updateList = new ArrayList<>(updateList);
    return clonedCacheKey;
  }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章