03 - Java核心类库—IO

Java核心类库-IO

一、文件读写

1、java.io.File

1.1 概述

在这里插入图片描述

1.2 构造方法

  1. 构造方法的声明
    在这里插入图片描述
  2. 应用常用构造方法
    A.File(String pathname)
import java.io.File;
import java.io.IOException;

public class Demo_IO_1 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        File file = new File("E:\\study01"); // 创建文件对象
        boolean flag = file.createNewFile();// creatNewFile():判断是否创建成功;---true:如果指定的文件不存在且已成功创建; false:如果指定的文件已存在
        System.out.println(flag?"study01文件 创建成功!":"study01文件 创建失败!");
    }
}

运行结果:【前提是E盘不存在此文件】
在这里插入图片描述
在这里插入图片描述

B.File​(File parent, String child)

上面的File parent指的是父文件夹
在这里插入图片描述
结果显示:在这里插入图片描述
C.File​(String parent, String child)

上面String parent指的是父文件
在这里插入图片描述
结果显示:
在这里插入图片描述

1.3 字段

不同操作系统路径分隔符可能不同,"//"不会适用于所有操作系统
在这里插入图片描述在这里插入图片描述

1.4 常用方法

Java使用File工具操作磁盘文件,只要在构造方法中填写某个文件的完整路径,即可通过创建好的文件对象开展各项处理。相关处理有以下4大类。

tips:所有的路径都是抽象路径;绝对路径的对立面是相对路径

绝对路径:从系统根目录或者盘符开始的路径就是绝对路径,就是一条完整的路径比如“c:\program files\abc.txt”

相对路径:是相对当前项目的,你只写a.txt,它就会建立在当前项目的根目录下路径 aaaa\a.txt,没有盘符,就是相对你项目根目录下一个aaaa目录下的一个a.txt

1G=1024MB=1024KB=1024字节

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

1.4.1 检查文件状态

File工具既可操作某个文件又可操作某个目录。

  • exists:判断当前文件/目录是否存在,若存在则返回true。
  • canExecute:判断当前文件是否允许执行,若允许返回true。
  • canRead:判断当前文件是否允许读取,若允许返回true。
  • canWrite:判断文件是否允许写入,若允许返回true。
  • isHidden:判断当前文件/目录是否隐藏,若隐藏返回true。
  • isDirectory:判断当前是否为目录,若为目录返回true。
  • isFile:判断当前是否为文件,若为文件则返回true。

1.4.2 获取文件信息

只要磁盘中存在某个文件/目录,就能调用相关方法获取该文件/目录的基本信息。

  • getAbsolutePath:获取当前文件/目录的绝对路径
  • getPath:获取当前文件/目录的相对路径
  • getName:如果当前为文件,就返回文件名称;如果为目录,则返回目录名称
  • getParent:获取当前文件/目录的上级目录路径
  • length:如果当前为文件,就返回文件大小;如果为空目录,就返回0;如果当前目录非空,就返回目录的索引空间大小,索引保存了目录内部文件的基本信息。
  • lastModified:获取当前文件/目录的最后修改时间,单位为毫秒

1.4.3 管理文件操作

对文件进行创建,删除,更名等管理操作。

  • mkdir:只创建最后一级目录,如果上级目录不存在,就返回false
  • mkdirs:创建文件路径中所有不存在的目录
  • createNewFile:创建新文件。如果文件目录不存在,那就仍出异常IOException
  • delete:删除文件,也可以删除空目录
  • renameTo:重命名文件,把源文件的名称改为目录名称

1.4.4 遍历某目录下的文件

文件遍历操作是专门提供给目录专用的,主要遍历方法有list和listFiles两个。其中前者返回的文件路径数组是String类型,后者返回的文件路径数组是File类型的。
在这里插入图片描述
注意,listFiles还有三个同名的重载方法,要加以区分。

tips:FileFilter 与 FilenameFiler都是函数式接口,所以它们的实例可以采用Lambda表达式来改写。

