不要自卑,去提升实力
互联网行业谁技术牛谁是爹
如果文章可以带给你能量,那是最好的事!请相信自己
加油o~
对象序列化
当我们有时需要将Java中的对象进行传输时,需要将它转换成二进制流保存在文件中,然后其他程序通过这个二进制流再将其恢复成原有的Java类型数据
序列化分为两种,第一种就是序列化,即将Java对象转换成字节序列保存起来
第二种就是反序列化,即将处理后的字节序列恢复成Java对象
实现序列化两种方式:
方式一:实现Serializable接口
当我们要将我们的类对象序列化的时候就要使这个类实现Serializable接口,这个接口只是作为一种标记,标记这个类是否可以被序列化,不需要实现任何抽象方法,而另外一种方式就要去实现内部的抽象方法
序列化的流程:
1、创建一个对象输出流
2、调用该对象的writeObject方法,向指定流输出序列化对象
示例:将Teacher对象序列化存入Java.txt文件中
try(
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Java.txt"));
)
{
oos.writeObject(new Teacher("张三"));
}
class Teacher implements Serializable{
String name;
public Teacher(){}
public Teacher(String name){
this.name=name;
}
}
反序列化:
1、创建一个对象输入流
2、调用该对象的readObject方法,向指定流输入序列化对象
示例:将序列化后的Teacher读取出来
try(
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("Java.txt"));
)
{
Teacher t=(Teacher)ois.readObject();
}
class Teacher implements Serializable{
String name;
public Teacher(){}
public Teacher(String name){
this.name=name;
}
}
注意:反序列化得到的类对象源码中必须有相应的类对象,否则会反序列化失败
将序列化得到的Object对象强转成Teacher对象
反序列化是无需构造器来初始化恢复对象的
读取顺序要按照序列化顺序来读取
对象引用的序列化
由上文可以一个类要序列化必需要实现接口,那么当某个类中存在内部类,且该内部类未实现接口,那么整个大类也是无法序列化的。
例如Teacher类实现了接口,而Teacher类中的Student类没有实现接口,那么Teacher是无法序列化的,但是当我们用关键字transient修饰时,Teacher类序列化时就会忽略Student类,进而可以序列化了。
这里有个特殊情形:当重复序列化同一个对象,他会重复的写入文件吗?
答案是不会的,假设如果会重复写入的话,读取的时候就会读取多个对象,就会导致不一致的现象
Java序列化机制采用一种特殊算法,序列化一个对象时会得到一种编号,那么再次序列化这个对象时Java就会检测这个编号是否存在,如果存在只是输出这个序列化编号,而不会再次序列化
举个例子:
Student s=new Student();
Teacher t1=new Teacher("张三",s);
Teacher t2=new Teacher("李四",s);
由上述代码可以两个Teacher对象都引用了s对象,那么当序列化s后,再序列化两个Teacher对象时,引用的s对象,就不会再次序列化,只是会输出相应的序列化编号。
注意:
当我们将s对象序列化后,再在程序中修改s对象中的值是不会修改序列化中的内容的,反序列化时得到的也是修改之前的
示例:
try(
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Java.txt"));
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("Java.txt"));
)
{
Teacher t=new Teacher("张三",18);
oos.writeObject(t);
t.name="李四";
Teacher t2=(Teacher)oos.readObject();
System.out.println(t2.toString());
}
上述代码将t序列化存入文件后,再去修改t的姓名,再反序列化时,得到的依旧是修改之前的对象
自定义序列化
当我们有些时候,我们类中的数据并不希望全部序列化,有些数据保密,这就需要将这些数据编程不可序列化
这是我们就可以将该数据用transient关键字修饰,这时就不会因为不可序列化而报错,只是序列化时会忽略它
示例:
class Teacher implements Serializable{
String name;
transient int id;
}
这是当我们再去序列化Teacher类时只会序列化name,而id就不会写入,当我们再读取这个对象时,得到的id就为0,因为是默认值
有时我们可以自定义方法去满足我们自定义序列化要求
示例:
class Teacher implements Serialiable{
String name;
int id;
private void writeObject(ObjectOutputStream out) throws IOException{
out.writeObject(new StringBuffer(name));
out.writeInt(id);
}
private void readObject(ObjectInputStream in) throws IOException{
this.name=(StringBuffer)in.readObject();
this.id=in.readInt();
}
}
序列化机制还有一个代替方法,就是序列化对象转换成其他对象
class Teacher implements Serialiable{
String name;
int id;
private Object writeReplace() throws IOException{
ArrayList<Object> list=new ArrayList<>();
list.add(name);
list.add(id);
return list;
}
}
这是当我们再去序列化Teacher对象时,我们会将Teacher里面的数据已ArrayList形式进行序列化,再进行反序列化时得到的也是一个ArrayList形式的集合
由上可知,Java在序列化某个对象时,会调用该对象的writeReplace方法和writeObject方法
方式二:实现Externalizable接口
注意该接口与上面的接口不同,实现该接口时要实现两个抽象方法
- void readExternal(ObjectInput in):
- void writeExternal(ObjectOutput out):
示例:
class Teacher implements Externalizable{
String name;
int id;
public Teacher(){}
public Teacher(String name,int id){
this.name=name;
this.id=id;
}
public void writeExternal(ObjectOutput out) throws IOException{
out.writeObject(new StringBuffer(name));
out.writeInt(id);
}
public void readExternal(ObjectInput in) throw IOException{
this.name=((StringBuffer)in.readObject()).toString;
this.id=in.readInt();
}
}
注意该方法序列化时必须提供无参构造器,因为反序列化时会调用无参构造器创建一个示例,再去调用readExternal方法进行反序列化。
两种方式对比:
- Serializable会自动存储所有数据,而Externaliable可以程序员决定序列化哪些数据
- Serializable易于实现,只需实现接口,而Externaliable需要重写两个方法
- Serializable相比于Externaliable的性能较差