Java进阶之File类&递归&IO流
一、File类
1.1 File类概述
在Java中,有一个类叫做File,这个类表示计算机中的文件或文件夹。
在英文单词中file表示的是文件的意思,但是在Java中,File类不仅仅表示文件,也可以表示文件夹。
我们可以调用File类中的方法完成文件或文件夹的操作。
计算机词汇认识:
- 目录(directory):指的是文件夹,可以存放文件
- 文件(file):指的是文件,可以存放数据
- 路径(path):表示计算机的一个位置,这个位置可以是文件也可以是文件夹
1.2 File类的构造方法
File(String pathname)
:根据文件或文件夹的路径创建一个File对象File(String parent, String child)
:根据父路径和子路径创建File对象
如文件路径:D:\iotest\aa.txt
父路径:D:\iotest
子路径:aa.txtFile(File parent, String child)
: 根据父路径和子路径创建File对象
File可以表示计算机中的文件,也可以表示计算机中的文件夹
File表示的文件或文件夹在计算机中可以是存在的,也可以是不存在的
public class Demo02Constructor {
public static void main(String[] args) {
//File(String pathname):根据文件或文件夹的路径创建一个File对象
//在Java中\是特殊的字符,叫做转义字符,表示将后面的字符改变含义。
//如果要表示一个普通的反斜线,需要写两个\\
File file = new File("d:\\iotest\\aa.txt");
System.out.println(file);\\d:\iotest\aa.txt
//File(String parent, String child):根据父路径和子路径创建File对象。
//创建File对象,表示D:\iotest\aa.txt
File file2 = new File("d:\\iotest", "aa.txt");
System.out.println(file2);\\d:\iotest\aa.txt
//File(File parent, String child): 根据父路径和子路径创建File对象。
//创建File对象,表示D:\iotest\aa.txt
File parent = new File("d:\\iotest");
File file3 = new File(parent, "aa.txt");
System.out.println(file3);\\d:\iotest\aa.txt
}
}
1.3 绝对路径和相对路径
- 相对路径:是一个简单的路径,不是从盘符开始的。在idea中,相对路径相对的是当前项目,指的是当前项目下的内容,与module同级。 比如:aa.txt
- 绝对路径:是一个详细的路径,是从盘符开始的。比如:D:\iotest\aa.txt
要区分相对路径和绝对路径只看是否从盘符开始。
public class Demo03Path {
public static void main(String[] args) {
//根据相对路径创建File对象,该File对象表示当前项目下的aa.txt
File file = new File("aa.txt");
}
}
1.4 File的获取方法
String getAbsolutePath()
:获取File对象所表示的文件或文件夹的绝对路径String getPath()
:获取路径(如果创建File对象时传递的是相对路径,获取到的就是相对路径。如果传递的是绝对路径,那么获取到的就是绝对路径)String getName()
:获取文件或文件夹的名字long length()
:获取文件的字节数大小。只能对文件使用,如果File对象表示的是文件夹,调用length方法,结果是不确定的
public class Demo04GetMethod {
public static void main(String[] args) {
method4();
}
/*
long length():获取文件的字节数大小
*/
public static void method4() {
File file = new File("d:\\iotest\\aa.txt");
//获取文件的字节数大小并输出
System.out.println(file.length());
}
/*
String getName():获取文件或文件夹的名字
*/
public static void method3() {
//创建File对象
File file = new File("d:\\iotest\\zz.txt");
//获取文件名
System.out.println(file.getName());
}
/*
String getPath():获取路径。
如果创建File对象时传递的是相对路径,获取到的就是相对路径。如果传递的是绝对路径,那么获取到的就是绝对路径。
*/
public static void method2() {
//创建File对象
//File file = new File("d:\\iotest\\aa.txt");
File file = new File("aa.txt");
//获取路径
System.out.println(file.getPath());//aa.txt
}
/*
String getAbsolutePath():获取File对象所表示的文件或文件夹的绝对路径。
*/
public static void method() {
//创建File对象
//File file = new File("d:\\iotest\\aa.txt");
File file = new File("aa.txt");
//获取绝对路径
System.out.println(file.getAbsolutePath());
}
}
1.5 File类的判断方法
boolean exists()
:判断File对象表示的文件或文件夹是否存在boolean isFile()
:判断File对象表示的是否是文件boolean isDirectory()
:判断File对象表示的是否是文件夹
public class Demo05PanDuanMethod {
public static void main(String[] args) {
//创建File对象
File file = new File("d:\\iotest\\bb");
//boolean exists():判断File对象表示的文件或文件夹是否存在。
System.out.println("是否存在:" + file.exists());
//boolean isFile():判断File对象表示的是否是文件。
System.out.println("是否是文件:" + file.isFile());
//boolean isDirectory():判断File对象表示的是否是文件夹。
System.out.println("是否是文件夹:" + file.isDirectory());
}
}
1.6 File类的创建功能
- boolean createNewFile():创建文件,如果文件已经存在,那么创建失败(创建文件时,如果上级目录不存在,那么会报错)
- boolean mkdir():创建文件夹,如果文件夹已经存在,那么创建失败
- boolean mkdirs():创建文件夹,如果文件夹已经存在,那么创建失败
mkdir和mkdirs的区别
- 在调用mkdir方法创建文件夹时,如果上级目录不存在,那么创建失败,但是不会报错。
- 在调用mkdirs方法创建文件夹时,如果上级目录不存在,那么同时会创建上级目录
public class Demo06CreateMethod {
public static void main(String[] args) throws IOException {
method3();
}
/*
boolean mkdirs():创建文件夹,如果文件夹已经存在,那么创建失败。
在调用mkdirs方法创建文件夹时,如果上级目录不存在,那么同时会创建上级目录。
*/
public static void method3() {
//创建File对象
File file = new File("d:\\iotest2\\cc");
//创建文件夹
boolean flag = file.mkdirs();
System.out.println("flag:" + flag);
}
/*
boolean mkdir():创建文件夹,如果文件夹已经存在,那么创建失败。
在调用mkdir方法创建文件夹时,如果上级目录不存在,那么创建失败,但是不会报错。
*/
public static void method2() {
//创建File对象
File file = new File("d:\\iotest2\\bb");
//创建文件夹
boolean flag = file.mkdir();
System.out.println("flag:" + flag);
}
/*
boolean createNewFile():创建文件,如果文件已经存在,那么创建失败。
创建文件时,如果上级目录不存在,那么会报错。
*/
public static void method() throws IOException {
//创建File对象
File file = new File("d:\\iotest\\aa.txt");
//新建文件
boolean flag = file.createNewFile();
System.out.println("flag:" + flag);
}
}
1.7 File类的删除功能
boolean delete()
:删除File对象表示的文件或文件夹
使用delete方法删除的文件夹必须是一个空文件夹,如果文件夹中有内容,是无法删除的。
【使用代码删除的内容不走回收站】
public class Demo07DeleteMethod {
public static void main(String[] args) {
//创建File对象
File file = new File("D:\\develop");
//调用方法删除
boolean flag = file.delete();
System.out.println(flag);
}
}
1.8 File类的遍历方法
String[] list()
:获取指定目录下所有文件和文件夹的名字并放入到字符串数组中返回File[] listFiles()
:获取指定目录下所有的文件和文件夹并放入到File数组中返回
注意:
- 如果File对象表示的是一个文件,调用listFiles结果是null值。
- 如果File对象表示的是不存在的内容,调用listFiles结果是null值。
在调用listFiles之前一定要确定File对象表示的是已经存在的文件夹
public class Demo08ListMethod {
public static void main(String[] args) {
method3();
}
public static void method3() {
//创建File对象
File file = new File("d:\\iotest\\zz");
//调用listFiles获取内容
File[] files = file.listFiles();
System.out.println(files);
}
/*
File[] listFiles():获取指定目录下所有的文件和文件夹并放入到File数组中返回。
*/
public static void method2() {
//创建File对象
File file = new File("d:\\aaa");
//创建list方法,获取aaa中所有的文件和文件夹
File[] files = file.listFiles();
//遍历数组。
for (File thisFile : files) {
System.out.println(thisFile);
}
}
/*
String[] list():获取指定目录下所有文件和文件夹的名字并放入到字符串数组中返回。
*/
public static void method() {
//创建File对象
File file = new File("d:\\aaa");
//调用list方法,获取aaa文件夹中的所有的文件和文件夹
String[] strArr = file.list();
//遍历数组,输出里面的每一个元素
for (String s : strArr) {
System.out.println(s);
}
}
}
二、递归
递归指的是方法调用方法自己
注意:
- 1.递归必有要有出口(结束条件)
- 2.递归次数不能太多(会使栈内存溢出)
递归适用于未知层级的场景。比如遍历文件夹。
2.1 递归遍历文件夹
步骤:
- 1.定义一个方法,该方法用来遍历指定的文件夹
- 2.调用listFiles方法,获取该文件夹下所有的文件和文件夹放入到File数组中保存
- 3.遍历File数组,拿到里面的每一个文件和文件夹
- 4.判断如果遍历到的是文件夹,那么就应该继续遍历该文件夹,输出该文件夹下所有文件的名字;如果遍历到的的是文件,那么直接获取文件名然后输出
public class Demo03PrintDirTest {
public static void main(String[] args) {
//调用printDir,遍历指定文件夹,输出该文件夹下所有文件的名字
printDir(new File("d:\\test"));
}
/*
定义一个方法,该方法用来遍历指定的文件夹
*/
public static void printDir(File dir) {
//调用listFiles方法,获取该文件夹下所有的文件和文件夹放入到File数组中保存
File[] files = dir.listFiles();
//遍历File数组,拿到里面的每一个文件和文件夹。
for (File thisFile : files) {
//判断如果遍历到的是文件夹
if (thisFile.isDirectory()) {
//如果是文件夹,那么就应该继续遍历该文件夹,输出该文件夹下所有文件的名字
//继续递归调用自己,继续遍历
printDir(thisFile);
} else {
//如果遍历到的的是文件,那么直接获取文件名然后输出
System.out.println(thisFile.getName());
}
}
}
}
2.2 过滤器的使用
File[] listFiles(FileFilter filter)
:获取目录下所有的文件和文件夹,并根据过滤器过滤掉不符合规则的内容。
FileFilter表示过滤器,里面的方法
boolean accept(File pathname)
:参数表示指定目录下的每个文件和文件夹。返回值如果为true,表示该文件留下。
listFiles方法参数是FileFilter函数式接口,所以可以传递Lambda表达式,该Lambda表达式表示FileFilter接口中的唯一的抽象方法accept的内容。
public class Demo01Test {
public static void main(String[] args) {
printJavaFile(new File("D:\\aaa"));
}
public static void printJavaFile(File dir) {
File[] files = dir.listFiles(pathname -> {
//以.java结尾就保留
if (pathname.getName().endsWith(".java")) {
return true;
}
//如果是文件夹也保留,继续下一步遍历
if (pathname.isDirectory()) {
return true;
}
return false;
});
for (File thisFile : files) {
if (thisFile.isDirectory()) {
printJavaFile(thisFile);
} else {
System.out.println(thisFile.getAbsolutePath());
}
}
}
}
三、IO流
3.1 初识IO流
3.2 字节流
计算机中所有的数据都是二进制【0和1】,8个二进制组成一个字节。
计算机中最小的存储单位就是字节,计算机中所有的文件内部都是以字节为单位保存。
3.2.1 字节输出流
OutputStream
是字节输出流,用来写,可以将Java程序中的数据写到文件中。
OutputStream
可以以字节为单位写数据OutputStream
是所有字节输出流的顶层父类,他是抽象类,如果要用,需要使用子类,最常用的子类是FileOutputStream
FileOutputStream构造方法:
FileOutputStream(String name)
:参数要传递一个字符串类型的文件路径,表示向这个文件中写数据。FileOutputStream(File file)
:参数要传递一个File类型的文件,表示向这个文件中写数据
FileOutputStream其他方法:
void write(int b)
:向文件中写一个字节void write(byte[] b)
:向文件中写一个字节数组void write(byte[] b, int off, int len)
:向文件中写字节数组的一部分。参数off表示从哪个位置开始写,参数len表示写几个void close()
:关闭流(释放资源)
字节输出流的使用步骤:
- 1.创建字节输出流,并绑定一个目的地文件
- 2.调用write方法写数据
- 3.调用close方法,关闭流
在ASCII码表上的内容在计算机中都是占一个字节的。
在文件中汉字是占用多个字节的。
- 如果采用的是GBK编码,汉字占2个字节
- 如果采用的是UTF-8的编码,汉字占3个字节
汉字因为在计算机中占多个字节,所以无法使用write方法写一个字节的方式向文件中写中文。
/*
字节输出流一次写一个字节
*/
public class Demo01OutputStream {
public static void main(String[] args) throws IOException {
//1. 创建字节输出流,并绑定一个目的地文件。
/*
下面代码做了哪些事情
a. 创建了字节输出流对象。
b. 调用系统资源,创建aa.txt文件。如果文件已经存在,会覆盖掉原来的文件。
c. 将字节输出流和文件绑定,以后通过这个字节输出流操作的就都是这个文件了。
*/
OutputStream os = new FileOutputStream("d:\\aa.txt");
//2. 调用write方法写数据。
//void write(int b):向文件中写一个字节
os.write(100);//会查询ASCII码表,写入对应的字符
//os.write('a');//a
//os.write('中');//乱码
//3. 调用close方法,关闭流。
//关流可以解决文件被占用的状态
os.close();
//如果不关流,那么程序不结束的话会导致文件一直处于被占用的状态。
}
}
/*
字节输出流一次写一个字节数组
*/
public class Demo03OutputStream {
public static void main(String[] args) throws IOException {
//创建字节输出流对象。
OutputStream os = new FileOutputStream("iotest\\file01.txt");
//调用write方法写数据。
//void write(byte[] b):向文件中写一个字节数组
byte[] bArr = "hello".getBytes();
//byte[] bArr = "中文".getBytes();
os.write(bArr);
//void write(byte[] b, int off, int len):向文件中写字节数组的一部分。参数off表示从哪个位置开始写,参数len表示写几个。
os.write("abcde".getBytes(), 1, 3);
//调用close方法使用资源
os.close();
}
}
3.2.1.1 文件续写
我们之前使用的FileOutputStream构造方法创建对象会新建文件,并覆盖掉原来的文件。
如果不想覆盖掉原来的文件,而是要在原来的文件后面继续写内容,那么可以使用下面的构造方法完成。
FileOutputStream(String name, boolean append)
:第二个参数表示是否续写,如果为true就表示续写,就不会覆盖掉原来的文件了FileOutputStream(File file, boolean append)
:第二个参数表示是否续写,如果为true就表示续写,就不会覆盖掉原来的文件了
public class Demo04AppendWrite {
public static void main(String[] args) throws IOException {
//创建字节输出流对象
OutputStream os = new FileOutputStream("iotest\\file02.txt", true);//true表示要对文件续写,此时就不会覆盖原来的文件了
//调用write方法写数据
os.write("举头望明月 ".getBytes());
//释放资源
os.close();
}
}
3.2.1.2 文件换行
如果要向文件中写换行,需要使用换行符。
- windows:
\r\n
- linux:
\n
最近win10更新后,也识别\n
作为换行符。
public class Demo05WriteLine {
public static void main(String[] args) throws IOException {
//创建字节输出流对象
OutputStream os = new FileOutputStream("day10\\file03.txt");
//调用write方法,写两句诗
os.write("床前明月光\r\n".getBytes());
os.write("疑是地上霜".getBytes());
//释放资源
os.close();
}
}
3.2.2 字节输入流
InputStream
是字节输入流,用来读,可以将文件中的数据读取到Java程序中。
InputStream
是字节输入流,所以会以字节为单位进行读取InputStream
是所有字节输入流的顶层父类,是一个抽象类,如果要用,需要使用子类,最常用的子类是FileInputStream
FileInputStream构造方法:
FileInputStream(String name)
:根据字符串的文件路径创建字节输入流对象。以后通过这个字节输入流都是从该文件中读取数据FileInputStream(File file)
:根据File对象创建一个字节输入流对象
FileInputStream其他方法:
int read()
:从文件中读取一个字节并返回;如果已经读取结束了,返回-1int read(byte[] b)
:从文件中读取数据放到字节数组中,返回读取到的字节个数;如果已经读取结束了,返回-1void close()
:释放资源,关闭流
字节输入流的使用步骤:
- 1.创建字节输入流对象,绑定数据源文件
- 2.调用read方法,从文件中读取数据
- 3.调用close方法,释放资源
中文在文件中是占用多个字节的,我们不能使用一次读取一个字节的方式读取中文。
/*
字节输入流一次读一个字节
*/
public class Demo01InputStream {
public static void main(String[] args) throws IOException {
//创建字节输入流对象,绑定数据源文件。
//创建字节输入流时,如果要读取的文件不存在,那么会报错
InputStream is = new FileInputStream("iotest\\source01.txt");
//调用read方法,从文件中读取数据。
//定义变量i,用来保存读取到的字节
int i;
//开始循环读取
/*
条件位置做了哪些事情:
1. 通过输入流调用read方法读取了一个字节。
2. 将读取到的字节赋值给了变量i
3. 判断变量i是否不等于-1,如果i不等于-1,那么表示读取到了内容,那么就可以对读取到的内容进行处理.
*/
while ((i = is.read()) != -1) {
System.out.print((char)i);
}
}
}
/*
字节输入流一次读一个字节数组
*/
public class Demo02InputStream {
public static void main(String[] args) throws IOException {
//1. 创建流
InputStream is = new FileInputStream("iotest\\source02.txt");
//2. 读取数据
//使用循环进行读取
//定义字节数组,用来保存读取到的字节
byte[] bArr = new byte[3];//数组长度一般是1024的整数倍。 综合效率最高的是1024 * 8
//定义变量len,用来接收读取到的字节个数
int len;
//开始循环
/*
条件位置做了哪些事情
a. 通过输入流调用了read方法将数据读取到了bArr字节数组中
b. read方法的返回值是读取到的字节个数,将这个返回值赋值给了变量len
c. 判断len是否不等于-1,如果len不是-1,那么表示读取到了内容,那么就进行处理。
读取到的内容保存在了bArr字节数组中,len中保存的是读取到的字节个数
*/
while ((len = is.read(bArr)) != -1) {
//如果条件成立就表示读取到了数据,就进行处理【将数组转成字符串,读取到几个,就转几个】
System.out.print(new String(bArr, 0, len));
}
}
}
3.2.3 文件复制
文件复制本质就是读和写,从源文件中读取字节,然后写到目标文件。
步骤:
- 1.创建字节输入流,用来读取
- 2.创建字节输出流,用来写
- 3.使用循环读取,将数据读取到字节数组中,然后将读取到的数据写到目的地文件
- 4.释放资源
public class Demo03CopyFile {
public static void main(String[] args) throws IOException {
//1. 创建字节输入流,用来读取。
InputStream is = new FileInputStream("d:\\aa.jpg");
//2. 创建字节输出流,用来写。
OutputStream os = new FileOutputStream("d:\\bb.jpg");
//3. 使用循环读取,将数据读取到字节数组中,然后将读取到的数据写到目的地文件。
//定义数组,用来保存读取到的数据
byte[] bArr = new byte[1024];
//定义变量表示读取到的字节个数
int len;
//开始循环
while ((len = is.read(bArr)) != -1) {
//如果条件成立,表示读取到了数据,那么就将读取到的数据写到目的地文件。
os.write(bArr, 0, len);
}
//4. 释放资源,先开的后关
os.close();
is.close();
}
}
3.3 字符流
如果使用字节流读取中文数据,会有乱码。因为字节流会以字节为单位进行读取,有可能将中文拆开读取,会产生乱码。
字符流可以以字符为单位进行读取,可以解决读取中文的乱码问题。
3.3.1 字符输入流
Reader
是字符输入流,用来读取,可以将文件中的数据读取到Java程序中。
Reader
是字符输入流,会以字符为单位进行读取Reader
是所有字符输入流的顶层父类,是一个抽象类,如果要用需要使用子类,常用的子类FileReader
FileReader构造方法:
FileReader(String fileName)
:参数是字符串类型的文件路径,表示向该文件中写数据。FileReader(File file)
:参数是File类型的文件,表示向该文件中写数据
FileReader其他方法:
int read()
:读取一个字符并返回,如果已经读取结束了,返回-1(读取仍是一个)int read(char[] cbuf)
:将数据读取到字符数组中,并返回读取到的字符个数,如果已经读取结束了,返回-1
字符输入流的使用步骤:
- 创建字符输入流
- 调用read方法读取
- 释放资源
/*
字符输入流读取一个字符
*/
public class Demo01Reader {
public static void main(String[] args) throws IOException {
//1. 创建字符输入流。
Reader r = new FileReader("iotest\\source03.txt");
//2. 调用read方法读取。(一次读一个字符)
//定义变量i用来接收读取到的字符
int i;
//开始循环
while ((i = r.read()) != -1) {
System.out.print((char)i);
}
//3.释放资源
r.close();
}
}
/*
字符输入流读取一个字符数组
*/
public class Demo02Reader {
public static void main(String[] args) throws IOException {
//创建字符输入流
Reader r = new FileReader("iotest\\source03.txt");
//开始读取
//定义字符数组,用来保存读取到的字符数据。
char[] cArr = new char[1024];
//定义变量len,用来接收读取到的字符个数
int len;
//开始循环
while ((len = r.read(cArr)) != -1) {
//条件成立就表示读取到了数据,那么就进行处理。读取到的数据在字符数组cArr中保存,我们将cArr字符数组转成字符串,读到几个就转几个
System.out.print(new String(cArr, 0, len));
}
//释放资源
r.close();
}
}
3.3.2 字符输出流
Writer是字符输出流,用来写,可以将Java程序中的数据写到文件中。
Writer
是字符流,会以字符为单位写数据Writer
是所有字符输出流的顶层父类,是一个抽象类,如果要用,需要使用子类,常用的子类FileWriter
FileWriter构造方法:
FileWriter(String fileName)
:参数要传递字符串类型的文件路径,表示向该文件中写数据FileWriter(File file)
:参数要传递File类型的文件,表示向该文件中写数据
FileWriter其他方法:
void write(String str)
:向文件中写字符串void write(String str, int off, int len)
:向文件中写字符串的一部分。从off位置开始写,写len个void write(int c)
:向文件中写一个字符void write(char[] cbuf)
: 向文件中写字符数组void write(char[] cbuf, int off, int len)
:向文件中写字符数组的一部分。 参数off表示从哪个位置写,参数len表示写几个void flush()
:刷新,将内存缓冲区中的数据刷新的文件中。void close()
:关闭流
字符输出流的使用步骤:
- 创建字符输出流
- 调用write方法写数据
- 刷新
- 关流
只有字符输出流必须要刷新,数据放在内存缓冲区,刷新之后才会写入文件
- flush方法:刷新。 流在刷新之后还可以使用(写入大量数据最好写一段就刷新一次)
- close方法:先刷新,然后关流。 流在关闭之后就不能使用了
public class Demo03Writer {
public static void main(String[] args) throws IOException {
//创建字符输出流对象
Writer w = new FileWriter("day10\\file06.txt");
//void write(String str):向文件中写字符串
//w.write("hello");
//void write(String str, int off, int len) :向文件中写字符串的一部分。从off位置开始写,写len个。
//w.write("hello", 1, 3);
//void write(int c):向文件中写一个字符
//w.write(100);//d 查询ASCII码表,写入对应的字符
//w.write('a');
//w.write('中');
//void write(char[] cbuf): 向文件中写字符数组
char[] cArr = {'h', 'e', 'l', 'l', 'o'};
//w.write(cArr);
//void write(char[] cbuf, int off, int len):向文件中写字符数组的一部分。 参数off表示从哪个位置写,参数len表示写几个。
w.write(cArr, 1, 3);
//刷新
w.flush();
//释放资源
w.close();
}
}
3.4 应用小结
字节流适合处理非文本类型的数据,如图片,视频等;
字符流适合处理文本类型的数据,如txt文档等。