Java-IO流

11次阅读

共计 2969 个字符,预计需要花费 8 分钟才能阅读完成。

概述

Java 程序中,对于数据的 输入 / 输出 操作以 “流(stream)” 的方式进行。

流的分类

  • 按操作数据单位的不同分为:字节流(8bit)字符流(16 bit)
  • 按数据流的流向不同分为:输入流 输出流
  • 按流的角色不同分为:节点流 处理流

关于字节流和字符流的区别:

  • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
  • 处理对象不同:字节流能处理所有类型的数据(如图片、avi 等),而字符流只能处理字符类型的数据。
  • 字节流:一次读入或读出是 8 位二进制。
  • 字符流:一次读入或读出是 16 位二进制。

输入流和输出流

  • 输入流只能进行读操作
  • 输出流只能进行写操作

节点流和处理流

节点流:直接从数据源或者目的地读写数据

处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

Java IO 流体系

Java 中 IO 流体系如下表所示,最基本的四个抽象基类分别是:InputStreamOutputStreamReaderWriter。表格每一列的类都是继承与第一行的抽象基类的。

常用的字符流

FileReader

通常一个流的使用由以下 4 个步骤组成:

  1. 提供 File 类的对象,指明要操作的文件
  2. 提供具体的流
  3. 根据不同流,进行不同的操作
  4. 流的关闭操作

比如以 FileReader 流为例,那么它的使用步骤是:

  1. 实例化 File 类的对象,指明要操作的文件
  2. 提供具体的流,FileReader 流的实例化
  3. 进行数据的读入操作
  4. 流的关闭操作

虽然 Java IO 流体系有众多的类,但是所有的流都基本按照这几个步骤来使用的。下面记录一下 FileReader 的使用,数据的读入操作。

    @Test
    public void testFileReader() throws IOException { // 抛出异常
        // 1. 实例化 File 类对象,指明要操作的文件
        File file = new File("Hello.txt");
        // 2. 提供具体的流
        FileReader fr = new FileReader(file);
        // 3. 数据的读入
        // read():返回读入的一个字符,如果达到文件末尾,那么返回 -1
        int data = fr.read();
        while(data != -1){System.out.print((char)data);
            data = fr.read();}
        // 4. 流的关闭操作
        fr.close();}

上面例子中,通过 File 类实例化了一个 File 对象,然后将其当做参数传入了 FileReader 类进行实例化,之后调用 read()方法进行数据的读入,最后执行流的关闭。

关于上面代码有几个需要注意的点:

  • File file = new File("Hello.txt");这段代码使用的是相对路径,如果是使用 IDEA 进行编写的,那么这个相对路径在单元测试方法和 main 方法中有所区别。在单元测试方法中使用相对路径,那么相对的是模块的路径,而在 main 方法中相对的是项目的路径。比如,这里模块名 Day07, 这个模块下有文件 Hello.txt,而模块位于项目 JavaSenior 下, 那么 File 类实例化后的实例文件路径如下代码所示:
public class FileReaderTest {
    // 在 main 方法中,输出:D:\code\Java\JavaSenior\Hello.txt
    public static void main(String[] args) {File file = new File("Hello.txt");
        System.out.println(file.getAbsolutePath());
    }
    
     // 在单元测试方法中,输出:D:\code\Java\JavaSenior\Day07\Hello.txt
    @Test
    public void test4(){File file = new File("Hello.txt");
        System.out.println(file.getAbsolutePath());
    }
}
  • 由于创建流不能被 Java 垃圾回收机制所处理,所以需要手动进行流的关闭操作,但是由于异常,在步骤 1,2,3 可能出现异常导致第 4 步 (流的关闭) 不能被执行,导致资源不能及时回收,所以为了保证资源一定可以执行关闭操作,需要使用 try-catch-finally 进行处理,而不建议使用抛出的方式。所以,比较规范的流使用代码应该如下:
    @Test
    public void test2(){
        FileReader fr = null;
        try{File file = new File("Hello.txt");
            fr = new FileReader(file);
            int data;
            while ((data = fr.read()) != -1){System.out.println((char)data);
            }
        }catch (IOException e){e.printStackTrace();
        }finally {
            try {if (fr != null){fr.close();
                }
            } catch (IOException e) {e.printStackTrace();
            }
        }

    }
  • read()方法:读取单个字符。作为整数读取的字符,范围在 0 -65535 之间(2 个字节的 Unicode 码),如果已到达流的末尾,则返回 -1。
  • read(char cbuf[]):将字符存入数组,并且返回本次读取的字符数,如果到达流的末尾,则返回 -1。
    @Test
    public void test3()  {
        FileReader fr = null;
        try {
            // 1. File 类的实例化
            File file = new File("Hello.txt");

            // 2. FileReader 流的实例化
            fr = new FileReader(file);

            // 3. 读入的操作
            char[] cbuf = new char[5];
            int len;
             /*
             假如 Hello.txt 里的文件内容是 helloworld123,这里的 read(cbuf)指的是读取 5 个字符,即第一次读取 hello,返回 5,第二次读取 world,返回 5,第三次读取 123,因为只有 123 了,返回 3,第四次返回 -1。所以 3 次循环,len 变量的值为 5,5,3,最后一次为 -1。*/
            while ((len = fr.read(cbuf)) != -1) {
                // 遍历操作错误的写法
//                for (int i = 0; i < arr.length; i++) {//                    System.out.println(arr[i]);
//                }
                // 正确的遍历操作一://                for (int i = 0; i < len; i++) {//                    System.out.print(arr[i]);
//                }

                // 错误的写法二://                String str = new String(arr);
//                System.out.println(str);

                // 正确的写法二:String str = new String(cbuf,0,len);
                System.out.print(str);
            }
        }catch (IOException e){e.printStackTrace();
        }finally {
            // 4. 流资源的关闭
            try {if (fr != null) {fr.close();
                }
            }catch (IOException e){e.printStackTrace();
            }
        }
    }

未完待续 …

正文完
 0