Java中字节流与字符流详解

转:http://blog.csdn.net/zxman660/article/details/7875799

一、流的概念

    程序中所有的数据都是以流的方式进行传输或保存的,而流中实际上全都是字节文件。

    程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。

    所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再把这些字节储存到磁盘上。在读取文件(特别是文本文件)时,也是一个字节一个字节的地读取,形成字节序列。


二、字节流与字符流:

在java.io包中操作文件内容的主要有两大类:字节流、字符流(注:两类都有输入流和输出流)。


  1、字节流和字符流对应的类

  字节流:输出数据使用OutputStream完成,输入数据使用InputStream完成。

  字符流:输出数据使用Writer完成,输入数据使用Reader类完成。

  注:上面的四个都是抽象类


  2、字节流和字符流的操作对象

字节流(InputStream、OutputStream):

1、字节流可用于任何类型的对象。主要用来处理字节、二进制对象(如:音频文件、图片、歌曲)。

2、字节流处理单元为1个字节,用来操作字节、字节数组。

3、字节流提供了处理任何类型的IO操作的功能,但是不能直接处理Unicode字符。

4、字节流是最基本的,采用ASCII编码。

字符流(Reader、Writer):

1、字符流只能处理字符或者字符串。主要用来处理字符、字符串(一个字符占两个字节)。

2、字符流处理单元为2个字节(Unicode字符),用来操作字符、字符数组、字符串。

3、字符流能直接处理Unicode字符。

4、字符流是采用Unicode编码,它是按虚拟机的encode来处理

注:因为字节流采用的是ASCII编码,字符流采用Unicode编码,所以他们之间的转化也就是要进行字符集的转化。

字节流和字符流之间是通过InputStreamReader和OutputStreamWriter来关联,实际上底层是通过byte[]和String来关联。

在实际开发中出现的汉字问题实际上都是在字符流和字节流之间的转化不统一而造成的。

(注:上面的四点查看的时候要上下结合一起查看!)


  3、字节流和字符流的选择

字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,是按虚拟机的encode来处理,要进行字符集的转化,对多国语言支持性比较好!

字节流:如果是音频文件、图片、歌曲,就用字节流好点。

字符流:如果是关系到中文(文本)的,用字符流好点,在实际开发中出现的汉字问题实际上都是在字符流和字节流之间的转化不统一而造成的。


那开发中究竟用字节流好还是用字符流好呢?

在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,而字符是只有在内存中才会形成的,所以使用字节的操作是最多的。

如果要java程序实现一个拷贝功能,应该选用字节流进行操作(可能拷贝的是图片),并且采用边读边写的方式(节省内存)。


  4、字符输入流和字节输入流之间的对比

字符输入流Reader的read()方法返回类型为int ,作为整数读取的字符(占两个字节共16位),范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1。

字节输入流inputStream的read()虽然也返回int,但由于此类是面向字节流的,一个字节占8个位,所以范围在 0 到 255 之间,如果已到达流的末尾,则返回值 -1。

总结:因此对于不能用0-255来表示的值就得用字符流来读取!比如说汉字。


  5、字符输入流和字节输入流的区别

字节流和字符流使用是非常相似的,那么上面的不同之外,还有哪些不同呢?

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

2、字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容。


  6、什么叫缓冲区?

    在很多地方都碰到缓冲区这个名词,那么到底什么是缓冲区?又有什么作用呢?

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


  7、使用字节流好还是字符流好?
        回答:使用字节流更好。但是如果遇到关于中文等的问题的时候要考虑字符流。
注:在回答之前,先为读者讲解这样的一个概念,所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛。


三、流的操作流程

  在JavaIO操作也是有相应步骤的,以文件操作为例,主要的操作流程如下:

  1、使用File类打开一个文件

  2、通过字节流或字符流的子类,指定输出的位置

  3、进行读/写操作

  4、关闭输入/输出

  IO操作属于资源操作,一定要记得关闭


四、实例说明

1、字节输出流:OutputStream

OutputStream是使用字节的方式向文件中写数据

类的定义如下:

public abstract class OutputStream extends Object implements Closeable,Flushable

Closeable:表示可以关闭的操作,因为程序运行到最后肯定要关闭

Flushable:表示刷新、清空内存中的数据

从以上的定义可以发现,此类是一个抽象类,如果想要使用此类的话,则首先必须通过子类实例化对象。


如果现在要操作的是一个文件,则可以使用:FileOutputStream类。通过向上转型之后,可以为OutputStream实例化。

FileOutputStream类的构造方法:public FileOutputStream(File file)throws FileNotFoundException

实例写数据:

 1 import java.io.File;
 2 import java.io.FileOutputStream;
 3 import java.io.IOException;
 4 import java.io.OutputStream;
 5
 6 public class Test11 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         OutputStream out=new FileOutputStream(f);//如果文件不存在会自动创建,会把内容写到test.txt这个文件里面
10         String str="Hello World";
11         byte[] b=str.getBytes();
12         out.write(b);//因为是字节流,所以要转化成字节数组进行输出
13         out.close();
14     }
15 }


也可以一个字节一个字节的进行输出,代码如下:

 1 import java.io.File;
 2 import java.io.FileOutputStream;
 3 import java.io.IOException;
 4 import java.io.OutputStream;
 5
 6 public class Test11 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         OutputStream out=new FileOutputStream(f);//如果文件不存在会自动创建,会把内容写到test.txt这个文件里面
10         String str="Hello World";
11         byte[] b=str.getBytes();
12         for(int i=0;i<b.length;i++){
13             out.write(b[i]);
14         }

15         out.close();
16     }
17 }


以上输出只会进行覆盖,如果要实现追加的话,

