保姆级演示!一文让你彻底通关JavaIO流

在这里插入图片描述

一、IO流,什么是IO?

  • I :Input

  • O:Output

  • 通过IO可以完成硬盘文件的读和写

二、IO流的分类

有多种分类方式:

1. 按照流的方向进行分类(输入流、输出流):

以内存作为参照物

  • 往内存中去,叫做输入(Input),或者叫做读(Read)。
  • 从内存中出来,叫做输出(Output),或者叫做写(Write)。
    在这里插入图片描述
2. 按照读取数据方式不同进行分类(字节流、字符流):
  • 按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制。这种流是万能的,什么类型的文件都可以读取。包括:文本文件、图片、声音文件、视频文件等。

假设文件file.txt,采用字节流的话是这样读的:
文件内容:
a我是中国人
第一次读:一个字节,正好读到 ‘a’
第二次读:一个字节,正好读到’中’字符的一半
第三次读:一个字节,正好读到’中’字符的另外一半
……

  • 按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片、声音、视频等文件,只能读纯文本文件(能用 .txt 打开的文件,没有特殊格式,文件名后缀不一定是.txt,比如.java文件也属于纯文本文件),word文件不属于纯文本文件

假设文件file.txt,采用字符流的话是这样读的:
文件内容:
a我是中国人
第一次读:一个字符,正好读到 ‘a’(‘a’字符在Windows系统中占用1个字节)
第二次读:一个字节,正好读到’中’('中’字符在Windows系统中占用2个字节)
……

三、IO流的四大家族

首先,Java中所有的流都是在java.io.*

四大家族:

在这里插入图片描述
Tips:如何分辨字节流和字符流:在Java中只要“类名”以Stream结尾的都是字节流;以“Reader/Writer”结尾的都是字符流

两点注意:

1、所有的流都实现了java.io.Closeable接口,都是可关闭的,都有close()方法。流是一个管道,连接内存和硬盘,用完之后一定要关闭,不然会耗费(占用)很多资源。
2、所有的输出流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法。在输出流的最终输出之后,一定要记得flush()刷新一下,这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道),如果没有flush()可能会导致丢失数据。

四、需要掌握的流

java.io包下需要掌握的流有16个

在这里插入图片描述
文件流专属:

  • java.io.FileInputStream
  • java.io.FileOutputStream
  • java.io.FileReader
  • java.io.FileWriter

转换流:(将字节流转换成字符流)

  • java.io.InputStreamReader
  • java.io.OutputStreamReader

缓冲流专属:

  • java.io.BufferedReader
  • java.io.BufferedWriter
  • java.io.BufferedInputStream
  • java.io.BufferedOutputStream

数据流专属:

  • java.io.DataInputStream
  • java.io.DataOutputStream

标准输出流:

  • java.io.PrintWriter
  • java.io.PrintStream

对象专属流:

  • java.io.ObjectInputStream
  • java.io.ObjectOutputStream

五、代码详解

  • 接下来的代码演示中,我把很多重点直接放在了注释中,方便大家理解
  • 先展示一下示例文档的内容,接下来我们开始对其读取

在这里插入图片描述

