关于Java中<字节流>和<字符流>的操作

  在程序中所有的数据都是以流的方式进行传输或者保存的,程序需要数据的时候需要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流。

  可以通过下面的输入输出流关系图表示这种方式。

  

 

  在java.io包中流的操作主要有字节流字符流两大类,并且两个都具备输入输出的操作。

  在字节流中输出数据主要使用OutputStream类完成,输入则是InputStream类。

  在字符流中输出数据主要使用Writer类完成,输入则是Reader类。

  任何一种流接口,它下面都会有多个实现类。下图展示的是流之间的层次关系图。

  

 

字节流:

  字节流主要操作byte类型数据,以byte数组为准,主要操作类是OutputStream类和InputStream类。

  1)字节输出流:OutputStreame

  OutputStream是整个IO包中字节输出流最大的父类,此类的定义如下:

public abstract class OutputStream extends Object implements Closeable, Flushable

  从这个定义中可以发现,OutputStream类是一个抽象类,如若要使用此类,首先就必须要通过子类来实例化对象。假设现在要操作的事一个文件,则可以使用FileOutputStream类,通过向上转型后,可以为OutputStream实例化,在OutputStream类中的主要操作方法如下:

1 public void close() throws IOException  //关闭输出流
2 public void flush() throws IOException  //刷新缓冲区
3 public void write(byte[] b) throws IOException //将一个byte数组写入数据流
4 public void write(byte[] b,int off,int len) throws IOException //将一个指定范围的byte数组写入数据流
5 public abstract void write(int b) throws IOException //将一个字节数据写入数据流

  此时会用FileOutputStream子类,此类的构造方法如下:

public FileOutputStream(File file) throws FileNotFoundException

  操作时必须接收File类实例,指明要输出的文件路径。

  在OutputStream类的定义中可以发现此类实现了Closeable和Flushable两个接口,这两个接口的作用从其定义方法中可以得知,Closeable表示可关闭,Flushable表示可刷新,而且在OutputStream类中已经有了这两个方法的实现,所以操作时一般不会关心这两个接口,而直接使用OutputStream类就行了。下面举一个例子:

 1 import java.io.*;
 2 
 3 /**
 4  * Author : Rabbit Yulin
 5  * Created on 2017/2/24 0024 上午 11:10
 6  * Description : 向文件中写入字符串
 7  */
 8 public class OutputStreamDemo1 {
 9     public static void main(String[] args) throws IOException {
10         /*使用File指定一个文件*/
11         File f = new File("d:"+File.pathSeparator+"test.txt");//申明File对象
12         /*通过子类实例化父类*/
13         OutputStream out;  //输出对象
14         out = new FileOutputStream(f); //通过对象多态性进行实例化
15         /*写入操作*/
16         String str = "Hello , World"; //字符串
17         byte b[] = str.getBytes(); //因为只能输出byte数组,所以将字符串变为byte数组
18         out.write(b);  //将内容输出
19         /*关闭输出流*/
20         out.close();
21     }
22 }

  程序运行后可以发现D盘下面已经新增了一个test.txt的文件,打开查看的时候妥妥的内容是:“Hello , World”。上面的代码在实例化、写、关闭的时候都会有异常,为了方便起见所以在主方法上直接throws了IOException抛出异常,这样可以减少try…catch语句。

  可以很清楚的知道一个问题,那就是test.txt文件在程序操作之前是不存在的(当然如果硬是说“我创建了一个”那也没办法,反正会被覆盖掉),程序会自动创建这个文件,并且将内容写入到这个文件之中。将一个字符串变为byte的数组之后,然后使用write(byte[] b)的方式将byte数组直接写入到文件中,当然也可以通过循环把每一个字节一个一个地写入到文件中(其实并没有什么不同,实现的功能都是一样的,可以根据喜好随意),同样的,下面放出一个Demo:

 1 import java.io.*;
 2 /**
 3  * Author : Rabbit Yulin
 4  * Created on 2017/2/24 0024 上午 11:29
 5  * Description :
 6  */
 7 public class OutputStreamDemo2 {
 8     public static void main(String[] args) throws IOException {
 9         /*File指定一个文件*/
10         File f = new File("d:"+File.pathSeparator+"test2.txt"); //申明对象
11         /*子类实例化父类*/
12         OutputStream out; //输出对象
13         out = new FileOutputStream(f); //实例化
14         /*写操作*/
15         String str = "Hello , Again";
16         byte b[] = str.getBytes();
17         for(int i = 0;i<b.length;i++){
18             out.write(b[i]);
19         }
20         /*关闭输出流*/
21         out.close();
22     }
23 }

  2)追加内容

  刚才有提到过,假若在指定的目录之下已经有一个同名文件,在程序运行的时候该文件会被覆盖。一样的,如果再直接往文本中写入内容,会造成原有数据的覆盖。那么此时就可以通过FileOutputStream向文件中追加内容,FileOutputStream的另外一个构造方法:

