1 File类的使用
1.1 概述
-
File类及本章下的各种流,都定义在java.io包下。
-
一个File对象代表硬盘或网络中可能存在的一个文件或者文件目录(俗称文件夹),与平台无关。(体会万事万物皆对象)
-
File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
-
File对象可以作为参数传递给流的构造器。
-
-
想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
1.2 构造器
-
public File(String pathname)
:以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。 -
public File(String parent, String child)
:以parent为父路径,child为子路径创建File对象。 -
public File(File parent, String child)
:根据一个父File对象和子文件路径创建File对象
关于路径:
-
绝对路径:从盘符开始的路径,这是一个完整的路径。
-
相对路径:相对于
项目目录
的路径,这是一个便捷的路径,开发中经常使用。-
IDEA中,main中的文件的相对路径,是相对于"
当前工程
" -
IDEA中,单元测试方法中的文件的相对路径,是相对于"
当前module
"
-
举例:
import java.io.File; public class FileObjectTest { public static void main(String[] args) { // 文件路径名 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 childFile = "bbb.txt"; File file4 = new File(parentDir, childFile); } @Test public void test01() throws IOException{ File f1 = new File("d:\\atguigu\\javase\\HelloIO.java"); //绝对路径 System.out.println("文件/目录的名称:" + f1.getName()); System.out.println("文件/目录的构造路径名:" + f1.getPath()); System.out.println("文件/目录的绝对路径名:" + f1.getAbsolutePath()); System.out.println("文件/目录的父目录名:" + f1.getParent()); } @Test public void test02()throws IOException{ File f2 = new File("/HelloIO.java");//绝对路径,从根路径开始 System.out.println("文件/目录的名称:" + f2.getName()); System.out.println("文件/目录的构造路径名:" + f2.getPath()); System.out.println("文件/目录的绝对路径名:" + f2.getAbsolutePath()); System.out.println("文件/目录的父目录名:" + f2.getParent()); } @Test public void test03() throws IOException { File f3 = new File("HelloIO.java");//相对路径 System.out.println("user.dir =" + System.getProperty("user.dir")); System.out.println("文件/目录的名称:" + f3.getName()); System.out.println("文件/目录的构造路径名:" + f3.getPath()); System.out.println("文件/目录的绝对路径名:" + f3.getAbsolutePath()); System.out.println("文件/目录的父目录名:" + f3.getParent()); } @Test public void test04() throws IOException{ File f5 = new File("HelloIO.java");//相对路径 System.out.println("user.dir =" + System.getProperty("user.dir")); System.out.println("文件/目录的名称:" + f5.getName()); System.out.println("文件/目录的构造路径名:" + f5.getPath()); System.out.println("文件/目录的绝对路径名:" + f5.getAbsolutePath()); System.out.println("文件/目录的父目录名:" + f5.getParent()); } }
注意:
无论该路径下是否存在文件或者目录,都不影响File对象的创建。
window的路径分隔符使用“\”,而Java程序中的“\”表示转义字符,所以在Windows中表示路径,需要用“\”。或者直接使用“/”也可以,Java程序支持将“/”当成平台无关的
路径分隔符
。或者直接使用File.separator常量值表示。比如:File file2 = new File("d:" + File.separator + "atguigu" + File.separator + "info.txt");
当构造路径是绝对路径时,那么getPath和getAbsolutePath结果一样
当构造路径是相对路径时,那么getAbsolutePath的路径 = user.dir的路径 + 构造路径
1.3 常用方法
1.3.1 获取文件和目录基本信息
-
public String getName() :获取名称
-
public String getPath() :获取路径
-
public String getAbsolutePath()
:获取绝对路径 -
public File getAbsoluteFile():获取绝对路径表示的文件
-
public String getParent()
:获取上层文件目录路径。若无,返回null -
public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
-
public long lastModified() :获取最后一次的修改时间,毫秒值
如果File对象代表的文件或目录存在,则File对象实例初始化时,就会用硬盘中对应文件或目录的属性信息(例如,时间、类型等)为File对象的属性赋值,否则除了路径和名称,File对象的其他属性将会保留默认值。
举例:
import java.io.File; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; public class FileInfoMethod { public static void main(String[] args) { File f = new File("d:/aaa/bbb.txt"); System.out.println("文件构造路径:"+f.getPath()); System.out.println("文件名称:"+f.getName()); System.out.println("文件长度:"+f.length()+"字节"); System.out.println("文件最后修改时间:" + LocalDateTime.ofInstant(Instant.ofEpochMilli(f.lastModified()),ZoneId.of("Asia/Shanghai"))); File f2 = new File("d:/aaa"); System.out.println("目录构造路径:"+f2.getPath()); System.out.println("目录名称:"+f2.getName()); System.out.println("目录长度:"+f2.length()+"字节"); System.out.println("文件最后修改时间:" + LocalDateTime.ofInstant(Instant.ofEpochMilli(f.lastModified()),ZoneId.of("Asia/Shanghai"))); } }
输出结果: 文件构造路径:d:\aaa\bbb.java 文件名称:bbb.java 文件长度:636字节 文件最后修改时间:2022-07-23T22:01:32.065 目录构造路径:d:\aaa 目录名称:aaa 目录长度:4096字节 文件最后修改时间:2022-07-23T22:01:32.065
1.3.2 列出目录的下一级
-
public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
-
public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
import org.junit.Test; import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; public class DirListFiles { @Test public void test01() { File dir = new File("d:/atguigu"); String[] subs = dir.list(); for (String sub : subs) { System.out.println(sub); } } }
1.3.3 File类的重命名功能
-
public boolean renameTo(File dest):把文件重命名为指定的文件路径。
1.3.4 判断功能的方法
-
public boolean exists()
:此File表示的文件或目录是否实际存在。 -
public boolean isDirectory()
:此File表示的是否为目录。 -
public boolean isFile()
:此File表示的是否为文件。 -
public boolean canRead() :判断是否可读
-
public boolean canWrite() :判断是否可写
-
public boolean isHidden() :判断是否隐藏
举例:
package com.atguigu.file; import java.io.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
如果文件或目录不存在,那么exists()、isFile()和isDirectory()都是返回true
1.3.5 创建、删除功能
-
public boolean createNewFile()
:创建文件。若文件存在,则不创建,返回false。 -
public boolean mkdir()
:创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。 -
public boolean mkdirs()
:创建文件目录。如果上层文件目录不存在,一并创建。 -
public boolean delete()
:删除文件或者文件夹 删除注意事项:① Java中的删除不走回收站。② 要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录。
举例:
package com.atguigu.file; import java.io.File; import java.io.IOException; public class FileCreateDelete { public static void main(String[] args) throws IOException { // 文件的创建 File f = new File("aaa.txt"); System.out.println("aaa.txt是否存在:"+f.exists()); System.out.println("aaa.txt是否创建:"+f.createNewFile()); System.out.println("aaa.txt是否存在:"+f.exists()); // 目录的创建 File f2= new File("newDir"); System.out.println("newDir是否存在:"+f2.exists()); System.out.println("newDir是否创建:"+f2.mkdir()); System.out.println("newDir是否存在:"+f2.exists()); // 创建一级目录 File f3= new File("newDira\\newDirb"); System.out.println("newDira\\newDirb创建:" + f3.mkdir()); File f4= new File("newDir\\newDirb"); System.out.println("newDir\\newDirb创建:" + f4.mkdir()); // 创建多级目录 File f5= new File("newDira\\newDirb"); System.out.println("newDira\\newDirb创建:" + f5.mkdirs()); // 文件的删除 System.out.println("aaa.txt删除:" + f.delete()); // 目录的删除 System.out.println("newDir删除:" + f2.delete()); System.out.println("newDir\\newDirb删除:" + f4.delete()); } }
运行结果: aaa.txt是否存在:false aaa.txt是否创建:true aaa.txt是否存在:true newDir是否存在:false newDir是否创建:true newDir是否存在:true newDira\newDirb创建:false newDir\newDirb创建:true newDira\newDirb创建:true aaa.txt删除:true newDir删除:false newDir\newDirb删除:true
API中说明:delete方法,如果此File表示目录,则目录必须为空才能删除。
1.4 练习
练习1:利用File构造器,new 一个文件目录file
1) 在其中创建多个文件和目录
2) 编写方法,实现删除file中指定文件的操作
练习2:判断指定目录下是否有后缀名为.jpg的文件。如果有,就输出该文件名称
public class FindJPGFileTest { //方法1: @Test public void test1(){ File srcFile = new File("d:\\code"); String[] fileNames = srcFile.list(); for(String fileName : fileNames){ if(fileName.endsWith(".jpg")){ System.out.println(fileName); } } } //方法2: @Test public void test2(){ File srcFile = new File("d:\\code"); File[] listFiles = srcFile.listFiles(); for(File file : listFiles){ if(file.getName().endsWith(".jpg")){ System.out.println(file.getAbsolutePath()); } } } //方法3: /* * File类提供了两个文件过滤器方法 * public String[] list(FilenameFilter filter) * public File[] listFiles(FileFilter filter) */ @Test public void test3(){ File srcFile = new File("d:\\code"); File[] subFiles = srcFile.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jpg"); } }); for(File file : subFiles){ System.out.println(file.getAbsolutePath()); } } }
练习3:遍历指定目录所有文件名称,包括子文件目录中的文件。
拓展1:并计算指定目录占用空间的大小
拓展2:删除指定文件目录及其下的所有文件
public class ListFilesTest { //练习3:(方式1) public static void printSubFile(File dir) { // 打印目录的子文件 File[] subfiles = dir.listFiles(); for (File f : subfiles) { if (f.isDirectory()) {// 文件目录 printSubFile(f); } else {// 文件 System.out.println(f.getAbsolutePath()); } } } // //练习3:(方式2) public void listAllSubFiles(File file) { if (file.isFile()) { System.out.println(file); } else { File[] all = file.listFiles(); // 如果all[i]是文件,直接打印 // 如果all[i]是目录,接着再获取它的下一级 for (File f : all) { listAllSubFiles(f);// 递归调用:自己调用自己就叫递归 } } } @Test public void testListAllFiles(){ // 1.创建目录对象 File dir = new File("E:\\teach\\01_javaSE\\_尚硅谷Java编程语言\\3_软件"); // 2.打印目录的子文件 printSubFile(dir); } // 拓展1:求指定目录所在空间的大小 public long getDirectorySize(File file) { // file是文件,那么直接返回file.length() // file是目录,把它的下一级的所有file大小加起来就是它的总大小 long size = 0; if (file.isFile()) { size = file.length(); } else { File[] all = file.listFiles();// 获取file的下一级 // 累加all[i]的大小 for (File f : all) { size += getDirectorySize(f);// f的大小; } } return size; } // 拓展2:删除指定的目录 public void deleteDirectory(File file) { // 如果file是文件,直接delete // 如果file是目录,先把它的下一级干掉,然后删除自己 if (file.isDirectory()) { File[] all = file.listFiles(); // 循环删除的是file的下一级 for (File f : all) {// f代表file的每一个下级 deleteDirectory(f); } } // 删除自己 file.delete(); } }
2. IO流原理及流的分类
2.1 Java IO原理
-
Java程序中,对于数据的输入/输出操作以“
流(stream)
” 的方式进行,可以看做是一种数据的流动。 -
I/O流中的I/O是
Input/Output
的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。-
输入input
:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。 -
输出output
:将程序(内存)数据输出到磁盘、光盘等存储设备中。
-
2.2 流的分类
java.io
包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法
输入或输出数据。
-
按数据的流向不同分为:输入流和输出流。
-
输入流 :把数据从
其他设备
上读取到内存
中的流。-
以InputStream、Reader结尾
-
-
输出流 :把数据从
内存
中写出到其他设备
上的流。-
以OutputStream、Writer结尾
-
-
-
按操作数据单位的不同分为:字节流(8bit)和字符流(16bit)。
-
字节流 :以字节为单位,读写数据的流。
-
以InputStream、OutputStream结尾
-
-
字符流 :以字符为单位,读写数据的流。
-
以Reader、Writer结尾
-
-
-
根据IO流的角色不同分为:节点流和处理流。
-
节点流:直接从数据源或目的地读写数据
-
处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
-
小结:图解
2.3 流的API
-
Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
(抽象基类) | 输入流 | 输出流 |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
-
由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
常用的节点流:
-
文件流: FileInputStream、FileOutputStrean、FileReader、FileWriter
-
字节/字符数组流: ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter
-
对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)。
-
常用处理流:
-
缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
-
作用:增加缓冲功能,避免频繁读写硬盘,进而提升读写效率。
-
-
转换流:InputStreamReader、OutputStreamReader
-
作用:实现字节流和字符流之间的转换。
-
-
对象流:ObjectInputStream、ObjectOutputStream
-
作用:提供直接读写Java对象功能
-
2.4 节点流之一:FileReader\FileWriter
2.4.1 Reader与Writer
Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。不能操作图片,视频等非文本文件。
常见的文本文件有如下的格式:.txt、.java、.c、.cpp、.py等
注意:.doc、.xls、.ppt这些都不是文本文件。
2.4.1.1 字符输入流:Reader
java.io.Reader
抽象类是表示用于读取字符流的所有类的父类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
-
public int read()
: 从输入流读取一个字符。 虽然读取了一个字符,但是会自动提升为int类型。返回该字符的Unicode编码值。如果已经到达流末尾了,则返回-1。 -
public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。每次最多读取cbuf.length个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。 -
public int read(char[] cbuf,int off,int len)
:从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,从cbuf[off]开始的位置存储。每次最多读取len个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。 -
public void close()
:关闭此流并释放与此流相关联的任何系统资源。
注意:当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏。
2.4.1.2 字符输出流:Writer
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
-
public void write(int c)
:写出单个字符。 -
public void write(char[] cbuf)
:写出字符数组。 -
public void write(char[] cbuf, int off, int len)
:写出字符数组的某一部分。off:数组的开始索引;len:写出的字符个数。 -
public void write(String str)
:写出字符串。 -
public void write(String str, int off, int len)
:写出字符串的某一部分。off:字符串的开始索引;len:写出的字符个数。 -
public void flush()
:刷新该流的缓冲。 -
public void close()
:关闭此流。
注意:当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏。
2.4.2 FileReader 与 FileWriter
2.4.2.1 FileReader
java.io.FileReader
类用于读取字符文件,构造时使用系统默认的字符编码和默认字节缓冲区。
-
FileReader(File file)
: 创建一个新的 FileReader ,给定要读取的File对象。 -
FileReader(String fileName)
: 创建一个新的 FileReader ,给定要读取的文件的名称。
举例:读取hello.txt文件中的字符数据,并显示在控制台上
public class FileReaderWriterTest { //实现方式1 @Test public void test1() throws IOException { //1. 创建File类的对象,对应着物理磁盘上的某个文件 File file = new File("hello.txt"); //2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中 FileReader fr = new FileReader(file); //3. 通过相关流的方法,读取文件中的数据 // int data = fr.read(); //每调用一次读取一个字符 // while (data != -1) { // System.out.print((char) data); // data = fr.read(); // } int data; while ((data = fr.read()) != -1) { System.out.print((char) data); } //4. 关闭相关的流资源,避免出现内存泄漏 fr.close(); } //实现方式2:在方式1的基础上改进,使用try-catch-finally处理异常。保证流是可以关闭的 @Test public void test2() { FileReader fr = null; try { //1. 创建File类的对象,对应着物理磁盘上的某个文件 File file = new File("hello.txt"); //2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中 fr = new FileReader(file); //3. 通过相关流的方法,读取文件中的数据 /* * read():每次从对接的文件中读取一个字符。并将此字符返回。 * 如果返回值为-1,则表示文件到了末尾,可以不再读取。 * */ // int data = fr.read(); // while(data != -1){ // System.out.print((char)data); // data = fr.read(); // } int data; while ((data = fr.read()) != -1) { System.out.println((char) data); } } catch (IOException e) { e.printStackTrace(); } finally { //4. 关闭相关的流资源,避免出现内存泄漏 try { if (fr != null) fr.close(); } catch (IOException e) { e.printStackTrace(); } } } //实现方式3:调用read(char[] cbuf),每次从文件中读取多个字符 @Test public void test3() { FileReader fr = null; try { //1. 创建File类的对象,对应着物理磁盘上的某个文件 File file = new File("hello.txt"); //2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中 fr = new FileReader(file); //3. 通过相关流的方法,读取文件中的数据 char[] cbuf = new char[5]; /* * read(char[] cbuf) : 每次将文件中的数据读入到cbuf数组中,并返回读入到数组中的 * 字符的个数。 * */ int len; //记录每次读入的字符的个数 while ((len = fr.read(cbuf)) != -1) { //处理char[]数组即可 //错误: // for(int i = 0;i < cbuf.length;i++){ // System.out.print(cbuf[i]); // } //错误: // String str = new String(cbuf); // System.out.print(str); //正确: // for(int i = 0;i < len;i++){ // System.out.print(cbuf[i]); // } //正确: String str = new String(cbuf, 0, len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { //4. 关闭相关的流资源,避免出现内存泄漏 try { if (fr != null) fr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
2.4.2.2 FileWriter
java.io.FileWriter
类用于写出字符到文件,构造时使用系统默认的字符编码和默认字节缓冲区。
-
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。 -
FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。 -
FileWriter(File file,boolean append)
: 创建一个新的 FileWriter,指明是否在现有文件末尾追加内容。 <