理解Java中字符流与字节流
1. 什么是流
Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列。和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序列的对象被称为输入流;能够向其写入一个字节序列的对象被称为输出流。
2. 字节流
Java中的字节流处理的最基本单位为单个字节,它通常用来处理二进制数据。Java中最基本的两个字节流类是InputStream和OutputStream,它们分别代表了组基本的输入字节流和输出字节流。InputStream类与OutputStream类均为抽象类,我们在实际使用中通常使用Java类库中提供的它们的一系列子类。下面我们以InputStream类为例,来介绍下Java中的字节流。
InputStream类中定义了一个基本的用于从字节流中读取字节的方法read,这个方法的定义如下:
public abstract int read() throws IOException;
这是一个抽象方法,也就是说任何派生自InputStream的输入字节流类都需要实现这一方法,这一方法的功能是从字节流中读取一个字节,若到了末尾则返回-1,否则返回读入的字节。关于这个方法我们需要注意的是,它会一直阻塞知道返回一个读取到的字节或是-1。另外,字节流在默认情况下是不支持缓存的,这意味着每调用一次read方法都会请求操作系统来读取一个字节,这往往会伴随着一次磁盘IO,因此效率会比较低。有的小伙伴可能认为InputStream类中read的以字节数组为参数的重载方法,能够一次读入多个字节而不用频繁的进行磁盘IO。那么究竟是不是这样呢?我们来看一下这个方法的源码:
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
它调用了另一个版本的read重载方法,那我们就接着往下追:

public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}

从以上的代码我们可以看到,实际上read(byte[])方法内部也是通过循环调用read()方法来实现“一次”读入一个字节数组的,因此本质来说这个方法也未使用内存缓冲区。要使用内存缓冲区以提高读取的效率,我们应该使用BufferedInputStream。
3. 字符流
Java中的字符流处理的最基本的单元是Unicode码元(大小2字节),它通常用来处理文本数据。所谓Unicode码元,也就是一个Unicode代码单元,范围是0x0000~0xFFFF。在以上范围内的每个数字都与一个字符相对应,Java中的String类型默认就把字符以Unicode规则编码而后存储在内存中。然而与存储在内存中不同,存储在磁盘上的数据通常有着各种各样的编码方式。使用不同的编码方式,相同的字符会有不同的二进制表示。实际上字符流是这样工作的:
- 输出字符流:把要写入文件的字符序列(实际上是Unicode码元序列)转为指定编码方式下的字节序列,然后再写入到文件中;
- 输入字符流:把要读取的字节序列按指定编码方式解码为相应字符序列(实际上是Unicode码元序列从)从而可以存在内存中。
我们通过一个demo来加深对这一过程的理解,示例代码如下:

