ObjectOutputStream 追加写入读取错误

本文详细解释了使用ObjectOutputStream在文件中追加写入对象时遇到的StreamCorruptedException问题,通过分析其原因并提供了一个自定义类MyObjectOutputStream的解决方案,该方案覆盖了ObjectOutputStream的writeStreamHeader()方法,以避免在追加写入时产生额外的头部信息,从而确保对象可以正确读取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

用类ObjectOutputStream向文件写读对象时,碰到一个问题:新建一个文件,用输出流ObjectOutputStream向文件连续写几个对象,关闭输出流,然 后读取,这些对象都可以读出;这时在向该文件增加对象,新写的对象就读不出了

问题出现的原因:

ObjectOutputStream建立后第一次写入一个对象时, 会在对象数据前写入一些标志的数据“AC ED  00 05”(用二进制方式查看打开),应该是流相关的信息。当你关闭 ObjectOutputStream 后再重新打开往文件里面写对象时(append方式),就会再一次把“AC ED 00 05”写入文件,而这些信息并不是你写入对象的数据,所以当你用ObjectInputStream来读取对象时,流会将除第一个“AC ED 00 05”以外的数据当作各个对象的数据,造成无法解析,所以读取时有一个java.io.StreamCorruptedException出现。 这个可以通过编辑Info.dat来验证,只要将“AC ED 00 05”删除(第一个“AC ED 00 05”保留)就可以正常读出后来加入的对象。 给出一个比较笨的解决方法: 在以后要添加新的对象到Info.dat时,将里面原有的对象读出放入ArrayList中,清空文件,再将对象集一次写入。 

尝试解决办法: 

那个“AC ED 00 05”是  ObjectOutputStream.writeSystemHeader()写进去的,你可以继承ObjectOutputStream类,覆盖这个方法。 在你自己的writeSystemHeader()里判断是不是第一次写入一个文件,如果是向一个文件大小不为零的文件追加的话,就调用  super.reset(),如果是第一次写这个文件,不是追加,就调用super.writeSystemHeader() 

自定义类MyObjectOutputStream 

	/**
	 * 此类继承ObjectOutputStream,重写writeStreamHeader()方法,以实现追加写入时去掉头部信息
	 */
	public static class MyObjectOutputStream extends ObjectOutputStream {
		private static File f;

		// writeStreamHeader()方法是在ObjectOutputStream的构造方法里调用的
		// 由于覆盖后的writeStreamHeader()方法用到了f。如果直接用此构造方法创建
		// 一个MyObjectOutputStream对象,那么writeStreamHeader()中的f是空指针
		// 因为f还没有初始化。所以这里采用单态模式
		private MyObjectOutputStream(OutputStream out, File f) throws IOException, SecurityException {
			super(out);
		}

		// 返回一个MyObjectOutputStream对象,这里保证了new MyObjectOutputStream(out, f)
		// 之前f已经指向一个File对象
		public static MyObjectOutputStream newInstance(File file, OutputStream out) throws IOException {
			f = file;// 本方法最重要的地方:构建文件对象,两个引用指向同一个文件对象
			return new MyObjectOutputStream(out, f);
		}

		@Override
		protected void writeStreamHeader() throws IOException {
			// 文件不存在或文件为空,此时是第一次写入文件,所以要把头部信息写入。
			if (!f.exists() || (f.exists() && f.length() == 0)) {
				super.writeStreamHeader();
			} else {
				// 不需要做任何事情
			}
		}
	}


测试方法: 

	public static void writeObject() throws Exception {
		String filename = "test.txt";
		File f = new File(filename);
		OutputStream os = new FileOutputStream(f, true);
		MyObjectOutputStream moos = MyObjectOutputStream.newInstance(f, os);
		moos.writeObject("abc");
		moos.writeObject(1);
		moos.close();
	}