2、文件遍历案例

import java.io.File;

/**
 * 文档的遍历
 */
public class Demo_IO_4 {
    
    

    private static File[] file;

    public static void main(String[] args) {
    
    
        File file = new File("E:\\"); // E盘下的文件对象
        File[] files = file.listFiles(); // E盘下文件夹的文件数组
        listFile(files); // 遍历
    }
    /**
     * 遍历文件数组
     * @param file
     */
    public static void listFile(File[] file) {
    
    
        if (file != null && file.length > 0) {
    
    // 文件存在且不为空
            for (File file_1 : file) {
    
    
                if (file_1.isFile()) {
    
     // 判断是否为文件
                    if (file_1.getName().endsWith(".mp4")) {
    
      // 判断是否有以.mp4结尾的文件
                        if (file_1.length() > 900 * 1024 * 1024) {
    
     // 文件大小大于900M
                            System.out.println("找到了一个mp4文件" + file_1.getAbsolutePath()); // 输出文件的路径
                        }
                    }
                } else {
    
     // 文件夹
                    File[] file2 = file_1.listFiles(); // 文件数组
                    listFile(file2); // 递归
                }
            }
        }
    }
}

3、文件过滤器(了解)

3.1 概述

在这里插入图片描述
在这里插入图片描述

3.2 应用实例

简单粗暴:

package com.kaikeba;
 
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
 
public class Demo1 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        File e = new File("d://");
        listFiles(e);
    }
    public static void listFiles(File file){
    
        // 自定义的方法
        if(file != null && file.length() != 0){
    
     // 文件存在且不为空
            // 1,创建一个过滤器,并描述规则
            FileFilter filter = new AVIFileFilter();
            // 2,通过文件获取子文件夹
            File [] files = file.listFiles(filter); // 对象的方法
            // 3,递归遍历所有文件
            for(File f : files) {
    
    
                if(f.isDirectory()) {
    
    
                    listFiles(f);
                }else {
    
    
                    System.out.println("发现一个txt文件:" + f.getAbsolutePath());
                }
            }
        }
    }
    static class AVIFileFilter implements FileFilter{
    
        // 为实现过滤器接口 需要定义一个类 这里为静态内部类
        @Override
        public boolean accept(File pathname) {
    
    
            // 只保留txt文件和文件夹
            if(pathname.getName().endsWith(".txt") || pathname.isDirectory()) {
    
    
                return true;
            }
            return false;
        }
    }
}

改进:

用匿名内部类实现FileFilter接口,并直接作为参数,调用listFiles获得筛选过后的文件

package com.kaikeba;
 
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
 
public class Demo1 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        File e = new File("d://");
        listFiles(e);
    }
    public static void listFiles(File file){
    
    
        if(file != null && file.length() != 0){
    
     // 文件存在且不为空
            File [] files = file.listFiles(new FileFilter() {
    
       // 通过匿名内部类作为参数的方法 实现过滤
                @Override
                public boolean accept(File pathname) {
    
    
                    // 只保留txt文件和文件夹
                    if(pathname.getName().endsWith(".txt") || pathname.isDirectory()) {
    
    
                        return true;
                    }
                    return false;
                }
            });
            // 递归遍历所有文件
            for(File f : files) {
    
    
                if(f.isDirectory()) {
    
    
                    listFiles(f);
                }else {
    
    
                    System.out.println("发现一个txt文件:" + f.getAbsolutePath());
                }
            }
        }
    }
}

4、相对路径与绝对路径

绝对路径:以盘符开始,是一个完整的路径,例如c://a.txt
相对路径:在Java中相对于本项目目录路径(即 本项目的根目录),这是一个不完整的路径,在Java开发中很常用 ,例如 a.txt

tips:这里讲的相对路径只适用于Java中的,而之后学习的网络编程等就有另外定义!!!

import java.io.File;

