Java IO流介绍

前言

最近入职两个月了,感觉每天都在看业务相关的东西,技术上没任何进步,这种感觉非常慌张。因为现在疫情肆虐,经济下行,大环境如此不好,随时都有面临失业的风险,如果不能进步,肯定会被优化掉,所以需要好好学习技术来提升自己。在这里给自己定个目标,至少要两周产出一篇博客,算是学习过程中的总结吧。
Java IO流是非常常用的一种技术,我之前对IO不怎么了解,但每次使用的时候总是在网上搜搜博客来实现自己的功能,虽然可以实现相应功能,但下次使用的时候还是需要在继续在网上搜索,每次搜索还是非常浪费时间的,遂打算系统学习一下进行总结。我先翻看了《Java 编程思想》中对IO流的介绍,可能自己基础比较薄弱的原因,看完后感觉看的云里雾里。然后在B站上看到韩顺平对Java IO流的介绍,感觉非常不错,介绍非常详细,非常推荐基础不好的同学进行观看学习,链接为:韩顺平讲Java IO流专题

一、什么是Java IO流

IO流是Java中的一个重要构成部分。IO,即inout,也就是输入和输出,指java应用程序和外部设备之间进行数据传递,常见的外部设备包括文件、管道、网络连接等,本文介绍主要依托文件进行。

Java中通过流(Stream)来处理IO,流是一个抽象的概念,是指一连串的数据(字符或字节)以先进先出的方式发送信息的通道。

一般来说流具有一下特点:

  • 先进先出:最先写入流中的数据也最先读取。
  • 顺序存取:流要按照顺序进行存读,不能随机访问中间的数据。
  • 只读或只写: 每个流只能是输入流或输出流中的一种,不能同时具备两个功能,输入流只能进行读操作,输出流只能进行写操作。在一个数据传输过程中,如果既要读取数据,又要写入数据,则要分别提供两个流。

1.1 流的分类

  • 按操作数据单位不同分为:字节流(8 bit)二进制文件,字符流(按字符)文本文件
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流/包装流
抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

(1) Java的IO流共设计40多个类,实际上非常规则,都是从如上四个抽象基类派生的
(2)由这四个类派生出来的子类名称都是以其父类名称作为子类名后缀

1.2 认识文件

想学习Java IO流,首先要认识文件。
文件对我们来说并不陌生,文件是保存数据的地方,比如我们经常使用的word文件,txt文件,excel文件等,还有一张图片,一个视频,一段音乐等也都是文件。

在Java中操作文件有专门的对象,即文件对象。

1.2.1 构建文件对象相关构造器和方法

相关方法

  • new File(String pathname) //根据路径构建一个File对象
  • new File(File parent, String child) //根据父目录文件+子路径创建
  • new FIle(String Parent, String child) //根据父目录+子路径创建
    在这里插入图片描述
  • createNewFile() 创建新文件

代码实例:创建文件news1.txt、news2.txt、news3.txt,用三种不同的方式创建

public class FileDemo01 {
    // 方法1创建
    @Test
    public void test01() throws IOException {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/news1.txt";
        File file = new File(filePath);
        if (file.createNewFile()) {
            System.out.println("文件创建成功");
        } else {
            System.out.println("文件创建失败");
        }
    }

    // 方法2创建
    @Test
    public void test02() throws IOException {
        String parentPath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/";
        File parentFile = new File(parentPath);
        String fileName = "news2.txt";
        File file = new File(parentFile, fileName);
        if (file.createNewFile()) {
            System.out.println("文件创建成功");
        } else {
            System.out.println("文件创建失败");
        }
    }

    // 方法3创建
    @Test
    public void test03() throws IOException {
        String parentPath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/";
        String fileName = "news3.txt";
        File file = new File(parentPath, fileName);
        if (file.createNewFile()) {
            System.out.println("文件创建成功");
        } else {
            System.out.println("文件创建失败");
        }
    }
}

创建文件的结果:
文件创建结果

1.2.2 获取文件的相关信息