public FileOutputStream(File file,boolean append) throws FileNotFoundException

  在该构造方法中,如果将append的值设置为true,则表示在文件的末尾追加内容。如果要在文件之后的追加内容,是紧跟在原有的内容之后增加换行,使文件内容显示更加清晰,则可以使用"\r\n"换行。

  3)字节输入流 InputStream

  既然程序可以向文件写入内容,则可以通过InputStream从文件中把数据读取进程序当中。InputStream类的定义如下:

public abstract class InputStream extends Object implements Closeable

  与OutputStream类一样,InputStream本身也是一个抽象类,必须依靠子类。如果现在从文件中读取,子类肯定是FileInputStream。InputStream类中的主要方法如下:

public int availabel() throws IOException //可以取得输入文件的大小
public void close() throws IOException //关闭输入流
public abstract int read() throws IOException //读取内容,以数字方式读取
public int read(byte[] b) throws IOException //将内容读到byte数组中同时返回读入的个数

  通过一个例子来看看如何从文件中读取内容:

import java.io.*;

/**
 * Author : Rabbit Yulin
 * Created on 2017/2/24 0024 下午 1:46
 * Description :
 */
public class InputStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*File指定一个文件*/
        File f = new File("d:"+File.pathSeparator+"test.txt"); //申明File文件
        /*子类实例化父类*/
        InputStream is;  //准备一个输入对象
        is = new FileInputStream(f); //通过对象多态性进行实例化
        /*进行读操作*/
        byte[] b = new byte[1024]; //所有的内容读到次数组中
        is.read(b);/*关闭输入流*/
        is.close();
        System.out.println("内容为:"+new String(b)); //把数组变为字符串
    }
}

  虽然内容都被读取进来了,但是有一个问题就是打印结果的后面有很多个空格。这是因为byte数组的大小有1024,然而实际的内容只有13个字节,也就是说还存在着1011个空白的弓箭,在将byte数组变为字符串时也将这1011个无用的空间转为了字符串,这样的操作肯定是不合理的。如果要想要解决以上的问题,则要观察read的方法,在次方法上有一个返回值,此返回值表示想数组中写入了多少个数据。因此把上面的程序修改一下的代码如下:

 1 import java.io.*;
 2 
 3 /**
 4  * Author : Rabbit Yulin
 5  * Created on 2017/2/24 0024 下午 1:46
 6  * Description :
 7  */
 8 public class InputStreamDemo1 {
 9     public static void main(String[] args) throws IOException {
10         /*File指定一个文件*/
11         File f = new File("d:"+File.pathSeparator+"test.txt"); //申明File文件
12         /*子类实例化父类*/
13         InputStream is;  //准备一个输入对象
14         is = new FileInputStream(f); //通过对象多态性进行实例化
15         /*进行读操作*/
16         byte[] b = new byte[1024]; //所有的内容读到次数组中
17         int len = is.read(b);
18         /*关闭输入流*/
19         is.close();
20         System.out.println("读取的数据长度:"+len);
21         System.out.println("内容为:"+new String(b,0,len)); //把数组变为字符串
22     }
23 }

  这样处理之后的代码再次运行,发现那些多余的空格就不会再产生了。因为程序在最后只是将byte数组指定范围中的内容编程了字符串。需要注意的一点是,FileInputStream读取时如果指定的路径不存在,则程序会出现异常。

  以上的这个问题解决的方式似乎还是有些不太完美,因为虽然最后指定了byte数组的范围,但是程序依然开辟了很多的无用空间,这样肯定会造成资源的浪费,那么此时能否根据根据文件的数据长度来开辟空间大小?查看了API可以得知File类中存在一个length()方法,此方法可以取得文件的大小,来开辟指定的byte数组空间。

 1 import java.io.*;
 2 
 3 /**
 4  * Author : Rabbit Yulin
 5  * Created on 2017/2/24 0024 下午 2:37
 6  * Description :
 7  */
 8 public class InputStreamDemo2 {
 9     public static void main(String[] args) throws IOException {
10         File f = new File("d:"+ File.pathSeparator+"test.txt");
11         InputStream is;
12         is = new FileInputStream(f);
13         byte[] b = new byte[(int)f.length()];
14         is.read(b);
15         is.close();
16         System.out.println("内容为:"+new String(b));
17     }
18 }

  这种读取的方式其实适用范围比较小,因为文本数据只要很大,那就造成一些未知的错误了。

 