/**
 * 相对路径与绝对路径
 */
public class Demo_IO_5 {
    
    
    public static void main(String[] args) {
    
    
        File file1 = new File("c:\\a.txt");// 绝对路径
        File file2 = new File("a,txt");// 相对路径
        System.out.println("file1:" + file1.getAbsolutePath());
        System.out.println("file2:" + file2.getAbsolutePath());
    }
}

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

---------------------------------------------------------

二、I/O输入输出流

这里的出和入是个相对的概念,出就是相对程序的出,入也是相对程序的入
(即:从程序里出去就输出;从别处进到程序叫输入 )

1. IO流概述

理解:可以将这种数据传输操作,看做一种数据的流动 , 按照流动的方向分为输入Input和输出Output

Java中的IO操作主要指的是 java.io包下的一些常用类的使用;通过这些常用类对数据进行读取(输入Input) 和 写出(输出Output)

tips:一切皆字节:

  • 计算机中的任何数据(文本,图片,视频,音乐等等)都是以二进制的形式存储的.
  • 在数据传输时 也都是以二进制的形式存储的.
  • 后续学习的任何流 , 在传输时底层都是二进制.

2. IO流的分类

按照流的方向来分,可以分为: 输入流 和 输出流.

按照流动的数据类型来分,可以分为: 字节流 和 字符流.[其都包含输入/输出流]

字节流:(顶级父类)

  • 输入流 : InputStream
  • 输出流 : OutputStream

字符流:(顶级父类)

  • 输入流 : Reader
  • 输出流 : Writer

3. IO流的类----字节流

3.1 java.io.OutputStream

3.1.1 输出字节流的概述

输出字节流 — OutputStream是抽象类

说白了,就是从控制台输出到磁盘文件中去。
在这里插入图片描述

3.1.2 常用方法

在这里插入图片描述
注意:

写完一定要采用关闭close()方法;

有关写入的方法中,write方法传入int类型的参数解释:
在这里插入图片描述
从上可知,说白了就是将int类型变为8位的字节

3.2 java.io.FileOutputStream

java.io.FileOutputStream是OutputStream抽象类的常用直接类,其读写方法与抽象类没啥区别。

3.2.1 概述

在这里插入图片描述

3.2.2 构造方法

在这里插入图片描述

tip: 其中append为true则表示,在文件末尾添加数据,否则表示重新写入数据

3.2.3 方法应用

有关读写以及构造方法的运用

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * OutputStream抽象类的实体类
 * FileOutputStream
 */
public class Demo_IO_6 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileOutputStream file = new FileOutputStream("E:\\a.txt");// 没有添加append参数,表示默认false 即覆盖原数据
        //写法一
        file.write(65);

        //写法二
        byte[] bytes = {
    
    'A','B','C'};
        file.write(bytes);

        //写法三
        byte[] bytes_1 = {
    
    'A','B','C','D'};
        file.write(bytes_1,1,3);// off:从索引位置开始 len:索引的长度
        //file.close(); //如果添加这行,则会报错:Stream Closed 流被关闭
        byte[] bytes_3= {
    
    'A','B','C'};
        file.write(bytes);
        file.close(); //写在哪在哪关闭
        System.out.println("已写出");
}

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

通过字符串+getBytes函数,获得字节数组:

import java.io.FileOutputStream;
import java.io.IOException;

/**
 * OutputStream抽象类的实体类
 * FileOutputStream
 */
public class Demo_IO_6 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileOutputStream file = new FileOutputStream("E:\\a.txt");// 没有添加append参数,表示默认false 即覆盖原数据
        byte[] bytes = "ABCDEF".getBytes();
        file.write(bytes);
        file.close();
        System.out.println("已写出");
    }
}

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

3.3 java.io.InputStream

3.3.1 输入字节流的概述

输入字节流—IntputStream是抽象类

说白了就是从磁盘把文件输入到控制台上。
在这里插入图片描述

