Java从入门到实战总结-3.3、Java的IO处理
文章目录
- 6、字节流
- (3)、示例
IO表示In和out,表示对计算机资源的读入和写出,包括文件、网络等内容的读取和写入。
为了避免对流的流向的理解混乱,记住一点:对于流的入和出都是针对内存的,即input是到内存,output是出内存;读和写则是针对外部介质的,read是从外部介质读取,write是写入外部介质;打印就好理解了,打印到屏幕上或者打印到其它介质中,都是对内存内容的output。
1、java.io.File
java.io.File类是文件和目录路径名的抽象表示。以下是有关文件的要点:
- 实例可以或可以不表示实际的文件系统对象,如文件或目录。如果是这样表示这样一个对象,然后该对象位于一个分区。分区是存储为文件系统的操作系统的特定部分。
- 文件系统可以实现限制某些操作的实际文件系统对象,如读,写,和执行上。这些限制统称为访问权限。
- File类的实例是不可变的;也就是说,一旦创建,由一个File对象表示的抽象路径名是不会改变的。
(1)、类的声明
以下是声明java.io.File类:
(2)、字段
- static String pathSeparator – 这是系统相关的路径分隔符,表示为一个字符串以方便使用。
- static char pathSeparatorChar – 这是依赖于系统的路径分隔符。
- static String separator – 这是与系统有关的默认名称分隔符,表示为一个字符串以方便使用。
- static char separatorChar – 这是与系统有关的默认名称分隔符。
(3)、类的构造函数
SN | 构造函数& 描述 |
1 | File(File parent, String child) 此方法创建从父抽象路径名和child路径名字符串的新File实例。 |
2 | File(String pathname) 该方法通过将给定路径名字符串转换为抽象路径名来创建一个新File实例。 |
3 | File(String parent, String child) 此方法创建从父路径名字符串和child路径名字符串的新File实例。 |
4 | File(URI uri) URI转换成抽象路径名:此方法通过给定的文件将创建一个新的File实例。 |
(4)、类方法
SN | 方法& 描述 |
1 | boolean canExecute() 此方法测试应用程序是否可以执行表示此抽象路径名的文件。 |
2 | boolean canRead() 这种方法测试应用程序是否可以读取表示此抽象路径名的文件。 |
3 | boolean canWrite() 此方法测试应用程序是否可以修改表示此抽象路径名的文件。 |
4 | int compareTo(File pathname) 这种方法比较两个抽象路径名的字典顺序。 |
5 | boolean createNewFile() 此方法自动创建此抽象路径名命名的,当且仅当具有此名称的文件尚不存在一个新的空文件。 |
6 | static File createTempFile(String prefix, String suffix) 此方法创建的默认临时文件目录的空文件,使用给定前缀和后缀生成其名称。 |
7 | static File createTempFile(String prefix, String suffix, File directory) 此方法会在指定的目录中一个新的空文件,使用给定前缀和后缀字符串生成其名称。 |
8 | boolean delete() 此方法删除表示此抽象路径名的文件或目录。 |
9 | void deleteOnExit() 此方法要求将表示此抽象路径名的文件或目录在虚拟机终止时被删除。 |
10 | boolean equals(Object obj) 此方法测试此抽象路径名与给定对象是否相等。 |
11 | boolean exists() 此方法测试表示此抽象路径名的文件或目录是否存在。 |
12 | File getAbsoluteFile() 此方法返回此抽象路径名的绝对形式。 |
13 | String getAbsolutePath() 此方法返回此抽象路径名的绝对路径名字符串。 |
14 | File getCanonicalFile() 此方法返回此抽象路径名的规范形式。 |
15 | String getCanonicalPath() 此方法返回此抽象路径名的规范路径名字符串。 |
16 | long getFreeSpace() 此方法返回此抽象路径名的分区中的未分配的字节数。 |
17 | String getName() 此方法返回表示此抽象路径名的文件或目录的名称。 |
18 | String getParent() 此方法返回此抽象路径名的父路径名的字符串,或者null,如果此路径名冇有指定父目录。 |
19 | File getParentFile() 此方法返回此抽象路径名的父抽象路径名,或null,如果此路径名冇有指定父目录。 |
20 | String getPath() 此方法此抽象路径名转换为一个路径名字符串。 |
21 | long getTotalSpace() 此方法返回此抽象路径名的分区的大小。 |
22 | long getUsableSpace() 此方法返回可用字节数这个虚拟机上命名此抽象路径名的分区。 |
23 | int hashCode() 此方法用于计算此抽象路径名的哈希码。 |
24 | boolean isAbsolute() 此方法测试此抽象路径名是否是绝对的。 |
25 | boolean isDirectory() 此方法测试表示此抽象路径名的文件是否是一个目录。 |
26 | boolean isFile() 此方法测试表示此抽象路径名的文件是否是一个正常的文件。 |
27 | boolean isHidden() 此方法测试此抽象路径名的文件是否是一个隐藏文件。 |
28 | long lastModified() 此方法返回的时候,表示此抽象路径名的文件的最后修改 |
29 | long length() 此方法返回表示此抽象路径名的文件的长度。 |
30 | String[] list() 此方法返回的字符串命名表示此抽象路径名的目录中的文件和目录的数组。 |
31 | String[] list(FilenameFilter filter) 此方法返回的字符串命名的目录表示此抽象路径名满足指定过滤器的文件和目录的数组。 |
32 | File[] listFiles() 此方法返回抽象路径名表示在表示此抽象路径名的目录中的文件的数组。 |
33 | File[] listFiles(FileFilter filter) 此方法返回抽象路径名表示的目录表示此抽象路径名满足指定过滤器的文件和目录的数组。 |
34 | File[] listFiles(FilenameFilter filter) 此方法返回抽象路径名表示的目录表示此抽象路径名满足指定过滤器的文件和目录的数组。 |
35 | static File[] listRoots() 此方法列出可用的文件系统的根。 |
36 | boolean mkdir() 此方法创建此抽象路径名的目录。 |
37 | boolean mkdirs() 此方法创建此抽象路径名的目录,包括任何必需但不存在的父目录 |
38 | boolean renameTo(File dest) 这种方法将重命名表示此抽象路径名的文件。 |
39 | boolean setExecutable(boolean executable) 这是一个方便的方法来设置所有者对于此抽象路径名执行权限。 |
40 | boolean setExecutable(boolean executable, boolean ownerOnly) 此方法设置所有者或每个人的执行权限,此抽象路径名。 |
41 | boolean setLastModified(long time) 此方法设置此抽象路径名的文件或目录的最后修改时间。 |
42 | boolean setReadable(boolean readable) 这是一个方便的方法来设置此抽象路径名的所有者的读取权限。 |
43 | boolean setReadable(boolean readable, boolean ownerOnly) 此方法设置所有者或在此抽象路径名大家的读取权限。 |
44 | boolean setReadOnly() 此方法标志着此抽象路径名命名的,这样只允许读操作的文件或目录。 |
45 | boolean setWritable(boolean writable) 这是一个方便的方法来设置此抽象路径名的所有者的写权限。 |
46 | boolean setWritable(boolean writable, boolean ownerOnly) 此方法设置此抽象路径名的所有者或每个人的写权限。 |
47 | String toString() 此方法返回此抽象路径名的路径名字符串。 |
48 | URI toURI() 这种方法构造一个文件:URI表示此抽象路径名。 |
(5)、示例
结果:
2、文件遍历
对某个文件夹进行遍历,打印出文件名字,找到某个后缀的文件进行统计:
结果:
3、文件过滤器
使用文件过滤器接口对文件进行过滤,不用再对文件名进行判断:
结果:
4、相对路径和绝对路径
绝对路径:从盘符开始,是一个完整的路径,例如:c://a.txt
相对路径:在Java代码中是相对于项目目录路径,这是一个不完整的便捷路径,在Java开发中很常用
结果:
5、IO流
(1)、概述
可以将这种数据传输操作,看做一种数据的流动,按照流动的方向分为输入Input和输出Output。
Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。
(2)、分类
按照流的方向分为:输入流和输出流
按照流动的数据类型分为:字节流和字符流
字节流:输入流的父类InputStream,输出流父类OutputStream
字符流:输入流父类Reader,输出流Writer
6、字节流
这输入字节流的顶级父类,先来了解一切皆字节的描述:计算机中的任何数据(文本、图片、视频、音乐等)都是以二进制形式存储的。在数据传输时也都是以二进制的形式存储的。后续的学习的任何流,在传输时底层都是二进制。
(1)、InputStream 和 OutputStream
Java.io.InputStream 类是表示字节输入流的所有类的超类。这需要定义InputStream的子类的应用程序必须始终提供返回输入的下一个字节的方法。OutputStream类似。
(2)、FileInputStream 和 FileOutputStream
A、java.io.FileInputStream
构造方法:
SN | 构造函数& 描述 |
1 | FileInputStream(File file) 这通过打开一个到实际文件,命名在文件系统中的File对象文件的文件的连接创建一个FileInputStream。 |
2 | FileInputStream(FileDescriptor fdObj) 这通过使用文件描述符fdObj,它代表在文件系统中某个实际文件的现有连接创建一个FileInputStream。 |
3 | FileInputStream(String name) 这将创建一个FileInputStream通过打开一个到实际文件的连接,通过路径名名在文件系统命名的文件。 |
类方法:
SN | 方法& 描述 |
1 | int available() 此方法从输入流中通过一个方法的下一次调用阻塞该输入流返回可以读取(或跳过)的剩余字节数的估计值。 |
2 | void close() 此方法关闭此文件输入流并释放与该流关联的所有系统资源。 |
3 | protected void finalize() 此方法可确保此文件输入流的close方法被调用当它冇有更多的参考引用。 |
4 | FileChannel getChannel() 此方法返回与此文件输入流关联的唯一文件通道对象。 |
5 | FileDescriptor getFD() 此方法返回FileDescriptor对象,表示连接到正在使用此文件输入流文件系统的实际文件。 |
6 | int read() 此方法读取当前输入流中一个字节的数据。 |
7 | int read(byte[] b) 此方法从这个输入流中数据读取可达b.length个字节到字节数组。 |
8 | int read(byte[] b, int off, int len) 此方法读取最多len个从这个输入流中数据的字节到字节数组。 |
9 | long skip(long n) 此方法跳过并丢弃n个字节从输入流中的数据。 |
B、java.io.FileOutputStream
Java.io.FileOutputStream 类是用于将数据写入一个文件或FileDescriptor的输出流。以下是关于文件输出流的要点:
- 这个类是指用于记录的原始字节,例如图像数据流。
- 写字符流,可以使用文件字符 FileWriter
类构造函数:
SN | 构造函数& 描述 |
1 | FileOutputStream(File file) 这将创建一个文件输出流写入到由指定的File对象表示文件。 |
2 | FileOutputStream(File file, boolean append) 这将创建一个文件输出流写入到由指定的File对象表示的文件。 |
3 | FileOutputStream(FileDescriptor fdObj) 这将创建一个输出文件流写入到指定的文件描述符,它代表了文件系统中的某个实际文件的现有连接。 |
4 | FileOutputStream(String name) 这将创建一个输出文件流写入到具有指定名称的文件。 |
5 | FileOutputStream(String name, boolean append) 这将创建一个输出文件流写入到具有指定名称的文件。 |
类方法:
SN | 方法& 描述 |
1 | void close() 此方法关闭此文件输出流并释放与此流有关的所有系统资源。 |
2 | protected void finalize() 这种方法清除连接到文件,并确保此文件输出流的close方法被调用时,此流不再有引用。 |
3 | FileChannel getChannel() 此方法返回与此文件输出流关联的唯一文件通道对象。 |
4 | FileDescriptor getFD() 此方法返回与此流有关的文件描述符。 |
5 | [void write(byte] b) 此方法写入b.length个字节从指定的字节数组到该文件输出流。 |
6 | [void write(byte] b, int off, int len) 此方法从指定的字节数组开始到该文件输出流关闭写入len字节。 |
7 | void write(int b) 此方法写入指定的字节写入此文件输出流。 |
示例
结果:
7、字符编码
计算机中储存的信息都是用二进制数表示的;而我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果。通俗的说,按照何种规则将字符存储在计算机中,如’a’用什么表示,称为"编码";反之,将存储在计算机中的二进制数解析显示出来,称为"解码",如同密码学中的加密和解密。在解码过程中,如果使用了错误的解码规则,则导致’a’解析成’b’或者乱码。
字符集(Charset):是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。
字符编码(Character Encoding):是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。即在符号集合与数字系统之间建立对应关系,它是信息处理的一项基本技术。通常人们用符号集合(一般情况下就是文字)来表达信息。而以计算机为基础的信息处理系统则是利用元件(硬件)不同状态的组合来存储和处理信息的。元件不同状态的组合能代表数字系统的数字,因此字符编码就是将符号转换为计算机可以接受的数字系统的数,称为数字代码。
常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。计算机要准确的处理各种字符集文字,需要进行字符编码,以便计算机能够识别和存储各种文字。
这个是漫画介绍,更生动一些:https://mp.weixin.qq.com/s/CSsN5rd-a6yPEI7hzLMtFw
8、字符流
字节流处理文字一般会遇到乱码,读取文字一半等问题,所以需要更改为字符流处理。
(1)、Reader和Writer
Java.io.Reader 类是一个抽象类,用于读取字符流。Java.io.Writer 类是一个抽象类,用于写入字符流。
(2)、FileWriter和FileReader
Java.io.FileReader 类是一个方便的类用于读取字符文件。以下是有关的FileReader要点:
- 这个类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。
- FileReader是为字符读取流。对于原始字节流读取,使用文件输入流-FileInputStream。
序号 | 构造函数& 描述 |
1 | FileReader(File file) 创建一个新的FileReader,从给定的文件读取。 |
2 | FileReader(FileDescriptor file) 创建一个新的FileReader,从给出的FileDescriptor读取。 |
3 | FileReader(String fileName) 创建一个新的FileReader,因为要读取的文件的名称。 |
Java.io.FileWriter 类是一个方便的类写入字符文件。以下是关于FileWriter中重要的几点:
- 这个类的构造方法假定默认字符编码和默认字节缓冲区大小都是可以接受的。
- 文件字符写的意思是写入字符流。对于原始字节写入流,使用文件输出流。
以下是Java.io.FileWriter类的声明:
以下是Java.io.FileWriter类中的字段:
SN | 构造函数& 描述 |
1 | FileWriter(File file) 此构造一个文件字符写对象给出一个File对象。 |
2 | FileWriter(File file, boolean append) 此构造一个文件字符写对象给出一个File对象。 |
3 | FileWriter(FileDescriptor fd) 此构造与文件描述符相关联的文件字符写的对象。 |
4 | FileWriter(String fileName) 此构造一个文件字符写对象给出文件名。 |
5 | FileWriter(String fileName, boolean append) 此构造函数一个文件字符写对象给出一个布尔值,指示是否附加写入数据的文件名。 |
(3)、示例
代码如下,如果加了append为true则后续追加的内容会添加到文件中,否则会创建新的文件:
append为true的结果:
没有append的结果:
结果(不设置长度的话会有很多空字符):
9、flush刷新管道
默认关闭文件时会执行一次刷新。当不关闭时写入则需要调用flush()进行写入,再比如循环写入时记得及时刷新,否则可能导致未及时写入。
10、字节装饰为字符流InputStreamReader和OutputStreamWriter
使用了装饰者设计模式,把字节流装饰为字符流。
Java.io.InputStreamReader 类是一座桥从字节流与字符流。它读取字节并将其解码为使用指定的字符集的字符。
类构造函数:
序号 | 构造函数& 描述 |
1 | InputStreamReader(InputStream in) 这将创建一个使用默认字符集的输入流。 |
2 | InputStreamReader(InputStream in, Charset cs) 这将创建一个使用给定字符集的输入流。 |
3 | InputStreamReader(InputStream in, CharsetDecoder dec) 这将创建一个使用给定的charset解码器的输入流。 |
4 | InputStreamReader(InputStream in, String charsetName) 这将创建一个使用指定的字符集的输入流。 |
类方法:
序号 | 方法 & 描述 |
1 | void close() 此方法关闭该流并释放与之关联的所有系统资源。 |
2 | String getEncoding() 此方法返回正在使用此流的字符编码的名称。 |
3 | int read() 此方法读取单个字符。 |
4 | [int read(char] cbuf, int offset, int length) 此方法读取字符到一个数组中的一部分。 |
5 | boolean ready() 此方法通知此流是否已准备好被读取。 |
Java.io.OutputStreamWriter 类是从字符流桥接字节流。写入字符编码成使用指定的字符集的字节。
类构造函数:
序号 | 构造函数& 描述 |
1 | OutputStreamWriter(OutputStream out) 这将创建一个使用默认的字符编码的OutputStreamWriter。 |
2 | OutputStreamWriter(OutputStream out, Charset cs) 这将创建一个使用给定字符集的OutputStreamWriter。 |
3 | OutputStreamWriter(OutputStream out, CharsetEncoder enc) 这将创建一个使用给定的charset编码器的OutputStreamWriter。 |
4 | OutputStreamWriter(OutputStream out, String charsetName) 这将创建一个使用指定的字符集的OutputStreamWriter。 |
类方法:
序号 | 方法 & 描述 |
1 | void close() 此方法关闭该流,但要先刷新它。 |
2 | void flush() 此方法刷新流。 |
3 | String getEncoding() 此方法返回正在使用此流的字符编码的名称。 |
4 | [void write(char] cbuf, int off, int len) 此方法写入字符数组的一部分。 |
5 | void write(int c) 此方法写入的单个字符。 |
6 | void write(String str, int off, int len) 此方法将写入一个字符串的一部分。 |
结果:
11、打印流和缓存流Print和Buffered
这里主要是格式化字节流,便于将字节流输出或输入为字符流,相对于刚才的文件流装饰,还可以用于其它位置读取或者写入的字节流进行格式化。
(1)、PrintStream和PrintWriter
A、PrintStream
Java.io.PrintStream类添加功能到另一个输出流,打印各种数据值表示形式方便的能力。
类构造函数:
序号 | 构造函数& 描述 |
1 | PrintStream(File file) 这将创建一个新的打印流,与指定的文件无自动行刷新。 |
2 | PrintStream(File file, String csn) 这将创建一个新的打印流,无自动行刷新,指定文件和字符集。 |
3 | PrintStream(OutputStream out) 这将创建一个新的打印流。 |
4 | PrintStream(OutputStream out, boolean autoFlush) 这将创建一个新的打印流。 |
5 | PrintStream(OutputStream out, boolean autoFlush, String encoding) 这将创建一个新的打印流。 |
6 | PrintStream(String fileName) 这将创建一个新的打印流,无自动行刷新的,与指定的文件名。 |
7 | PrintStream(String fileName, String csn) 这将创建一个新的打印流,无自动行刷新,用指定的文件名和字符集。 |
类方法:
B、PrintWriter
Java.io.PrintWriter 类打印格式化对象的表示到文本输出流。
类构造函数:
序号 | 构造函数& 描述 |
1 | PrintWriter(File file) 这将创建一个新的PrintWriter,不带自动行刷新指定文件。 |
2 | PrintWriter(File file, String csn) 这将创建一个新的PrintWriter,不带自动行刷新的指定文件和字符集。 |
3 | PrintWriter(OutputStream out) 这将创建一个新的PrintWriter,无自动行刷新,从现有的OutputStream。 |
4 | PrintWriter(OutputStream out, boolean autoFlush) 这将创建从现有的OutputStream一个新的PrintWriter。 |
5 | PrintWriter(String fileName) 这将创建一个新的PrintWriter,无自动行刷新,用指定的文件名。 |
6 | PrintWriter(String fileName, String csn) 这将创建一个新的PrintWriter,无自动行刷新,用指定的文件名和字符集。 |
7 | PrintWriter(Writer out) 这将创建一个新的PrintWriter,无自动行刷新。 |
8 | PrintWriter(Writer out, boolean autoFlush) 这将创建一个新的PrintWriter。 |
类方法:
(2)、Buffered
A、BufferedReader
Java.io.BufferedReader 类从字符输入流中读取文本,缓冲各个字符,从而提供字符,数组和行的高效读取。以下是有关的BufferedReader要点:
- 缓冲区的大小可以被指定或默认的大小也可使用。
- Reader的每一个读取请求会导致相应的读取请求底层字符或字节流。
类构造方法:
序号 | 构造函数& 描述 |
1 | BufferedReader(Reader in) 这将创建一个使用默认大小输入缓冲区的缓冲字符输入流。 |
2 | BufferedReader(Reader in, int sz) 这将创建一个使用指定大小输入缓冲区的缓冲字符输入流。 |
类方法:
序号 | 方法与说明 |
1 | void close() 此方法关闭该流并释放与之关联的所有系统资源。 |
2 | void mark(int readAheadLimit) 此方法标记流中的当前位置。 |
3 | boolean markSupported() 这个方法告诉此流是否支持mark()操作,这确实如此。 |
4 | int read() 此方法读取单个字符。 |
5 | int read(char[] cbuf, int off, int len) 此方法读取字符到一个数组中的一部分。 |
6 | String readLine() 此方法读取一行文本。 |
7 | boolean ready() 这个方法告诉此流是否已准备好被读取。 |
8 | void reset() 这个方法重置流。 |
9 | long skip(long n) 此方法跳过n个字符。 |
B、BufferedWriter
Java.io.BufferedWriter 类将文本写入字符输出流,缓冲各个字符,从而提供单个字符,数组和字符串的高效写入。以下是有关的BufferedWriter要点:
- 缓冲区的大小可以被指定或默认的大小也可使用。
- Writer 发送其输出到底层字符或字节流。
构造函数:
序号 | 构造函数与说明 |
1 | BufferedWriter(Writer out) 这将创建一个使用默认大小输出缓冲区的缓冲字符输出流。 |
2 | BufferedWriter(Writer out, int sz) 这将创建一个使用给定大小的输出缓冲区的新缓冲字符输出流。 |
类方法:
序号 | 方法与说明 |
1 | void close() 此方法关闭该流,但要先刷新它。 |
2 | void flush() 这个方法刷新流。 |
3 | void newLine() 此方法写入一个行分隔符。 |
4 | [void write(char] cbuf, int off, int len) 此方法写入字符数组的一部分。 |
5 | void write(int c) 此方法写入的单个字符。 |
6 | void write(String s, int off, int len) 此方法写入一个字符串的一部分。 |
(3)、示例
结果:
print字符格式化打印到文件中:
Buffered缓存后写入:
12、收集异常日志
将printStackTrace的异常日志通过printWriter写入到文件中,用来收集异常日志到文件中。
结果:
13、java.util.Properties
可以以map形式存储到文件中。
java.util.Properties类是代表一个持久的一套详细属性,属性可以被保存到一个流或从流中加载的类。以下是关于属性的要点:
- 属性列表中每个键及其对应值是一个字符串。
- 一个属性列表可包含另一个属性列表作为它的“默认”,第二个属性可在列表中搜索,如果冇有在原有的属性列表中找到的属性键。
- 这个类是线程安全的;多个线程可以共享一个Properties对象,而不需要外部同步。
构造函数:
SN | 构造函数& 描述 |
1 | Properties() 这种构造创建一个空的属性列表,冇有默认值。 |
2 | Properties(Properties defaults) 这种构造创建一个空的属性列表中具有指定默认值。 |
类方法:
示例:
结果:
14、序列化和反序列化
序列化和反序列化用的非常多,分布式技术以及大多框架都在使用,但是Java官方发现其会产生大量的bug,因此宣布将在后续逐渐在JDK中取消序列化和反序列化相关内容。
我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化。
结果:
该文件在mac上预览如下,实际上打开应该是一堆乱码:
15、try-with-resources
在try()中进行new的对象存在close时在调用结束后会自动调用close方法。