【JAVA基础】流-输入输出流的使用详解

一. 流

1. 什么是流?

在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成。程序中的输入输出都是以流的形式保存的,流中保存的实际上全都是字节文件。

2. 流分类

按照流向可以分为:输入流和输出流
按照传输单位可以分为:字节流和字符流

3.字节流和字符流

  • 字节流:以字节(8位)为单位进行数据传输,适用于处理任何类型的二进制数据,如图片、音频、视频等。Java中的InputStreamOutputStream是字节流的抽象基类。
  • 字符流:以字符(16位Unicode)为单位进行数据传输,主要用于处理文本数据。ReaderWriter是字符流的抽象基类。

事实上,字节流也可以操作一部分文本文件,但不提倡这样做,因为用字节流操作文本文件如果文件中有汉字,可能会出现乱码。这是因为字节流不能直接操作unicode 字符所致。

最常见的区分方式就是将不同的文件在记事本当中打开,如果看得懂那就是字符流,看不懂的话那就是字节流。

二. 使用 InputStream OutputStream

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

1.InputStream流类

方法说明
public int read()从输入流中的当前位置读入一个字节(8bit)的二进制数据,然后以此数据为低位字节,配上8个全0的高位字节合成一个16位的整型量(0~255)返回给调用此方法的语句,若输人流中的当前位置没有数据,则返回 -1
public int read(byte[ ] b)从输入流中的当前位置连续读人多个字节保存在数组b中,同时返回所读到的字节数
public int read(byte[ ] b,int off,int len)从输人流中的当前位置连续读人len个字节,从数组b的第off+1个元素位置处开始存放,同时返回所读到的字节数
public void close()关闭输入流与外设的连接并释放所占用的系统资源

以上罗列了最常用的InputStream中的方法,当程序员需要从外设、网络请求等不同来源读入数据时,应该创建对应的输入流来读取数据。

由于InputStream是一个抽象类,所以应该根据不同的使用选择与之对应的实现类,并使用继承或重写的read方法进行读取。

2.OutputStream流类

方法说明
public void write(int b)将参数b的低位字节写入到输出流
public void write(byte[ ] b)将字节数组b中的全部字节按顺序写人到输出流
public void write ( byte[ ] b, int off,int len)将字节数组b中第off+1个元素开始的len个数据,顺序地写人到输出流
public void flush( )强制清空缓冲区并执行向外设写操作
public void close()关闭输出流与外设的连接并释放所占用的系统资源

以上罗列了OutputStream常用的几个方法,主要是通过调用write方法对数据进行写入,以及关闭流的方式。

同样的,要根据不同的情况选择不同的子类进行操作。

3.使用案例

public static void main(String[] args) {
        try {
            //创建一个输入流
            FileInputStream fileInputStream = new FileInputStream("C:\\Users\\f\\Desktop\\123.txt");
            //创建一个输出流
            FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\f\\Desktop\\2.txt");
            //创建一个缓冲暂存区
            byte[] bytes = new byte[1024];
            int temp;
            //循环传输
            while((temp=fileInputStream.read(bytes))!=-1){
                //写文件
                fileOutputStream.write(bytes);
            }
            //出循环以后传输完成关闭流
            fileOutputStream.close();
            fileInputStream.close();

        }  catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

三. 使用 Reader 和 Writer 流类

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

1.Writer流类

常用方法说明
public void write(int c)将单一字符c输出到流中
public void write(String str)将字符串输出到流中
public void write(char[] cbuf)将字符数组cbuf输出到流
public void write(char[] cbuf, int off, int len)将字符数组按指定格式输出
public void flush()将缓冲区的数组写到文件
public void close()关闭输出流

2.Reader流类

常用方法说明
public int read()从输入流中读一个字符
publici int read(char[ ] cbuf)从输入流中读最多cbuf.length个字符,存入字符数组cbuf
public int read(char[ ] cbuffer, int off, int len)从输入流中读最多len个字符,存入字符数组cbuffer中从off开始的位置
public void close()关闭输入流

3.使用示例

import java.io.FileWriter;
import java.io.IOException;


public class IoDemo {
    public static void main(String[] args) throws IOException {
        //定义文件地址
        String filePath = "D:\\f\\text.txt";
        //写入内容
        String content = "欢迎来到Java~";
        //因为是写入文件,所以要使用FileWriter方法
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(filePath);
            fileWriter.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
        //关闭流
            fileWriter.close();
        }
    }
}

