Java.关于IO代码复用

本文探讨了Java中如何利用IO流进行高效的数据读写,并通过代码复用提高程序的灵活性与可维护性。文章重点介绍了如何使用System.in和System.out进行数据交互,以及如何通过函数封装读取流程来应对不同输入源。

重视IO程序代码的复用 System.in是连接到键盘的,是InputStream类型的实例对象。System.out连接到显示器,是PrintStream类的实例对象。 不管各种底层物理设备用什么方式实现数据的终止点,InputStream的read方法总是返回-1来表示输入流的结束 在windows下,按下Ctrl+Z组合键可以产生键盘输入流的结束标记,在linux下则是按下Ctrl+D组合键来产生键盘输入流的结束标记。 要编程从键盘上连续读取一大段数据时,应尽量将读取数据的过程放在函数中完成,使用-1来作为键盘输入的结束点。在改函数中编写的程序不应直接使用System.in读取数据,而是用一个InputStream类型的形式参数对象来读取数据,然后将System.in作为实参传递给InputStream类型的形式参数来调用该函数。 用基类类型的对象作为形参,然后传递子类类型对象来使用,以不变应万变。提高服用性

import java.io.*; class ByteArrayTest { public static void main(String[] args) { // TODO: Add your code here /* String tmp="abcdefjklmnopqrst"; byte [] src=tmp.getBytes(); ByteArrayInputStream input=new ByteArrayInputStream(src); ByteArrayOutputStream output=new ByteArrayOutputStream();//这里不用传递字节数组,ByteArrayOutputStream会自动创建一个32字节的缓冲区用来写入数据"Creates a new byte array output stream." transform(input,output); byte[] result=output.toByteArray(); //public byte[] toByteArray() "Creates a newly allocated byte array. Its size is the current size of this output stream and the valid contents of the buffer have been copied into it." System.out.println(new String(result)); */ transform(System.in,System.out); } public static void transform(InputStream in,OutputStream out){ int ch=0; try{ while((ch=in.read())!=-1){ int upperCh=/*(int)*/Character.toUpperCase((char)ch); //char表示的范围比int小 out.write(upperCh); } }catch(Exception e){ e.printStackTrace(); } } }

<think>嗯,用户遇到了一个Java反序列化时的StreamCorruptedException异常,具体错误信息是"invalid stream header: 47455420"。这个十六进制值对应ASCII字符"GET ",非常有意思的线索。 用户可能正在开发网络应用,因为"GET"是HTTP方法的标志。这个异常通常发生在试图用ObjectInputStream读取非序列化数据时。让我仔细分析几种可能性: 首先想到HTTP协议混淆的情况。用户的服务端可能错误地打开了HTTP端口而非Socket端口,导致ObjectInputStream读取到HTTP请求头。比如客户端误将HTTP请求发送到对象流端口,或者服务端SocketServer绑定到了HTTP服务端口。 其次是流协议不匹配的问题。用户可能在同一个Socket连接上混用了不同协议,比如先发送HTTP请求再用对象流读取响应。或者客户端和服务端的序列化/反序列化逻辑不对称,比如服务端发送JSON而客户端尝试反序列化。 数据截断或污染的可能性也不能排除。如果传输过程中数据被截断,或者缓冲区未正确清空,都可能出现这种异常。特别是"GET "出现在开头,暗示传输起始部分被污染了。 从用户引用的资料看,用户已经意识到数据库IO可能引发类似问题,但这次明显是网络层问题。用户应该是个有经验的开发者,因为ta能提供具体的错误码和引用资料。 解决方案需要分步骤排查: 1. 先用十六进制查看器确认原始数据内容,这是最直接的证据 2. 检查端口绑定是否正确,避免协议冲突 3. 确保通信双方都使用纯粹的ObjectOutputStream/ObjectInputStream 4. 在对象流传输前不要发送任何其他数据 5. 网络调试时用Wireshark抓包会事半功倍 用户可能还需要注意连接复用的问题。比如在HTTP连接上尝试对象流操作,或者忘记关闭输出流导致数据堆积。这些细节在实际开发中经常被忽视。 最后生成的三个问题都很有针对性:第一个帮助用户确认当前问题类型,第二个涉及根本解决方案,第三个则考虑未来预防措施。这样既解决当务之急,又提供长期开发指导。</think>## Java反序列化 `StreamCorruptedException: invalid stream header: 47455420` 解决方案 ### 问题分析 错误信息 `java.io.StreamCorruptedException: invalid stream header: 47455420` 表明: 1. **无效流头**:十六进制值 `47 45 54 20` 对应 ASCII 字符 `"GET "` 2. **根本原因**:`ObjectInputStream` 尝试读取的数据不是 Java 序列化对象,而是 **HTTP 请求的起始部分**(`GET` 是 HTTP 方法) ### 常见原因及解决方案 #### 🚫 1. 协议混淆(最常见) * **原因**:客户端向服务端的 **对象流端口发送了 HTTP 请求**,或服务端在对象流端口接收到了 HTTP 流量 * **排查**: * 检查客户端是否误将 HTTP URL (如 `http://...`) 用于 `Socket` 连接 * 确认服务端端口未被 HTTP 服务(如 Tomcat)占用 * **解决**: ```java // 错误:使用 HTTP URL 连接对象流 // Socket socket = new Socket("http://example.com", 8080); // 正确:使用纯 IP/主机名 + 端口 Socket socket = new Socket("192.168.1.100", 9090); ``` #### 🔄 2. 流污染 * **原因**:同一 Socket 连接先被用于 **HTTP 通信**,后续未关闭就直接用于 **对象流传输** * **解决**: * **严格隔离协议**:同一连接不同时混用 HTTP 和对象流 * 重建连接或确保流完全重置: ```java // 错误:复用含 HTTP 数据的连接 // OutputStream out = httpSocket.getOutputStream(); // ObjectOutputStream oos = new ObjectOutputStream(out); // 污染! // 正确:为对象流创建专用连接 Socket objSocket = new Socket(host, objPort); ObjectOutputStream oos = new ObjectOutputStream(objSocket.getOutputStream()); ``` #### 📦 3. 数据截断/损坏 * **原因**:网络中断或缓冲区未刷新导致数据不完整 * **解决**: * 发送后强制刷新输出流: ```java oos.writeObject(myObject); oos.flush(); // 确保数据完全发送 ``` * 添加传输完整性校验(如 MD5) #### ⚙ 4. 服务端配置错误 * **原因**:服务端 `ServerSocket` 绑定到 **HTTP 服务端口**(如 80/8080),但客户端期望对象流 * **解决**: * 为对象流分配**独立端口**(如 9090) * 确保服务端代码正确处理对象流: ```java // 服务端正确示例 try (ServerSocket server = new ServerSocket(9090); Socket client = server.accept(); ObjectInputStream ois = new ObjectInputStream(client.getInputStream())) { MyObject obj = (MyObject) ois.readObject(); // 正确反序列化 } ``` ### 🔍 诊断步骤 1. **十六进制验证**:检查接收的前4字节是否为 `AC ED 00 05`(Java序列化魔数) 2. **网络抓包**:用 Wireshark 捕获流量,确认传输内容 3. **日志检查**:在 `ObjectInputStream` 前打印输入流前4字节: ```java InputStream is = socket.getInputStream(); byte[] header = new byte[4]; is.read(header); // 先读4字节 System.out.println("Header: " + Arrays.toString(header)); // 应输出 [-84, -19, 0, 5] // 若为 [71, 69, 84, 32] 则确认是 "GET " ``` > **关键结论**:`47455420` 明确指向 **HTTP 协议污染**。需隔离对象流通信与 HTTP 流量,确保协议一致性[^1][^2]。 --- ### ❓相关问题 1. **如何区分 Java 序列化流和其他协议的数据?** > 可通过检查流开头的**魔数**(Magic Number):Java 序列化流的头4字节固定为十六进制 `AC ED 00 05`。 2. **在混合协议系统中如何安全地复用 Socket 连接?** > 需设计**协议协商机制**:连接建立后先发送协议标识符(如 `"OBJSTREAM"`),接收方根据标识符切换处理方式。 3. **除了 `StreamCorruptedException`,反序列化还可能遇到哪些常见异常?** > 典型异常包括: > - `InvalidClassException`(类版本不匹配) > - `OptionalDataException`(预期对象但遇到原始数据) > - `ClassNotFoundException`(缺少依赖类)[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值