Java SE:详解IO流(下)

简介: Java SE:详解IO流(下)

五、IO流问题

5.1 关于直接基于“文件夹/目录”创建IO流对象是错误会报java.io.FileNotFoundException: d: download (拒绝访问。)

代码使用演示:

@Test
    public void test11() throws IOException{
        FileInputStream fis = new FileInputStream("d:\\download");
        //java.io.FileNotFoundException: d: download (拒绝访问)文件IO必须基于文件路径创建对象
        // "d:lldownload"这个是一个文件夹的路径,不是一个文件的路径
        FileOutputStream fos = new FileOutputStream( "d:\\download");
        //java.io.FileNotFoundException: d: download (拒绝访问)
        //就算是让 FiLeOutputStream帮你自动创建文件,也必须在IO流的构造器中,指明文件的名称。
    }

5.2 输出流如果不能及时关闭(因为可能后面还要用),但是又想要数据及时输出,可以调用flush方法。

但是如果后面不用了,一定要close。因为close不仅仅是刷数据的问题,还涉及内存回收问题。

代码使用演示:

@Test
public void test03() throws IOException{
    FileWriter fw=new FileWriter("1.txt");
    fw.write("天地有正道");
}

🔔注意:

像Filewriter等很多的输出流的内部有自己的一个小小缓冲区。它在调用write方法时,会先将数据写到缓冲区。当缓冲区满的时候,会自动“溢出”到文件。当缓冲区没满的时候,调用close 方法执行时,会把缓冲区的数据“清空”输出。

如果希望数据及时写出,但是暂时还不close,可以使用flush()方法刷新缓存区


六、缓冲流

6.1 分类

缓冲流又称高效流,按照数据类型分类:

  • 字节缓冲流BufferedInputStreamBufferedOutputStream
  • 字符缓冲流BufferedReaderBufferedWriter

🤖缓冲流的基本原理:

是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

6.2 构造方法

  • public BufferedInputStream(InputStream in)创建一个 新的缓冲输入流。
  • public BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流。

🔔注意:

缓冲流构造方法中必须包含字节输入流【InputStream】或字节输出流【OutputStream 】对象,才行

代码使用演示:

// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("ab.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("ac.txt"));
  • public BufferedReader(Reader in)创建一个 新的缓冲输入流。
  • public BufferedWriter(Writer out)创建一个新的缓冲输出流。

代码使用演示:

// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("12.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("13.txt"));

6.3 使用缓冲流前后耗时对比

代码使用演示:

@Test
//不使用缓冲流
public void test04() throws IOException{
    long start=System.currentTimeMillis();
    FileInputStream fis=new FileInputStream("D:\\BaiduNetdiskDownload\\尚硅谷2022Java\\01-Java基础【完结】\\尚硅谷_JavaEE_01_JavaSE课程资料\\01_sofeware\\IDE\\ideaIU-Ultimate-2019.2.3.zip");
    FileOutputStream fos=new FileOutputStream("D:\\ideaIU-Ultimate-2019.2.3-副本.zip");
    byte[] data=new byte[1024];
    int len;
    while ((len=fis.read(data))!=-1){
        fos.write(data,0,len);
    }
    fis.close();
    fos.close();
    long end=System.currentTimeMillis();
    System.out.println("耗时:"+(end-start)+"毫秒");//耗时:6200毫秒
}
@Test
//使用缓冲流
public void test05() throws IOException{
    long start=System.currentTimeMillis();
    FileInputStream fis=new FileInputStream("D:\\BaiduNetdiskDownload\\尚硅谷2022Java\\01-Java基础【完结】\\尚硅谷_JavaEE_01_JavaSE课程资料\\01_sofeware\\IDE\\ideaIU-Ultimate-2019.2.3.zip");
    FileOutputStream fos=new FileOutputStream("D:\\ideaIU-Ultimate-2019.2.3-副本1.zip");
    BufferedInputStream bis=new BufferedInputStream(fis);
    BufferedOutputStream bos=new BufferedOutputStream(fos);
    byte[] data=new byte[1024];
    int len;
    while ((len=bis.read(data))!=-1){
        bos.write(data,0,len);
    }
    fis.close();
    fos.close();
    long end=System.currentTimeMillis();
    System.out.println("耗时:"+(end-start)+"毫秒");//耗时:1778毫秒
}

🤮OMG!

不使用缓冲流耗时:6200毫秒;

