当前位置: 首页 > news >正文

Java网络编程中的I/O操作:从字节流到对象序列化

1.Java I/O 基础体系

1. 字节流与字符流的核心区别

Java I/O 基于流(Stream)的概念,根据处理数据类型分为:

  • 字节流(Byte Stream):以8位字节为单位处理数据,适用于二进制数据(如图片、音频)
  • 基类:InputStream / OutputStream
  • 字符流(Character Stream):以16位Unicode字符为单位处理数据,自动处理字符编码转换,适用于文本数据
  • 基类:Reader / Writer

▶ 核心流类层次结构

                  +------------+
                  |  抽象基类  |
                  +------------+
                  /           \
         +----------------+ +----------------+
         |  字节流基类    | |  字符流基类    |
         |InputStream/Out| | Reader/Writer  |
         +----------------+ +----------------+
         /       |        \      /       |        \
+--------+ +--------+ +--------+ +--------+ +--------+ +--------+
|FileInput| |Buffered| |DataInput| |FileReader| |Buffered| |PrintWriter|
|Stream   | |InputStream| |Stream  | |        | |Reader  | |        |
+--------+ +--------+ +--------+ +--------+ +--------+ +--------+

2. 网络编程中的 I/O 应用场景

  • TCP通信:通过Socket.getInputStream()和Socket.getOutputStream()获取流对象
  • UDP通信:结合DatagramPacket使用字节流手动处理数据
  • 对象传输:通过序列化将对象转换为字节流在网络中传输

2.字节流在网络编程中的应用

1. 基于字节流的 TCP 通信示例

▶ 示例场景:客户端向服务器发送文件

① 客户端代码(发送文件)

import java.io.*;
import java.net.Socket;public class TCPClient {public static void main(String[] args) {try (Socket socket = new Socket("localhost", 8888);FileInputStream fileIn = new FileInputStream("example.jpg");OutputStream socketOut = socket.getOutputStream()) {// 发送文件长度long fileLength = new File("example.jpg").length();DataOutputStream dataOut = new DataOutputStream(socketOut);
            dataOut.writeLong(fileLength);// 发送文件内容byte[] buffer = new byte[4096];int bytesRead;while ((bytesRead = fileIn.read(buffer)) != -1) {
                socketOut.write(buffer, 0, bytesRead);}System.out.println("文件发送完成");} catch (Exception e) {
            e.printStackTrace();}}
}

② 服务器代码(接收文件)

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class TCPServer {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888)) {System.out.println("服务器启动,等待连接...");Socket socket = serverSocket.accept();System.out.println("客户端已连接");// 接收文件长度DataInputStream dataIn = new DataInputStream(socket.getInputStream());long fileLength = dataIn.readLong();// 接收文件内容FileOutputStream fileOut = new FileOutputStream("received.jpg");InputStream socketIn = socket.getInputStream();byte[] buffer = new byte[4096];long totalBytesRead = 0;int bytesRead;while (totalBytesRead < fileLength && (bytesRead = socketIn.read(buffer, 0, (int) Math.min(buffer.length, fileLength - totalBytesRead))) != -1) {
                fileOut.write(buffer, 0, bytesRead);
                totalBytesRead += bytesRead;}System.out.println("文件接收完成,大小:" + totalBytesRead + " 字节");} catch (Exception e) {
            e.printStackTrace();}}
}

2. 关键技术点解析

  • DataOutputStream:用于写入基本数据类型(如writeLong()),确保跨平台数据一致性
  • 缓冲策略:使用固定大小的缓冲区(如4096字节)避免频繁I/O操作
  • 文件长度处理:先发送文件长度,确保接收方正确读取完整文件

3.字符流在网络编程中的应用

1. 基于字符流的 TCP 文本通信

▶ 示例场景:客户端与服务器进行多行文本交互

① 客户端代码(发送/接收文本)

