2016-11-08 字节流与字符流

本文详细介绍了Java中的IO流概念及其实现方式,包括字节流与字符流的区别、具体使用方法及其应用场景。从OutputStream和InputStream的基本操作讲起,进而深入探讨了字符流Writer和Reader的特性。

      前言:前面可以知道File类可以操作并不是文件内容,然后文件本身,如果要进行文件内容的操作就必须依靠:字节流与字符流。

一、简介

      如果要进行文件的输入与输出操作一般需要按照下面的步骤进行操作:

           |-通过File类定义一个要操作文件的路径;

           |-通过字节流或者字符流的子类对象为父类对象进行对象实例化;

           |-进行数据的读(输入),写(输出)操作;

           |-数据流属于资源操作,资源操作必须关闭。

      对于java.io包而言它定义了两类流:

           |-字节流:(JDK1.0):InputStream、OutputStream

           |-字符流:(JDK1.1):Reader、Writer

二、字节输出流(OutputStream)(重点)

      OutputStream类的定义:public abstract class OutputStream extends Object implements Closeable, Flushable;

      首先实现了两个接口:Closeable, Flushable

Closeable接口:JDK1.5

Flushable接口:JDK1.5

public interface Closeable

extends AutoCloseable{

public void close()throws IOException;

}

public interface Flushable{

public void flush() throws IOException;

}

      但是由于,OutputStream在JDK1.0的时候就已经定义了close()和flush()两个操作方法,所以现在上面的两个接口就几乎也不会再使用了。

      在OutputStream类中提供了三个输出方法:

           |-输出单个字节:public abstract void write(int b)throws IOException;

           |-输出全部字节数组:public void write(byte[] b)throws IOException;

           |-输出部分字节数组:public void write(byte[] b,int off,int len)throws IOException;

      OutputStream本身是属于抽象类,如果要进行实例化的操作,需要是利用子类进行,由于要使用的是文件操作所以可以使用FileOutputStream子类。此子类之中由如下的构造方法:

           |-创建或覆盖已有文件:public FileOutputStream(File file) throws FileNotFoundException;

           |-文件内容追加:public FileOutputStream(File file,boolean append)throws FileNotFoundException;

范例代码:

package cn.mldn.demo17;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.定义文件输出的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        //2.此时由于目录不存在,所以文件不能构输出,那么应该首先创建目录
        if(!file.getParentFile().exists()){
           file.getParentFile().mkdirs();//文件目录不存在 
        }
        //3.应该OutputStream和其子类进行对象实例化,此时目录存在文件还不存在
        OutputStream output = new FileOutputStream(file);//向上转型操作
        //4.要进行文件内容的输出
        String str = "天道酬勤,人生精彩!";
        byte data[] = str.getBytes();//将字符串变成字符数组
        output.write(data);//将内容输出
        //5.资源操作结束,最后一定要关闭
        output.close();
    }

      运行结果:

image

      通过上面的OutputStream类中定义的三种输出方法:上面采样的是全部字节数据输出,其他两种请看下面的范例。

范例代码:

//采用单个字节的方式输出
        for(int x = 0;x<data.length;x++){
            output.write(data[x]);
        }

范例代码:

package cn.mldn.demo17;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.定义文件输出的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        //2.此时由于目录不存在,所以文件不能构输出,那么应该首先创建目录
        if(!file.getParentFile().exists()){
          //文件目录不存在
            file.getParentFile().mkdirs();
        }
       //3.应该OutputStream和其子类进行对象实例化,此时目录存在文件还不存在
        OutputStream output = new FileOutputStream(file);//向上转型操作
        //4.要进行文件内容的输出
        String str = "天道酬勤,人生精彩!";
       byte data[] = str.getBytes();//将字符串变成字符数组
        //输出部分字节数组内容
        //这里表示:第1个参数是起始开始,第二个参数是数据长度
        output.write(data, 6, 6);
       //5.资源操作结束,最后一定要关闭
        output.close();
    }
}

运行结果:

image

      但是上面的操作,当每一次重复执行的时候都会覆盖掉之前的输出内容,在实际中有的时候还是需要在内容的后面实现追加,那么要想实现这样的操作,应该这样操作;

      只要更改FileOutputStream类的构造方法:OutputStream output = new FileOutputStream(file,true);//向上转型操作

范例代码:

