文件IO流

观前提示:本篇博客演示使用的 IDEA 版本为2021.3.3版本,使用的是Java8(又名jdk1.8)

电脑使用的操作系统版本为 Windows 10


目录

文件

什么是文件

保存路径的写法

/ 正斜杠和 \ 反斜杠

盘符

保存路径 

文件的类型

Java对文件的操作

File

属性

构造方法

方法

针对文件内容的操作

InputStream

方法

FileInputStream 概述

构造方法

read 三个重载

修改 IDEA 字符集 

 OutputStream

方法

字符流


文件

什么是文件

文件指的是计算机中存储数据的一种形式,可以包含文本、图像、音频、视频等各种类型的数据。在计算机中,文件通常以磁盘、固态硬盘、U盘等存储设备的形式存在,也可以通过网络进行传输和共享。

保存路径的写法

文件存储在硬盘上,每个文件都有一个具体的保存路径

下图的红框里面就是文件的保存路径

 在Windows 上面文件保存的写法: D:\GAME

 表示一个文件的具体位置路径, 就可以使用 / 来分割不同的目录级别

/ 正斜杠和 \ 反斜杠

/ 正斜杠, \ 反斜杠 

上古时期,操作系统,分割目录,都是使用 /

后来(1981年),微软搞了一个操作系统,叫做 DOS .搞这个系统的时候,

发布之前,产品经理临时更改需求,要求使用 \ 来代替 / 分割路径

发布之后,差评如潮,大家都给差评, 你咋使用 \ 呢?

迫于用户压力,微软重新支持了 / 作为分隔符

后来 Windows 也继承了这样的设定 

虽然 正斜杠和反斜杠 都可以使用,但是建议优先使用 正斜杠 / 

如果使用 \ 写代码,很不方便

例如:

String path = "D:\cat.jpg";

就容易把 \c 识别成一个转义字符,而不是 \ 本身

盘符

在Windows系统中,可以通过打开"我的电脑"或者"此电脑",查看各分区的盘符

通常情况下,系统盘符为C盘,它包含了Windows操作系统的安装文件、系统文件、启动文件、注册表等重要的系统信息。 

D:/game 中的 D 就是系统盘符

系统盘符从 C 开头 只要你分盘分的够多,可以一直分到 Z

这些盘符就是通过"硬盘分区"来的

每个盘符可以是一个单独的硬盘,也可以是若干个盘符对应一个硬盘

但是,却没有见到 A 盘和 B 盘,这是为什么呢?

在古老的计算机时代,使用的存储设备主要是软盘和硬盘。其中,软盘分别插在"A驱动器"和"B驱动器"上,称为"A盘"和"B盘"。而当时,系统安装文件和启动文件等通常存储在软盘中,因此软盘的盘符被设置为A盘和B盘。

随着存储设备的不断发展,如光盘、USB闪存驱动器、云存储等的普及,软盘逐渐退出历史舞台,A盘和B盘的盘符也就没有了实际意义。而现代操作系统中,C盘默认为系统盘,储存着系统文件、启动文件、注册表等重要信息。

总结,A盘和B盘的盘符之所以没有被使用,是因为随着存储设备的发展和技术的进步,它们的作用和地位逐渐被替代和淡化了。

保存路径 

 路径有两种写法

1.绝对路径:以 c: d: 盘符开头的路径

2.相对路径:以当前所在的目录为基准,以 . 或者 .. 开头(. 有时候省略),找到指定路径

当前所在的目录称为 工作目录,每个程序运行的时候,都有一个工作目录

(在控制台里通过命令行操作的时候,是特别明显的,后来进化到图形化界面工作目录就没那么直观)

WIN + R 输入 cmd(大小写均可) 

上图为:默认的工作路径

输入一个d:

 可以看到已经切换到了D 盘 

工作路径是可以修改的

 假设当前的工作路径是 D:/blogTest, 我们要定位到 Test1

定位到这个目录就可以写成 ./Test1 (./ 就表示当前目录)

如果工作目录不同,定位到同一个文件,相对路径写法是不一样的

比如定位到的 Test1 这里

如果工作目录是 d:/ 相对路径就写作 ./blogTest/Test1

如果工作目录是 d:/blogTest 相对路径就写作 ./Test1

