• Java SE 学习笔记(十四)—— IO流(2)


    1 字节流

    1.1 字节流写数据

    1.1.1 创建字节输出流对象


    • 字节流抽象基类
      • InputStream:这个抽象类是表示字节输入流的所有类的超类
      • OutputStream:这个抽象类是表示字节输出流的所有类的超类
      • 子类名特点:子类名称都是以其父类名作为子类名的后缀
    • 字节输出流
      • FileOutputStream(String name):创建文件输出流以指定的名称写入文件
    • 使用字节输出流写数据的步骤
      • 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
      • 调用字节输出流对象的写数据方法
      • 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)

    在这里插入图片描述

    示例代码

    public static void main(String[] args) throws IOException {
    	//1.创建字节输出流的对象
    	FileOutputStream fos = new FileOutputStream("D:\\a.txt");
    	//FileOutputStream fos = new FileOutputStream(new File("D:\\a.txt"));
    
    • 1
    • 2
    • 3
    • 4

    注意:

    • 如果文件不存在,会帮我们自动创建出来.
    • 如果文件存在,会把文件清空.

    1.1.2 字节流写数据


    在这里插入图片描述

    示例代码:

    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("bytestream\\a.txt");
      /*fos.write(97);
        fos.write(98);
        fos.write(99);*/
        
        /* byte[] bys = {97,98,99};
        fos.write(bys);*/
    
        byte [] bys = {97,98,99,100,101,102,103};
        fos.write(bys,1,2); //98,99即写入bc
        
        fos.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    那么,现在有两个小问题

    • 字节流写数据如何实现换行?
      • windows:\r\n
      • linux:\n
      • mac:\r
      • getBytes()是字符串的一个方法,可以将字符串转换为字节
    • 字节流写数据如何实现追加写入?
      • public FileOutputStream(String name,boolean append)
      • 创建文件输出流对象以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头

    示例代码:

    public static void main(String[] args) throws IOException {
    	//第二个参数就是续写开关,如果没有传递,默认就是false,
    	//表示不打开续写功能,那么创建对象的这行代码会清空文件.
    
    	//如果第二个参数为true,表示打开续写功能
    	//那么创建对象的这行代码不会清空文件.
    	FileOutputStream fos = new FileOutputStream("bytestream\\a.txt",true);
    
    	fos.write(97);
    	//加一个换行
    	fos.write("\r\n".getBytes());
    	fos.write(98);
    	//加一个换行
    	fos.write("\r\n".getBytes());
    	fos.write(99);
    	//加一个换行
    	fos.write("\r\n".getBytes());
    	fos.write(100);
    	//加一个换行
    	fos.write("\r\n".getBytes());
    	fos.write(101);
    	//加一个换行
    	fos.write("\r\n".getBytes());
    
    	fos.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    1.2 字节流读数据

    1.2.1 创建字节输入流对象


    字节输入流

    • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名

    在这里插入图片描述

    1.2.2 字节流读数据


    在这里插入图片描述

    字节输入流读取数据的步骤:

    • 创建字节输入流对象
    • 调用字节输入流对象的读数据方法
    • 释放资源

    1. 一次读一个字节数据

    public static void main(String[] args) throws IOException {
    	//如果文件存在,那么就不会报错.
    	//如果文件不存在,那么就直接报错.
    	FileInputStream fis = new FileInputStream("bytestream\\a.txt");
    
    	int read = fis.read();
    	//一次读取一个字节,返回值就是本次读到的那个字节数据.
    	//也就是字符在码表中对应的那个数字.
    	//如果我们想要看到的是字符数据,那么一定要强转成char
    
    	System.out.println(read); // 97
    	System.out.println((char)read); //a
    
    	//释放资源
    	fis.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    改进:使用循环

    public static void main(String[] args) throws IOException {
    	FileInputStream fis = new FileInputStream("bytestream\\a.txt");
    	//文件中多个字节我怎么办?
    	/*while(true){
    		int i1 = fis.read(); 内容读取结束读取到空格时返回-1
    		System.out.println(i1); 
    	}*/ 
    
    	int b;
    	while ((b = fis.read())!=-1){
    		System.out.println((char) b);
    	}
    	fis.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    每个读取一个字节存在什么问题呢?

    • 性能较慢
    • 读取中文字符输出无法避免乱码问题

    2. 一次读一个字节数组

    一次读一个字节数组的方法:

    • public int read(byte[] b):从输入流读取最多b.length个字节的数据
    • 返回的是读入缓冲区的总字节数,也就是实际的读取字节个数

    注意:

    • hello.txt文件内容为:ab3abccd
    • io为当前模块名
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("io/src/hello.txt");
        byte[] bytes = new byte[3];
    
        int len1 = fis.read(bytes);
        System.out.println("读取了" + len1 + "字节"); // 读取了3字节
        String s1 = new String(bytes);
        System.out.println(s1); // ab3
    
        int len2 = fis.read(bytes);
        System.out.println("读取了" + len2 + "字节"); // 读取了3字节
        String s2 = new String(bytes);
        System.out.println(s2); // abc
    
        int len3 = fis.read(bytes);
        System.out.println("读取了" + len3 + "字节"); // 读取了2字节
    //        String s3 = new String(bytes);
    //        System.out.println(s3); // cdc
    //        注意:上一次读取的是abc,这一次由于只读两个,所以只会覆盖前两个字符
        
        // 读多少倒出多少
        String s3 = new String(bytes, 0, 2);
        System.out.println(s3); // cd
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    每次读取一个字节数组存在什么问题?

    • 读取的性能得到了提升
    • 读取中文字符输出无法避免乱码问题。

    3. 一次读完全部字节

    (1)定义一个与文件一样大的字节数组byte[] bytes=new byte[(int) f.length()],一次性读完文件的全部字节,这样直接把文件数组全部读取到一个字节数组里可以避免乱码,但是如果文件过大,字节数组可能会引起内存溢出。

    (2)官方为字节输入流 InputStream 提供了如下 API 可以直接把文件的全部数据读取到一个字节数组中

    在这里插入图片描述

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    
    public class Test {
        public static void main(String[] args) throws IOException {
            File f = new File("io/src/hello.txt");
            FileInputStream fis = new FileInputStream(f);
    
            // 方法一
    //        byte[] bytes = new byte[(int) f.length()];
    //        int len = fis.read(bytes);
    //        System.out.println("读取了" + len + "字节"); // 读取了8字节
    //        System.out.println("文件的大小:" + f.length()+"字节"); // 文件的大小:8字节
    //        System.out.println(new String(bytes)); // ab3abccd
            // 方法二
            byte[] bytes1 = fis.readAllBytes();
            System.out.println(new String(bytes1)); // ab3abccd
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.3 字节流复制文件


    1. 小文件复制

    C:\it\a.jpg的文件复制到模块 bytestream 下

    public static void main(String[] args) throws IOException {
    	//创建了字节输入流,准备读数据.
    	FileInputStream fis = new FileInputStream("C:\\test\\a.jpg");
    	//创建了字节输出流,准备写数据.
    	FileOutputStream fos = new FileOutputStream("bytestream\\a.jpg");
    
    	int b;
    	while((b = fis.read())!=-1){
    		fos.write(b);
    	}
    
    	fis.close();
    	fos.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 大文件复制

    对于大文件的复制问题,字节流通过创建字节数组,可以一次读写多个数据

    public static void main(String[] args) throws IOException {
    	FileInputStream fis = new FileInputStream("C:\\test\\a.avi");
    	FileOutputStream fos = new FileOutputStream("bytestream\\a.avi");
    
    	byte [] bytes = new byte[1024];// 该字节数组的大小是1024字节
    	int len;//本次读到的有效字节个数 --- 这次读了几个字节
    
    	while((len = fis.read(bytes))!=-1){  // 循环读取
    		fos.write(bytes,0,len);//0索引开始,读取len个字节
    	}
    
    	fis.close();
    	fos.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    字节流适合做一切文件数据的拷贝吗?

    • 任何文件的底层都是字节,拷贝是一字不漏的转移字节,只要前后文件格式,编码一致就没有任何问题。

    1.4 流的刷新与关闭


    以下两种方法能够让写出去的数据成功生效

    在这里插入图片描述

    1.5 资源释放方式

    1.5.1 try-catch-finally


    有如下代码:

    try {
        FileOutputStream fos = new FileOutputStream("a.txt");
        fos.write(97);
        fos.close();
    }catch (IOException e){
        e.printStackTrace();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们如何操作才能让close方法一定执行呢?

    异常处理的标准格式:

    try{
    	可能出现异常的代码;
    }catch(异常类名 变量名){
    	异常的处理代码;
    }finally{
    	执行所有清除操作; // 在异常处理时提供finally块来执行所有的清除操作,比如IO流的释放资源,被finally控制的语句一定会执行,除非JVM退出
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    加异常处理后的代码如下:

    public static void main(String[] args) {
    	FileOutputStream fos = null;
    	try {
    		//System.out.println(2/0);
    		fos = new FileOutputStream("D:\\a.txt");
    		//FileOutputStream fos = new FileOutputStream("D:\\a.txt");
    		//此时fos是局部变量,在finally里无法执行fos.close();
    		fos.write(97);
    	}catch(IOException e){
    	   e.printStackTrace();
    	}finally {
    		//finally语句里面的代码,一定会被执行.
    		if(fos != null){//如果fos为null就不会和上述路径的文件产生关系,就不用关闭文件
    			try {
    				fos.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    finally 虽然可以用于释放资源,但是释放资源的代码过于繁琐

    1.5.2 try-with-resource


    在这里插入图片描述

    JDK 7 以及 JDK 9 的 ()只能 放置资源对象,用完会自动关闭,自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)

    • 什么是资源呢?
      • 资源都是实现了 Closeable/AutoCloseable 接口的类对象

    在这里插入图片描述

    2 字符流

    2.1 字符流概述


    既然字节流可以操作所有文件,为什么要学习字符流?

    • 如果利用字节流,把文本文件中的中文,读取到内存中,有可能出现乱码
    • 如果利用字节流,把中文写到文本文件中,也有可能出现乱码

    为什么字节流读取文本文件,可能会出现乱码?

    • 因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以会出现乱码的问题

    由于字节流操作中文不是特别的方便,所以Java就提供字符流(字符流 = 字节流 + 编码表)。字符流更适合操作中文,最小单位就是按照单个字符读写的。

    2.2 字符流写数据


    Writer: 用于写入字符流的抽象父类
    FileWriter: 用于写入字符流的常用子类

    构造方法:

    在这里插入图片描述

    成员方法:

    在这里插入图片描述

    public static void main(String[] args) throws IOException {
        //创建字符输出流的对象
        //FileWriter fw = new FileWriter(new File("charstream\\a.txt"));
        FileWriter fw = new FileWriter("charstream\\a.txt");
    
    
        //写一个字符
        fw.write(97);
        fw.write(98);
        fw.write(99);
    
    
        //写出一个字符数组
        char [] chars1 = {97,98,99,100,101};
        fw.write(chars1);
    
    
        //写出字符数组的一部分
        char [] chars2 = {97,98,99,100,101};
        fw.write(chars2,0,3);
    
    
        //写一个字符串
        String line1 = "黑马程序员abc";
        fw.write(line1);
    
    
        //写一个字符串的一部分
        String line2 = "黑马程序员abc";
        fw.write(line2, 0, 2);
    
        //释放资源
        fw.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    注意:

    • 在创建字符输出流对象时,如果文件存在就清空,如果文件不存在就创建,但是要保证父级路径存在
    • 写数据时,写出int类型的整数,实际写出的是整数在码表上对应的字母,写出字符串数据,是把字符串本身原样输出

    2.3 字符流读数据


    Reader: 用于读取字符流的抽象父类
    FileReader: 用于读取字符流的常用子类

    构造方法

    在这里插入图片描述
    成员方法

    在这里插入图片描述

    public static void main(String[] args) throws IOException {
        //创建字符输入流的对象
        // FileReader fr = new FileReader(new File("charstream\\a.txt"));
        FileReader fr = new FileReader("charstream\\a.txt");
    
        //读取数据
        
        //一次读取一个字符
    /*        int ch;
        while((ch = fr.read()) != -1){
            System.out.println((char) ch);
        }*/
    
    
        //一次读取多个字符。
        //创建一个数组
        char [] chars = new char[1024];
        int len;
        //read方法还是读取,但是是一次读取多个字符
        //他把读到的字符都存入到chars数组。
        //返回值:表示本次读到了多少个字符。
        while((len = fr.read(chars))!=-1){
            System.out.println(new String(chars,0,len));
        }
    
        //释放资源
        fr.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    小结:

    • 字节流适合做一切文件的拷贝
    • 字符流适合做文本文件的操作(读写)
      • 想要把文本文件中的数据读到内存中,使用字符输入流
      • 想要把内存中的数据写到文本文件中,使用字符输出流
  • 相关阅读:
    [C#] 允许当前应用程序通过防火墙
    Java数据结构与算法
    在Bat To Exe Converter,修改为当异常结束或终止时,程序重新启动执行
    C++ 中的 this 指针
    Docker Desktop Windows 无法启动
    Mybiosource丨Mybiosource重组表皮葡萄球菌磷酸结合蛋白 pstS
    前端 WebSocket 的一些使用
    【Hack The Box】linux练习-- Sunday
    【iOS】面试整理
    x86下docker镜像中arm64v8/openjdk:8-jre添加vi/vim/ping/curl命令
  • 原文地址:https://blog.csdn.net/hu_wei123/article/details/134061334