接上文,在小陈合理的推广下,小陈的铺货量也是越来越大,越来越多,订单数量也是越来越多,大到上市公司,运动品牌专卖店,小到街边饰品小店等都来向小陈这边进货,但是随着订单的越来越多,现在每天的订单量都要将近10万量的计数,这时候一件让小陈头疼的事情来了,因为订单量过于多,现在小陈那边的出单机的速率已经跟不上了,因为原本出单机是一单单的打出来的,但是因为订单量过于庞大,速度实在跟不上了,而且小陈认真的观察了一下,订单的格式都是差不多的,都是订单时间,订单数量,订单的产品信息,预定公司的信息,基本上都是信息都是大同小异,我们先来看看街边小店的订单格式:
1 public class OrderInfo { 2 private String shopName; 3 4 private String shopAddr; 5 6 private String shopMobile; 7 8 private String productName; 9 10 private String productNumber; 11 }//因为篇幅原因,setter/getter方法就不写了
街边小店的订单格式比较简单,就基本信息和地址就是了,但是别看这种街边小店,现在有将近3K家小店跟小陈的公司有合作,每个月的营收可不少,有的时候小店的名字、地址、手机一样,就产品和数量不一样,又要重新生成一个订单,有的是小店的开了分店,地址不一样,但是其他的信息又一样,但是每个订单新生成的速度忒慢(如果每个订单都要生成,既浪费虚拟机的性能,而且每次填写相同的信息也要花费很多时间),这个时候,小陈想既然都一样,干脆复制得了,然后再重新填上区别的信息,这样就可以一个订单范本多家公司使用了。(大家可以想想为什么这里不用工厂模式来生成订单?)
接下来我们就来看看怎么来进行复制:
1 public class OrderInfo implements Cloneable{ 2 private String shopName; 3 private String shopAddr; 4 private String shopMobile; 5 private String productName; 6 private String productNumber; 7 8 @Override 9 protected Object clone() { 10 try { 11 return super.clone(); 12 } catch (CloneNotSupportedException e) { 13 e.printStackTrace(); 14 } 15 return null; 16 } 17 }//因为篇幅原因,setter/getter方法就不写了
在这里OrderInfo必须实现Cloneable,让虚拟机知道该对象是可复制的,在这里必须说明一点,clone对象不是Cloneable中的方法,如果我们点进源代码里面看的话,会发现这个接口是个空接口,那这个clone方法是哪里来的呢?Java里面所有的对象都继承自Object,所以clone方法是Object对象中的方法,我们这里是重写了他的方法。好了,这样的话,我们来看看客户端来怎么生成,一家小店有两家分店,除了地址其他的信息都一样的订单了。
1 @Test 2 public void clien() { 3 OrderInfo orderInfo = new OrderInfo("饰品小店", "北京王府井大街", "13888888888", "骑士徽章", "10000"); 4 5 OrderInfo cloneInfo = (OrderInfo)orderInfo.clone(); 6 7 cloneInfo.setShopAddr("北京王府井大街2号"); 8 9 System.out.println(orderInfo); 10 System.out.println(cloneInfo); 11 }
OrderInfo [shopName=饰品小店, shopAddr=北京王府井大街, shopMobile=13888888888, productName=骑士徽章, productNumber=10000]
OrderInfo [shopName=饰品小店, shopAddr=北京王府井大街2号, shopMobile=13888888888, productName=骑士徽章, productNumber=10000]
这里的话,我们可以看到除了店面的地址不一样,其他的信息全部一样了,这样子就省去了很多成本了,订单的生产速度也快了不止一个级别,小陈非常开心,但是这个时候,订单出单员小A发现了个问题了,当订单是街边小店的订单的时候,订单会没有问题,但是当是上市公司的订单,或者品牌大店的订单的时候,出单就会有问题,小陈观察了一下,原来上市集团的大单的订单格式是下面这种方式的:
1 public class OrderInfo implements Cloneable{ 2 private Product productInfo; 3 private Company company; 4 5 @Override 6 protected Object clone() { 7 try { 8 return super.clone(); 9 } catch (CloneNotSupportedException e) { 10 e.printStackTrace(); 11 } 12 return null; 13 } 14 } 15 16 class Product { 17 private String productName; 18 private String productBuildeAddr; 19 private String productBuildeDate; 20 //...等一系列关于产品的信息,因为篇幅略过 21 } 22 23 class Company { 24 private String companyName; 25 private String companyAddr; 26 private String companyMobile; 27 //...等一系列关于公司的信息,因为篇幅略过 28 }
大型公司的信息都比较完善,而且他们都把信息封装到了一个个的对象中去了,订单中展示出了一个个的对象,那为什么我们clone的时候不起作用了呢?我们再来看看客户端的调用:
1 @Test 2 public void clien() { 3 OrderInfo orderInfo = new OrderInfo(new Product("骑士徽章","克利夫兰","20160601"),new Company("骑士公司", "北京骑士大道", "13666666666")); 4 5 OrderInfo cloneInfo = (OrderInfo)orderInfo.clone(); 6 7 cloneInfo.getCompany().setCompanyAddr("北京骑士大道2号"); 8 9 System.out.println(orderInfo); 10 System.out.println(cloneInfo); 11 }
OrderInfo [productInfo=Product [productName=骑士徽章, productBuildeAddr=克利夫兰, productBuildeDate=20160601], company=Company [companyName=骑士公司, companyAddr=北京骑士大道2号, companyMobile=13666666666]]
OrderInfo [productInfo=Product [productName=骑士徽章, productBuildeAddr=克利夫兰, productBuildeDate=20160601], company=Company [companyName=骑士公司, companyAddr=北京骑士大道2号, companyMobile=13666666666]]
我们这里就可以看到了clone订单更改了地址之后,原对象也更改了,这里就涉及到了jvm虚拟中的对象的引用了。(说的简单点就是,其实java中的每个对象在jvm中算是一个引用,引用指向了堆中的对象,而我们在克隆一个对象时,如果进行了浅拷贝的话,那只是将这个引用复制了一次,而没有对这个对象进行克隆,所以会导致,对象的克隆失败,因为两个引用都指向了堆中的同一个对象,所以在这里我们就需要进行deep Clone),这里的deep Clone有两种方式:
一种就是对需要克隆的对象里面的对象,全部实现clone的方法,那样的话,clone的时候,他就会全部进行复制了,类似上文中的方法
1 class Product implements Cloneable{ 2 @Override 3 protected Object clone() { 4 try { 5 return super.clone(); 6 } catch (CloneNotSupportedException e) { 7 e.printStackTrace(); 8 } 9 return null; 10 } 11 } 12 13 class Company implements Cloneable{ 14 @Override 15 protected Object clone() { 16 try { 17 return super.clone(); 18 } catch (CloneNotSupportedException e) { 19 e.printStackTrace(); 20 } 21 return null; 22 } 23 }
这里必须注意到的是,如果对象里面还有对象的话,那就必须一直实现Cloneable。
接下来我们重点介绍第二种方法:流式克隆
在流式克隆中,我们这边需要使用到Serializable这个接口,这个接口跟Cloneable一样,也是标识接口,这个接口也是空方法,实现了该接口的对象被标识为可序列化的
1 public class OrderInfo implements Cloneable,Serializable{ 2 private Product productInfo; 3 private Company company; 4 5 @Override 6 protected Object clone() { 7 try { 8 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 9 ObjectOutputStream oos = new ObjectOutputStream(baos); 10 oos.writeObject(this); 11 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 12 ObjectInputStream ois = new ObjectInputStream(bais); 13 return ois.readObject(); 14 } catch (IOException e) { 15 e.printStackTrace(); 16 } catch (ClassNotFoundException e) { 17 e.printStackTrace(); 18 } 19 return null; 20 } 21 }
这里对其进行IO操作是使用JVM中的将对象写到流中是对象的一个拷贝的这一个特性,而源对象仍然保留在了jvm中,所以重新读取出来的话又是一条好对象啦,但是这里必须注意的是如果使用流式拷贝的话,那内部所有的被拷贝的对象都要实现Serializable这个接口,只需要实现Serializable接口即可,不需要其他的操作。然后我们再执行客户端的操作就可以看到,客户端得到的是两个不同的对象了。
现在小陈又解决了一个问题了,现在订单的问题也解决了,也不再会被大量的订单导致出货慢,让客户投诉了,在创业的这段时间遇到了不同的问题,但是小陈有自信可以一步步的解决掉,下回小陈还会遇到什么样的问题呢?
欲知下回如何,且听下文分解。