如果工作目录是 d:/blogTest/aaa 相对路径就写作 ../Test1(.. 表示当前目录的上一级目录)

如果工作目录是 d:/blogTest/aaa/a11 相对路径就写作 ../../Test1

文件的类型

常见的文件类型有:

  • 文本文件:txt、doc、docx、ppt、pdf等。
  • 图像文件:jpg、png、bmp、gif等。
  • 音频文件:mp3、wav、wma等。
  • 视频文件:mp4、avi、mov、wmv等。
  • 压缩文件:zip、rar、7z等。

以上这些文件类型,整体可以归纳为两类

1>文本文件(存的是文本,字符串)(字符串,是由字符构成的,每个字符都是通过一个数字来表示的,这个文本文件里存的数据,一定是合法的字符,都是在你指定字符编码的码表之内的数据)

2>二进制文件(存的是二进制数据,不一定是字符串)(没有任何限制,可以存储任何你想要的数据)

那么随便给你个文件,如何区分是文本文件还是二进制文件呢?

直接用记事本打开

如果乱码就说明是二进制文件

不乱码,说明就是文本

Java对文件的操作

Java 对文件的操作有以下两种

1.针对文件系统的操作(文件的创建,删除,重命名)

2.针对文件内容的操作(文件的读和写)

File

我们先来看看 File 类中的常见属性、构造方法和方法

属性

修饰符及类型属性说明
static StringpathSeparator依赖于系统的路径分隔符,String 类型的表示
static charpathSeparator依赖于系统的路径分隔符,char 类型的表示

构造方法

签名说明
File(File parent, String
child)
根据父目录 + 孩子文件路径,创建一个新的 File 实例
File(String pathname)根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者
相对路径
File(String parent, String
child)
根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用
路径表示

在 new File 对象的时候,构造方法参数中,可以指定一个路径,此时 File 对象就代表这个路径对应的文件(这个路径,可以是绝对路径,也可以是相对路径)

parent 表示当前文件所在的目录

child 自身的文件名

方法

修饰符及返回
值类型
方法签名说明
StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径
booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件
booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返
回 true
booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到
JVM 运行结束时才会进行
String[]list()返回 File 对象代表的目录下的所有文件名
File[]listFiles()返回 File 对象代表的目录下的所有文件,以 File 对象
表示
booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目
booleanrenameTo(File
dest)
进行文件改名,也可以视为我们平时的剪切、粘贴操
booleancanRead()判断用户是否对文件有可读权限
booleancanWrite()判断用户是否对文件有可写权限

代码案例 

import java.io.File;

public class IODemo4 {
    public static void main(String[] args) {
        File file = new File();
    }
}
import java.io.File; 这个包里面的 io

i : input 输入

o : output 输出

针对文件系统的操作

 File 方法代码演示

StringgetParent()返回 File 对象的父目录文件路径
StringgetName()返回 FIle 对象的纯文件名称
StringgetPath()返回 File 对象的文件路径
StringgetAbsolutePath()返回 File 对象的绝对路径
StringgetCanonicalPath()返回 File 对象的修饰过的绝对路径

import java.io.File;
import java.io.IOException;

public class IODemo4 {
    public static void main(String[] args) throws IOException {
//throws IOException 输入输出异常
        File file = new File("d:/blogText.txt");
//1.路径不需要真实存在,2.盘符可以大写也可以小写
        System.out.println(file.getName());
        System.out.println(file.getParent());
        System.out.println(file.getPath());
        System.out.println(file.getAbsoluteFile());
        System.out.println(file.getCanonicalPath());
    }
}

 上面是绝对路径的写法

下面演示相对路径

import java.io.File;
import java.io.IOException;

public class IODemo4 {
    public static void main(String[] args) throws IOException {
        File file = new File("./blogText.txt");
// 这个 ./ 代表在 idea 的工作路径下面运行 
        System.out.println(file.getName());
        System.out.println(file.getParent());
        System.out.println(file.getPath());
        System.out.println(file.getAbsoluteFile());
        System.out.println(file.getCanonicalPath());
    }
}

这是我的 idea 工作路径


接着往下看