使用缓冲流耗时:1778毫秒

我们可以很明确的看到,在复制文件时,尤其时很大的文件时,缓冲流真的

非常高效!

why?

原理剖析如下:

6.4 字符缓冲流特有方法

字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,让我们来看它们具备的特有方法。

  • BufferedReader:public String readLine(): 读一行文字。
  • BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。

代码使用演示:

@Test
        public void test12() throws IOException{
            // 创建流对象
            BufferedReader br = new BufferedReader(new FileReader("in.txt"));
            // 定义字符串,保存读取的一行文字
            String line;
            // 循环读取,读取到最后返回null
            while ((line = br.readLine())!=null) {
                System.out.println(line);
            }
            // 释放资源
            br.close();
        }
@Test
        public void testNewLine()throws IOException{
            // 创建流对象
            BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
            // 写出数据
            bw.write("你");
            // 写出换行
            bw.newLine();
            bw.write("好");
            bw.newLine();
            bw.write("吗");
            bw.newLine();
            // 释放资源
            bw.close();
        }

七、IO流的关闭问题

IO流的关闭顺序有什么问题?

莫急,请看下面的代码:

@Test
public void test06() throws IOException{
    long start=System.currentTimeMillis();
    FileInputStream fis=new FileInputStream("D:\\BaiduNetdiskDownload\\尚硅谷2022Java\\01-Java基础【完结】\\尚硅谷_JavaEE_01_JavaSE课程资料\\01_sofeware\\IDE\\ideaIU-Ultimate-2019.2.3.zip");
    FileOutputStream fos=new FileOutputStream("D:\\ideaIU-Ultimate-2019.2.3-副本2.zip");
    BufferedInputStream bis=new BufferedInputStream(fis);
    BufferedOutputStream bos=new BufferedOutputStream(fos);
    byte[] data=new byte[1024];
    int len;
    while ((len=bis.read(data))!=-1){
        bos.write(data,0,len);
    }
    fis.close();
    fos.close();
    bis.close();
    bos.close(); //java.io.IOException: Stream Closed
    long end=System.currentTimeMillis();
    System.out.println("耗时:"+(end-start)+"毫秒");//耗时:1778毫秒
}

why?

为社么会出现“stream closed”?

🤮原因:

IO流有依赖关系。外层的包装流依赖于内存的被包装流。 比如: BufferedInputStream 依赖于 FileInputStream BufferedOutputstream 依赖于 FileOutputstream

如果把内层的IO先关闭了,外层IO流就失去了依赖,就会报错。 比喻:我坐在登子上,我依赖于凳子,如果把登子先撤了,我就会摔倒。

先让我站起来,然后撤登子。

关闭的顺序是: 先关闭外层的包装流,再关闭内层的被包装流

🔔本质:

这段代码数据的流向: 源文件 ->fis ->bis ->data数组 ->bos ->fos

->目标文件BufferedOutputStream和FileOutputStream,如果把fos先关了,bos中的缓冲的数据没有办法完全写出。因为close时,会清空缓冲区

好记: fis被包装在里面的,比喻成内衣,先穿内衣,再穿外衣bis相当于外套。 fos是内衣,bos是外套 关闭好比是脱衣服。

先脱外套,再脱内衣。


八、转换流

8.1 编码与解码

计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。基于某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码

比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。

乱码问题举例如下:

如何解决乱码问题?

请看下面的类,就是解决乱码问题

8.2 InputStreamReader类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

  • InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
  • InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。

解决方案代码如下:

@Test
public void  test08() throws IOException{
    FileInputStream fr=new FileInputStream("1.txt");
    InputStreamReader isr=new InputStreamReader(fr,"GBK");//文件的编码格式是GBK,用GBK来解文件的编码
    char[] data=new char[10];
    int len;
    while ((len=isr.read(data))!=-1){
        System.out.println(new String(data,0,len));
    }
    fr.close();
}

8.3 OutputStreamWriter类

转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

  • OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
  • OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。

8.4 案例

8.4.1 案例①

🔔输出”你太棒了“在GBk格式的1.txt文件中

代码演示如下:

@Test
public void  test09() throws IOException{
    //案例:输出”你太棒了“在GBk格式的1.txt文件中
    FileOutputStream fos=new FileOutputStream("1.txt",true);
    OutputStreamWriter isr=new OutputStreamWriter(fos,"GBK");//把字符数据用GBk的格式转换为字节数据写入1.txt文件中
    isr.write("你太棒了");
    isr.close();
    fos.close();
}

