1. 不同场景下字节流与字符流的选择
- 简单文本传输:
- 选择字符流:文本本质是字符组成,字符流处理文本更方便,它可以按照字符编码(如UTF - 8、GBK等)进行处理。例如使用
BufferedReader
和BufferedWriter
,它们提供了按行读取和写入的方法,适合处理文本行数据。
- 原因:字符流在处理文本时,会自动处理字符编码转换,开发者无需手动处理字节到字符的复杂转换过程。
- 二进制文件传输:
- 选择字节流:二进制文件(如图片、视频、可执行文件等)不能简单地按字符处理,字节流能直接处理原始字节数据,不会对数据进行额外的字符编码转换等操作,确保数据的完整性。例如使用
FileInputStream
和FileOutputStream
结合Socket.getInputStream()
和Socket.getOutputStream()
进行文件传输。
- 原因:如果使用字符流处理二进制文件,可能会因为字符编码转换导致数据损坏,因为字符流的设计是针对字符数据处理的。
2. 性能优化
- 字节流性能优化:
- 使用缓冲:使用
BufferedInputStream
和BufferedOutputStream
,它们内部有缓冲区,减少了实际的I/O操作次数。例如在文件传输时,通过缓冲流一次读取或写入较大的数据块,而不是单个字节地操作。
- 示例代码:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("sourceFile"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("destinationFile"))) {
byte[] buffer = new byte[1024 * 8];//8KB缓冲区
int length;
while ((length = bis.read(buffer)) != -1) {
bos.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
- 字符流性能优化:
- 同样使用缓冲:使用
BufferedReader
和BufferedWriter
,提高字符读取和写入的效率。例如在文本传输时,缓冲流可以按行高效读取和写入,减少I/O操作次数。
- 示例代码:
try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
3. 编码问题及解决方案
- 编码问题:
- 字符流编码不匹配:在字符流处理中,如果发送端和接收端使用的字符编码不一致,会导致乱码问题。例如发送端使用UTF - 8编码,而接收端使用GBK解码。
- 字节流转换字符流编码问题:在将字节流包装成字符流时,如果指定的字符编码与实际数据的编码不一致,也会出现乱码。比如字节流中的数据是UTF - 8编码,但使用
InputStreamReader
包装时指定了ISO - 8859 - 1编码。
- 解决方案:
- 统一编码:在通信双方事先约定好使用的字符编码,并且在代码中确保使用一致的编码。例如都使用UTF - 8编码。在创建字符流时,明确指定编码,如
new InputStreamReader(socket.getInputStream(), "UTF - 8")
和new OutputStreamWriter(socket.getOutputStream(), "UTF - 8")
。
- 检测编码:可以使用第三方库(如juniversalchardet)来检测字节流数据的编码,然后根据检测结果正确地将字节流转换为字符流。不过这种方法相对复杂,并且可能存在一定的误判率,所以尽量以双方约定编码的方式为主。