3.3.2 常用方法

在这里插入图片描述在这里插入图片描述
理解最后两个方法:
在这里插入图片描述

3.4 java.io.FileInputStream

java.io.FileIntputStream是IntputStream抽象类的常用直接类,其读写方法与抽象类没啥区别。

3.4.1 概述

在这里插入图片描述

3.4.2 构造方法

在这里插入图片描述

3.4.3 方法应用

  • read()
import java.io.FileInputStream;
import java.io.IOException;

/**
 * a.txt 文件有 abcdef 文字
 */
public class Demo_IO_7 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileInputStream file = new FileInputStream("E:\\a.txt");
        while(true){
    
    
            byte a = (byte)file.read();// 每次只读取一个字节,且为整型,但可以做强转
            if(a == -1){
    
    // 用于判断如果文本读取到最后(末尾),则会返回-1的标记
                break;
            }
            System.out.println(a);
        }
        file.close();
    }
}

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

  • read​(byte[] b)
import java.io.FileInputStream;
import java.io.IOException;

/**
 * a.txt 文件有abcdefghijklmnopqrstuvwxyz 文字
 */
public class Demo_IO_8 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileInputStream file = new FileInputStream("E:\\a.txt");
        byte[] byte_1 = new byte[10];
        file.read(byte_1);
        System.out.println(new String(byte_1));
        file.read(byte_1);
        System.out.println(new String(byte_1));
        file.read(byte_1);
        System.out.println(new String(byte_1));
    }
}

运行结果:
在这里插入图片描述
从上面运行结果可以看出:

read(byte[] buff) 这个方法,是传一个数组做容器,将读到的每个byte数据,都放到这个数组里,一个位置放一个。如果流里没那么多了,自然后面就空着。
如果上面这种用过了的byte_1又接着去用来存新数据,必然是有可能被上一次读到的数据干扰。所以第二次读10个,第三次读6个,第三次只会覆盖掉前面6个,后面4个位置还是上一次的。

所以我们读是用read(buff),里面的真实数据new String(buff,0,len)。len就是到底这次读取有多少个数据是有效的,是本次读取到的。这个len对应你这个demo来说,第一次是10,第二次是10,第三次是6.

改进代码如下:

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

/**
 * a.txt 文件有abcdefghijklmnopqrstuvwxyz 文字
 */
public class Demo_IO_8 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileInputStream file = new FileInputStream("E:\\a.txt");
        byte[] byte_1 = new byte[10];
        int len = file.read(byte_1);
        System.out.println(new String(byte_1,0,len));
        len = file.read(byte_1);
        System.out.println(new String(byte_1,0,len));
        len = file.read(byte_1);
        System.out.println(new String(byte_1,0,len));
    }
}

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

3.5 文件加密和解密工具

import java.io.*;
import java.util.Scanner;

/**
 * 加密或解密
 */
public class Demo_IO_9 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        System.out.println("请您输入您要加密/解密文件的全路径:");
        Scanner input = new Scanner(System.in);
        String s = input.nextLine();
        // 原文件 :study.text
        File oldFile = new File(s);
        // 新文件 : new_study.text
        File newFile = new File(oldFile.getParentFile(),"new_" + oldFile.getName());
        // 输入流 :从磁盘输入到控制台
        FileInputStream in = new FileInputStream(oldFile);
        // 输出流:从控制台输出到磁盘
        FileOutputStream out = new FileOutputStream(newFile);
        while(true){
    
    
            byte bytes = (byte)in.read();
            // 判断是否读取到内容
            if(bytes == -1){
    
    
                break;
            }
            // 异或特点:任何数据异或 相同的数字异或两次,结构都是其本身
            out.write(bytes^10);
        }
        in.close();
        out.close();
        System.out.println("加密或解密成功!");
    }
}

加密
在这里插入图片描述
tip:异或的特点
任何数据异或 相同的数字异或两次,结构都是其本身