8.4.2 案例②

🔔案例需求不变,把缓冲流加入进去

代码演示如下:

@Test
public void  test010() throws IOException{
    //案例:输出”你太棒了“在GBk格式的1.txt文件中
    //将缓冲流加入进去
    FileOutputStream fos=new FileOutputStream("1.txt",true); //字节流
    BufferedOutputStream bos=new BufferedOutputStream(fos);//缓冲字节流包含字节流
    OutputStreamWriter isr=new OutputStreamWriter(fos,"GBK");//字符流 把字符数据转换为GBk格式的数据写入1.txt文件中
    BufferedWriter bw=new BufferedWriter(isr);//缓冲字符流包含字符流
    isr.write("你太棒了1");
    bw.close();
    bos.close();
    isr.close();
    fos.close();
}

8.4.3 案例③

🔔 将编码为GBK的”file_gbk.txt"转换为编码为utf-8的文件”file_utf8.txt"

代码演示如下:

@Test
public void test11() throws IOException{
    //案例:将编码为GBK的”file_gbk.txt"转换为编码为utf-8的文件”file_utf8.txt"
    FileInputStream fis=new FileInputStream("file_gbk.txt"); //字节输入流
    InputStreamReader isr=new InputStreamReader(fis,"GBK");//字符输入流
    BufferedReader br=new BufferedReader(isr);//缓冲字符输入流
    String readLine;
    FileOutputStream fos=new FileOutputStream("file_utf8.txt"); //字节输出流
    OutputStreamWriter osw=new OutputStreamWriter(fos,"utf-8"); //字符输出流
    BufferedWriter bw=new BufferedWriter(osw);  //缓冲字符输出流
    while ((readLine=br.readLine())!=null){
        bw.write(readLine);
        bw.newLine();
    }
    bw.close();
    osw.close();
    fos.close();
    br.close();
    isr.close();
    fis.close();
}

8.5 转换流原理

原理图像如下:

相关文章
|
16天前
|
Java Unix Windows
|
1天前
|
Java 开发者
Java一分钟之-Java IO流:文件读写基础
【5月更文挑战第10天】本文介绍了Java IO流在文件读写中的应用,包括`FileInputStream`和`FileOutputStream`用于字节流操作,`BufferedReader`和`PrintWriter`用于字符流。通过代码示例展示了如何读取和写入文件,强调了常见问题如未关闭流、文件路径、编码、权限和异常处理,并提供了追加写入与读取的示例。理解这些基础知识和注意事项能帮助开发者编写更可靠的程序。
7 0
|
5天前
|
存储 缓存 Java
Java IO 流详解
Java IO 流详解
15 1
|
10天前
|
存储 Java
Java的`java.io`包包含多种输入输出类
Java的`java.io`包包含多种输入输出类。此示例展示如何使用`FileInputStream`从`input.txt`读取数据。首先创建`FileInputStream`对象,接着分配一个`byte`数组存储流中的数据。通过`read()`方法读取数据,然后将字节数组转换为字符串打印。最后关闭输入流释放资源。`InputStream`是抽象类,此处使用其子类`FileInputStream`。其他子类如`ByteArrayInputStream`、`ObjectInputStream`和`BufferedInputStream`各有特定用途。
19 1
|
11天前
|
存储 Java
java IO接口(Input)用法
【5月更文挑战第1天】Java的`java.io`包包含多种输入输出类。此示例展示了如何使用`FileInputStream`从`input.txt`读取数据。首先创建`FileInputStream`对象,接着创建一个字节数组存储读取的数据,调用`read()`方法将文件内容填充至数组。然后将字节数组转换为字符串并打印,最后关闭输入流。注意,`InputStream`是抽象类,此处使用其子类`FileInputStream`。其他子类如`ByteArrayInputStream`、`ObjectInputStream`和`BufferedInputStream`各有特定用途。
21 2
|
12天前
|
存储 Java Linux
【Java EE】 文件IO的使用以及流操作
【Java EE】 文件IO的使用以及流操作
|
17天前
|
存储 Java 数据库
[Java 基础面试题] IO相关
[Java 基础面试题] IO相关
|
18天前
|
缓存 Java API
Java NIO和IO之间的区别
NIO(New IO),这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。
16 1
|
19小时前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
9 3
|
19小时前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
10 2