在这里插入图片描述

  • getName() :获取文件名字
  • getAbsolutePath():获取绝对地址
  • getParent():获取父目录地址
  • length():获取文件长度,单位是字节
  • exists():文件是否存在
  • isFile():是否是文件
  • isDirectory():是否是目录
    代码示例
	@Test
    public void getFileInfo() throws IOException {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/file.txt";
        File file = new File(filePath);
        // 1.获取文件名字
        String name = file.getName();
        System.out.println(name);
        // 2.获取绝对地址
        String absolutePath = file.getAbsolutePath();
        System.out.println(absolutePath);
        // 3.获取父目录地址
        String parent = file.getParent();
        System.out.println(parent);
        // 4.获取文件长度,单位是字节
        long length = file.length();
        System.out.println(length);
        // 5.判断文件是否存在
        boolean exists = file.exists();
        System.out.println(exists);
        // 6.判断是否是文件
        boolean file1 = file.isFile();
        System.out.println(file1);
        // 7.判断是否是目录
        boolean directory = file.isDirectory();
        System.out.println(directory);
    }

1.2.3 目录操作和文件删除

  • mkdir():创建一级目录
  • mkdirs():创建多层目录
  • delete():删除空目录或文件

代码示例

    @Test
    public void test05() throws IOException {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/a/b/c";
        File file = new File(filePath);
        // 目录不存在时进行创建
        if (!file.exists()) {
            file.mkdirs();
            System.out.println("目录创建成功");
        }
        // 目录存在时进行删除
        if (file.exists()) {
            file.delete();
            System.out.println("目录删除成功");
        }
    }

二、节点流

2.1 Java IO流体系

请添加图片描述请添加图片描述

2.2 FileInputStream介绍

FileInputStream是常用的字节输入流
在这里插入图片描述

代码示例:使用FileInputStream读取文件hello.txt文件,并将文件内容显示到控制台
hello.txt内容中不包含中文,当文件中有中文时会出现乱码现象