1、最原始,逐字节读取(使用java.io.FileInputStream

package JavaIO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Test {
    public static void main(String[] args) {
        //创建一个输入流
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream("Text");
            int readData = 0;
            //逐字节读取,当文档被读完时,read()方法返回-1,循环结束
            while ((readData = fileInputStream.read()) != -1)
            {
                System.out.println(readData);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //在finally块中关闭流
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}
  • 运行结果:

可见读到了文档每个字符的ASCII码值

在这里插入图片描述

2、上述方法内存和硬盘之间的交互过于频繁,每个字节都要交互一次,效率极低,所以接下来我们使用int read(byte[] b)方法,一次读取多个字符,减少硬盘和内存的交互,提高程序的执行效率。(使用java.io.FileInputStream

package JavaIO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Test {
    public static void main(String[] args) {
        //创建一个输入流
        FileInputStream fileInputStream = null;
        try {
            //这里说明一下,工程Project的根就是IDEA的默认当前路径,注意相对路径和绝对路径的区别
            fileInputStream = new FileInputStream("Text");
            //准备一个4个长度的byte数组,一次最多读取4个字节,这里大小可以任意定
            //这里再次说明,数组开的太大会导致内存溢出,所以一定要合理选择
            byte[] bytes = new byte[4];
            //用来记录每次读到的字节数,很重要!
            int readCount = 0;
            //每4个字节读取一次,当文档被读完时,read()方法返回-1,循环结束
            while ((readCount = fileInputStream.read(bytes)) != -1)
            {
                //把byte转换为字符串,读到多少个转换多少个
                System.out.println(new String(bytes,0,readCount));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //在finally块中关闭流
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}
  • 运行结果:

通过String类将字节转换成字符串输出,输出达到预期

在这里插入图片描述

这里必须要解释一下int read(byte[] b)方法的运行机制,大家就能明白readCount这个变量的重要性了

在这里插入图片描述

根据上图,如果把readCount换成数组长度
System.out.println(new String(bytes,0,readCount));
换成
System.out.println(new String(bytes,0,bytes.length));
则运行结果如下图,显然不符合我们的要求,所以要注意int read(byte[] b)方法返回值的使用:

在这里插入图片描述

在这里我们再介绍两种常用方法:
// @return     an estimate of the number of remaining bytes that can be read
//            (or skipped over) from this input stream without blocking.
//此方法可以返回此文件还有多少字节没有读,可以做为while循环终止条件使用
public int available() throws IOException {
        return available0();
}

//This method may skip more bytes than what are remaining in the backing file.
//此方法可以跳过几个字节不读取,比如一些文件有规定好的读取方式,跳过几个字节读几个字节才是正确的读入
 public long skip(long n) throws IOException {
        return skip0(n);
}

3、FileOutputStream的使用和FileOutputStream类似,这里举一个文件复制的例子(使用java.io.FileInputStreamjava.io.FileOutputStream;

package JavaIO;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test2 {
    public static void main(String[] args) {
        //创建一个输入流
        FileInputStream fileInputStream = null;
        //创建一个输出流
        FileOutputStream fileOutputStream = null;
        try {
            //这里说明一下,工程Project的根就是IDEA的默认当前路径,注意相对路径和绝对路径的区别
            fileInputStream = new FileInputStream("Text");
            //下面如果写成这样,就会在原文档之后累加,之后的输出流也一样
            //fileOutputStream = new FileOutputStream("Output.txt",true);
            fileOutputStream = new FileOutputStream("Output.txt");
            //准备一个4个长度的byte数组,一次最多读取4个字节,这里大小可以任意定
            //这里再次说明,数组开的太大会导致内存溢出,所以一定要合理选择
            byte[] bytes = new byte[4];
            //用来记录每次读到的字节数,很重要!
            int readCount = 0;
            //每4个字节读取一次,当文档被读完时,read()方法返回-1,循环结束
            while ((readCount = fileInputStream.read(bytes)) != -1)
            {
                //最核心的:一边读,一边写
                fileOutputStream.write(bytes,0,readCount);
            }
            //输出流最后要刷新
            fileOutputStream.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //在finally块中关闭流
            //两部分要分开写,否则发生异常可能会导致有通道没有关闭
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

  • 运行结果:

在这里插入图片描述

被复制到了

在这里插入图片描述

4、FileReader的使用,只不过是按字符读,和fileInputStream没什么区别

package JavaIO;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Test3 {
    public static void main(String[] args) {
        //创建一个输入流
        FileReader fileReader = null;
        try {
            //这里说明一下,工程Project的根就是IDEA的默认当前路径,注意相对路径和绝对路径的区别
            fileReader = new FileReader("Text");
            //准备一个char数组
            //这里再次说明,数组开的太大会导致内存溢出,所以一定要合理选择
            char[] chars = new char[4];
            //往char数组中读
            int readCount = 0;
            while ((readCount = fileReader.read(chars)) != -1)
            {
                for (int i = 0; i < readCount; i++) {
                    System.out.println(chars[i]);
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //在finally块中关闭流
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}
  • 运行结果:

可见是逐个读取的字符

在这里插入图片描述

5、再讲讲BufferedReader缓冲流

这个流说白了就是无需我们自己定义byte[]数组来承接,其自带缓冲
这里还要明白节点流和包装流的概念,这两个概念往往是相对的
package JavaIO;

import java.io.*;

public class Test4 {
    public static void main(String[] args) throws Exception{
        //这里对异常不再处理只是为了演示方便,实际情况千万不要这么做
        FileReader fileReader = new FileReader("Text");
        // 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
        // 外部负责包装的这个流,叫做:包装流,或者叫处理流
        // 像当前这个程序来说:FileReader就是一个节点流,BufferedReader就是包装流/处理流
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        //readLine()方法读取一个文本行,但不带换行符
        String s = null;
        while ((s = bufferedReader.readLine()) != null)
        {
            System.out.println(s);
        }
        //对于包装流来说,只需关闭最外层流就行,里面的节点流会自动关闭。
        bufferedReader.close();
    }
}
  • 运行结果:

成功读取

在这里插入图片描述

6、唠唠转换流

转换流的作用就是三层套娃中间的二娃
这里对异常不再处理只是为了演示方便,实际情况千万不要这么做
package JavaIO;

import java.io.*;

public class Test5 {
    public static void main(String[] args) throws Exception{
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("Text2")));
        out.write("我们都有一个家");
        out.write("名字叫中国!");
        //刷新
        out.flush();
        //关闭最外层即可
        out.close();
    }
}

运行结果:
在这里插入图片描述

7、再谈谈数据流

能干什么:DataOutputStream写的文件,只有DataInputStream去读,并且已知读取规则,才可以正常取出数据
package JavaIO;

import java.io.*;

public class Test6 {
    public static void main(String[] args) throws Exception{
        DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("Text3"));
        byte b = 100;
        short s = 200;
        int i = 300;
        long l = 400L;
        float f = 3.0F;
        double d = 3.14;
        boolean flag = false;
        char c = 'a';

        //开始写,把数据以及数据的类型一并写入到文件当中
        dataOutputStream.writeByte(b);
        dataOutputStream.writeShort(s);
        dataOutputStream.writeInt(i);
        dataOutputStream.writeLong(l);
        dataOutputStream.writeFloat(f);
        dataOutputStream.writeDouble(d);
        dataOutputStream.writeBoolean(flag);
        dataOutputStream.writeChar(c);
        
        dataOutputStream.flush();
        dataOutputStream.close();
        
        DataInputStream dataInputStream = new DataInputStream(new FileInputStream("Text3"));

        //开始读
        byte b1 = dataInputStream.readByte();
        short s1 = dataInputStream.readShort();
        int i1 = dataInputStream.readInt();
        long l1 = dataInputStream.readLong();
        float f1 = dataInputStream.readFloat();
        double d1 = dataInputStream.readDouble();
        boolean flag1 = dataInputStream.readBoolean();

        System.out.println(b1);
        System.out.println(s1);
        System.out.println(i1);
        System.out.println(l1);
        System.out.println(f1);
        System.out.println(d1);
        System.out.println(flag1);
        
        dataInputStream.close();
    }
}
  • 运行结果:

在这里插入图片描述

8、标准输出流

本质是向控制台输出,但是我们可以改变输出方向,一般将其作为日志工具类
定义Logger类
package JavaIO;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Logger {
    public static void log(String msg) {
        try {
            //指向一个日志文件
            PrintStream printStream = new PrintStream(new FileOutputStream("log.txt", true));
            //改变输出方向
            System.setOut(printStream);
            //取当前日期和时间
            Date date = new Date();
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String str = simpleDateFormat.format(date);

            System.out.println(date + ":" + msg);
            printStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
定义测试类
package JavaIO;

public class Test7{
    public static void main(String[] args) {
        //测试工具类
        Logger.log("程序运行正常");
        Logger.log("xxxxxx");
    }
}
  • 运行结果:

在这里插入图片描述

好了,今天这篇文章就到这里啦,相信你已经大体掌握了JavaIO流👏,Java的学习一定要多写多练。笔者会不定期做一些技术分享和工具使用心得,欢迎大家点赞和收藏!

在这里插入图片描述

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值