字符流:

  在程序中一个字符等于两个字节,那么Java提供了Reader和Writer两个专门操作字符流的类。

  1)字符输出流 Writer

  Writer 本身是一个字符流的输出类,此类的定义如下:

public abstract calss Writer extends Object implements Appendable,Closeable,Flushable;

  此类也是一个抽象类,如果要使用这个类则需要使用其子类,这个时候如果要往文件中写入数据,应该使用FileWriter的子类。Writer类的常用方法如下:

public abstract void close() throws IOException //关闭输出流
public void write(String str) throws IOException //将字符串输出
public void write(char[] cbuf) throws IOException //将字符数组输出
public abstract void flush() throws IOException //强制性清空缓存

  FileWriter类的构造方法定义如下:

public FileWriter(File file) throws IOException

  在Writer类中除了实现Closeable和Flushable两个接口之外,还实现一个Appendable接口,此接口表示的是内容可以被追加,接受的参数是CharSequence,实际上String类就实现了此接口,所以可以直接通过次接口的方法向输出流中追加内容。例如:

 1 import java.io.File;
 2 import java.io.FileWriter;
 3 import java.io.IOException;
 4 import java.io.Writer;
 5 
 6 /**
 7  * Author : Rabbit Yulin
 8  * Created on 2017/2/24 0024 下午 3:31
 9  * Description :
10  */
11 public class WriterDemo1 {
12     public static void main(String[] args) throws IOException {
13         File f = new File("d:"+File.pathSeparator+"test.txt");
14         Writer out;
15         out = new FileWriter(f);
16         String str = "Hello,World";
17         out.write(str);
18         out.flush();
19         out.close();
20     }
21 }

  整个代码看下来和OutputStream没有太大的区别,唯一的好处就是可以直接输出字符串,而不用再将字符串变为byte数组之后再输出。

  2)使用FileWriter追加文件的内容

  在使用字符流操作的时候,也可以实现文件的追加功能,直接使用FileWriter类黄总的一下构造,就可以实现追加:

public FileWriter(File file,boolean append) throws IOException

  将append的值设置为truw,表示追加。

  3)字符输入流Reader

  Reader是使用字符的方式从文件中取出数据,Reader类的定义如下:

public abstract class Reader extends Object implement Readable,Closeable

  Reader不用说,也是一个抽象类,如果要在从文件中刦内容时,则可以直接使用FileReader子类。Reader类的常用方法:

1 public abstract void close() throws IOException //关闭输出流
2 public int read() throws IOException //读取单个字符
3 public int read(char[] cbuf) throws IOException //将内容读到字符数组内,返回读入的长度

  FileReader的构造方法定义如下:

public FileReader(File file) throws FileNotFoundException

  以下放出实例来读取文件中的文本内容

 1 import java.io.*;
 2 
 3 /**
 4  * Author : Rabbit Yulin
 5  * Created on 2017/2/24 0024 下午 3:51
 6  * Description :
 7  */
 8 public class ReaderDemo1 {
 9     public static void main(String[] args) throws IOException {
10         File f = new File("d:"+File.pathSeparator+"test.txt");
11         Reader reader = new FileReader(f);
12         char[] c = new char[1024];
13         int len = reader.read(c);
14         reader.close();
15         System.out.println("内容为:"+new String(c,0,len));
16     }
17 }

  如果此时不知道数据的长度,也可以使用循环的方式进行内容的读取:

 1 import java.io.*;
 2 
 3 /**
 4  * Author : Rabbit Yulin
 5  * Created on 2017/2/24 0024 下午 3:54
 6  * Description :
 7  */
 8 public class ReaderDemo2 {
 9     public static void main(String[] args) throws IOException {
10         File f = new File("d:"+ File.pathSeparator+"test.txt");
11         Reader reader = new FileReader(f);
12         int len = 0;
13         char[] c = new char[1024];
14         int temp = 0;
15         while((temp = reader.read()) != -1){
16             c[len] = (char)temp;
17             len++;
18         }
19         reader.close();
20         System.out.println("内容为:"+ new String(c,0,len));
21     }
22 }

 

 

 

posted @ 2017-02-24 15:59  木栩  阅读(9747)  评论(0编辑  收藏  举报