Java IO 2-字节流与字符流

  FILE类只参与文件的创建删除等操作,而不对文件本身内容进行修改。如果要处理文件内容,就需要使用流来进行操作。

1. 流的分类

流分为输入流与输出流。
输入输出流又分为字节流与字符流,所以总共加起来有如下四种流:

  • InputStream: 字节输入流
  • Reader: 字符输入流
  • OutputStream: 字节输出流
  • Writer: 字符输出流
    四者之间的关系如下:

字节输入流与输出流

字符输入流与输出流

  字节流与字符流操作的本质区别只有一个:字节流是原生的操作,而字符流是经过处理后的操作。

  在进行网络数据传输、磁盘数据保存所保存所支持的数据类型只有:字节。
而所有磁盘中的数据必须先读取到内存后才能进行操作,而内存中会帮助我们把字节变为字符。字符更加适合处理。
中文。

  另外,由于IO操作属于资源处理操作,所以在使用完毕之后必须进行关闭。

2. 字节输出流OutputStream

  OutputStream是字节输出流的顶层抽象类,它的定义如下:

public abstract class OutputStream implements Closeable, Flushable

  它实现了两个接口,这两个接口中各自只有一个抽象方法,所以这两个接口又称为标识接口。
  实现的抽象方法定义如下:

// Closeable
public void close() throws IOException;

// Flushable
void flush() throws IOException

  OutputStream类中常用的方法:

// 将给定的字节数组内容全部输出
public void write(byte b[]) throws IOException

// 将部分字节数组内容输出
public void write(byte b[], int off, int len) throws IOException

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

  因为OutputStream是顶层抽象类,所以我们一般使用它的一个实现子类FileOutputStream进行父类的实例化。

  下面是FileOutputStream的两个构造。

// 接收File类,输出时会覆盖原有内容
public FileOutputStream(File file) throws FileNotFoundException

// 接收File类,输出时在原有内容后面追加
public FileOutputStream(File file, boolean append)

  我们看下面的例子

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**
 * 测试路径:E:\IO\demo.txt
 */

public class Test {
    public static void main(String[] args) throws Exception {
        File file = new File("E:\\IO\\demo.txt");
        // 如果父目录不存在,创建
        if(!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }       
        // 字节输出流
        OutputStream outputStream = new FileOutputStream(file);
        // 输出内容
        String msg = "hello world";
        // 字节输出流只接受字节数组,所以要将字符串转换为字节数组
        outputStream.write(msg.getBytes());
        // 关闭输出流
        outputStream.close();
    }
}

运行程序前,测试文件夹如下:

运行前

运行后,如下:

运行后

  注意,只要是输出流,输出文件如果不存在,JDK会自动帮用户创建,不需要用户手工调用createNewFile()。但是文件路径必须存在,所以一定要在程序中检查父路径是否存在。

  JDK还提供了一种自动关闭流的接口。

// 该接口也只有一个抽象方法,为标识接口
public interface AutoCloseable {
    void close() throws Exception;
}

  关于流的四个顶层抽象类都实现了该接口,都可以直接使用。

  虽然使用该接口可以省掉恼人的关闭流操作,可是它还要与try…catch一起使用,这样变得更加复杂了,操作如下:

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

/**
 * 测试路径:E:\IO\demo.txt
 */


public class Test {
    public static void main(String[] args) throws Exception {
        File file = new File("E:\\IO\\demo.txt");
        if(!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }

        try(OutputStream output = new FileOutputStream(file)) {// 实现了AutoCloseable接口的字节输出流要定义在try内 
            String str = "hello world";
            output.write(str.getBytes());
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

  这种反人类的操作大家自己取舍。

3. 字节输入流InputStream

  InputStream与OutputStream一样,时字节输入的顶层抽象类,具体应用时使用的它的实现子类。

public abstract class InputStream implements Closeable

  对应字节输出流,字节输入流的常用方法为 read 方法,这里我们要注意它的返回值。

// 读取数据到字节数组中,返回数据的读取个数
// 如果此时开辟的字节数组大小大于读取的数据大小,则返回的就是读取个数
// 如果要读取的数据大于数组的内容,那么这个时候返回的就是数组长度
// 如果没有数据了还在读,则返回-1
public int read(byte b[]) throws IOException (最常用方法)

// 读取部分数据到字节数组中,每次只读取传递数组的部分内容
// 如果读取满了则返回长度(len)
// 如果没有读取满则返回读取的数据个数
// 如果读取到最后没有数据了返回-1
public int read(byte b[], int off, int len) throws IOException

// 读取单个字节,每次读取一个字节的内容,直到没有数据了返回-1
public abstract int read() throws IOException;

  看下面的例子,我们读取的文件为上个例子中写入”hello world”的文件。

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 测试路径:E:\IO\demo.txt
 */


public class Test {
    public static void main(String[] args) throws Exception {
        File file = new File("E:\\IO\\demo.txt");
        // 输入流和输出流不同,不会自动创建文件,所以这里要保证文件存在
        if(file.exists()) {
            InputStream in = new FileInputStream(file);
            // 存放读取数据的字节数组
            byte[] data = new byte[1024];
            // 将内容读取到数组中
            int len = in.read(data);
            // 将数组内容转换为字符串,便于我们观察结果
            String result = new String(data, 0, len);
            System.out.println(result);
            in.close();
        }
    }
}

运行结果:

运行结果

4. 字符输出流Writer

  字符比起字节来说能够更好的处理中文数据,Writer类定义如下:

public abstract class Writer implements Appendable, Closeable, Flushable

  看下面的例子:

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;

/**
 * 测试路径:E:\IO\demo.txt
 */


public class Test {
    public static void main(String[] args) throws Exception {
        File file = new File("E:\\IO\\demo.txt");

        // 同字节输出流,一定要保证父路径存在
        if(!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        String str = "爱生活爱Java";
        Writer out = new FileWriter(file);
        out.write(str);
        out.close();
    }
}

运行前:

运行前

运行后:

运行后

  Writer类的结构与方法的使用与OutputStream非常相似,只是Writer类对于中文的支持很好并且提供了直接写入String的方法而已,而OutputStream的输出方法writer()必须先将字符串转为字节数组再进行参数传入。所以以后输出字符串中文的场景,最好使用Writer类,能省下很多的功夫。

5. 字符输入流Reader

  Reader依然是一个抽象类。使用时使用它的实现子类 FileReader。

  Writer类中有直接接受字符串的write方法,而同作为字符操作流的Reader是没有提供的,所以Reader也只能通过字符数组进行操作。

  看下面的例子,文件使用上个例子中被写入的文件。

import java.io.File;
import java.io.FileReader;
import java.io.Reader;

/**
 * 测试路径:E:\IO\demo.txt
 */


public class Test {
    public static void main(String[] args) throws Exception {
        File file = new File("E:\\IO\\demo.txt");

        // 保证文件存在才能进行操作
        if(file.exists()) {
            Reader in = new FileReader(file);
            char data[] = new char[1024];
            int len = in.read(data);
            String str = new String(data, 0, len);
            System.out.println(str);
            in.close();
        }
    }
}

运行结果:

运行结果

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值