• Java学习笔记(十五):IO流



      
      
      

      

    Java IO流原理

      

      

    I/O 是 Input/Output 的缩写,I/O技术非常实用,用于处理设备之间的数据传输。如:读/写文件,网络通讯等。

    Java程序中,对于数据的输入/输出操作以 的方式进行。

    java.io包下提供了各种 “流” 类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。

      

    输入input :读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。

    输出output :将程序(内存)中数据输出到磁盘、光盘等存储设备中,能实现永久存储。

      
      
      

      

    流的分类

      

      

    按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)

    按数据流的流向不同分为:输入流输出流

    按流的角色不同分为:节点流处理流

      

    抽象基类字节流字符流
    输入流InputStreamReader
    输出流OutputStreamWriter

    Java的IO流共涉及40多个类,看着多但实际上非常规则,都是从上面4个抽象基类派生出来的。

    由这4个类派生出来的子类名称都是以其父类名作为子类名后缀。

      

    如何选择:

    对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
    对于非文本文件(.jpg,.mp3,.avi,.doc,.ppt,…),使用字节流处理

      
      
      

      

    IO流体系

      

      

    只写常用的几个
      

    分类字节输入流字节输出流字符输入流字符输出流
    抽象基类InputStreamOutputStreamReaderWriter
    文件流 (节点流)FileInputStreamFileOutputStreamFileReaderFileWriter
    缓冲流 (处理流)BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
    转换流 (处理流)InputStreamReaderOutputStreamWriter

      

    处理流是包裹在节点流外层的一种流,作用于已有流的基础之上。

      
      
      

      

    节点流

      

      


    先明确IO流的操作步骤,这就是模板,下面所有都是这样写的:

    1. 实例化File类的对象,指明要操作的文件

    2. 提供具体的流

    3. 数据的具体操作(读或写)

    4. 流的关闭


      

      

    FileReader

      

    分布来看:

    // 1.实例化File类的对象,指明要操作的文件
    File file = new File("hello.txt");
    
    • 1
    • 2
    // 2.FileReader流的实例化
    FileReader fr = new FileReader(file);
    
    • 1
    • 2
    // 3.数据的读入
    // read(char[] cbuf):返回每次读入cbuf数组中的字符个数。如果达到文件末尾,返回-1
    char[] cbuf = new char[5];
    int len;
    while((len = fr.read(cbuf)) != -1){
    
    	// 方式一:循环遍历每次读到的cbuf
    //    for (int i = 0; i < len; i++) {
    //        System.out.print(cbuf[i]);
    //    }
    
        // 方式二:把每次读到的cbuf变成字符串输出
        String str = new String(cbuf,0,len);
        System.out.print(str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // 4.资源关闭
    fr.close();
    
    • 1
    • 2

      

    但此时并不完整,有编译时异常需要异常处理,加上 try-catch-finally,完整代码:

    package IO.FileReaderWriterTest;
    
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileReaderTest2 {
        public static void main(String[] args) {
        
            FileReader fr = null;
    
            try {
            
                // 1.File类的实例化,指明要操作的文件
                File file = new File("hello.txt");
    
                // 2.FileReader流的实例化
                fr = new FileReader(file);
    
                // 3.数据的读入
                // read(char[] cbuf):返回每次读入cbuf数组中的字符个数。如果达到文件末尾,返回-1
                char[] cbuf = new char[5];
                int len;
                while((len = fr.read(cbuf)) != -1){
    
                    // 方式一:循环遍历每次读到的cbuf
        //            for (int i = 0; i < len; i++) {
        //                System.out.print(cbuf[i]);
        //            }
    
                    // 方式二:把每次读到的cbuf变成字符串输出
                    String str = new String(cbuf,0,len);
                    System.out.print(str);
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    // 4.资源关闭
                    assert fr != null;
                    fr.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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

      

    补充一点:
    try-catch-finally 快捷键:Ctrl+Alt+T

    在这里插入图片描述

    Ctrl+Alt+T 不仅仅能用于生成 try-catch-finally 结构,还能一键生成各种结构体,非常好用!

      

      

    FileWriter

      

    // 1.实例化File类的对象,指明写到的文件
    File file = new File("hello1.txt");
    
    • 1
    • 2
    // 2.提供FileWriter流的对象
    FileWriter fw = new FileWriter(file);
    
    • 1
    • 2

    注意:
    这里的 FileWriter构造器 new FileWriter(file,append) 可以选参数

    1. 不写默认为false或写上false:意思是在原文件上直接覆盖新的数据。
    2. 写true:在文件后加新的数据,就不覆盖原有的了。
    // 3.数据写入
    fw.write("我有一个梦想\n");
    fw.write("你也得有一个梦想\n");
    
    • 1
    • 2
    • 3
    // 4.资源关闭
    fw.close();
    
    • 1
    • 2

      
    最后别忘了 try-catch-finally!

      

    完整代码:

    package IO.FileReaderWriterTest;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class FileWriterTest {
        public static void main(String[] args) {
            FileWriter fw = null;  // 追加模式写入
    
            try {
            
                // 1.实例化File类的对象,指明写到的文件
                File file = new File("hello1.txt");
    
                // 2.提供FileWriter流的对象
    //        FileWriter fw = new FileWriter(file);   // append默认为false,直接覆盖
                fw = new FileWriter(file,true);
    
                // 3.数据写入
                fw.write("我有一个梦想\n");
                fw.write("你也得有一个梦想\n");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    // 4.资源关闭
                    assert fw != null;
                    fw.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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

      

    最后补充一点 read() 及其重载方法的使用:

    在这里插入图片描述
    读取单个字符。

    返回值:读取的字符,如果已到达流的结尾,则为-1

    抛出:IOException–如果发生I/O错误

      

    在这里插入图片描述
    将字符读入数组。此方法将一直阻塞,直到有一些输入可用、发生I/O错误或到达流的结尾。

    参数:cbuf–目标缓冲区

    返回值:读取的字符数,如果已到达流的结尾,则为-1

    抛出:IOException–如果发生I/O错误

      

      

    FileReader和FileWriter一起使用实现文件的复制

      

    package IO.FileReaderWriterTest;
    
    import java.io.*;
    
    public class TestAll {
        public static void main(String[] args) {
    
            FileReader fr = null;
            FileWriter fw = null;
            try {
            
                // 1.造文件
                File file1 = new File("hello.txt");
                File file2 = new File("hello2.txt");
    
                // 2.造流
                fr = new FileReader(file1);
                fw = new FileWriter(file2,true);
    
                // 3.数据操作
                char[] cbuf = new char[5];
                int len;
                while((len = fr.read(cbuf)) != -1){
                    // 方法一:
                    for (int i = 0; i < len; i++) {
                        fw.write(cbuf[i]);
                    }
    
                    //方法二:  每次写出0到len的字符
    //                fw.write(cbuf,0,len);
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    // 4.关闭流
                    assert fr != null;
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                try {
                    assert fw != null;
                    fw.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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

      

      

      

    注意⭐细节:

    在第3步数据操作时,字符流和字节流有所区别:
      

    1. 字符流:用 char[](char型数组)
        
    2. 字节流:用 byte[](byte型数组)

      

      

      

    FileInputStream和FileOutputStream一起使用实现图片的复制

      

    由于字节流和字符流使用方式几乎一模一样,所以直接写个总的了

    package IO.FileInputOutputStreamTest;
    
    import java.io.*;
    
    public class TestAll {
        public static void main(String[] args) {
    
            FileInputStream fws = null;
            FileOutputStream fos = null;
            try {
                File file1 = new File("a3141ac538443882b3aca9f5e462b31.jpg");
                File file2 = new File("picture.jpg");
    
                fws = new FileInputStream(file1);
                fos = new FileOutputStream(file2);
    
                byte[] b = new byte[10];
                int len;
                while((len = fws.read(b)) != -1){
                	fos.write(b,0,len);
                }
                System.out.println("复制成功!");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    assert fws != null;
                    fws.close();
                    assert fos != null;
                    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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

      
      
      

      

    处理流

      

    就是“套接”在已有流的基础上,进一步加工,实现更多功能。

      

      

      

    缓冲流

      

    缓冲流是处理流,是包裹在节点流上的流。
      

    具体做法:

    造流的时候多加一步。造完节点流后,造缓冲流即可。

    之后就是按部就班的操作数据和关闭资源。

      

    用的类有:

    • BufferedInputStream:字节缓冲输入流
        
    • BufferedOutputStream:字节缓冲输出流
        
    • BufferedReader:字符缓冲输入流
        
    • BufferedWriter:字符缓冲输出流

      

    作用: 提高流的读取、写入速度。

    能提高读写速度的原因: 内部提供了一个缓冲区。

      

      

    缓冲流(字节型)实现非文本文件的复制(具体步骤)

      
    1、造文件

    File srcFile = new File("D:\\Java\\StudyPlus\\test.txt");
    File destFile = new File("D:\\Java\\StudyPlus\\test2.txt");
    
    • 1
    • 2

      
    2、造流 —— 先造里层的流,后造外层的流。就像穿衣服:先穿里层,后穿外层。

    2.1、造节点流

    FileInputStream fis = new FileInputStream(srcFile);
    FileOutputStream fos = new FileOutputStream(destFile);
    
    • 1
    • 2

    2.2、造缓冲流

    BufferedInputStream bis = new BufferedInputStream(fis);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    
    • 1
    • 2

      
    3、数据操作:读取,写入

    byte[] b = new byte[10];
    int len;
    while((len = bis.read(b)) != -1){
        bos.write(b,0,len);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

      
    4、关资源

    要求:先关闭外层的流,再关闭内层的流。即,先关外面后关里面。就像脱衣服:先脱外层,后脱里层。

    偷懒: 关闭外层流的同时,内层流也会自动关闭。所以我们只需关外层即可,内层流的关闭可以省略。

    bis.close();
    bos.close();
    
    • 1
    • 2

      

    总的代码:

    package IO.Buffered;
    
    import java.io.*;
    
    public class BufferedInputOutputStream {
        public static void main(String[] args) {
            BufferedInputStream bis = null;
            BufferedOutputStream bos = null;
    
            try {
                // 1.造文件
                File srcFile = new File("D:\\Java\\StudyPlus\\a3141ac538443882b3aca9f5e462b31.jpg");
                File destFile = new File("D:\\Java\\StudyPlus\\picture1.jpg");
    
                // 2.造流     先造里层的流,后造外层的流。   穿衣服先穿里层,后穿外层。
                // 2.1造节点流
                FileInputStream fis = new FileInputStream(srcFile);
                FileOutputStream fos = new FileOutputStream(destFile);
                // 2.2造缓冲流(处理流)
                bis = new BufferedInputStream(fis);
                bos = new BufferedOutputStream(fos);
    
                // 3.复制的细节:读取、写入
                byte[] b = new byte[10];
                int len;
                while((len = bis.read(b)) != -1){
                    bos.write(b,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    // 4.关资源
                    // 要求:先关闭外层的流,再关闭内层的流。  先关外面后关里面。   脱衣服先脱外层,后脱里层。
                    assert bis != null;
                    bis.close();
                    assert bos != null;
                    bos.close();
                    // 说明:关闭外层流的同时,内层流也会自动关闭。所以我们只需关外层即可,内层流的关闭可以省略。
    				// fis.close();
    				// 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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

      

      

    缓冲流(字符型)实现文本文件的复制

      

    package IO.Buffered;
    
    
    import java.io.*;
    
    public class BufferedReaderWriter {
        public static void main(String[] args) {
    
            BufferedReader br = null;
            BufferedWriter bw = null;
            try {
                File srcFile = new File("D:\\Java\\StudyPlus\\test.txt");
                File destFile = new File("D:\\Java\\StudyPlus\\test2.txt");
    
                FileReader fr = new FileReader(srcFile);
                FileWriter fw = new FileWriter(destFile);
    
                br = new BufferedReader(fr);
                bw = new BufferedWriter(fw);
    
                // 方法一:使用char型数组
    //            char[] cbuf = new char[1024];
    //            int len;
    //            while((len = br.read(cbuf)) != -1){
    //                bw.write(cbuf,0,len);
    //            }
    
                // 方法二:使用String
                String data;
                while((data = br.readLine()) != null){
                    // 换行的方式一: 手动添加 \n 换行
    //                bw.write(data + "\n");  // data中不包含换行符
                    // 换行的方式二: 调用方法
                    bw.write(data);
                    bw.newLine();   // 提供换行的操作
                }            
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    assert br != null;
                    br.close();
                    assert bw != null;
                    bw.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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

      

      

      

    转换流

      

    在这里插入图片描述
      

    转换流: 属于字符流

    • InputStreamReader :将一个字节的输入流转换为字符的输入流
    • OutputStreamWriter :将一个字符的输出流转换为字节的输出流

      

    作用: 提供字节流与字符流之间的转换

    应用: 能改变文件的编码方式。如:utf-8 与 gbk间的相互转换

      

    解码:字节、字节数组 ——→ 字符串、字符数组
    编码:字符串、字符数组 ——→ 字节、字节数组

      
      
    好记的口诀:
      
    1、转化顺序和方向:

    • 先是 InputStreamReader :从左到右看 InputStream ——→ Reader 字节转字符
    • 然后 OutputStreamWriter :从右到左看 Writer ——→ OutputStream 字符转字节

    2、用的流:

    • InputStreamReaderFileInputStream
    • OutputStreamWriterFileOutputStream

      
      

    注意:

    因为要实现编码格式的转换,所以在 new转换流时,在构造器里写转换前后的参数。

    InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
    OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
    
    • 1
    • 2

    转换前是 utf-8,转换后为 gbk

    还有一点:

    InputStreamReader 构造器中默认编码方式为 utf-8

      

      

    转换流实现文件的读入和写出

      

    package IO.转换流;
    
    import java.io.*;
    import java.nio.charset.StandardCharsets;
    
    public class Test {
        public static void main(String[] args) {
    
            InputStreamReader isr = null;
            OutputStreamWriter osw = null;
            try {
                FileInputStream fis = new FileInputStream("test.txt");
                FileOutputStream fos = new FileOutputStream("test_gbk.txt");
    
                isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
                osw = new OutputStreamWriter(fos,"gbk");
    
                char[] cbuf = new char[20];
                int len;
                while((len = isr.read(cbuf)) != -1){
                    osw.write(cbuf,0,len);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    assert isr != null;
                    isr.close();
                    assert osw != null;
                    osw.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
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    此时,新文件的编码方式就变了,变成 gbk了。

  • 相关阅读:
    Django笔记二十九之中间件介绍
    用helm安装chart的时候如何使用master的IP
    高级管理学:大题
    [Pandas] 数据连接pd.concat
    PyTorch深度学习(七)【循环神经网络-提高】
    JS常用的函数
    结合 LDAP 和 RADIUS 认证的无线网络安全方案
    15.一种坍缩式的简单——组合模式详解
    C语言基础知识:内存分配
    Vuex从了解到实际运用(二),获取vuex中的全局状态(state,getters)
  • 原文地址:https://blog.csdn.net/qq_56952363/article/details/125424461