booleanexists()判断 File 对象描述的文件是否真实存在
booleanisDirectory()判断 File 对象代表的文件是否是一个目录
booleanisFile()判断 File 对象代表的文件是否是一个普通文件

import java.io.File;

public class IODemo5 {
    public static void main(String[] args) {
        File file = new File("d:/blogText.txt");
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
    }
}

 

 为什么是 false ,因为我的 D 盘里面没有 bolgText.txt 文件

所以 他的 isFile 是false

exists 也是 false


我们可以使用这个方法创建一个

booleancreateNewFile()根据 File 对象,自动创建一个空文件。成功创建后返回 true
import java.io.File;
import java.io.IOException;

public class IODemo5 {
    public static void main(String[] args) throws IOException {
        File file = new File("d:/blogText.txt");
        file.createNewFile();
        System.out.println(file.exists());
        System.out.println(file.isFile());
        System.out.println(file.isDirectory());
    }
}

 


我们既可以创建,也可以删除 

booleandelete()根据 File 对象,删除该文件。成功删除后返回 true
import java.io.File;

public class IODemo6 {
    public static void main(String[] args) {
        File file = new File("D:/blogText.txt");
        file.delete();
    }
}

 


voiddeleteOnExit()根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行

 这个东西,在其他地方演示更加明显

比如,你在桌面创建一个文档

 上面这个实的就是创建的文档,当你打开以后再里面输入内容,但是不保存,他还会出现一个虚的,就是下面的

这个就是 word 文档生成的临时文件,这个临时文件,相当于保存了你当前实际编辑的内容(尤其是你还没有保存的时候)

防止你编辑了很多东西,但是还没有保存,突然停电了,蓝屏了,死机了,等等一系列不可抗力事件发生了以后,

他会在你下次启动的时候,提醒你是否要恢复之前的编辑内容

这种功能很多程序都有,就可以使用 deleteOnExit 的方式来删除


booleanmkdir()创建 File 对象代表的目录
booleanmkdirs()创建 File 对象代表的目录,如果必要,会创建中间目

我们也可以使用这个方法来创建一个文件

import java.io.File;

public class IODemo7 {
    public static void main(String[] args) {
        File dir = new File("test");
        dir.mkdir();
    }
}

这是运行代码前

运行完,我们得到了这个 test 文件

 

 如果你的没有刷新出来,建议刷新一下

右键就可以得到这个,然后点击蓝色的地方,刷新一下

 如果想要创建多级目录就使用 dir.mkdirs

import java.io.File;

public class IODemo7 {
    public static void main(String[] args) {
        File dir = new File("test/aaa/bbb");
        dir.mkdirs();
    }
}


我们还可以给 test 改个名字

booleanrenameTo(File
dest)
进行文件改名,也可以视为我们平时的剪切、粘贴操作
import java.io.File;

public class IODemo8 {
    public static void main(String[] args) {
        File file = new File("./test");
        File dest = new File("./testBBB");
        file.renameTo(dest);
    }
}


 


针对文件内容的操作

针对文件内容,使用"流对象"进行操作,这个"流对象"是一种比喻方式

你想象有一个水龙头,通过这个水龙头进行接水

假设你要接1000ml的水

你可以一次性接完这1000ml

也可以是每次接500ml,分成两次来接完

也可以是每次接100ml,分成十次来接完

你怎么接都可以

这个就是"流对象"

同理,你在想象你有一个羽毛球筒,筒里面有很多羽毛球,但是你每次只能从筒里面取出一个羽毛球

这个就不是"流对象"

你有一个文件,大小为1000个字节

假设你要读取文件里面的1000个字节

你可以一次性读取这1000字节

也可以是每次读取500个字节,分成两次来读完

也可以是每次读取100字节,分成十次来读完

也可以是其他的读取方式

写文件同理

因此就把读写文件,称为"流对象"

这个比喻并非是 Java 独有的,而是操作系统 api 的设定

进一步的各种编程语言,操作文件也是继承了这个概念

Java 标准库的流对象

从类型上分为两个大类

1.字节流:操作二进制数据

InputStream          FileInputStream

OutputStream       FileOutputStream

2.字符流:操作文本数据

Reader       FileReader

Writer         FileWriter


InputStream

方法