<think>嗯,用户想了解Java的输入输出怎么搞。首先,我得回忆一下Java中常用的输入输出方法。Java的I/O涉及很多类,但初学者可能更需要基础的输入输出操作,比如控制台输入和文件操作。 先想控制台输出。System.out.println是最常见的,但用户可能已经知道这个,不过还是得提一下。然后是格式化输出,比如printf,可以用格式字符串,比如%d、%s这些,这个可能对用户有帮助。 接下来是控制台输入。Java里用Scanner类比较多,记得要导入java.util.Scanner。Scanner可以读取不同类型的输入,比如nextInt(), nextLine()之类的。但要注意nextInt()之后可能会有换行符的问题,需要用nextLine()来吸收。还有可能用户会问为什么有时候输入没反应,可能就是缓冲区的问题。 另一种输入方式是BufferedReader,这个可能更高效,尤其是读取大文件的时候。不过得处理IOException,可能对新手来说有点麻烦,但需要提到。 然后,文件读写部分。用户可能也需要知道如何读写文件。用FileReader和FileWriter的话,可能会有编码问题,所以可能应该推荐使用FileInputStream和FileOutputStream,或者更好的方式是用BufferedReader和BufferedWriter来包装,提高效率。还有Java 7之后的try-with-resources语法,可以自动关闭资源,避免忘记关闭流。 可能需要分步骤讲解,比如控制台输出、控制台输入、文件读写,每个部分给出示例代码,并解释注意事项。比如Scanner的陷阱,比如nextInt()后接nextLine()的问题,或者文件路径的问题,绝对路径和相对路径的区别。 另外,异常处理也很重要,比如IOException必须处理,在Scanner和BufferedReader中都需要捕获异常或者抛出。对于新手来说,可能用try-catch块来包裹代码,避免程序崩溃。 还要注意字符编码的问题,比如读写文件时默认使用平台编码,可能需要指定UTF-8之类的,尤其是跨平台时。 最后,可能需要总结一下常用的类和方法,推荐使用Scanner处理控制台输入,BufferedReader处理大文件,PrintWriter做格式化文件输出,以及try-with-resources自动管理资源。 结构上,先讲控制台输出,再讲输入,然后文件读写,每个部分分点说明,给出代码示例,指出常见问题和注意事项。使用中文,代码部分用正确的Java语法,可能需要用代码块标记,但用户这里要求用$$...$$,不过用户的问题是关于Java的,可能更希望看到代码示例。不过根据系统指令,数学公式才用$或$$,代码应该用其他方式处理,可能用代码块或者缩进。但用户给的例子中用的是中文回答,所以可能需要将代码放在段落中,或者用文字描述清楚。 需要检查是否有遗漏的部分,比如二进制文件的读写,可能用户暂时不需要,但可以提一下有DataInputStream/DataOutputStream。或者对象序列化,但可能超出基础范围。 总之,整理思路,分步骤讲解,给出示例代码,并指出常见错误和解决方法,确保用户能逐步理解和应用Java的输入输出操作。</think>Java的输入输出(I/O)操作主要涉及控制台交互和文件读写,以下是逐步说明: --- ### 一、控制台输出 Java通过`System.out`对象实现控制台输出: ```java // 基础输出 System.out.println("Hello World"); // 输出后换行 System.out.print("不换行输出"); // 不换行 // 格式化输出 int num = 10; System.out.printf("数字:%d, 字符串:%s", num, "Java"); // 类似C语言格式 ``` --- ### 二、控制台输入 #### 方法1:使用`Scanner`类(推荐) ```java import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("输入整数:"); int a = scanner.nextInt(); // 读取整数 System.out.print("输入字符串:"); scanner.nextLine(); // 吸收换行符(关键!) String s = scanner.nextLine(); // 读取整行 scanner.close(); // 关闭资源 } } ``` **注意**:`nextInt()`等非整行读取方法后需调用`nextLine()`吸收残留的换行符。 #### 方法2:使用`BufferedReader` ```java import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class Main { public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); System.out.print("输入内容:"); String input = reader.readLine(); // 直接读取整行 reader.close(); } } ``` --- ### 三、文件读写 #### 1. 写入文件 ```java import java.io.FileWriter; import java.io.IOException; public class Main { public static void main(String[] args) { // 使用try-with-resources自动关闭资源(Java 7+) try (FileWriter writer = new FileWriter("output.txt")) { writer.write("Hello File I/O\n"); writer.append("追加内容"); } catch (IOException e) { e.printStackTrace(); } } } ``` #### 2. 读取文件 ```java import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class Main { public static void main(String[] args) { try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } } } ``` --- ### 四、关键注意事项 1. **资源释放**:务必关闭`Scanner`、`BufferedReader`等资源,或使用`try-with-resources`语法自动关闭。 2. **异常处理**:文件操作需处理`IOException`(通过`try-catch`或声明`throws`)。 3. **性能优化**:大文件读写建议用`BufferedReader`/`BufferedWriter`减少I/O次数。 4. **路径问题**:文件路径建议用绝对路径(如`C:/data.txt`),或相对路径(相对于项目根目录)。 --- ### 五、扩展建议 - 二进制文件:使用`FileInputStream`和`FileOutputStream` - 对象序列化:通过`ObjectOutputStream`和`ObjectInputStream` - 字符编码:可通过`InputStreamReader`指定编码(如`UTF-8`) 通过以上步骤,可覆盖Java基础I/O操作的常见场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值