简析Java字节流和字符流

Java的IO操作分为对字节流的操作和对字符流的操作。针对这两类操作,java.io包有不同的类来提供相应的不同读写功能。本文以输出流作为例子,聚焦字节流和字符流在操作上的差异。

字节流,顾名思义是以字节作为基本单位来操作的,将原始数据的原始字节写入文件等。字节流操作一个显著的特点在于,它不使用缓冲区,每一个字节都直接写入文件,然后再进行下一个字节的操作。FileOutputStream是一个文件输出流类。来观察一下它的使用。

String testStr = "Iam无与伦比的美丽";
byte b[] = testStr.getBytes();
logger.info("" + b.length);
		
File f1 = new File(pathOfTestfile + "testOutput1.txt");
try {
	FileOutputStream out1 = new FileOutputStream(f1);
	out1.write(b[0]);
	out1.write(b[1]);
	out1.write(b[2]);
	out1.write(b[3]);
	out1.write(b[4]);
	out1.write(b[5]);
	out1.write(b[6]);
//	out.close();
} catch (IOException e) {
	e.getMessage();
}
结果发现,byte类型数组b的长度是17,而文件已经被写入了一些数据:

需要注意,我们从字符串通过getBytes()方法得到的byte数组长度是17,这是因为getBytes()会将字符串变成系统默认编码的字节序列。这里用windows平台进行测试,所以系统默认编码是GBK。GBK编码兼容ASCII字符集,所以“Iam”只需要3个字节,而“无与伦比的美丽”这7个中文字分别用了2个字节。注意到写入b[6],其实只将表示“与”字的其中一个字节写入,所以显示乱码。

而更加需要留意的是,尽管我们没有执行close()方法,但是依然将b[0]到b[6]写入了文件。而其后我们将会看到,字符流操作不具有这个性质。

FileWriter也是一个文件输出流类,但是是面向字符流的。

File f2 = new File(pathOfTestfile + "testOutput2.txt");
try {
	FileWriter out2 = new FileWriter(f2);
	out2.write(testStr);
//	out2.close();
} catch (IOException e) {
	e.getMessage();
}
结果发现,字符串并没有被写入文件,文件依然为空。这是因为字符流是使用缓冲区的,只有在关闭流或者使用flush()来刷新输出流,才能把缓冲区中的字符串序列写入文件。

来看一下取消了close()方法后,写入字符串后的文件结果如何。可以看到字符串显示正常。其实这个结果也又可以讨论的点:Java内部用utf-16来编码字符串,但是它们写入一个GBK编码的文件中却没有显示乱码。实际上,Java并非直接将utf-16编码的字符串直接写入文件,而是先将它们转换成系统默认编码的序列(在这里是GBK),再写入文件。这也是字符流使用缓冲区的一个显著优势,如果没有缓冲区的话,这种编码转换是难以完成的。

实际上,字符归根结底是字节,只有当字节显示成文本时,字符这个概念才有了实际的意义。

结合一些博客资料,认为处理文本文件的时候可以用字符流,而处理其他二进制文件则应该使用字节流。

字符流以字符为基本操作单位,使用缓冲区。

字节流以字节为基本操作单位,不使用缓冲区。

经验分享 程序员 微信小程序 职场和发展