运行结果:
在这里插入图片描述
再次运行程序进行解密
在这里插入图片描述

3.6 字节流读取文字

由于工程使用的是UTF-8字符编码,所以在读取工程中的文本文件时,不会出现乱码
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
由于提前限定了一次读取的字节数为10,所以出现了读取不到一个完整汉字的情况,因而出现乱码。但是UTF-8使用动态编码表,由于提前不知道每个字符需要多少字节,所以此方法不行。

故下面引入了字符流,用来解决读取半字的问题。

4. IO流的类----字符流

4.1 java.io.Writer

4.1.1 写入字符流的概述

写入字符流 — Writer是抽象类
在这里插入图片描述

4.1.2 常用方法

在这里插入图片描述

  • write:往文件写入字符串。注意该方法存在多个同名的重载方法
  • append:也是往文件写入字符串。按字面意思,append方法是往文件末尾追加字符串,然而并非如此,append方法与write方法的写入位置是同样的,二者的区别在于,append方法会把空指针当作null写入文件,而write方法不知此写入空指针
  • close:关闭文件写入器

4.2 java.io.FileWriter

java.io.FileWriter是Writer抽象类的常用直接类,其读写方法与抽象类没啥区别。

4.2.1 概述

在这里插入图片描述

4.2.2 构造方法

在这里插入图片描述

4.2.3 方法应用

  • write()
import java.io.FileWriter;
import java.io.IOException;

/**
 * 字符流Writer
 */
public class Demo_IO_11 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileWriter file = new FileWriter("E:\\study_1.txt");
        //写入一
        file.write("我是爱摸鱼的TT");
        //写入二
        file.write('爱');
        //写入...      
        file.close();
        System.out.println("写入成功");
    }
}

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

  • append()
import java.io.FileWriter;
import java.io.IOException;

/**
 * 字符流Writer
 */
public class Demo_IO_11 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileWriter file = new FileWriter("E:\\study_1.txt");
        file.write("我是爱摸鱼的TT");
        file.append(',').append("很久不见").append('!');
        file.close();
        System.out.println("写入成功");
    }
}

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

注意:

决定是否在原文件基础上追加的,是声明字符流对象是的append属性(为true则是追加模式);

append与write在实际实现上,没有区别,但是append会返回Writer对象(即自己本身);
在这里插入图片描述
append方法的返回值(Writer类型)可以强转为该对象(FileWriter类型【上转型】),所以可以继续调用append,因而称为“追加”;
在这里插入图片描述

4.3 java.io.Reader

4.3.1 读取字符流的概述

在这里插入图片描述

4.3.2 常用方法

在这里插入图片描述

  • skip:跳出若干字符。注意FileReader的skip方法跳过的是字符数,不是字节数。
  • read:从文件读取数据到字节数组。注意该方法存在多个同名的重载方法。
  • close:关闭文件读取器

4.4 java.io.FileReader

4.4.1 概述

java.io.FileReader是Reader抽象类的常用直接类,其读写方法与抽象类没啥区别。
在这里插入图片描述

4.4.2 构造方法

在这里插入图片描述

4.4.3 方法应用

在这里插入图片描述在这里插入图片描述

5. flush刷新管道—Writer

字符输出时,以字符为单位,但计算机中都是以字节为单位。当一个字符占用多个字节时,字符输入流未读取单个字符全部字节之前,会将已读取字节放入缓存;

字符输出流fw.flush()会将缓存中字符强制写入到文件中,fw.close()也会有此效果;

如果不执行的话,就不会将字符写入文件中,如图:
在这里插入图片描述
加入其中一个方法,即会写入文件中:
在这里插入图片描述

6.字节转换字符流

转换流:将字节流装饰为字符流:使用了装饰者模式;

为什么要使用字节流+转换流?直接字符流不香吗?:由于平常使用时,可能获取的是字节流,所以才有这种转换方式.

  • 字节流的输出转换为字符流的写入
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

