如果想将一个对象存入文件中,就要让这个类实现标记接口: Serializable
并使用IO流中的ObjectInputStream/ObjectOutputStream过滤流进行输出
如果,将一个对象,向文件中重复输出,是放进了新的对象,还是同一个对象,用代码实践下:
Student类:
class Student implements Serializable{
String name;
int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
分别输出一次,两次:
public static void main(String[] args) throws Exception{
Student s1=new Student("小明",30);
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("d:\\copy\\Tos02.dat"));
oos.writeObject(s1);
oos.writeObject(s1);
}
创建一个新对象,内容一样
public static void main(String[] args) throws Exception{
Student s1=new Student("小明",30);
Student s2=new Student("小明",30);
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("d:\\copy\\Tos02.dat"));
oos.writeObject(s1);
oos.writeObject(s2);
}
运行结果:
第一次Tos02.dat文件大小是110字节,第二次是115字节大小
第三次是125字节大小
可以很清楚的看出,第一次运行时,输出一个Student对象的文件大小是110字节,第二次比其大了5个字节,第三次比第一次大了15个字节,也就是说:一个Student对象大小是15字节
那么为什么第二次是5字节呢?原因就是:JVM在向文件输出时,检测到第二次输出时的对象是同一个对象,地址一样,直接在文件中写了一句话**:同上!**
当然事实上不是这么简略.
因为在序列化时,只看了地址一样,所以即使你在两次序列化间隔内,改变了对象的属性,实际上文件中存入的还是一句话:**同上!**所以输入打印时,两个打印的都是未改变前的属性
那么如何存入两个内容一样,地址不一样的对象呢?
1.在创建一个新对象即可,即new一个新对象
2.使用clone()方法
Student s2=new Student("小明",30);
//或者
Student s2=(Student) s1.clone();
使用clone()方法时,需要在自定义类中重写clone,并进行提权,即将其变为public修饰
并给自定义类实现接口Cloneable,这是一个标记接口,声明这个类可以被克隆
class Student implements Serializable,Cloneable{
//略
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
但是,如果给Student中增加一个对象属性,clone()只会克隆地址,而不是对这个属性创建新的对象
String name;
int age;
Adress adress;
class Adress{}
Adress adress=new Adress();
Student s1=new Student("小明",30,adress);
Student s2=(Student)s1.clone();
System.out.println(s1.adress==s2.adress);
结果是true,clone方法只是浅克隆
所以,我们还需要另外的方法,来解决浅克隆
解决:
利用序列化解决,浅层克隆,序列化时,是将对象中的所有对象属性的内容存入,而不是存的地址
所以需要在Studnet类中重写的clone(),将s1输出文件后,在输入,赋给s2,这样经过序列化后,的对象属性就是一个新的对象,不是单纯的克隆地址
public Object clone() throws CloneNotSupportedException {
Object o = null;
try{
ObjectOutputStream oos=new ObjectOutputStream(
new FileOutputStream("d:\\copy\\Tos02.dat"));
oos.writeObject(this);
oos.close();
ObjectInputStream ois=new ObjectInputStream(
new FileInputStream("d:\\copy\\Tos02.dat"));
o=ois.readObject();
ois.close();
}catch (Exception e){
e.printStackTrace();
}
return o;
}
当然,为了保证属性也可序列化,这个Address也要实现可序列化
class Adress implements Serializable{}
再运行
System.out.println(s1.adress==s2.adress);
结果是false,这就是深克隆
当然,这样的深克隆,代码是十分麻烦的,有没有可替代品呢?
有
节点流
ByteArrayInputStream/ByteArrayOutputStream
向内存中的字节数组中输入输出
类似于File节点流,只不过一个是向文件中进行操作,一个向内存中进行操作
更改一下重写的clone()
class Child implements Serializable{
String name;
Toy toy;
public Child() {
}
public Child(String name, Toy toy) {
this.name = name;
this.toy = toy;
}
@Override
public String toString() {
return "Child{" +
"name='" + name + '\'' +
", toy=" + toy +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object o=null;
try{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);//存入内存
byte[] bytes=bos.toByteArray();//将当前对象装入数组
oos.close();
ByteArrayInputStream bis=new ByteArrayInputStream(bytes);//读字节数组
ObjectInputStream ois=new ObjectInputStream(bis);
o=ois.readObject();
ois.close();
}catch (Exception e){
e.printStackTrace();
}
return o;
}
}
class Toy implements Serializable{
}
在此克隆和属性的地址比较
Child child=new Child("lcy",new Toy());
Child child1=(Child) child.clone();
System.out.println(child1.toy==child.toy);
结果是false
使用ByteArray流克隆较于向文件中克隆会更加迅速;