在刚写第一个项目的时候,想做一个存储和读取用户账号密码的对象序列化和反序列化到文件中,在同一个进程存入的多个对象确实可以同时取出
package T;
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String src = "C:\\Users\\32179\\Desktop\\test.txt";
FileOutputStream fos = new FileOutputStream(new File(src));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(new TT("name1","password1"));
oos.writeObject(new TT("name2","password2"));
oos.writeObject(new TT("name3","password3"));
oos.flush();
oos.close();
fos.close();
FileInputStream fis = new FileInputStream(new File(src));
ObjectInputStream ois = new ObjectInputStream(fis);
System.out.println(ois.readObject());
System.out.println(ois.readObject());
System.out.println(ois.readObject());
ois.close();
fis.close();
}
}
但是在不同进程存入的多个对象在读取中会遇到这样一个问题: invalid type code: AC
这是什么原因呢,就是每次执行一次ObjectOutputStream对象输入流时,在往这个文件中输入对象时,会先输入一个流头信息,而且是每开启对象输入流时都会。就是每new 一次ObjectOutputStream都会输入一个流头信息,而ObjectInputStream这个对象输出流,是每new一次只能读取文件开头的流头信息
所以会导致什么情况呢,就会导致输出流只能读到第一次输入流加入文件的对象,后面加入的对象前的流头信息并不能被识别,然后被当作不正常的对象,输出流也就会卡在这里
既然问题的原因找到了,那么解决办法也有了
1.让这个程序只在文件开头加入流头信息
2.让这个程序不在文件加入流头信息,也不读取文件的流头信息
第一种方法如下:
这是创建了一个继承ObjectOutputStream类用于序列化的类:
import java.io.*;
public class MyObjectOutputStream extends ObjectOutputStream {//继承ObjectOutStream类
private static File file;//这是将要传入的文件
public static void initFile(File file){//通过这个静态方法在输入流开始前得到file
MyObjectOutputStream.file = file;
}
public MyObjectOutputStream(File file) throws IOException{
super(new FileOutputStream(file,true));
}
@Override
public void writeStreamHeader() throws IOException{//重写了写入流头的方法
if(file == null || file.length() == 0){
super.writeStreamHeader();//通过initFile方法得到file文件的大小进行判断是否写入流头信息
}
else {
this.reset();
}
}
}
这是对象类:
package H;
import java.io.*;
public class DD implements Serializable{//必须实现Serializable接口才能进行对象序列化和反序列化
public String name;
public String password;
public DD(String name, String password) {
this.name = name;
this.password = password;
}
public DD() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {//重写Object类中的toString方法输出对象
return ("用户名:"+this.getName()+"\t\t密码:"+this.getPassword());
}
}
这是输入流和输出流的两种方法 :
public void addRoot(String src) throws IOException, ClassNotFoundException {//测试类
MyObjectOutputStream.initFile(new File(src));
ObjectOutputStream oos = new MyObjectOutputStream(new File(src));//一定new自己写的那个输入类
Scanner sc = new Scanner(System.in);
System.out.println("请设置管理员账号:");
String name = sc.nextLine();
System.out.println("请设置管理员密码:");
String password = sc.nextLine();
oos.writeObject(new DD(name,password));
oos.flush();//清缓存
oos.close();//关闭流
System.out.println("添加管理员成功-------");
}//第一种方法是输入流
public void checkRoot(String src) throws IOException{
FileInputStream fis = new FileInputStream(src);
ObjectInputStream ois = new ObjectInputStream(fis);
while (fis.available() > 0){//fis.available是返回文件流中数据的数量int值类型的,可以通过这个判断文件流中是否还有内容
try {
System.out.println(ois.readObject());//如果遇到了这行有问题,那是因为文件读完了,只要catch一下将Exception的异常改为输出完毕就行了
} catch (Exception e) {
System.out.println("读取数据完毕");
}
}
ois.close();//关闭输出流
fis.close();//关闭文件流
}//第二种方法是输出流
这个方法的关键,是通过重写ObjectOutputStream类中的writeStreamHeader方法就可以控制什么时候加流头信息什么时候不加流头信息,通过静态方法拿到当前的文件,通过对文件是否有内容的判断,来决定是否输入流头信息。
第二种方法如下:
反序列化类:
package T;
import java.io.*;
public class In extends ObjectInputStream {//反序列化类
public In(InputStream in) throws IOException {//这个方法是打开输出流的方法
super(in);
}
@Override
protected void readStreamHeader() throws IOException {
}
}//继承ObjectInputStream的一个类,重写 readStreamHeader 方法,来实现输出流时不读取流头信息
序列化类:
package T;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class Out extends ObjectOutputStream {//序列化类
public Out(OutputStream out) throws IOException {//创建输入流的一个方法
super(out);
}
@Override
protected void writeStreamHeader() throws IOException {
}
}//继承ObjectOutputStream的一个类,重写了 writeStreamHeader 方法,使其在序列化对象前,不在文件中输入流头信息
对象类:
package T;
import java.io.Serializable;//对象类
public class TT implements Serializable {//必须实现Serializable接口才能进行对象序列化和反序列化
public String name;
public String password;
public TT(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {//重写object类中的toString方法输出对象
return "TT{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
测试类:
package T;
import java.io.*;
public class Test {//测试类
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("C:\\Users\\32179\\Desktop\\test.txt");
Out ou = new Out(new FileOutputStream(file,true));//true这个参数是表示文件追加的意思,不然每次打开文件流就会new一个新的空白文件覆盖原文件
ou.writeObject(new TT("name1","password1"));
ou.writeObject(new TT("name2","password2"));//写入对象
ou.flush();//清缓存
ou.close();//关闭输入流
In in = new In(new FileInputStream(file));
TT tt = null;
try {
while ((tt = (TT) in.readObject()) != null){
System.out.println(tt);
}
} catch (Exception e) {//readObject方法读不到对象时便会执行这个异常
System.out.println("读取完毕");
}
in.close();关闭输出流
}
}
这个方法的关键在于,既不在打开输入流时加入流头信息到文件中,也不在打开输出流时读取文件中的流头信息,比第一种方法要简单粗暴一点
有些小伙伴可能就觉得,既然它都是可有可无的,干嘛在java的IO流里有这个方法呢?
诶,此言差矣,
存在即合理,不要因为影响了对象的序列化和反序列化就觉得这个writeStreamHeader和readStreamHeader就一定没有用。
比如在检测文件是否被二次执行的时候,就可以通过有没有多条流头信息来判断文件是否被多次执行,我觉得既然它有这个方法,那肯定自有它的用处,这些都得靠你们自己去研究咯。
这个问题目前来说我只发现这两种解决方案,如果还有其他解决方案欢迎在评论区讨论,哪些有错误的也请大佬在评论区帮我指正一下。
以上,就是关于Java对象序列化和反序列化遇到java.io.StreamCorruptedException: invalid type code: AC报错的两种解决方法
如果这篇文章能帮助到你,希望可以点赞收藏评论转发,让更多人看见,你的支持就是我更新的最大动力!