/**
 * 字节转换字符流:写入
 */
public class Demo_IO_14 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileOutputStream file = new FileOutputStream("E:\\study_1.txt"); // 字节流的输出流
        OutputStreamWriter out = new OutputStreamWriter(file); // 转换流
        out.write("我是爱摸鱼的TT,很久不见!");
        System.out.println("写入成功!");
        out.flush();// 字符流更新
        file.close(); // 字节流关闭
    }
}

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

  • 字节流的输入转换为字符流的读取
    在这里插入图片描述

7. Print与缓冲区

7.1 打印流

7.1.1 打印字节流与打印字符流

  • 打印字节流
    在这里插入图片描述
  • 打印字符流(记得flush或close)
    在这里插入图片描述
    注意:
    打印字节流和打印字符流在使用上差别不大,但字符流(Writer)需要调用flush方法,否则不会写入到文件中;

7.1.2 字节流转换为打印流

在这里插入图片描述

7.2 缓冲I/O字符流

Java设计了缓冲区就是预留下来的做为急用的那一部分,为暂时置放输出或输入资料的内存。

7.2.1 缓存写入流(BufferedWriter)

7.2.1.1 概述

Java设计了缓冲区写入器BufferedWriter,它的write方法并不是直接写入文件,而是先写入一块缓存,等到缓存写满了再将缓存上的数据写入文件。由于缓存空间位于内存,写入缓存等同于访问内存,这样大大提高了写文件的性能,比FileWriter好多了。
在这里插入图片描述

7.2.1.2 构造方法

在这里插入图片描述

7.2.1.3 方法应用

与FileWriter相比,新增的方法:
在这里插入图片描述

  • newLine:在当前位置添加换行标记(Win系统是回车加换行)
  • flush:立即将缓冲区中的数据写入磁盘。默认情况下,要等缓冲区满了才会写入磁盘中,或者是调用了close方法,但有时一定要立即写入,那就强用flush方法强行写磁盘
import java.io.*;

/**
 * 缓存区读写
 */
public class Demo_IO_16 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileWriter file_1 = new FileWriter("E:\\study_1.txt");
        BufferedWriter bwriter = new BufferedWriter(file_1);// 根据文件读取器创建缓存写入器
        bwriter.write("我是爱摸鱼的TT!");
        bwriter.newLine(); // 另起一行
        bwriter.write("很久不见呀~");
        bwriter.close();
        file_1.close();
	}
}
7.2.1.4 小结
  • FileWriter的每次write调用都会直接写入磁盘,不但效率低,性能也差
  • BufferedWriter的每次write调用会先写入缓冲区,知道缓冲区满了才写入磁盘
  • 缓冲区大小默认是8KB,查看源码defaultCharBufferSize = 8192
  • 资源释放的close方法会把缓冲区的剩余数据写入磁盘
  • 或者中途调用flush方法也可以提前将缓冲区的数据写入磁盘

7.2.2 缓存读取流(BufferedReader)

7.2.2.1 概述

在这里插入图片描述
字符输入流转换为带有缓存可以一次读取一行的缓存字符读取流

字节流 —> 字符流 —> 缓存读取流

7.2.2.2 构造方法

在这里插入图片描述

7.2.2.3 方法应用

在这里插入图片描述补充readLine()方法:从文件中读取一行数据
在这里插入图片描述

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

/**
 * 缓存读取流
 */
public class Demo_IO_16 {
    
    
    public static void main(String[] args) throws IOException {
    
    
        FileReader file_2 = new FileReader("E:\\study_1.txt");
        BufferedReader read = new BufferedReader(file_2);
        for(int i = 1;;i++){
    
    
            String s = read.readLine();
            if(s == null){
    
     // 读到了空指针,表示已经到了文件末尾了
                break;
            }
            System.out.println(s);
        }
        read.close();
        file_2.close();
    }
}

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

  • FileReader只能一个一个字符地读,或者一次性读进字符数组
  • BufferedReader还支持一行一行地读