try (BufferedReader br = new BufferedReader(new FileReader("file.txt"));
     BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        bw.write(line);
        bw.newLine();
    }
} catch (IOException e) {
    e.printStackTrace();
}

4.子类补充

4.1 FileReader类
4.1.1 创建输入流对象

FileReader(File file): 创建一个新的 FileReader,给定要读入的File对象。

FileReader(String fileName): 创建一个新的 FileReader ,给定要读入的文件名称。

4.1.2 读入字符数据

read(char[] cbuf):每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1 ,代码使用演示:

4.1.3 实例
public class FISRead {
    public static void main(String[] args) throws IOException {
      	// 使用文件名称创建流对象
       	FileReader fr = new FileReader("read.txt");
      	// 定义变量,保存有效字符个数
        int len ;
        // 定义字符数组,作为装字符数据的容器
        char[] cbuf = new char[2];
        // 循环读取
        while ((len = fr.read(cbuf)) != -1) {
            System.out.println(new String(cbuf, 0, len));
        }
    	// 关闭资源
        fr.close();
    }
}

4.2 FileWriter类
4.2.1 创建输出流对象
  • FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。
  • FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取文件的名称。
  • FileWriter(File file, boolean append):在写入文件时,如果文件已经存在,FileWriter会默认覆盖该文件。如果想要追加内容,可以在构造函数中传入true作为第二个参数。
4.2.2 写出字符数据

​ write(int b) :写出一个字符

4.2.3 关闭和刷新

这里有一个问题:为什么我们要在输出流这里不断刷新呢?

字符输出流需要刷新,‌主要是因为字符流在操作时使用了缓冲区,‌通过缓冲区再操作文件。‌如果不刷新输出流,‌可能会导致输出的顺序与预期不同,‌甚至有些输出内容被吞掉。‌这是因为字符流在输出时,‌会将数据先写入缓冲区,‌然后在适当的时机再将缓冲区的内容写入文件或设备。‌如果程序在写入数据后立即结束,‌而缓冲区中的数据尚未被完全写入,‌那么这些未写入的数据就会丢失。‌因此,‌为了确保数据的完整性和正确性,‌需要在适当的时候刷新输出流,‌即将缓冲区中的数据强制写入文件或设备,‌避免数据丢失或顺序混乱

flush:刷新缓冲区,流对象可以继续使用。

close:先刷新缓冲区,然后通知系统释放资源,流对象不可以再被使用了。

public class FWWrite {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileWriter fw = new FileWriter("fw.txt");
        // 写出数据,通过flush
        fw.write('刷'); // 写出第1个字符
        fw.flush();
        fw.write('新'); // 继续写出第2个字符,写出成功
        fw.flush();
      
      	// 写出数据,通过close
        fw.write('关'); // 写出第1个字符
        fw.close();
        fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed
        fw.close();
    }
}
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterExample {
    public static void main(String[] args) {
        try {
            // 创建文件字符输出流
            FileWriter writer = new FileWriter("output.txt");
            String str = "Hello, World!";
            // 写入数据
            writer.write(str);
            // 关闭流
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四.注意点

1.在使用字节流处理文本时,如果不指定或忽略编码,可能导致乱码。

​ 解决方案是明确指定编码,如使用FileInputStream时配合InputStreamReader指定编码。

FileInputStream fis = new FileInputStream("file.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");

2.处理二进制数据时误用字符流,或处理文本数据时误用字节流,可能导致数据丢失或错误。确保根据数据类型选择正确的流类型。

3.在操作完流后忘记关闭,可能导致资源泄漏。

使用try-with-resources语句可以自动关闭流。(这是JDK1.7提供的新方法,让代码的可读性更高,但是并不是所有的对象都可以这样,当调用的类实现了closable接口就可以使用此种方式)

4.在使用字节流操作中,即使没有关闭资源(close方法),也能输出;而字符流不使用close方法的话,不会输出任何内容。

  • 43
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卑微的小红猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值