import java.io.*;
import java.net.Socket;
import java.util.Scanner;public class TextClient {public static void main(String[] args) {try (Socket socket = new Socket("localhost", 9999);PrintWriter out = new PrintWriter(socket.getOutputStream(), true);BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));Scanner scanner = new Scanner(System.in)) {// 发送文本消息System.out.print("请输入消息(输入exit结束):");String message;while (!(message = scanner.nextLine()).equalsIgnoreCase("exit")) {
                out.println(message);// 接收响应String response = in.readLine();System.out.println("服务器响应:" + response);System.out.print("请输入消息:");}} catch (Exception e) {
            e.printStackTrace();}}
}

② 服务器代码(接收/响应文本)

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class TextServer {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(9999)) {System.out.println("文本服务器启动,等待连接...");Socket socket = serverSocket.accept();System.out.println("客户端已连接");BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(socket.getOutputStream(), true);String inputLine;while ((inputLine = in.readLine()) != null) {System.out.println("客户端消息:" + inputLine);
                out.println("已收到:" + inputLine);}} catch (Exception e) {
            e.printStackTrace();}}
}

2. 字符流使用要点

  • 转换流:InputStreamReader/OutputStreamWriter实现字节流与字符流的转换
  • 自动刷新:PrintWriter构造函数的第二个参数设为true可自动刷新缓冲区
  • 行结束符处理:BufferedReader.readLine()会自动去除行结束符,发送时需用println()添加

4.缓冲流与性能优化

1. 缓冲流的工作原理

缓冲流(BufferedInputStream/BufferedOutputStream、BufferedReader/BufferedWriter)通过内部缓冲区减少直接I/O操作次数:

  • 读操作:一次性从底层流读取大量数据到缓冲区,后续读取直接从缓冲区获取
  • 写操作:数据先写入缓冲区,缓冲区满或调用flush()时才写入底层流

▶ 缓冲流工作示意图

+----------------+       +----------------+       +----------------+
| 应用程序        | <---> | 缓冲流           | <---> | 底层流(网络/文件) |
+----------------+       +----------------+       +----------------+

2. 缓冲流性能对比测试

以下代码对比使用缓冲流与不使用缓冲流的文件复制速度:

import java.io.*;public class BufferPerformanceTest {
    private static final String SOURCE_FILE = "large_file.zip";
    private static final String DEST_FILE1 = "copy_without_buffer.zip";
    private static final String DEST_FILE2 = "copy_with_buffer.zip";    public static void main(String[] args) throws IOException {
        // 测试1:不使用缓冲流
        long startTime = System.currentTimeMillis();
        try (FileInputStream fis = new FileInputStream(SOURCE_FILE);
             FileOutputStream fos = new FileOutputStream(DEST_FILE1)) {
            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("不使用缓冲流耗时:" + (endTime - startTime) + "ms");        // 测试2:使用缓冲流
        startTime = System.currentTimeMillis();
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SOURCE_FILE));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(DEST_FILE2))) {
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
        }
        endTime = System.currentTimeMillis();
        System.out.println("使用缓冲流耗时:" + (endTime - startTime) + "ms");
    }
}

典型测试结果(复制100MB文件):

  • 不使用缓冲流:约5000ms
  • 使用缓冲流:约50ms(性能提升100倍)

5.对象序列化与网络传输

1. 序列化基础概念

  • 序列化:将Java对象转换为字节流的过程(ObjectOutputStream)
  • 反序列化:将字节流恢复为Java对象的过程(ObjectInputStream)
  • 要求:被序列化的类必须实现Serializable接口(标记接口,无方法)

▶ 序列化流程示意图

Java对象 --(序列化)--> 字节流 --(网络传输)--> 字节流 --(反序列化)--> Java对象

2. 网络传输对象示例

▶ 定义可序列化对象

import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L;  // 序列化版本号private String username;private int age;private transient String password;  // transient字段不参与序列化public User(String username, int age, String password) {this.username = username;this.age = age;this.password = password;}// Getters and setterspublic String getUsername() { return username; }public int getAge() { return age; }public String getPassword() { return password; }@Overridepublic String toString() {return "User{username='" + username + "', age=" + age + ", password=" + password + "}";}
}

▶ 客户端发送对象