7.3 缓冲I/O字节流

文件输出流FileOutputStream跟FileWriter同样有个毛病,每次调用write都会直接写到磁盘,使得频繁地写操作,性能极其低下,缓存输出流的用法与缓存写入器非常相似,主要体现在以下4点:

  • 每次创建缓存输出流对象之前,都要先构建文件输出流对象,然后据此构建缓存输出流对象。
  • 它的write方法先把数据写到缓存,等到缓存满了才写入磁盘,或者调用close方法时自动将缓存数据写入磁盘
  • 缓存输出流仍然提供flush方法,该方法可将缓存中的数据立即写入磁盘。
  • 由于字节流操作的数据形式为字节数组,因此无论是缓存输出流还是缓存输入流,都不提供按行读写的功能。
import java.io.*;

/**
 * 缓存I/O字节流
 */
public class Demo_IO_19 {
    
    
    public static void main(String[] args)  {
    
    
        // 缓存流输入
        try (FileInputStream file = new FileInputStream("E:\\study.txt")){
    
    
            BufferedInputStream bis = new BufferedInputStream(file);
            byte[] bytes = new byte[100];
            while(true){
    
    
                int n = bis.read(bytes);
                if(n == -1){
    
    
                    break;
                }
                System.out.println(new String(bytes));//把字节数组转换为字符串
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

        // 缓存流输出
        try (FileOutputStream file_2 = new FileOutputStream("E:\\study.txt")) {
    
    
            BufferedOutputStream bof = new BufferedOutputStream(file_2);
            String str = "我是爱摸鱼的TT~";
            bof.write(str.getBytes());// 把字节数组写入缓存输出流
        }catch(Exception e){
    
    
            e.printStackTrace();
        }
    }
}

8. 收集异常日志

普通的异常控制台显示
在这里插入图片描述
可以将异常信息保存在txt文档中,并加上日期,便于后期核查
在这里插入图片描述
这种收集异常日志方法作为了解,后续会有专门成熟异常收集的框架,依赖库等等,就不用编写。

9. Properties类

9.1 概述

Properties(Java.util.Properties),该类主要用于读取Java的配置文件,不同的编程语言有自己所支持的配置文件,配置文件中很多变量是经常改变的,为了方便用户的配置,能够让用户脱离程序本身去修改相关的变量设置。就像在Java中,其配置文件常为**.properties文件**,是以键值对的形式进行参数配置的。

在这里插入图片描述
properties继承HashTable属于Map集合(键值对),但其扩展部分含有IO相关用法(配置文件)

9.2 常用方法

既然其属于Map集合,那么Map集合的所有操作都可用于Properties类中,但是还有部分补充的方法,这样便于理解,见名知意。

  • store方法:将properties对象内容写入字节流/字符流所指的文件中
    在这里插入图片描述
    //字符流的写入
    在这里插入图片描述在这里插入图片描述
  • load方法:将字节流/字符流指向的文件内容加载到properties对象中
    在这里插入图片描述
    //字符流的读取
    在这里插入图片描述在这里插入图片描述
  • get与getProperty
    在这里插入图片描述
    get返回Object对象,getProperty返回字符串。
    看其源码:
    在这里插入图片描述在这里插入图片描述
    //字符流的读取
    在这里插入图片描述

10. 序列化技术(了解)

10.1 概述

由于垃圾回收机制(GC)的存在,一些属性或对象,在程序关闭之后,便消失,无法重复利用,所以有人在想能不能将把对象完整的存储在文件中,使用时再取出来,即对象在内存中存储的字符序列(看上去像是乱码);

虽然序列化很方便,但是却十分容易产生Bug(占Bug总数将近三分之一),所以Java官方提出近几年将要进行整改,建议大家不要使用此方法;(了解下也是有必要的)

编程应注意:
读写序列化对象的专用I/O流包括对象输入流ObjectInputStream 和 对象输出流ObjectOutputStream。其中,前者用于从文件中读取对象信息,它的readObject方法完成了读对象操作;后者用于将对象信息写入文件,它的writeObject方法完成了写对象操作。

10.2 序列化

序列化:将内存对象存储在磁盘文件数据的过程

Java官方规定,所有对象均不能序列化,想要序列化,需添加标记—Serializable接口
在这里插入图片描述
添加标记:实现接口Serializable。添加代码后没有任何飘红,即说明不需要实现任何方法,因此这个接口被称为标记接口。
在这里插入图片描述

10.3 反序列化

反序列化:将磁盘文件内容转化成内存对象的过程
在这里插入图片描述

11. try-with-resources的表达式

这块知识既用到了IO流的知识,又用到了异常处理的知识

11.1 问题引入

在上面所举例代码里都是直接throws抛出异常,没有进行异常处理,而我们写项目时,经常都是要进行异常处理的try
-catch(-finally),不能直接throws抛出,所以就存在以下的问题。

1)在文件流使用完毕后需要关闭
在这里插入图片描述
2)为了使close一定被执行,需要将其放在finally中
在这里插入图片描述
3)所以需要将fr提到try之前,但有可能没东西传入,导致产生空指针异常
在这里插入图片描述
4)又得继续try-catch处理
在这里插入图片描述
5)综上,为了获取一个字符,需要进行这么多操作,不仅空间上浪费,时间也得浪费很多,因此就需要采取对应的解决方案。

