【转】理解Java字节流与字符流的区别与联系

Java中的“流”是什么?

    一个Java I/O对象叫做数据流。读取数据到内存的对象叫做输入流,内存写出数据的对象叫做输出流。

针对其面向的不同角度,可以将流分为下面几种类型:

按照数据流的方向不同分为输入流和输出流。这种分类不是绝对的,例如在向一个文件写入数据时,它就是输出流;而在读取数据时,它就是输入流。

按照处理数据的单位不同分为字节流和字符流。表示以字节为单位从stream中读取或往stream中写入信息,通常用来读取二进制数据。字符流:以Unicode字符为单位从stream中读取或往stream中写入信息。

按照功能的不同分为节点流和处理流。节点流是从特定的数据节点(文件、数据库、内存等)读写数据;处理流是连接在已有的流上,通过对数据的处理为程序提供更多功能。

将流分为字节流和字符流,Java中流的层级结构可以通过下图来表示:蓝色是抽象类,绿色是具体类。


字节流在操作时本身不会用到缓冲区(内存),是在文件本身直接操作的;而字符流在操作时使用了缓冲区,通过缓冲区再操作文件,如图。

通过下面的字节流输出代码和字符流的输出代码可以更好的理解这一点。

import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;
public class TestOutputStream
{
    public static void main(String[] args) throws Exception //异常直接抛出,不处理
    {
        File f = new File("d:" + File.separator + "test.txt"); //File.separaor是文件路径分隔符
        OutputStream out = new FileOutputStream(f);//通过子类实例化父类对象,OutputStream是抽象类
        String str = "Hello World!!!";//准备一个字符串
        byte b[] = str.getBytes();//将字符串转为byte数组
        out.write(b);//将byte数组的内容输出    
        //此时并没有用out.clase()关闭输出流
    }
}


可以在d盘根目录看到test.txt文件,打开发现

即使此时没有关闭字节流操作,但是文件中也依然存在了输出的内容,证明字节流是直接操作文件本身的。而下面继续使用字符流完成,再观察效果。(先把test.txt文件删除)

import java.io.File;
import java.io.Writer;
import java.io.FileWriter;
public class TestWriter
{
    public static void main(String[] args) throws Exception //异常直接抛出,不处理
    {
        File f = new File("d:" + File.separator + "test.txt"); //File.separaor是文件路径分隔符
        Writer out = new FileWriter(f);//通过子类实例化父类对象,Writer是抽象类
        String str = "Hello World!!!";//准备一个字符串
        out.write(str);
        //此时依旧没有用out.clase()关闭输出流
    }
}


依然可以在d盘根目录看到test.txt文件,不过打开发现里面是空的,并没有“Hello World”; 

这是因为字符流操作时使用了缓冲区,而在关闭字符流时会强制性地将缓冲区中的内容进行输出,但是如果程序没有关闭,则缓冲区中的内容是无法输出的。所以得出结论:字符流使用了缓冲区,而字节流没有使用缓冲区。

如何理解缓冲区这个词?

缓冲区可以简单地理解为一段内存区域。可以简单地把缓冲区理解为一段特殊的内存。
某些情况下,如果一个程序频繁地操作一个资源(如文件或数据库),则性能会很低,此时为了提升性能,就可以将一部分数据暂时读入到内存的一块区域之中,以后直接从此区域中读取数据即可,因为读取内存速度会比较快,这样可以提升程序的性能。
在字符流的操作中,所有的字符都是在内存中形成的,在输出前会将所有的内容暂时保存在内存之中,所以使用了缓冲区暂存数据。
如果想在不关闭时也可以将字符流的内容全部输出,则可以使用Writer类中的flush( )方法完成。如下面的代码:

import java.io.File;
import java.io.Writer;
import java.io.FileWriter;
public class TestWriterFlush
{
    public static void main(String[] args) throws Exception //异常直接抛出,不处理
    {
        File f = new File("d:" + File.separator + "test.txt"); //File.separaor是文件路径分隔符
        Writer out = new FileWriter(f);//通过子类实例化父类对象,Writer是抽象类
        String str = "Hello World";//准备一个字符串
        out.write(str);//将内容输出
        out.flush();//强制性清空缓冲区的内容,将缓冲区的内容全部。
        //没有用out.clase()关闭输出流
    }
}


运行结果如下:

此时,文件中已经存在了内容,更进一步证明内容是保存在缓冲区的。

在开发中如何选择字节流或字符流?

所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛。

字节流与字符流主要的区别是他们的处理方式。字节流是最基本的,主要用在处理二进制数据,它是按字节来处理的。但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化。这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[ ]和String来关联。

在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的。在从字节流转化为字符流时,实际上就是byte[ ]转化为String时,public String(byte bytes[ ], String charsetName),有一个关键的参数字符集编码,通常我们都省略了,那系统就用操作系统的lang,而在字符流转化为字节流时,实际上是String转化为byte[]时,byte[ ]    String.getBytes(String charsetName)也是一样的道理

至于java.io中还出现了许多其他的流,按主要是为了提高性能和使用方便,如BufferedInputStream,PipedInputStream等。
 

猜你喜欢

转载自blog.csdn.net/yemenlinweihan/article/details/107602388