public class FileStreamDemo01 {
    //1.单个字节读取
    @Test
    public void test01() {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        FileInputStream fileInputStream = null;
        try {
            // 由文件路径来构建文件输入流
            fileInputStream = new FileInputStream(filePath);
            // 字符和int类型可以相互转换
            int readData = 0;
            while ((readData = fileInputStream.read()) != -1) {
                System.out.print((char) readData);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //2.多个字节读取,提高效率
    @Test
    public void test02() {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        FileInputStream fileInputStream = null;
        int len = 0;
        byte[] buf = new byte[8];
        try {
            // 由文件路径来构建文件输入流
            fileInputStream = new FileInputStream(filePath);
            while ((len = fileInputStream.read(buf)) != -1) {
                System.out.print(new String(buf, 0, len));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.3 FileOutputStream介绍

在这里插入图片描述
FileOutputStream是字节输出流
代码演示:使用FileOutputStream在hello.txt文件中写入“Hello, world”,如果文件不存在,会创建文件

    // 使用FileOutputStream写入文件
    @Test
    public void test03() throws IOException {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        // 字节输出流,默认使用覆盖文件的方式,FileOutputStream(filePath, true)则为追加模式
        FileOutputStream fileOutputStream = new FileOutputStream(filePath);
        fileOutputStream.write("hello world".getBytes());
        // 使用完毕后一定要记得关闭文件流
        fileOutputStream.close();
    }

2.4 应用实例:文件拷贝

使用FileInputStreamFileOutputStream实现文件拷配
代码

// 进行文件拷贝
public class FileStreamDemo02 {
    public static void main(String[] args) {
        // 源目标文件路径
        String originPath = "/Users/liumingchao/Documents/Java IO.xmind";
        // 目标路径
        String destPath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/Java IO.xmind";
        // 创建输入流和输出流
        FileInputStream originStream = null;
        FileOutputStream destStream = null;
        // 读取长度
        int len = 0;
        // 缓冲字节数组
        byte[] buf = new byte[1024];

        try {
            originStream = new FileInputStream(originPath);
            destStream = new FileOutputStream(destPath);
            // 当文件复制到末尾时为-1
            while ((len = originStream.read(buf)) != -1) {
                destStream.write(buf, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                originStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                destStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("文件复制成功");
    }
}

2.5 FileReader和FileWriter介绍

在这里插入图片描述
FileReader相关方法

  • new FileReader(File/String)
  • read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
  • read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1

FileWriter相关方法

  • new FileWriter(File/String):覆盖模式,相当于流的指针在首端
  • new FileWriter(File/String, true):追加模式,相当于流的指针在尾端
  • write(int):写入单个字符
  • write(char[]):写入指定数组
  • writer(char[], off, len):写入字符串的指定部分

FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!

代码示例1:使用FileReader从文件中读取内容并显示,文件中有中文字符

public class FileStreamDemo03 {
    // 用FileReader读取文件内容并显示
    // 每次读入一个字符
    @Test
    public void test01() {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        FileReader fileReader = null;
        int data = 0;
        try {
            // 创建FileReader
            fileReader = new FileReader(filePath);
            // 使用while循环读入
            while ((data = fileReader.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                fileReader.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    // 用FileReader读取文件内容并显示
    // 每次读入一个字符数组,提高效率
    @Test
    public void test02() {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        FileReader fileReader = null;
        int len = 0;
        char[] buf = new char[8];
        try {
            // 创建FileReader
            fileReader = new FileReader(filePath);
            // 使用while循环读入
            while ((len = fileReader.read(buf)) != -1) {
                System.out.print(new String(buf, 0, len));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                fileReader.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

代码示例2:使用FileWriter将“好好学习,天天向上”写入到文件中,注意细节

    // 使用FileWriter讲"好好学习,天天向上"写入文件
    @Test
    public void test03() {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello2.txt";
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(filePath);
            fileWriter.write("好好学习,天天向上".toCharArray());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 这里一定要close,否则没有真正写入文件中
            try {
                fileWriter.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

三、处理流

上面介绍的都是节点流,节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter等
处理流(也叫包装流)是“连接”在已存在的流(节点流or处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader、BufferedWriter、ObjectInputStream、ObjectOutputStream等

节点流和处理流的区别和联系

  • 节点流是底层流/低级流,直接跟数据源相接
  • 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出
  • 处理流(包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连

处理流的功能主要体现在两个方面

  • 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
  • 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便

3.1 处理流BufferedReader和BufferedWriter介绍

在这里插入图片描述

BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的,关闭处理流时,只需要关闭外层流即可
在这里插入图片描述
代码示例1:使用BufferedReader读取文本文件,并显示在控制台中

    // 使用BufferedReader读取文本文件
    @Test
    public void test01() throws IOException {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        // 先构建一个FileReader节点流
        FileReader fileReader = new FileReader(filePath);
        // 将节点流封装成处理流
        BufferedReader bufferedReader = new BufferedReader(fileReader);

        String line = null;
        // 循环按行读入
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }

        // 只关闭外层流即可
        bufferedReader.close();
    }

代码示例2:使用BufferedWriter将“好好学习,天天向上”写入到文件中

    // 使用BufferedWriter将“好好学习,天天向上”写入到文件中
    @Test
    public void test02() throws IOException {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello2.txt";
        // 先构建一个FileWriter节点流
        FileWriter fileWriter = new FileWriter(filePath);
        // 将节点流封装成处理流
        BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

        bufferedWriter.write("好好学习,天天向上");

        bufferedWriter.close();
    }

代码示例3:使用BufferedReaderBufferedWriter完成文本文件拷贝,要注意文件编码

    // 使用BufferedReader和BufferedWriter完成文本文件拷贝,要注意文件编码
    @Test
    public void test03() {
        String originPath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        String destPath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello3.txt";

        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        String line = null;

        try {
            // 输入流
            bufferedReader = new BufferedReader(new FileReader(originPath));
            // 输出流
            bufferedWriter = new BufferedWriter(new FileWriter(destPath));
            // 循环读入
            while ((line = bufferedReader.readLine()) != null) {
                // 写入文件
                bufferedWriter.write(line);
                // 添加一个换行
                bufferedWriter.newLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                bufferedWriter.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

3.2 处理流BufferedInputStream和BufferedOutputStream介绍

BufferedInputStream是字节流,在创建该字节流时,会创建一个内部缓冲区数组
BufferedOutputStream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入时就调用底层系统
在这里插入图片描述
代码示例:编程完成视频的拷贝

    // 使用BufferedInputStream和BufferedOutputStream完成视频拷贝
    @Test
    public void test04() {
        // 源文件目录
        String originPath = "/Users/liumingchao/Downloads/chrome/870272070_nb3-1-30064.mp4";
        // 目标文件路径
        String destPath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/test01.mp4";
        //创建 BufferedOutputStream 对象 BufferedInputStream 对象
        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        int len = 0;
        byte[] buf = new byte[1024];

        try {
            // 组装输入流和输出流
            bis = new BufferedInputStream(new FileInputStream(originPath));
            bos = new BufferedOutputStream(new FileOutputStream(destPath));
            // 循环读入
            while ((len = bis.read(buf)) != -1) {
                bos.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                bis.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

3.3 对象流ObjectInputStream和ObjectOutputStream介绍

在这里插入图片描述

看一个需求

  1. 将int num = 100 这个int保存到文件中,注意不是100数字,而是int 100,并且能够从文件中直接恢复 int 100
  2. 将Dog dog = new Dog(“小黄”, 3)这个dog对象 保存到 文件中,并且能够从文件恢复
  3. 上面的要求,就是 能够将 基本数据类型 或者 对象进行 序列化 和 反序列化 操作

序列化和反序列化

  1. 序列化就是在保存数据时,保存数据的值和数据类型
  2. 反序列化就是在恢复数据时,恢复数据的值和数据类型过
  3. 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须要实现如下两个接口之一
    Serializable // 这是一个标记接口,没有方法
    Externalizable // 该接口有方法需要实现,因此我们一般实现上面的Serializable接口
    // ObjectOutputStream测试
    @Test
    public void test05() {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/tmp.dat";
        // 对象输出流
        ObjectOutputStream objectOutputStream = null;
        try {
            objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));

            // 1.写入数字
            objectOutputStream.writeInt(100);
            // 2.写入boolean
            objectOutputStream.writeBoolean(true);
            // 3.写入double
            objectOutputStream.writeDouble(1.0);
            // 4.写入String
            objectOutputStream.writeUTF("你好 Java");
            // 5.写入对象
            objectOutputStream.writeObject(new Dog("小刘", 3, "red"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                objectOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("data save success");
    }

    // ObjectInputStream测试
    @Test
    public void test06() {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/tmp.dat";
        // 对象输入流
        ObjectInputStream objectInputStream = null;
        try {
            objectInputStream = new ObjectInputStream(new FileInputStream(filePath));
            // 1.读int
            System.out.println(objectInputStream.readInt());
            // 2.读boolean
            System.out.println(objectInputStream.readBoolean());
            // 3.读double
            System.out.println(objectInputStream.readDouble());
            // 4.读String
            System.out.println(objectInputStream.readUTF());
            // 5.读对象
            Object dog = objectInputStream.readObject();
            System.out.println(dog.getClass());
            System.out.println(dog);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                objectInputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

3.4 转换流InputStreamReader和OutputStreamWriter介绍

可以用来解决文件乱码问题

在这里插入图片描述

  1. InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)
  2. OutputStreamWriter:Writer的子类,可以将OutputStream(字节流)包装成Writer(字符流)
  3. 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
  4. 可以在使用时指定编码格式(比如utf-8,gbk等)

代码示例:编程将字节流FileInputStream包装成字符流InputStreamReader,对文件进行读取(utf-8/gbk格式),进而包装成BufferedReader

    @Test
    public void test() throws IOException {
        String filePath = "/Users/liumingchao/Workspace/IdeaWorkspace/temp/hello.txt";
        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
        BufferedReader bufferedReader = new BufferedReader(isr);
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
        bufferedReader.close();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值