11.2 概述

在Java7开始,try语句支持try-with-resources的表达式,意思是携带某些资源去尝试干活,并在尝试结束自动释放这些资源。具体做法:

  • 在try(…)里面声明的资源会在try/catch结束自动释放
  • try(…)允许在圆括号内部拥有多个资源创建语句,语句之间以冒号分隔
  • 相当于编译器自动补充了finally代码块中的资源释放操作
  • 资源类必须实现java.lang.(Auto)Closeable接口,这样close方法才会由系统调用
  • 一般来说,文件I/O套接字数据库等均实现该接口

11.3 解决方法

1)jdk1.7时:在try()中new的对象会在try或catch块执行完毕后自动执行close。但要求,能使用此方法的类必须实现Closeable或AutoCloseable的接口;
【如果是自定义的类,也必须实现Closeable或AutoCloseable的接口】
在这里插入图片描述
2)但是,如果后面还有代码块需要用到try中的对象时,就显得不方便了。

因此,JDK9进行了改进
在这里插入图片描述
例如:

import java.io.*;

/**
 * try-with-resources
 */
public class Demo_IO_18 {
    
    
    public static void main(String[] args) {
    
    
        // 字符流的写出
        try(FileWriter file = new FileWriter("E:\\study_2.txt");) {
    
    
            file.write("我是爱摸鱼的TT!");// 往文件写入字符串
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        
        // 字符流的读取
        File file_1 = new File("E:\\study_2.txt");// 创建一个指定路径的文件对象
        try(FileReader file1 = new FileReader(file_1)) {
    
    
            char[] c = new char[(int)file_1.length()];// 创建一个与文件大小等长的字符数组
            int len = file1.read();
            System.out.println(new String(c));// 把字符数组转换为字符串
        } catch (IOException e) {
    
     // 捕获到输入输出异常
            e.printStackTrace();
        }
    }
}

总之,这种写法省时省力便捷,往后会经常用到。

三、总结

一定要清楚 IO流(输入流 读取数据, 输出流 写出数据)

IO用于在设备检进行数据传输的操作

IO流分类:

  • 字节流 InputStream FileInputStream BufferedInputStream
    OutputStream FileOutputStream BufferedOutputStream
  • 字符流 Reader FileReader BufferedReader
    Writer FileWriter BufferedWriter

猜你喜欢

转载自blog.csdn.net/weixin_46312449/article/details/114549115