JavaSE14、⽂件操作、IO流

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);

⼩贴⼠:

  1. ⼀个File对象代表硬盘中实际存在的⼀个⽂件或者⽬录。
  2. ⽆论该路径下是否存在⽂件或者⽬录,都不影响 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 ⽂件。
分析:

  1. ⽬录搜索,⽆法判断多少级⽬录,所以使⽤递归,遍历所有⽬录。
  2. 遍历⽬录时,获取的⼦⽂件,通过⽂件名称,判断是否符合条件。
    代码实现:
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");
 }
}
写出字节数据
  1. 写出字节: 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

⼩贴⼠:

  1. 虽然参数为int类型四个字节,但是只会保留⼀个字节的信息写出。
  2. 流操作完毕后,必须释放系统资源,调⽤close⽅法,千万记得。
  1. 写出字节数组: 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程序员
  1. 写出指定⻓度字节数组: 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");
 }
}

读取字节数据

  1. 读取字节: 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

⼩贴⼠:

  1. 虽然读取了⼀个字节,但是会⾃动提升为int类型。
  2. 流操作完毕后,必须释放系统资源,调⽤close⽅法,千万记得。
  1. 使⽤字节数组读取: 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 类是读取字符⽂件的便利类。构造时使⽤系统默认的字符编码和默认字节缓冲区。

⼩贴⼠:

  1. 字符编码:字节与字符的对应规则。Windows系统的中⽂编码默认是GBK编码表。 idea中UTF-8
  2. 字节缓冲区:⼀个字节数组,⽤来临时存储字节数据。

构造⽅法

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 :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使⽤了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值