修饰符及返回值类型方法签名说明
intread()读取一个字节的数据,返回 -1 代表已经完全读完了
intread(byte[] b)最多读取 b.length 字节的数据到 b 中,返回实际读到的数
量;-1 代表以及读完了
intread(byte[] b,
int off, int len)
最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返
回实际读到的数量;-1 代表以及读完了
voidclose()关闭字节流

FileInputStream 概述


构造方法

签名说明
FileInputStream(File file)利用 File 构造文件输入流
FileInputStream(String name)利用文件路径构造文件输入流

 上面这段代码是 InputStream 源码可以看到他是 抽象类,不能直接 new

那么:抽象类和接口的区别是什么?

  1. 抽象类是一个拥有抽象方法的类,而接口是一组抽象方法的集合,抽象类可以包含方法的定义与实现,而接口只能包含方法的声明,不包含实现。
  2. 在Java/C#等语言中,每个类只能继承一个父类(单继承),但是可以实现多个接口(多实现)。因此,抽象类通常用于表示类之间的共性,而接口更适用于描述行为的规范。
  3. 抽象类可以有构造函数,而接口不允许定义构造函数。
  4. 子类继承抽象类时,必须实现其中的抽象方法,否则子类也必须被定义为抽象类;而子类实现接口时,必须实现接口中所有的方法,否则子类也必须被定义为抽象类。
  5. 抽象类可以有成员变量和非抽象方法,而接口只能有常量和抽象方法。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class IoDemo9 {
    public static void main(String[] args) throws FileNotFoundException {
        //创建 InputStream 对象的时候,可以使用绝对路径/相对路径,也可以是 File 对象
        InputStream inputStream = new FileInputStream("D:\\blogTest\\test.txt");
    }
}

上面代码中的异常是:文件没找到异常

如果要想打开一个文件去读.就需要保证这个文件是存在的

下面我们来进行读操作

read 三个重载

我们可以看到 read() 方法有三个重载

read() 无参数:一次读一个字节

read() 一个参数:把读到的内容填充到参数的这个字节数组中(此处参数是"输出型参数"),返回值是实际读取的字节数

read() 三个参数:和 2 类似,只不过是往数组的一部分区间里尽可能填充

我们可以看一下 read 的源码

 read 读取一个字节,按理说应该返回一个 byte 就行了,但是实际上返回的是 int

因为,他除了要表示 byte 里面的 0~255(-128~127)这样的情况之外,还需要表示一个特殊情况, -1

这个情况表示读取文件结束了(读取到文件的末尾)

下面演示的是读文件操作,用的是 read() 无参数,我在文件里面放的是 HelloThere

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

public class IoDemo9 {
    public static void main(String[] args) throws IOException {
        //创建 InputStream 对象的时候,可以使用绝对路径/相对路径,也可以是 File 对象
        InputStream inputStream = new FileInputStream("D:\\blogTest\\test.txt");
        while (true) {
            //读操作
            int b = inputStream.read();
            if (b == -1) {
                break;
            }
            System.out.println("" + (byte) b);
        }
        inputStream.close();
    }
}

 可以使用 ASCII 表对照查看

  

 也可以用十六进制的方式打印出来

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

public class IoDemo9 {
    public static void main(String[] args) throws IOException {
        //创建 InputStream 对象的时候,可以使用绝对路径/相对路径,也可以是 File 对象
        InputStream inputStream = new FileInputStream("D:\\blogTest\\test.txt");
        while (true) {
            //读操作
            int b = inputStream.read();
            if (b == -1) {
                break;
            }
            System.out.printf("%x\n", (byte)b);
        }
        inputStream.close();
    }
}

 

 还可以读取汉字,我在里面放的 "你好"

这是 十六进制的, 你好 

下面的链接就是查看字符集编码的网站 

 字符编码(UTF-8)

修改 IDEA 字符集 

注意:我的 idea 字符集全部改过,所以演示的东西可能和你的 idea 实际情况不一样

如果想要修改 idea 的字符集,可以看以下操作

IDEA 右下角可以看到我的字符集是 UTF-8 

第一步:File -> Settings

 第二步:在搜索栏输入: File Encodings

把右边红框里面的东西改成 UTF-8 然后点击 OK 即可 

言归正传:

read() 一个参数 代码演示

我在 test.txt 里面放的是 HelloThere

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