package cn.mldn.demo17;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.定义文件输出的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        //2.此时由于目录不存在,所以文件不能构输出,那么应该首先创建目录
        if(!file.getParentFile().exists()){
            //文件目录不存在
            file.getParentFile().mkdirs();
        }
        //3.应该OutputStream和其子类进行对象实例化,此时目录存在文件还不存在
        OutputStream output = new FileOutputStream(file,true);//向上转型操作
        //4.要进行文件内容的输出
        String str = "天道酬勤,人生精彩!";
        byte data[] = str.getBytes();//将字符串变成字符数组
        //输出部分字节数组内容
        //这里表示:第1个参数是起始开始,第二个参数是数据长度
        output.write(data, 6, 6);
        //5.资源操作结束,最后一定要关闭
        output.close();
    }
}

运行结果:当连续执行三次后的执行结果:

image

      这样操作,由于不好分隔,应该在每次执行之后加入换行;

package cn.mldn.demo17;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.定义文件输出的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        //2.此时由于目录不存在,所以文件不能构输出,那么应该首先创建目录
        if(!file.getParentFile().exists()){
            //文件目录不存在
            file.getParentFile().mkdirs();
        }
        //3.应该OutputStream和其子类进行对象实例化,此时目录存在文件还不存在
        OutputStream output = new FileOutputStream(file,true);//向上转型操作
        //4.要进行文件内容的输出
        String str = "天道酬勤,人生精彩!\r\n";
        byte data[] = str.getBytes();//将字符串变成字符数组
        //输出部分字节数组内容
        //这里表示:第1个参数是起始开始,第二个参数是数据长度
        output.write(data);
        //5.资源操作结束,最后一定要关闭
        output.close();
    }
}

运行结果:连续执行三次输出结果;

image

三、字节输入流(InputStream)

      如果程序中需要进行数据的读取操作,那么就需要使用InputStream类功能实现。

      类的定义格式:public abstract class InputStream extends Object implements Closeable;

      在InputStream类中也定义了三种数据读取的方法:

          |-读取单个字节:public abstract int read()throws IOException;

          |-将读取的数据长度保存在字节数组里:public int read(byte[] b)throws IOException;返回值的类型是一个int类型:返回读取数据的长度,当文件已经读取到了结尾,返回-1;

          |-将数据保存在部分字节数组里:public int read(byte[] b,int off,int len)throws IOException;返回值与上面是一样的,当读取的结尾返回值为-1;

      InputStream是一个抽象类,所以如果要想进行文件读取使用FileInputStream子类,这个子类提供这样的一个构造方法:

           |-构造方法:public FileInputStream(File file)throws FileNotFoundException;

范例代码:

package cn.mldn.demo18;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.定义要输入文件的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        //2.判断是否是文件
        if(file.exists()){
            //如果文件存在那么使用InputStream进行文件读取
            //涉及的IOException异常统一由Exception抛出
            InputStream input = new FileInputStream(file);//向上转型实例化对象 
            //3.进行数据读取
            byte data[] = new byte[1024];//准备一个1024数组
            int len = input.read(data);//将内容保存在字节数组中
            //4.关闭流操作
            input.close();
            //5.将部分字节数组变成字符串
            System.out.println("【"+new String(data,0,len)+"】");
        }
    }
}

运行结果:

image

      注意:上面的代码有一个问题,这里给出的字节数组的空间大小为1024,我们知道一个汉子占用2个字节,因此给出的字节空间已经远远大于能够存储的汉子空间,但是如果我们给出的字节空间较小,不够存储的时候会出现保存不完整的情况,请看范例代码中出现的问题。

//3.进行数据读取
byte data[] = new byte[50];//准备一个1024数组
int len = input.read(data);//将内容保存在字节数组中

运行结果:

image

      这里出现了很明显的问题,数据在读取的时候出现了漏存的情况,但是在实际的运行过程中,由于文件的中内容的大小又是一个未知量?这个该怎么解决呢?

      因此,一个文件肯定有很多的字节数据,多以在读取的时候最好是能加入一个循环来读取,这样就能够完成上面所遇到的这个问题。这里循环的次数事先是无法判断的,因此选用while,代码实现看下面的范例代码

范例代码★:

