File、IO流(一)
为什么要有文件
存储数据的方案
变量、数组、内存和集合
它们都是内存中的数据容器,这些数据在断电或程序终止时会丢失。
文件
文件是非常重要的存储方式。它们存储在计算机硬盘中,即便断电,或者程序终止了,存储在硬盘文件中的数据也不会丢失。
File和IO流概述
File
- File是java.io包下的类, File类的对象,用于代表当前操作系统的文件(可以是文件或文件夹)。
- File的作用有:获取文件信息(大小,文件名,修改时间)、判断文件的类型、创建文件/文件夹、删除文件/文件夹……
- 注意:File类只能对文件本身进行操作,不能读写文件里面存储的数据。
IO流
用于读写数据的(可以读写文件,或网络中的数据…)
总结
File代表文件,IO流读写数据。
File
创建对象
构造器 | 说明 |
---|---|
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 根据父路径和子路径名字创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字创建文件对象 |
注意:
- File对象既可以代表文件、也可以代表文件夹。
- File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的。
- 路径分隔符可以使用“\\”或“/”,也可以使用File类的常量File.separator来根据系统类型自动补充分隔符。
绝对路径、相对路径
绝对路径:从盘符开始。
相对路径:不带盘符,默认直接到当前工程下的目录寻找文件(找模块下的文件时建议使用)。
常用方法1:判断文件类型、获取文件信息
方法名称 | 说明 |
---|---|
public boolean exists() | 判断当前文件对象对应的文件路径是否存在,存在返回true |
public boolean isFile() | 判断当前文件对象指代的是否是文件,是文件返回true,反之返回false |
public boolean isDirectory() | 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之返回false |
public String getName() | 获取文件的名称(包含后缀) |
public long length() | 获取文件的大小,返回字节个数,或返回文件夹本身的大小,不包括其内部的文件 |
public long lastModified() | 获取文件的最后修改时间 |
public String getPath() | 获取创建文件对象时使用的路径 |
public String getAbsolutePath() | 获取绝对路径 |
常用方法2:创建文件、删除文件
File类创建文件的功能
方法名称 | 说明 |
---|---|
public boolean createNewFile() | 创建一个新的空的文件 |
public boolean mkdir() | 只能创建一级文件夹 |
public boolean mkdirs() | 可以创建多级文件夹 |
File类删除文件的功能
方法名称 | 说明 |
---|---|
public boolean delete() | 删除文件、空文件夹 |
注意:delete方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站。
常用方法3:遍历文件夹
方法名称 | 说明 |
---|---|
public String[] list() | 获取当前目录下所有的"一级文件名称"到一个字符串数组中并返回 |
public File[] listFiles() | 获取当前目录下所有的"一级文件对象"到一个文件对象数组中并返回(重点) |
使用listFiles方法时的注意事项:
- 当主调是文件,或者路径不存在时,返回null。
- 当主调是空文件夹时,返回一个长度为0的数组。
- 当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回。
- 当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件。
- 当主调是一个文件夹,但是没有权限访问该文件夹时,返回null。
方法递归
认识方法递归
什么是方法递归
- 递归是一种算法,在程序设计语言中广泛应用。
- 从形式上说:方法调用自身的形式称为方法递归( recursion)。
递归的形式
- 直接递归:方法自己调用自己。
- 间接递归:方法调用其他方法,其他方法又回调方法自己。
使用方法递归时需要注意的问题
- 递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误。
递归算法三要素
- 递归的公式。
- 递归的终结点。
- 递归的方向必须走向终结点。
递归算法的应用
文件搜素、文件删除、文件复制……
字符集
常见字符集介绍
标准ASCII字符集
- ASCII(American Standard Code for Information Interchange): 美国信息交换标准代码,包括了英文、数字、符号等。
- 标准ASCII使用1个字节存储一个字符,首位是0,总共可表示128个字符,对美国来说完全够用。
GBK(汉字内码扩展规范,国标)
- 汉字编码字符集,包含了2万多个汉字等字符,GBK中一个中文字符集成两个字节的形式存储。
- 注意:GBK兼容了ASCII字符集。同时,GBK规定,汉字的第一个字节的第一位必须是1。
Unicode字符集(统一码,也叫万国码)
Unicode是国际组织制定的,可以容纳世界上所有文字、符号的字符集。
UTF-32
UTF-32是Unicode字符集的一种编码方案,它使用4个字节表示一个字符,非常占存储空间,且降低了通信效率。
UTF-8
- UTF-8是Unicode字符集的一种编码方案,它采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。
- 英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节。
- 技术人员在开发时都应该使用UTF-8编码。
- UTF-8在编码时,会在前方加标志位,如下表所示:
使用字节数 | UTF-8编码方式 |
---|---|
1 | 0xxxxxxx (ASCII码) |
2 | 110xxxxx 10xxxxxx |
3 | 1110xxxx 10xxxxxx 10xxxxxx |
4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
几点注意
-
字符集时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。
-
英文,数字一般不会乱码,因为很多字符集都兼容了ASCII编码。
字符集的编码、解码操作
Java代码完成对字符的编码
String提供了如下方法 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中 |
Java代码完成对字符的解码
String提供了如下方法 | 说明 |
---|---|
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的String |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来构造新的String |
IO流
-
IO流,即输入输出流,是用来读写数据的。
-
I指Input,称为输入流:负责把数据从硬盘或网络读到内存中去。
-
O指Output,称为输出流:负责从内存写数据出去到硬盘或网络。
IO流的分类
按流的方向分为:
按流中数据的最小单位,分为:
IO流总体来看就有四大流
- 字节输入流:以内存为基准,把来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流。
- 字符输入流:以内存为基准,把来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流。
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。
IO流的体系
如下图所示,这些类都在java.io包下:
IO流-字节流
FileInputStream(文件字节输入流)
作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
构造器 | 说明 |
---|---|
public FileInputStream(File file) | 创建字节输入流管道与源文件接通 |
public FileInputStream(String pathname) | 创建字节输入流管道与源文件接通(简化写法,推荐使用) |
方法名称 | 说明 |
---|---|
public int read() | 每次读取一个字节返回,如果发现没有数据可读会返回-1. |
public int read(byte[] buffer) | 每次用一个字节数组去读取数据,返回字节数组读取了多少个字节, 如果发现没有数据可读会返回-1 |
示例
InputStream is = new FileInputStream("C:/...");
byte[] buffer = new byte[3];
int len;
while ((len == is.read(buffer)) != -1) {
String rs = new String(buffer, 0, len);
System.out.print(rs);
}
is.close();
几点注意
-
使用FileInputStream每次读取一个字节时,会大量调用系统资源,导致读取性能很差,并且读取汉字输出会不可避免地乱码。
-
使用FileInputStream每次读取多个字节时,读取性能得到了提升,但读取汉字输出还是可能乱码。
-
流使用完毕时必须关闭,以释放系统资源:
对象名.close();
。
解决读取中文输出乱码的问题
方法一:一次读取完全部字节
-
方式一:使用方法
public int read(byte[] buffer)
自己定义一个字节数组与被读取的文件大小一样大,然后使用该字节数组,一次读完文件的全部字节。 -
方式二:Java官方为InputStream提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回。
方法名称 | 说明 |
---|---|
public byte[] readAllBytes() throws IOException | 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 |
方法二:使用字符流
读写文本内容更适合用字符流。
注意
- 使用方法一时,如果文件过大,创建的字节数组也会过大,可能引起内存溢出。
- 读写文本内容更适合用字符流。
- 字节流适合做数据的转移,如文件复制等。
FileOutputStream(文件字节输出流)
作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去。
构造器 | 说明 |
---|---|
public FileOutputStream(File file) | 创建字节输出流管道与源文件对象接通 |
public FileOutputStream(String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileOutputStream(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileOutputStream(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据 |
方法名称 | 说明 |
---|---|
public void write(int a) | 写一个字节出去 |
public void write(byte[] buffer) | 写一个字节数组出去 |
public void write(byte[] buffer , int pos , int len) | 写一个字节数组的一部分出去 |
public void close() throws IOException | 关闭流 |
- 换行:使用
"\r\n"
,即对象名.write("\r\n".getBytes());
。
文件复制
-
硬盘中的文件---创建字节输入流管道--->内存中的字节数组---创建字节输出流管道--->硬盘中的文件
-
字节流非常适合做一切文件复制操作,因为:任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题。
释放资源的方式
try-catch-finally
try {
...
...
} catch (IOException e) {
e.printStackTrace();
} finally {
}
- finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
- 不要在finally里返回数据,否则返回值永远都是finally里的。
- 作用:一般用于在程序执行完成后进行资源的释放操作。
try-with-resource
JDK 7开始提供了更简单的资源释放方案:try-with-resource。
try(定义资源1; 定义资源2; ...) {
可能出现异常的代码;
} catch (异常类名 变量名) {
异常的处理代码;
}
-
该资源使用完毕后,会自动调用其close()方法,完成对资源的释放。
-
() 中只能放置资源,否则报错,资源一般指的是最终实现了AutoCloseable接口。