public class IODemo10 {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = new FileInputStream("D:\\blogTest\\test.txt");
        while (true) {
            byte[] buffer = new byte[1024];
            int len = inputStream.read(buffer);
            // 可以看到有多少,多久读完
            System.out.println("len " + len);
            if (len == -1) {
                break;
            }
            //读取的结果被放到了 byte 数组中
            for (int i = 0; i < len; i++) {
                System.out.printf("%x \n", buffer[i]);
            }
        }
    }
}

read 的第二个重载,需要调用者准备一个数组

byte[] buffer = new byte[1024];

 这里的传参操作,相当于是把刚才准备好的数组,交给 read 方法,让 read 方法内部针对这个数组进行填写(此处参数相当于"输出型参数")

int len = inputStream.read(buffer);

Java 中一般习惯做法,输入的信息作为参数,输出的信息作为返回值,但是也有少数情况下,是使用参数来返回内容的

上面给的数组长度是1024(希望读取的长度),read 会尽可能读取1024个字节,填到数组里面,

但是实际上 ,文件剩余的长度是有限的,如果剩余的长度超过1024,此时1024个字节都会填满,

返回值就是1024,如果剩余长度不足1024,此时有多少就填多少,

read 方法就会返回当前实际读取的长度(实际读取的长度)

假设你要读取的文件长度超过1024,那么第二次读取的数据会覆盖第一次读取的.因此对于文件的基本操作都是,一边读,一边处理,处理好一部分,在处理下一部分

byte[] buffer = new byte[1024];

此处数组的名字为什么要叫做 buffer ?

因为:他不是缓存(cache)

而是缓冲区(buffer)

buffer 存在的意义,就是为了提高 IO 操作的效率

单词 IO 操作,是要访问硬盘 IO设备,单次操作是比较消耗的时间的

如果频繁进行这样的 IO 操作,耗时肯定更多

单次 IO 时间是一定的,如果可以减少 IO 次数,此时就可以提高程序整体的效率

第一个版本的代码,是读一次一个字节.循环次数比较高,read 次数就比较高

第二个版本的代码,是一次读1024个字节,循环次数就降低了很多,read 次数变少了

缓冲区(buffer),就是在缓和了一下冲突,减少冲击的次数

举个例子就是:

你在嗑瓜子,你是选择磕一个瓜子就去一次垃圾桶?

还是选择磕一把瓜子再去一次垃圾桶?


 OutputStream

方法

修饰符及返回值类型方法签名说明
voidwrite(int b)写入要给字节的数据
voidwrite(byte[]
b)
将 b 这个字符数组中的数据全部写入 os 中
intwrite(byte[]
b, int off,
int len)
将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个
voidclose()关闭字节流
voidflush()重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为
了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的
一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写
入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的
数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,
调用 flush(刷新)操作,将数据刷到设备中。

除了使用 InputStream 来读文件

还可以使用 OutputStream 来写文件

我们可以看到 write() 也是三种重载

 我在这里写入 97,98,99,100,这四个 ASCII码

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

public class IODemo11 {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = new FileOutputStream("D:\\blogTest\\test.txt");
        outputStream.write(97);
        outputStream.write(98);
        outputStream.write(99);
        outputStream.write(100);
        outputStream.close();
    }
}

运行程序,发现 IDEA 并没有什么变化,我们打开 text.txt 看看

 发现得到了abcd

 在运行代码之前,我并没有清除原先的 HelloThere,但是当程序运行完,发现只剩下了 abcd

这是因为: 对于 OutputStream 来说,默认情况下,打开一个文件,会先清理文件原有的内容

(所以前面的 HelloThere 被清除了)

如果不想清空,流对象还提供了一个"追加写"对象

通过这个就可以实现不清空文件,把新内容追加写到后面

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

public class IODemo12 {
    public static void main(String[] args) throws IOException {
        String FileName="D:\\blogTest\\test.txt";
        String content = "新增内容";

        OutputStream outputStream = new FileOutputStream(FileName,true);
        outputStream.write(content.getBytes(StandardCharsets.UTF_8));
        outputStream.close();
    }
}

运行程序,然后打开 text.txt


input 虽然是输出,但是要注意, input 和output 的方向