package cn.mldn.demo18;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class TestDemo01 {
    public static void main(String[] args)throws Exception{
        //1.定义文件要输入的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        //2.判断文件是否存在
        if(file.exists()){//如果文件存在才可以进行读取
            InputStream input = new FileInputStream(file);//向上转型实例化对象
            //3.进行数据读取
            byte data[] = new byte[1024];//准备一个1024空间大小的字节数组
            int foot = 0;//表示字节数组的操作角标
            int temp = 0;//表示接收每次读取的字节数据
            while((temp = input.read())!=-1){
                //强制转换将int转换成byte类型
                data[foot++]=(byte) temp;//有内容进行保存
            }
            //4.关闭输入流
            System.out.println("【"+new String(data,0,foot)+"】");
        }
    }
}

运行结果:

image

提别提醒:上面的操作方法,以后的开发中比较常用。

四、字符输出流(Writer)

       字符输出流的定义格式:public abstract class Writerextends Objectimplements Appendable, Closeable, Flushable;

       在Writer类中定义如下的输出方法:

           |-输出全部字符数组:public void write(char[] cbuf)throws IOException;

           |-输出字符串:public void write(String str)throws IOException;

       同样,Writer是一个抽象类,如果要对这个对象进行实例化要依靠其子类,应该使用子类:FileWriter子类。

范例代码:

package cn.mldn.demo19;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class TestDemo {
    public static void main(String[] args) throws Exception{
        //1.定位要输入文件的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"test.txt");
        //2.判断文件的父路径是否存在
        if(!file.getParentFile().exists()){//如果不存在就要创建
            file.getParentFile().mkdirs();
        }
        if(!file.exists()){//如果文件不存在的话,创建文件
            file.createNewFile();
        }
        //3.实例化Writer类对象
        Writer out = new FileWriter(file);
        //4.进行内容输出
        String str = "学习javaSE!";
        //5.按照字符串的方式输出
        out.write(str);
       out.close();
    }
}

运行结果:

image

      从上面的代码中,我们可以看出来这样的好处:使用字符输出流操作的时候与字节输出流的操作从基本的流程上看方法以及步骤是比较相似的,但是字符流可以直接进行字符串的输出,这样的操作来讲对于汉字的操作很是便利。那么同样的字符输入流是不是也会存在一些便利呢?

五、字符输入流(Reader)

       Reader类是一个抽象类:public abstract class Readerextends Objectimplements Readable, Closeable;

       在Reader类中提供了一系列的Read()方法:

           |- 读取内容到字符数组:public int read(char[] cbuf) throws IOException;

                  |-返回值:表示读取的数据长度,如果已经读取到结尾了返回-1;

       Reader类实例化的时候的就可以依靠子类:FileReade完成;

范例代码:

package cn.mldn.demo20;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.Reader;
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.定义要输出文件的路径
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        //2.判断文件是否存在
        if(file.exists()){
            //3.Reader类对象实例化
            Reader in = new FileReader(file);
            //4.进行数据读取
              char data[] = new char[1024];
            int len = in.read(data);
            System.out.println(new String(data,0,len));
        }
    }
}

运行结果:

image

六、字节流与字符流的区别

       字节流与字符流最大的区别:字节流直接与终端进行数据交互,字符流需要将数据进行缓冲区处理后才可以输出。

       在使用OutputStream输出数据的时候即使最后没有关闭输出流,那么内容也可以正常输出,但是反过来如果使用的是字符输出流,如果不关闭,那么就表示在缓冲区处理的内容不会被强制性的清空,所以就不会输出数据。如果现在有特殊情况不能够关闭字符输出流,可以使用flush()方法强制清空,缓冲区。

范例:

package cn.mldn.demo21;
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class TestDemo {
    public static void main(String[] args)throws Exception {
        File file = new File("H:"+File.separator+"demo"+File.separator+"my.txt");
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        Writer out = new FileWriter(file);
        String str = "学习JAVASE!";
        out.write(str);//输出
        //强制清空缓冲区
        out.flush();
    }
}

运行结果:

image

     在开发之中,对于字节数据处理是比较多,例如:图片、音乐、电影、文字。而对于字符流最大的好处是它可以进行有效的处理,那么在日后的开发中,如果要处理中文的时候请优先考虑字符流,如果没有中文问题,建议使用字节流。

转载于:https://www.cnblogs.com/Rober-gx/p/6045089.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值