java io流不关闭有什么后果_保姆级演示!一文让你彻底通关JavaIO流

7d75f0c30e11ae19bb8819c59f1a1d1d.png

一、IO流,什么是IO?

  • I :Input

  • O:Output

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

二、IO流的分类

有多种分类方式:

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

以内存作为参照物

  • 往内存中去,叫做输入(Input),或者叫做读(Read)。

  • 从内存中出来,叫做输出(Output),或者叫做写(Write)。 bddfd15fbdac5036d246f03e47242d6c.png

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.*

四大家族:

4cf8ca60ee0127fb06455e12d4ac661b.pngTips:如何分辨字节流和字符流:在Java中只要“类名”以 Stream结尾的都是字节流;以“ Reader/Writer”结尾的都是字符流

两点注意:

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

四、需要掌握的流

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

0cca3a6db373458d8f8a2771ef4f5e38.png文件流专属:

  • 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

五、代码详解

  • 接下来的代码演示中,我把很多重点直接放在了注释中,方便大家理解

  • 先展示一下示例文档的内容,接下来我们开始对其读取 d241cc7da05199ecf53e1c13c7fe475e.png

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码值

b36a5e00aa0b64834b6c8a1257fb042b.png

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类将字节转换成字符串输出,输出达到预期

2e745c6d3adce6bb672ea20a7736dcaa.png

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

3e9571a7eda587da5493d4c6f7a3237a.png

根据上图,如果把readCount换成数组长度

System.out.println(new String(bytes,0,readCount));

换成

System.out.println(new String(bytes,0,bytes.length));

则运行结果如下图,显然不符合我们的要求,所以要注意int read(byte[] b)方法返回值的使用:

b452edc95ccc372c19ec4714e5e77df6.png

在这里我们再介绍两种常用方法:
 // @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.FileInputStream和 java.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();                }            }        }    }}
  • 运行结果:

13cb73baa948ad16af7152b48a00e9fe.png

被复制到了

d2b3705e7d5cc533fb6d10da08e060f0.png

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();                }            }        }    }}
  • 运行结果:

可见是逐个读取的字符

0a3d679adb409b003e2bbb4b3da23315.png

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();    }}
  • 运行结果:

成功读取

d8cea8cbbc3aa476ec319dadeeacef8b.png

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();    }}
  • 运行结果:

d2d03b4b6b9f0c73957f28c916f0b4b0.png

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();    }}
  • 运行结果:

298826ceefe80fb5b9a441273ab11541.png

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");    }}
  • 运行结果:

deb9fb682aaae010f08b7cc6bbc67f33.png

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

6f07b60d8cc48cc8696b7acd1d001d85.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值