一、文件的基本认知
在计算机存储领域,文件是硬盘等持久化存储设备中独立的数据单位,如同办公桌上的一份份文档。每个文件不仅包含实际的数据内容,还有一系列描述信息,即文件元信息,像文件名、文件类型、文件大小、修改日期等,这些信息并非文件内容本身,却对文件管理至关重要。
二、文件的组织方式:树形结构与目录
随着文件数量增多,有序管理成为必然。树形结构因符合自然逻辑被广泛采用,由此诞生了专门用于管理文件的特殊 "文件"——目录(directory) 或文件夹(folder)。
目录就像树形结构的节点,里面存储着文件的元信息,通过层层嵌套,形成了清晰的层级关系。例如 Windows 系统中,从 "此电脑" 开始,包含 "视频"“图片”“文档” 等子目录,子目录下又可包含更多文件和子目录;Linux 系统也有类似的树形结构组织。这种结构让文件的查找和管理变得高效且易于理解。
三、文件路径:定位文件的关键
要在文件系统中准确找到一个文件,就需要文件路径(Path),主要分为以下两种:
- 绝对路径(absolute path):从树形结构的根节点开始,一直到目标文件的完整路径。例如
C:\Windows\System32\cmd.exe
,从 C 盘根目录出发,清晰指向了 cmd.exe 文件。 - 相对路径(relative path):从任意节点(当前所在位置)出发到目标文件的路径。其中,
.
代表当前节点,..
代表父节点。比如从C:\Program Files (x86)\WindowsPowerShell
去往Windows NT
,相对路径为..\Windows NT
。
不同操作系统的路径分隔符有所不同,Windows 常用\
,而 Unix、Linux 等常用/
。
四、文件的分类与特性
1. 按数据类型划分
- 文本文件:保存被字符集编码的文本,如 UTF-8 编码的中文文本,每个字符对应特定的字节序列。
- 二进制文件:按照标准格式保存的非字符集编码数据,如 24 位位图文件,其数据以特定的二进制格式存储。
2. 文件扩展名
Windows 系统通过文件名中的后缀(扩展名)确定文件类型和默认打开程序,如.exe
表示可执行程序,.dll
表示动态库。但在 OSX、Unix、Linux 等系统中,并无此严格习俗。
3. 文件权限
操作系统会为不同用户赋予文件不同权限,通常包括可读、可写、可执行权限,以此保障数据安全。
4. 特殊文件
- 快捷方式(Windows):对真实文件的引用,方便用户快速访问。
- 软链接(Unix/Linux 等):类似快捷方式,可对同一文件建立多个引用。
此外,Unix、Linux 操作系统秉持 “万物皆文件” 理念,将所有 I/O 设备都抽象为文件,实现了接口的统一性。
五、Java 中的文件操作
Java 通过java.io.File
类对文件(包括目录)进行抽象描述,但需注意,File
对象的存在并不代表真实文件存在。
1. File 类的核心内容
- 属性:包含依赖于系统的路径分隔符,有
String
类型的pathSeparator
和char
类型的pathSeparator
。 - 构造方法:
File(File parent, String child)
:根据父目录和子文件路径创建实例。File(String pathname)
:根据文件路径(绝对或相对)创建实例。File(String parent, String child)
:根据父目录路径和子文件路径创建实例。
- 常用方法:
- 获取信息:
getParent()
(父目录路径)、getName()
(文件名)、getPath()
(文件路径)、getAbsolutePath()
(绝对路径)、getCanonicalPath()
(修饰过的绝对路径)等。 - 判断状态:
exists()
(是否存在)、isDirectory()
(是否为目录)、isFile()
(是否为普通文件)等。 - 操作文件:
createNewFile()
(创建空文件)、delete()
(删除文件)、deleteOnExit()
(JVM 结束时删除文件)等。 - 操作目录:
list()
(目录下所有文件名)、listFiles()
(目录下所有文件的 File 对象)、mkdir()
(创建目录,中间目录不存在则失败)、mkdirs()
(创建目录,必要时创建中间目录)等。 - 其他:
renameTo(File dest)
(文件改名 / 剪切粘贴)、canRead()
(是否可读)、canWrite()
(是否可写)等。
- 获取信息:
2. 代码示例展示
- 获取路径信息:通过
getParent()
、getName()
等方法可获取文件的各类路径相关信息。 - 文件的创建与删除:
createNewFile()
创建文件,delete()
直接删除文件,deleteOnExit()
则在程序运行结束后删除。 - 目录的创建:
mkdir()
创建单级目录,mkdirs()
可创建多级目录。 - 文件重命名:
renameTo()
方法可实现文件的改名操作。 -
//示例 1:get 系列方法 import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file = new File("..\\hello-world.txt"); // 并不要求该文件真实存在 System.out.println(file.getParent()); System.out.println(file.getName()); System.out.println(file.getPath()); System.out.println(file.getAbsolutePath()); System.out.println(file.getCanonicalPath()); } }
运行结果: .. hello-world.txt ..\hello-world.txt D:\代码练习\文件示例1\..\hello-world.txt D:\代码练习\hello-world.txt
-
//示例 2:普通文件的创建、删除 import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file = new File("hello-world.txt"); // 要求该文件不存在 System.out.println(file.exists()); System.out.println(file.isDirectory()); System.out.println(file.isFile()); System.out.println(file.createNewFile()); System.out.println(file.exists()); System.out.println(file.isDirectory()); System.out.println(file.isFile()); System.out.println(file.createNewFile()); } }
//运行结果: false false false true true false true false
-
//示例 3:普通文件的删除 import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file = new File("some-file.txt"); // 要求该文件不存在 System.out.println(file.exists()); System.out.println(file.createNewFile()); System.out.println(file.exists()); System.out.println(file.delete()); System.out.println(file.exists()); } }
//运行结果: false true true true false
-
//示例 4:deleteOnExit 方法 import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file = new File("some-file.txt"); // 要求该文件不存在 System.out.println(file.exists()); System.out.println(file.createNewFile()); System.out.println(file.exists()); file.deleteOnExit(); System.out.println(file.exists()); } }
//运行结果: false true true true //程序运行结束后,文件会被删除。
-
//示例 5:目录的创建 import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File dir = new File("some-dir"); // 要求该目录不存在 System.out.println(dir.isDirectory()); System.out.println(dir.isFile()); System.out.println(dir.mkdir()); System.out.println(dir.isDirectory()); System.out.println(dir.isFile()); } }
//运行结果: false false true true false
-
//示例 6:目录创建(mkdir 与 mkdirs 的区别) //使用mkdir()时,若中间目录不存在,则无法创建成功: import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File dir = new File("some-parent\\some-dir"); // some-parent和some-dir不存在 System.out.println(dir.isDirectory()); System.out.println(dir.isFile()); System.out.println(dir.mkdir()); System.out.println(dir.isDirectory()); System.out.println(dir.isFile()); } }
运行结果: false false false false false
//使用mkdirs()可解决中间目录不存在的问题: import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File dir = new File("some-parent\\some-dir"); // some-parent和some-dir不存在 System.out.println(dir.isDirectory()); System.out.println(dir.isFile()); System.out.println(dir.mkdirs()); System.out.println(dir.isDirectory()); System.out.println(dir.isFile()); } }
运行结果: false false true true false
-
//示例 7:文件重命名 import java.io.File; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { File file = new File("some-file.txt"); // 要求some-file.txt存在 File dest = new File("dest.txt"); // 要求dest.txt不存在 System.out.println(file.exists()); System.out.println(dest.exists()); System.out.println(file.renameTo(dest)); System.out.println(file.exists()); System.out.println(dest.exists()); } }
//运行结果: true false true false true
六、文件内容的读写:数据流
1. 输入流(InputStream)
- 概述:用于读取数据,
InputStream
是抽象类,操作文件时常用其实现类FileInputStream
。 - 主要方法:
read()
:读取一个字节,返回 - 1 表示读完。read(byte[] b)
:最多读取b.length
字节到数组b
,返回实际读取数量,-1 表示读完。read(byte[] b, int off, int len)
:最多读取len - off
字节到数组b
的off
位置开始,返回实际读取数量,-1 表示读完。close()
:关闭字节流。
- 读取方式:可逐个字节读取,也可通过字节数组批量读取,后者 I/O 次数少,性能更好。对于字符读取,使用
Scanner
类更便捷,其Scanner(InputStream is, String charset)
构造方法可指定字符集扫描读取。
2. 输出流(OutputStream)
- 概述:用于写入数据,
OutputStream
是抽象类,操作文件时常用其实现类FileOutputStream
。 - 主要方法:
write(int b)
:写入一个字节。write(byte[] b)
:将字节数组b
的数据全部写入。write(byte[] b, int off, int len)
:将字节数组b
中从off
开始的len
个字节写入。close()
:关闭字节流。flush()
:将缓冲区数据刷入设备,因 I/O 速度慢,输出流通常先将数据写入缓冲区,需手动刷新。
- 写入方式:可直接写入字节或字节数组,对于字符写入,可结合
OutputStreamWriter
和PrintWriter
,PrintWriter
提供了熟悉的print/println/printf
方法,方便操作。
七、数据读写的常用模式
- 按字节读:使用
InputStream
结合字节数组,循环读取直至结束。 - 按字节写:使用
OutputStream
,将数据填入字节数组后写入,最后刷新缓冲区。 - 按字符读:使用
InputStream
配合Scanner
,按行读取字符内容。 - 按字符写:使用
OutputStream
、OutputStreamWriter
和PrintWriter
,按行写入字符内容,最后刷新。
掌握文件操作和 IO 是 Java 编程的重要基础,无论是简单的文件管理还是复杂的数据读写,都离不开这些核心知识和技能。