1 File类
1.1 概述
java.io.File 类是⽂件和⽬录路径名的抽象表示,主要⽤于⽂件和⽬录的创建、查找和删除等操作。
1.2 构造⽅法
public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File 实例。
public File(String parent, String child) :从⽗路径名字符串和⼦路径名字符串创建新的 File 实例。
public File(File parent, String child) :从⽗抽象路径名和⼦路径名字符串创建新的File 实例。
构造举例,代码如下:
// ⽂件路径名
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname);
// ⽂件路径名
String pathname2 = "D:\\aaa\\bbb.txt";
File file2 = new File(pathname2);
// 通过⽗路径和⼦路径字符串
String parent = "d:\\aaa";
String child = "bbb.txt";
File file3 = new File(parent, child);
// 通过⽗级File对象和⼦路径字符串
File parentDir = new File("d:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child);
⼩贴⼠:
- ⼀个File对象代表硬盘中实际存在的⼀个⽂件或者⽬录。
- ⽆论该路径下是否存在⽂件或者⽬录,都不影响 File 对象的创建。
1.3 常⽤⽅法
获取功能的⽅法
public String getAbsolutePath() :返回此File的绝对路径名字符串。
public String getPath() :将此File转换为路径名字符串。
public String getName() :返回由此File表示的⽂件或⽬录的名称。
public long length() :返回由此File表示的⽂件的⻓度。
⽅法演示,代码如下:
public class FileGet {
public static void main(String[] args) {
File f = new File("d:/aaa/bbb.java");
System.out.println("⽂件绝对路径:" + f.getAbsolutePath());
System.out.println("⽂件构造路径:" + f.getPath());
System.out.println("⽂件名称:" + f.getName());
System.out.println("⽂件⻓度:" + f.length() + "字节");
File f2 = new File("d:/aaa");
System.out.println("⽬录绝对路径:" + f2.getAbsolutePath());
System.out.println("⽬录构造路径:" + f2.getPath());
System.out.println("⽬录名称:" + f2.getName());
System.out.println("⽬录⻓度:" + f2.length());
}
}
输出结果:
⽂件绝对路径:d:\aaa\bbb.java
⽂件构造路径:d:\aaa\bbb.java
⽂件名称:bbb.java
⽂件⻓度:636字节
⽬录绝对路径:d:\aaa
⽬录构造路径:d:\aaa
⽬录名称:aaa
⽬录⻓度:4096
API中说明:length(),表示⽂件的⻓度。但是File对象表示⽬录,则返回值未指定。
绝对路径和相对路径
绝对路径:从盘符开始的路径,这是⼀个完整的路径。
相对路径:相对于项⽬⽬录的路径,这是⼀个便捷的路径,开发中经常使⽤。
public static void main(String[] args) {
// D盘下的bbb.java⽂件
File f = new File("D:\\bbb.java");
System.out.println(f.getAbsolutePath());
// 项⽬下的bbb.java⽂件
File f2 = new File("bbb.java");
System.out.println(f2.getAbsolutePath());
}
输出结果:
D:\bbb.java
D:\idea_project_test4\bbb.java
判断功能的⽅法
public boolean exists() :此File表示的⽂件或⽬录是否实际存在。
public boolean isDirectory() :此File表示的是否为⽬录。
public boolean isFile() :此File表示的是否为⽂件。
⽅法演示,代码如下:
public class FileIs {
public static void main(String[] args) {
File f = new File("d:\\aaa\\bbb.java");
File f2 = new File("d:\\aaa");
// 判断是否存在
System.out.println("d:\\aaa\\bbb.java 是否存在:" + f.exists());
System.out.println("d:\\aaa 是否存在:" + f2.exists());
// 判断是⽂件还是⽬录
System.out.println("d:\\aaa ⽂件?:" + f2.isFile());
System.out.println("d:\\aaa ⽬录?:" + f2.isDirectory());
}
}
输出结果:
d:\aaa\bbb.java 是否存在:true
d:\aaa 是否存在:true
d:\aaa ⽂件?:false
d:\aaa ⽬录?:true
创建删除功能的⽅法
public boolean createNewFile() :当且仅当具有该名称的⽂件尚不存在时,创建⼀个新的空⽂件。
public boolean delete() :删除由此File表示的⽂件或⽬录。
public boolean mkdir() :创建由此File表示的⽬录。
public boolean mkdirs() :创建由此File表示的⽬录,包括任何必需但不存在的⽗⽬录。
⽅法演示,代码如下:
public class FileCreateDelete {
public static void main(String[] args) throws IOException {
// ⽂件的创建
File f = new File("aaa.txt");
System.out.println("是否存在:" + f.exists()); // false
System.out.println("是否创建:" + f.createNewFile()); // true
System.out.println("是否存在:" + f.exists()); // true
// ⽬录的创建
File f2= new File("newDir");
System.out.println("是否存在:" + f2.exists()); // false
System.out.println("是否创建:" + f2.mkdir()); // true
System.out.println("是否存在:" + f2.exists()); // true
// 创建多级⽬录
File f3= new File("newDira\\newDirb");
System.out.println(f3.mkdir()); // false
File f4= new File("newDira\\newDirb");
System.out.println(f4.mkdirs()); // true
// ⽂件的删除
System.out.println(f.delete()); // true
// ⽬录的删除
System.out.println(f2.delete()); // true
System.out.println(f4.delete()); // false
}
}
API中说明:delete⽅法,如果此File表示⽬录,则⽬录必须为空才能删除。
1.4 ⽬录的遍历
public String[] list() :返回⼀个String数组,表示该File⽬录中的所有⼦⽂件或⽬录。
public File[] listFiles() :返回⼀个File数组,表示该File⽬录中的所有的⼦⽂件或⽬录。
public class FileFor {
public static void main(String[] args) {
File dir = new File("d:\\java_code");
// 获取当前⽬录下的⽂件以及⽂件夹的名称。
String[] names = dir.list();
for (String name : names) {
System.out.println(name);
}
// 获取当前⽬录下的⽂件以及⽂件夹对象,只要拿到了⽂件对象,那么就可以获取更多
信息
File[] files = dir.listFiles();
for (File file : files) {
System.out.println(file);
}
}
}
⼩贴⼠: 调⽤listFiles⽅法的File对象,表示的必须是实际存在的⽬录,否则返回null,⽆法进⾏遍历。
2 递归
2.1 概述
递归:指在当前⽅法内调⽤⾃⼰的这种现象。
递归的分类:
递归分为两种,直接递归和间接递归。
直接递归称为⽅法⾃身调⽤⾃⼰。
间接递归可以A⽅法调⽤B⽅法,B⽅法调⽤C⽅法,C⽅法调⽤A⽅法。
注意事项:
递归⼀定要有条件限定,保证递归能够停⽌下来,否则会发⽣栈内存溢出。
在递归中虽然有限定条件,但是递归次数不能太多。否则也会发⽣栈内存溢出。
构造⽅法,禁⽌递归
public class Demo01DiGui {
public static void main(String[] args) {
// a();
b(1);
}
/*
* 3.构造⽅法,禁⽌递归
* 编译报错:构造⽅法是创建对象使⽤的,不能让对象⼀直创建下去
*/
public Demo01DiGui() {
// Demo01DiGui();
}
/*
* 2.在递归中虽然有限定条件,但是递归次数不能太多。否则也会发⽣栈内存溢出。
* 4993
* Exception in thread "main" java.lang.StackOverflowError
*/
private static void b(int i) {
System.out.println(i);
//添加⼀个递归结束的条件,i==5000的时候结束
if (i == 5000) {
return; // 结束⽅法
}
b(++i);
}
/*
* 1.递归⼀定要有条件限定,保证递归能够停⽌下来,否则会发⽣栈内存溢出。
Exception in thread "main"
* java.lang.StackOverflowError
*/
private static void a() {
System.out.println("a⽅法");
a();
} }
3 综合案例
3.1 ⽂件搜索
搜索 D:\aaa ⽬录中的 .java ⽂件。
分析:
- ⽬录搜索,⽆法判断多少级⽬录,所以使⽤递归,遍历所有⽬录。
- 遍历⽬录时,获取的⼦⽂件,通过⽂件名称,判断是否符合条件。
代码实现:
public class DiGuiDemo3 {
public static void main(String[] args) {
// 创建File对象
File dir = new File("D:\\aaa");
// 调⽤打印⽬录⽅法
printDir(dir);
}
public static void printDir(File dir) {
// 获取⼦⽂件和⽬录
File[] files = dir.listFiles();
// 循环打印
for (File file : files) {
if (file.isFile()) {
// 是⽂件,判断⽂件名并输出⽂件绝对路径
if (file.getName().endsWith(".java")) {
System.out.println("⽂件名:" + file.getAbsolutePath());
}
} else {
// 是⽬录,继续遍历,形成递归
printDir(file);
}
}
}
}
4 IO概述
4.1 什么是IO
⽣活中,你肯定经历过这样的场景。当你编辑⼀个⽂本⽂件,忘记了 ctrl+s ,可能⽂件就⽩⽩编辑了。当你电脑上插⼊⼀个U盘,可以把⼀个视频,拷⻉到你的电脑硬盘⾥。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。
我们把这种数据的传输,可以看做是⼀种数据的流动,按照流动的⽅向,以内存为基准,分为 输 ⼊input 和 输出output ,即流向内存是输⼊流,流出内存的输出流。
Java中I/O操作主要是指使⽤ java.io 包下的内容,进⾏输⼊、输出操作。输⼊也叫做读取数据,输出也叫做作写出数据。
4.2 IO的分类
根据数据的流向分为:输⼊流和输出流。
输⼊流 :把数据从 其他设备 上读取到 内存 中的流。
输出流 :把数据从 内存 中写出到 其他设备 上的流。
格局数据的类型分为:字节流和字符流。
字节流 :以字节为单位,读写数据的流。
字符流 :以字符为单位,读写数据的流。
5 字节流
5.1 ⼀切皆为字节
⼀切⽂件数据(⽂本、图⽚、视频等)在存储时,都是以⼆进制数字的形式保存,都⼀个⼀个的字节,那么传输时⼀样如此。所以,字节流可以传输任意⽂件数据。
在操作流的时候,我们要时刻明确,⽆论使⽤什么样的流对象,底层传输的始终为⼆进制数据。
5.2 字节输出流【OutputStream】
java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到⽬的地。它定义了字节输出流的基本共性功能⽅法。
public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
public void write(byte[] b) :将 b.length字节从指定的字节数组写⼊此输出流。
public void write(byte[] b, int off, int len) :从指定的字节数组写⼊ len 字节,从偏移量 off 开始输出到此输出流。
public abstract void write(int b) :将指定的字节输出流。
⼩贴⼠: close⽅法,当完成流的操作时,必须调⽤此⽅法,释放系统资源。
5.3 FileOutputStream类
OutputStream 有很多⼦类,我们从最简单的⼀个⼦类开始。
java.io.FileOutputStream 类是⽂件输出流,⽤于将数据写出到⽂件。
构造⽅法
public FileOutputStream(File file) :创建⽂件输出流以写⼊由指定的 File对象表示的⽂件。
public FileOutputStream(String name) :创建⽂件输出流以指定的名称写⼊⽂件。
当你创建⼀个流对象时,必须传⼊⼀个⽂件路径。该路径下,如果没有这个⽂件,会创建该⽂件。如果有这个⽂件,会清空这个⽂件的数据。
构造举例,代码如下:
public class FileOutputStreamConstructor throws IOException {
public static void main(String[] args) {
// 使⽤File对象创建流对象
File file = new File("a.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使⽤⽂件名称创建流对象
FileOutputStream fos = new FileOutputStream("b.txt");
}
}
写出字节数据
- 写出字节: write(int b) ⽅法,每次可以写出⼀个字节数据,代码使⽤演示:
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使⽤⽂件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 写出数据
fos.write(97); // 写出第1个字节
fos.write(98); // 写出第2个字节
fos.write(99); // 写出第3个字节
// 关闭资源
fos.close();
}
}
输出结果:
abc
⼩贴⼠:
- 虽然参数为int类型四个字节,但是只会保留⼀个字节的信息写出。
- 流操作完毕后,必须释放系统资源,调⽤close⽅法,千万记得。
- 写出字节数组: write(byte[] b) ,每次可以写出数组中的数据,代码使⽤演示:
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使⽤⽂件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "Java程序员".getBytes();
// 写出字节数组数据
fos.write(b);
// 关闭资源
fos.close();
}
}
输出结果:
Java程序员
- 写出指定⻓度字节数组: write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节,代码使⽤演示:
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使⽤⽂件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b, 2, 2);
// 关闭资源
fos.close();
}
}
输出结果:
cd
数据追加续写
经过以上的演示,每次程序运⾏,创建输出流对象,都会清空⽬标⽂件中的数据。如何保留⽬标⽂件中数据,还能继续添加新数据呢?
public FileOutputStream(File file, boolean append) : 创建⽂件输出流以写⼊由指定的 File对象表示的⽂件。
public FileOutputStream(String name, boolean append) :创建⽂件输出流以指定的名称写⼊⽂件。
这两个构造⽅法,参数中都需要传⼊⼀个boolean类型的值, true 表示追加数据, false 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,代码使⽤演示:
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使⽤⽂件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt",true);
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b);
// 关闭资源
fos.close();
}
}
⽂件操作前:cd
⽂件操作后:cdabcde
5.4 字节输⼊流【InputStream】
java.io.InputStream 抽象类是表示字节输⼊流的所有类的超类,可以读取字节信息到内存中。
它定义了字节输⼊流的基本共性功能⽅法。
public void close() :关闭此输⼊流并释放与此流相关联的任何系统资源。
public abstract int read() :从输⼊流读取数据的下⼀个字节。
public int read(byte[] b) :从输⼊流中读取⼀些字节数,并将它们存储到字节数组 b中 。
⼩贴⼠: close⽅法,当完成流的操作时,必须调⽤此⽅法,释放系统资源。
5.5 FileInputStream类
java.io.FileInputStream 类是⽂件输⼊流,从⽂件中读取字节。
构造⽅法
FileInputStream(File file) :通过打开与实际⽂件的连接来创建⼀个 FileInputStream,该⽂件由⽂件系统中的 File对象 file命名。
FileInputStream(String name) :通过打开与实际⽂件的连接来创建⼀个FileInputStream ,该⽂件由⽂件系统中的路径名 name命名。
当你创建⼀个流对象时,必须传⼊⼀个⽂件路径。该路径下,如果没有该⽂件,会抛出FileNotFoundException 。
构造举例,代码如下:
public class FileInputStreamConstructor throws IOException{
public static void main(String[] args) {
// 使⽤File对象创建流对象
File file = new File("a.txt");
FileInputStream fos = new FileInputStream(file);
// 使⽤⽂件名称创建流对象
FileInputStream fos = new FileInputStream("b.txt");
}
}
读取字节数据
- 读取字节: read ⽅法,每次可以读取⼀个字节的数据,提升为int类型,读取到⽂件末尾,返回 -1 ,代码使⽤演示:
循环改进读取⽅式,代码使⽤演示:
public class FISRead {
public static void main(String[] args) throws IOException{
// 使⽤⽂件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 定义变量,保存数据
int b ;
// 循环读取
while ((b = fis.read()) != -1) {
System.out.println((char)b);
}
// 关闭资源
fis.close();
}
}
输出结果:
abcde
⼩贴⼠:
- 虽然读取了⼀个字节,但是会⾃动提升为int类型。
- 流操作完毕后,必须释放系统资源,调⽤close⽅法,千万记得。
- 使⽤字节数组读取: read(byte[] b) ,每次读取b的⻓度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回 -1 ,代码使⽤演示:
public class FISRead {
public static void main(String[] args) throws IOException {
// 使⽤⽂件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // ⽂件中为abcde
// 定义变量,作为有效个数
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b)) != -1) {
// 每次读取后,把数组变成字符串打印
System.out.println(new String(b));
}
// 关闭资源
fis.close();
}
}
输出结果:
ab
cd
ed
错误数据 d ,是由于最后⼀次读取时,只读取⼀个字节 e ,数组中,上次读取的数据没有被完全替换,所以要通过 len ,获取有效的字节,代码使⽤演示:
public class FISRead {
public static void main(String[] args) throws IOException{
// 使⽤⽂件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // ⽂件中为abcde
// 定义变量,作为有效个数
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b)) != -1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(b, 0, len));// len 每次读取的有效字节个数
}
// 关闭资源
fis.close();
}
}
输出结果:
ab
cd
e
⼩贴⼠: 使⽤数组读取,每次读取多个字节,减少了系统间的IO操作次数,从⽽提⾼了读写的效率,建议开发中使⽤。
5.6 字节流练习:图⽚复制
复制原理图解
public class Copy {
public static void main(String[] args) throws IOException {
// 1.创建流对象
// 1.1 指定数据源
FileInputStream fis = new FileInputStream("D:\\test.jpg");
// 1.2 指定⽬的地
FileOutputStream fos = new FileOutputStream("test_copy.jpg");
// 2.读写数据
// 2.1 定义数组
byte[] b = new byte[1024];
// 2.2 定义⻓度
int len;
// 2.3 循环读取
while ((len = fis.read(b)) != -1) {
// 2.4 写出数据
fos.write(b, 0, len);
}
// 3.关闭资源
fos.close();
fis.close();
}
}
⼩贴⼠: 流的关闭原则:先开后关,后开先关。
6 字符流
当使⽤字节流读取⽂本⽂件时,可能会有⼀个⼩问题。就是遇到中⽂字符时,可能不会显示完整的字符,那是因为⼀个中⽂字符可能占⽤多个字节存储。
所以 Java 提供⼀些字符流类,以字符为单位读写数据,专⻔⽤于处理⽂本⽂件。
6.1 字符输⼊流【Reader】
java.io.Reader 抽象类是表示⽤于读取字符流的所有类的超类,可以读取字符信息到内存中。
它定义了字符输⼊流的基本共性功能⽅法。
public void close() :关闭此流并释放与此流相关联的任何系统资源。
public int read() :从输⼊流读取⼀个字符。
public int read(char[] cbuf) :从输⼊流中读取⼀些字符,并将它们存储到字符数组cbuf 中 。
6.2 FileReader类
java.io.FileReader 类是读取字符⽂件的便利类。构造时使⽤系统默认的字符编码和默认字节缓冲区。
⼩贴⼠:
- 字符编码:字节与字符的对应规则。Windows系统的中⽂编码默认是GBK编码表。 idea中UTF-8
- 字节缓冲区:⼀个字节数组,⽤来临时存储字节数据。
构造⽅法
FileReader(File file) :创建⼀个新的 FileReader ,给定要读取的File对象。
FileReader(String fileName) :创建⼀个新的 FileReader ,给定要读取的⽂件的名称。
当你创建⼀个流对象时,必须传⼊⼀个⽂件路径。
类似于FileInputStream。
构造举例,代码如下:
public class FileReaderConstructor throws IOException{
public static void main(String[] args) {
// 使⽤File对象创建流对象
File file = new File("a.txt");
FileReader fr = new FileReader(file);
// 使⽤⽂件名称创建流对象
FileReader fr = new FileReader("b.txt");
}
}
6.3 字符输出流【Writer】
java.io.Writer 抽象类是表示⽤于写出字符流的所有类的超类,将指定的字符信息写出到⽬的地。它定义了字节输出流的基本共性功能⽅法。
void write(int c) :写⼊单个字符。
void write(char[] cbuf) :写⼊字符数组。
abstract void write(char[] cbuf, int off, int len) :写⼊字符数组的某⼀部分,off数组的开始索引,len写的字符个数。
void write(String str) :写⼊字符串。
void write(String str, int off, int len) :写⼊字符串的某⼀部分,off字符串的开始索引,len写的字符个数。
void flush() :刷新该流的缓冲。
void close() :关闭此流,但要先刷新它。
6.4 FileWriter类
java.io.FileWriter 类是写出字符到⽂件的便利类。构造时使⽤系统默认的字符编码和默认字节缓冲区。
构造⽅法
FileWriter(File file) :创建⼀个新的 FileWriter,给定要读取的File对象。
FileWriter(String fileName) :创建⼀个新的 FileWriter,给定要读取的⽂件的名称。
当你创建⼀个流对象时,必须传⼊⼀个⽂件路径,类似FileOutputStream。
构造举例,代码如下:
public class FileWriterConstructor {
public static void main(String[] args) throws IOException {
// 使⽤File对象创建流对象
File file = new File("a.txt");
FileWriter fw = new FileWriter(file);
// 使⽤⽂件名称创建流对象
FileWriter fw = new FileWriter("b.txt");
}
}
关闭和刷新
因为内置缓冲区的原因,如果不关闭输出流,⽆法写出字符到⽂件中。但是关闭的流对象,是⽆法继续写出数据的。如果我们既想写出数据,⼜想继续使⽤流,就需要 flush ⽅法了。
flush :刷新缓冲区,流对象可以继续使⽤。
close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使⽤了。