import java.io.*;
import java.net.Socket;public class ObjectClient {public static void main(String[] args) {try (Socket socket = new Socket("localhost", 12345);ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {// 创建并发送User对象User user = new User("Alice", 25, "securepass");
            oos.writeObject(user);System.out.println("对象已发送:" + user);} catch (Exception e) {
            e.printStackTrace();}}
}

▶ 服务器接收对象

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class ObjectServer {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(12345)) {System.out.println("对象服务器启动,等待连接...");Socket socket = serverSocket.accept();try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {User receivedUser = (User) ois.readObject();System.out.println("接收到对象:" + receivedUser);}} catch (Exception e) {
            e.printStackTrace();}}
}

3. 序列化注意事项

  • serialVersionUID:建议显式声明版本号,避免类修改导致反序列化失败
  • transient关键字:标记不需要序列化的字段(如密码、敏感数据)
  • 类兼容性:反序列化时需要确保类的结构与序列化时一致
  • 安全性:避免反序列化不可信来源的数据,防止反序列化漏洞

6.I/O 模型对比与选择

1. Java I/O 模型演进

模型

特点

适用场景

BIO

阻塞 I/O,每个连接分配一个线程

连接数少且稳定的场景

NIO

非阻塞 I/O,单线程管理多个连接

高并发、短连接场景

AIO

异步 I/O,基于回调处理结果

长连接、耗时操作场景

2. 网络编程 I/O 选择建议

  • 小并发场景:使用BIO + 缓冲流,代码简单易维护
  • 高并发场景:使用NIO(Java NIO包)或Netty框架
  • 对象传输:优先使用标准序列化,性能敏感场景考虑Protocol Buffers等替代方案

7.总结

Java网络编程中的I/O操作是构建高性能、可靠网络应用的基础:

  • 字节流适合处理二进制数据,是网络通信的基础
  • 字符流简化了文本数据处理,自动处理字符编码
  • 缓冲流通过减少系统调用显著提升性能
  • 对象序列化提供了便捷的对象传输机制

合理选择I/O模型和流类型,结合缓冲策略和序列化技术,能有效提升网络应用的性能和可维护性。

http://www.xdnf.cn/news/654445.html

相关文章:

  • DJI上云API官方demo学习
  • JavaSE核心知识点04工具04-01(JDK21)
  • 【opencv】vs2019中配置opencv
  • 同一个核磁共振(MRI)检查中,不同序列的图像之间空间坐标定位如何实现
  • Redis | 缓存技术对后端的重要性
  • STM32之SPI——外部FLASH和RFID
  • 宫格导航--纯血鸿蒙组件库AUI
  • 树莓派超全系列教程文档--(47)如何使用内核补丁
  • QT中常用的类
  • Cesium 实战 26 - 自定义纹理材质 - 实际应用之飞线(抛物线)
  • 并发的产生及对应的解决方案之服务架构说明
  • 第1章第1节:安全运维基础思维与体系建设-安全运维的定义与核心目标
  • Ext系列文件系统
  • 分布式缓存:证明分布式系统的 CAP 理论
  • [闲谈]C语言的面向对象
  • 易境通WMS系统:赋能快消品海外仓高效管理
  • 完美解决Docker镜像无法拉取问题(转载)
  • 服务器的IP是什么东西?
  • uniapp-商城-69-shop(2-商品列表,点击商品展示,商品的详情, vuex的使用,rich-text使用)
  • ESP8266_AP机械手 第三篇Uniapp遥控器
  • ElasticSearch--DSL查询语句
  • 信创 CDC 实战 | OGG、Attunity……之后,信创数据库实时同步链路如何构建?(以 GaussDB 数据入仓为例)
  • FreeRTOS 在物联网传感器节点的应用:低功耗实时数据采集与传输方案
  • 综合实现案例 LVS keepalived mysql 等
  • 《基于Keepalived+LVS+Web+NFS的高可用集群搭建》
  • MPI实现大数据Ring Broadcast逻辑
  • 关于 SSE(Server-Sent Events)过程的简要解剖
  • 07-后端Web实战(部门管理)
  • Prometheus、Exporter 和 Grafana:性能分析铁三角
  • 卷积神经网络(CNN)模型