在java中除了基本类型之外的一切对象皆是引用,那么就设计到了一个问题,当我们需要复制的时候就有了深度复制(deep copy)和浅度(shadow copy)复制两种了。
java中api的大部分复制都是前度的复制,例如Collections.ncopies()是只复制引用,并不复制对象。Object的clone()方法是复制对象的内存,这也存在一个问题,如果这个对象中有属性指向的是另一个对象,那么复制的这个对象和原对象中的属性指向的是同一个对象,所有也是浅复制。
在java中可以通过实现Serializable接口来实现深度复制。因为Serializable接口的序列化是序列化对象网,即对象的属性指向的对象也会被序列化,这样就可以实现深度复制了
只要将任何对象序列化到单一流中,就可以恢复出与我们写入是一样的对象网
方法如下:
/**
* 深度复制,实参类必须实现Serializable接口
* @param o
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Object deepCopy(Object o) throws IOException, ClassNotFoundException {
// //先序列化,写入到流里
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(o);
//然后反序列化,从流里读取出来,即完成复制
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return oi.readObject();
}
但是有一个问题是,序列化不会序列化静态的属性,这样会导致静态属性在返序列化的时候为空或者默认值,所有需要在类中添加静态的方法来自己序列化和反序列化静态属性
同样需要使用
public static void serializeStaticState(ObjectOutputStream os) throws IOException {
//使用os来序列化静态属性
}
public static void deserializeStaticState(ObjectInputStream os) throws IOException {
//反序列化时使用os来实现
}
具体的例子:
abstract class Shape implements Serializable{
public static final int RED = 1,BLUE = 2,GREEN = 3;
private int xPox,yPox,dimension;
private static Random random = new Random(47);
private static int counter = 0;
public abstract void setColro(int newColor);
public abstract int getColor();
public Shape(int xVale,int yVale,int dim){
xPox = xVale;
yPox = yVale;
dimension = dim;
}
@Override
public String toString() {
return getClass() +
"color[" + getColor() + "] xPos[" + xPox +
"] yPos[" + yPox + "] dim[" + dimension + "]\n";
}
public static Shape randomFactory(){
int xVal = random.nextInt(100);
int yVal = random.nextInt(100);
int dim = random.nextInt(100);
switch(counter++ % 3) {
default:
case 0: return new Circle(xVal, yVal, dim);
case 1: return new Square(xVal, yVal, dim);
case 2: return new Line(xVal, yVal, dim);
}
}
}
class Circle extends Shape{
private static int color;
public static void serializeStaticState(ObjectOutputStream os) throws IOException {
os.writeInt(color);
}
public static void deserializeStaticState(ObjectInputStream os)
throws IOException { color = os.readInt(); }
public Circle(int xVal, int yVal, int dim) {
super(xVal, yVal, dim);
}
@Override
public void setColro(int newColor) {
color = newColor;
}
@Override
public int getColor() {
return color;
}
}
class Square extends Shape{
private static int color;
public static void serializeStaticState(ObjectOutputStream os) throws IOException {
os.writeInt(color);
}
public static void deserializeStaticState(ObjectInputStream os)
throws IOException { color = os.readInt(); }
public Square(int xVale, int yVale, int dim) {
super(xVale, yVale, dim);
}
@Override
public void setColro(int newColor) {
color = newColor;
}
@Override
public int getColor() {
return color;
}
}
class Line extends Shape{
private static int color;
public Line(int xVale, int yVale, int dim) {
super(xVale, yVale, dim);
}
public static void serializeStaticState(ObjectOutputStream os) throws IOException {
os.writeInt(color);
}
public static void deserializeStaticState(ObjectInputStream os)
throws IOException { color = os.readInt(); }
@Override
public void setColro(int newColor) {
color = newColor;
}
@Override
public int getColor() {
return color;
}
}
public class CADSate {
public static void main(String[] args) throws IOException, ClassNotFoundException {
List shapeList = new ArrayList<>();
for(int i=0;i<10;i++){
shapeList.add(Shape.randomFactory());
}
for (int i=0;i<10;i++){
((Shape)shapeList.get(i)).setColro(Shape.GREEN);
}
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("F:\\workspace\\javaStudy\\src\\com\\thinkingInJava\\Serializable\\CAD.out"));
Circle.serializeStaticState(out);
Square.serializeStaticState(out);
Line.serializeStaticState(out);
out.writeObject(shapeList);
System.out.println(shapeList);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("F:\\workspace\\javaStudy\\src\\com\\thinkingInJava\\Serializable\\CAD.out"));
Circle.deserializeStaticState(in);
Square.deserializeStaticState(in);
Line.deserializeStaticState(in);
List sh = (List) in.readObject();
System.out.println(sh);
}
}
有时候我们需要序列化部分数据,例如在序列化一个login对象的时候,涉及到安全问题,我们不希望序列化password这个属性。所有我们可以使用关键字transient在login对象的类的password属性上添加transient时,就不会序列化这个字段。
或者使用接口Externalizable接口来实现序列化和反序列化,实现这个接口需要实现两个方法
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
通过这两个方法可以看出,这是希望我们自己序列化那些需要需要序列化的属性。
添加特别方法实现
如果不想用Externalizable接口,也不希望使用transient关键字的话,可以实现Serializable接口,并且添加(注意我说的是“添加”,而非“覆盖”或者“实现”)名为writeObject()和readObject()方法。这样一旦对象被序列化或者反序列化还原,就会自动的分别调用者两个方法
private void writeObject(ObjectOutputStream stream)throws IOException;
private void readObject(ObjectInputStream stream)throws IOException;
这两个方法并非接口所有的(因为接口的方法都自动是public的),也不是覆盖的父类的。在调用方法时,会检查所传递的Serializable对相,看看是否实现了它自己的writeObject()和readObject()方法,如果有就跳过正常的序列化过程,并且调用它的方法。
还有另一个技巧,在你的writeObject()内部,可以调用defaultWriteObject()来选择执行默认的writeObject()。在readObject()内部也是如此。
如果我们打算使用默认机制写入对象的非transient部分,那么必须调用defaultwriteobject()作为writeObject()中的第一个操作。同理read