要用到FileOutputStream类的另一个构造方法:public FileOutputStream(File file,boolean append)throws FileNotFoundException

在构造方法中,如果将append的值设置为true,则表示在文件的末尾追加内容。

代码如下:

1 import java.io.File;
 2 import java.io.FileOutputStream;
 3 import java.io.IOException;
 4 import java.io.OutputStream;
 5
 6 public class Test11 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         OutputStream out=new FileOutputStream(f,true);//追加内容
10         String str="\r\nHello World";
11         byte[] b=str.getBytes();
12         for(int i=0;i<b.length;i++){
13             out.write(b[i]);
14         }
15         out.close();
16     }
17 }

注:文件中换行为:\r\n


2、字节输入流:InputStream

InputStream使用字节的方式从文件中读取数据。

类的定义如下:public abstract class InputStream extends Object implements Closeable

OutputStream类一样,InputStream本身也是一个抽象类,必须依靠其子类。

如果现在是从文件中读取,就用FileInputStream来实现。

FileInputStream类的构造方法:public FileInputStream(File file)throws FileNotFoundException

实例读文件:

1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5
 6 public class Test12 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         InputStream in=new FileInputStream(f);
10         byte[] b=new byte[1024];
11         int len=in.read(b);

12         in.close();
13         System.out.println(new String(b,0,len));
14     }
15 }


但以上方法是有问题的,不用开辟这么大的一个字节数组,明显是浪费嘛!

我们可以根据文件的大小来定义字节数组的大小,用到File类中的方法:public long length()。

修改后的代码如下:

1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5
 6 public class Test13 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         InputStream in=new FileInputStream(f);
10         byte[] b=new byte[(int) f.length()];
11         in.read(b);

12         in.close();
13         System.out.println(new String(b));
14     }
15 }


换种方式,一个字节一个字节读入。

代码如下: 

1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5
 6 public class Test14 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         InputStream in=new FileInputStream(f);
10         byte[] b=new byte[(int) f.length()];
11         for(int i=0;i<b.length;i++){
12             b[i]=(byte) in.read();
13         }

14         in.close();
15         System.out.println(new String(b));
16     }
17 }


但以上情况只适合知道输入文件的大小的情况。

不知道的情况代码如下:

1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5
 6 public class Test15 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         InputStream in=new FileInputStream(f);
10         byte[] b=new byte[1024];
11         int temp=0;
12         int len=0;
13         while((temp=in.read())!=-1){//-1为文件读完的标志
14             b[len]=(byte) temp;

15             len++;
16         }
17         in.close();
18         System.out.println(new String(b,0,len));
19     }
20 }


3、字符输出流:Writer

Writer是使用字符的方式向文件中写数据。

类的定义如下:public abstract class Writer extends Object implements Appendable,Closeable,Flushable

此类本身也是一个抽象类,如果要使用此类,则肯定要使用其子类。

此时如果是向文件中写入内容,所以应该使用FileWriter的子类。

FileWriter类的构造方法:public FileWriter(File file)throws IOException


字符流的操作比字节流操作好在一点,就是可以直接输出字符串了,不用再像之前那样进行转换操作了。

实例写文件:

1 import java.io.File;
 2 import java.io.FileWriter;
 3 import java.io.IOException;
 4 import java.io.Writer;
 5
 6 public class Test16 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         Writer out=new FileWriter(f);
10         String str="Hello World";//中间不用byte来转化了
11         out.write(str);

12         out.close();
13     }
14 }


默认情况下再次输出会覆盖,追加的方法也是在构造函数上加上追加标记。

代码如下:

1 import java.io.File;
 2 import java.io.FileWriter;
 3 import java.io.IOException;
 4 import java.io.Writer;
 5
 6 public class Test17 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         Writer out=new FileWriter(f,true);//追加
10         String str="\r\nHello World";
11         out.write(str);
12         out.close();
13     }
14 }


4、字符输入流:Reader

Reader是使用字符的方式从文件中取出数据。

类的定义如下:public abstract class Reader extends Objects implements Readable,Closeable

Reader本身也是抽象类,如果现在要从文件中读取内容,则可以直接使用FileReader子类。

FileReader的构造方法:public FileReader(File file)throws FileNotFoundException


以字符数组的形式读取出数据: 

1 import java.io.File;
 2 import java.io.FileReader;
 3 import java.io.IOException;
 4 import java.io.Reader;
 5
 6 public class Test18 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         Reader input=new FileReader(f);
10         char[] c=new char[1024];
11         int len=input.read(c);
12         input.close();
13         System.out.println(new String(c,0,len));
14     }
15 }


也可以用循环方式,判断是否读到底:

1 import java.io.File;
 2 import java.io.FileReader;
 3 import java.io.IOException;
 4 import java.io.Reader;
 5
 6 public class Test19 {
 7     public static void main(String[] args) throws IOException {
 8         File f = new File("d:" + File.separator+"test.txt");
 9         Reader input=new FileReader(f);
10         char[] c=new char[1024];
11         int temp=0;
12         int len=0;
13         while((temp=input.read())!=-1){
14             c[len]=(char) temp;
15             len++;
16         }

17         input.close();
18         System.out.println(new String(c,0,len));
19     }
20 }


参考文件:
http://blog.sina.com.cn/s/blog_4b3c1f950100g45m.html
百度文库:
http://wenku.baidu.com/link?url=zP_JYeNInfE6m6fpSMZbKlPRponqFyFDLKmKH7ZQKqykuw4ikfuWkrZHNV4cJXbmv67tjg2YD_J5YZ1Tb-KEfqH2Db1CEaKOhz1OF2n8vRa
CSDN参考:
http://blog.csdn.net/daijunjian/article/details/7316293
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值