import java.io.FileWriter;
import java.io.IOException; public class FileWriterDemo {
public static void main(String[] args) {
FileWriter fileWriter = null;
try {
try {
fileWriter = new FileWriter("demo.txt");
fileWriter.write("demo");
} finally {
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

以上代码中,我们使用FileWriter向demo.txt中写入了“demo”这四个字符,我们用十六进制编辑器WinHex查看下demo.txt的内容:

从上图可以看出,我们写入的“demo”被编码为了“64 65 6D 6F”,但是我们并没有在上面的代码中显式指定编码方式,实际上,在我们没有指定时使用的是操作系统的默认字符编码方式来对我们要写入的字符进行编码。
由于字符流在输出前实际上是要完成Unicode码元序列到相应编码方式的字节序列的转换,所以它会使用内存缓冲区来存放转换后得到的字节序列,等待都转换完毕再一同写入磁盘文件中。
4. 字符流与字节流的区别
经过以上的描述,我们可以知道字节流与字符流之间主要的区别体现在以下几个方面:
- 字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
- 字节流默认不使用缓冲区;字符流使用缓冲区。
- 字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元。
以上是我对Java中字符流与字节流的一些认识,如有叙述不清晰或是不准确的地方希望大家可以指正,谢谢大家:)
5. 参考资料
《Java核心技术 卷二》
理解Java中字符流与字节流的更多相关文章
- 理解Java中字符流与字节流的区别(转)
1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序 ...
- 理解Java中字符流与字节流的区别
1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序 ...
- Java中字符流与字节流的区别
字符流处理的单元为2个字节的Unicode字符,分别操作字符.字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组.所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单 ...
- 【转】输入/输出流 - 深入理解Java中的流 (Stream)
基于流的数据读写,太抽象了,什么叫基于流,什么是流?Hadoop是Java语言写的,所以想理解好Hadoop的Streaming Data Access,还得从Java流机制入手.流机制也是JAVA及 ...
- 深入理解 Java中的 流 (Stream)
首先,流是什么? 流是个抽象的概念.是对输入输出设备的抽象,Java程序中,对于数据的输入/输出操作都是以"流"的方式进行.设备能够是文件,网络,内存等. 流具有方向性,至于是输入 ...
- Java:文件字符流和字节流的输入和输出
最近在学习Java,所以就总结一篇文件字节流和字符流的输入和输出. 总的来说,IO流分类如下: 输入输出方向: 输入流(从外设读取到内存)和输出流(从内存输出到外设) 数据的操作方式: 字节流 ...
- Java中的流(2)字节流-InputStream和OutputStream
字节流的两个顶层类是抽象类:InputStream和OutputStream 1. OutputStream void write(int b) 往流中写一个字节b void write(byte b ...
- java字符流与字节流的区别是什么
java中字符流与字节流的区别: 1.字节流操作的基本单元为字节:字符流操作的基本单元为Unicode码元. 2.字节流默认不使用缓冲区:字符流使用缓冲区. 3.字节流通常用于处理二进制数据,实际上它 ...
- Java字符流和字节流对文件操作
记得当初自己刚开始学习Java的时候,对Java的IO流这一块特别不明白,所以写了这篇随笔希望能对刚开始学习Java的人有所帮助,也方便以后自己查询.Java的IO流分为字符流(Reader,Writ ...
随机推荐
- Android面试题(2)
1.activity的生命周期 方法 描述 可被杀死 下一个 onCreate() 在activity第一次被创建的时候调用.这里是你做所有初始化设置的地方──创建视图.设置布局.绑定数据至列表等.如 ...
- 给ubuntu设置静态ip —— How to set static IP Address in Ubuntu Server 16.04
原文: http://www.configserverfirewall.com/ubuntu-linux/ubuntu-set-static-ip-address/ ----------------- ...
- LVS+keepalived+nginx
LVS是Linux Virtual Server的简写,基于4层协议不处理,不响应,只转发,速度更快 wget -c http://www.linuxvirtualserver.org/softwar ...
- Win7如何关闭操作中心的图标
运行gpedit.msc,打开组策略编辑器 双击"删除操作中心图标",将其启用即可(重启可见)
- js数组高阶方法reduce经典用法代码分享
以下是个人在工作中收藏总结的一些关于javascript数组方法reduce的相关代码片段,后续遇到其他使用这个函数的场景,将会陆续添加,这里作为备忘. javascript数组那么多方法,为什么我要 ...
- http keep-alive 解释
1.概念 keep-alive示例: keep-alive模式(又称持久连接.连接重用)时,keep-alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,keep-alive功 ...
- C++ 代码风格准则:POD
作者:一根筋的傻瓜链接:https://www.zhihu.com/question/36379130/answer/69853366来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...
- hibernate 关系映射之 单向外键关联一对一
这里的关系指的是对象与对象之间的关系 注解方式单向关联一对一: //这个类描述的husband是一个对应一个wife的 import javax.persistence.Entity; import ...
- 用 WebSocket 实现一个简单的客服聊天系统
一 需求 一个多商家的电商系统,比如京东商城,不同商家之间的客服是不同的,所面对的用户也是不同的.要实现一个这样的客服聊天系统,那该系统就必须是一个支持多客服.客服一对多用户的聊天系统. 二 思路 使 ...
- 数字图像和视频处理的基础-第4周运动预计matlab练习题
In this problem you will perform block matching motion estimation between two consecutive video fram ...