是以CPU 为中心,来看待这个方向的

如上图所示

内存更接近 CPU 

硬盘离 CPU 更远 

以 CPU 为核心

数据 朝着 CPU 的方向流向, 就是输入,所以就把 数据从硬盘到内存,这个过程称为:读,input

数据 远离 CPU 的方向流向, 就是输出,所以就把 数据从硬盘到内存,这个过程称为:写,output


言归正传 

outputStream.close();

 这里的 close 操作,含义是关闭文件

进程 -> 在内核里,使用PCB, 这样的数据结构来表示进程

一个线程对应一个 PCB

一个进程对应一个 PCB ,也可以对应多个

PCB 中有一个重要属性,文件描述表(相当于一个数组)记录了该进程打开了那些文件

(即使一个进程有多个线程多个 PCB, 也没关系,这些 PCB 共用一个文件描述表)

每次打开文件操作,就会在文件描述符表中,申请一个位置把这个位置信息放进去

每次关闭文件,也就会把这个文件描述符表项给释放掉

问题来了:如果这个 close 操作没有写,会怎么样

如果没有 close, 对应的表项,没有及时释放,虽然 Java 有 GC,GC操作会在回收这个 OutputStream 对象的时候 去完成这个释放操作,但是这个 GC 不一定及时

所以,如果不手动释放,意味着文件描述符表可能很快就被占满了(这个数组不能自动扩容,存在上限)

如果占满了,后面再次打开文件,就会失败

文件描述符表最大长度,不同系统上面不太一样,但是基本就是几百个到几千个左右

小结:

close 一般来说是要执行的

但是如果一个程序这里的文件自始至终都要使用,不关闭也可以

随着进程结束,PCB 销毁了,文件描述符表也就销毁,对应的资源操作系统就被自动回收了

(如果你的文件close 之后,程序立即结束了,你不 close也没事)

那么,一般写代码的时候,怎么样才能确保这个 close 被执行到了呢?

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

public class IODemo11 {
    public static void main(String[] args) throws IOException {
        OutputStream outputStream = null;
        try{
            outputStream = new FileOutputStream("D:\\blogTest\\test.txt");
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
        }finally {
            outputStream.close();
        }
    }
}

这个虽然可以保证执行,但是不够优雅

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

public class IODemo11 {
    public static void main(String[] args) throws IOException {
        try (OutputStream outputStream = new FileOutputStream("D:\\blogTest\\test.txt")){
            outputStream.write(97);
            outputStream.write(98);
            outputStream.write(99);
            outputStream.write(100);
        }
    }
}

这个写法虽然没有显示的写 close,实际上是会执行的,因为:只要 try 语句块执行完毕,就可以执行自动到 close.这个语法,在Java 中被称为 try with resource

不是随便拿个对象放到 try() 里就能自动释放,得满足一定需求

 实现了 Closeable 接口的类才可以放到 try 的() 中被自动关闭

这个接口提供的方法就是 close 方法


字符流

字符流的用法和字节流差不多

 字符流读文件代码演示

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class IODemo13 {
    public static void main(String[] args) {
        try (Reader reader = new FileReader("D:\\blogTest\\test.txt")) {
            while (true) {
                int ch = reader.read();
                if (ch == -1) {
                    break;
                }
                System.out.println(" " + (char) ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 也可以读汉字

 字符流 write

 字符流写文件代码演示

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class IODemo14 {
    public static void main(String[] args) {
        try(Writer writer = new FileWriter("D:\\blogTest\\test.txt")){
            writer.write("Hello There");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

            writer.write("Hello There");

像这样的写操作,其实是先写到缓冲区的(缓冲区存在很多状态)

写操作执行完,内容可能在缓冲区,还没有真正的进入硬盘

close 操作就会触发缓冲区的刷新(刷新操作就会让缓冲区把内容写到硬盘里面)

除了 close 方法,还可以通过 flush 方法,也能起到刷新缓冲区的效果

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class IODemo14 {
    public static void main(String[] args) {
        try(Writer writer = new FileWriter("D:\\blogTest\\test.txt")){
            writer.write("Hello There");
            writer.flush();
//手动刷新
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

本文完,感谢观看,有什么错误和不足的地方请在评论区指出,一起进步,谢谢 !

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值