Java——文件相关知识
文件
下面说文件是指硬盘上的文件,在“此电脑”中,我们可以看到文件包括文件和文件夹
。对于文件有各种类型的文件,如.doxc、.png等文件。文件夹也叫目录,其中又存放着许多文件和文件夹。像这样的结构可以看作一颗树(N叉树)。
文件路径
文件路径有两种表示方式:绝对路径和相对路径
绝对路径
从文件系统的根目录开始的完整的路径,像Windows系统就是从磁盘开始的
eg.D:\example\a\test.txt
相对路径
从当前所在目录出发的路径,这时候需要一个基准路径。
eg.
绝对路径:D:\example\a\test.txt
;以D:\example
为基准路径,相对路径:.\a\test.txt
。.
表示当前目录
绝对路径:D:\example\a\test.txt
;以D:\example\a\b
为基准路径,相对路径:..\test.txt
。..
表示当前目录的上一级目录
对于上面的路径都是用
\
作为目录之间的分隔符。对于Windows系统来说\
(反斜杠) 和/
(斜杠) 都可以作为目录之间的分隔符,而对于Linux、macOS等操作系统来说,只支持/
(斜杠) 都可以作为目录之间的分隔符。
根据文件存储的内容分类
文件可分为文本文件和二进制文件。
文本文件内容存储的是字符串,每个部分都是一个字符,可以在字符码表上查询到的。
而二进制文件存储内容就都可以。
区分两种文件最简单的方式就是用记事本打开,如果没有乱码就是文本文件,反之则是二进制文件。
文件操作
对于一个文件的操作可以分为两种:文件系统操作和文件内容操作。对于文件操作的类都在java.io.File
包中。
文件系统操作
也就是对文件进行创建、移动、复制、删除、查看目录等操作。
File类
构造方法
方法 | 说明 |
---|---|
File(File parent,String child) | 根据指定的父抽象路径名和子路径名字符串创建一个新的 File 对象。 |
File(String parent,String chile) | 根据指定的父路径名字符串和子路径名字符串创建一个新的 File 对象 |
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建一个新的 File 对象。 |
对于File(String pathname)
的参数可以是绝对路径也可以是相对路径。
在IDEA上使用相对路径,其基准路径为打开这个工程的路径
常用方法
返回值 | 方法名 | 说明 |
---|---|---|
String | getParent() | 返回File对象的父目录文件路径 |
String | getName() | 返回File对象文件名 |
String | getPath() | 返回File对象的文件路径 |
String | getAbsolutePath() | 返回File对象的绝对路径 |
String | getCanonicalPath() | 返回File对象的绝对路径 |
boolean | exists() | 判断File对象描述的文件是否存在 |
boolean | isDirectory | 判断File对象是否为一个目录(文件夹) |
boolean | isFile() | 判断File对象是否为一个普通文件 |
public class FileTest {public static void main(String[] args) throws IOException {File file = new File("./test.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.getAbsolutePath());System.out.println(file.exists());}
}
/*
输出
.
test.txt
.\test.txt
D:\JAVA\learning-java\J2025_9_25\.\test.txt
D:\JAVA\learning-java\J2025_9_25\test.txt
false
*/
getAbsolutePath()和getAbsolutePath(),当File构造方法参数是绝对路径,两者一样;而当是相对路径,getAbsolutePath()只是简单将基准路径和相对路径进行字符串拼接,getAbsolutePath()则进行了简化。
返回值 | 方法名 | 说明 |
---|---|---|
boolean | createNewFile() | 根据File对象创建普通文件,创建成功返回true |
boolean | delete() | 删除文件 |
boolean | deleteOnExit() | 将文件进行标记表示要删除,删除的操作会在JVM运行结束时发生 |
String[] | list() | 返回File对象代表的目录下所有的文件名 |
File[] | listFiles() | 返回File对象代表的目录下所有的文件,以File对象表示 |
boolean | mkdir() | 创建一层目录 |
boolean | mkdirs() | 创建多层或一层目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为 文件的移动 |
public class FileTest {public static void main(String[] args) throws IOException {test1();test2();}public static void test1() {File file = new File("d:/example/c/d/e");System.out.println(file.exists());System.out.println(file.mkdirs());//创建目录}public static void test2(){File file = new File("d:/example/d");File dest = new File("d:/example/c/d/e/D");System.out.println(file.renameTo(dest));//进行文件移动并同时修改了文件名}
}
对于文件操作是需要权限的,当没有权限时,将无法对文件进行一些操作。
返回值 | 方法名 | 说明 |
---|---|---|
boolean | canRead() | 判断用户是否对文件有读的权限 |
boolean | canWrite() | 判断用户是否对文件有写的权限 |
文件内容操作
也就是对文件内容进行读写操作。
读文件就是把文件内容的数据获取出来;写文件就是修改文件内容。
对于一次操作多大的内容,提供两种操作方式:字节流和字符流。
对于文件的输入输出都是站在CPU的角度上的。
字节流
以字节为单位,一次可以读写1个字节,也可以一次读写100字节等。Java中提供的抽象类:InputStream输入、OutputStream输出。我们使用其实现的子类FileInputStream
输入、FileOutputStream
输出。对于读写方式以字节流,适合操作二进制文件。
读文件
调用InputStream
类中的read()
方法。有三种重载方法,返回值都是int类型:
read()
:不带参数的,一次读一个字节
read(byte[] b)
:传一个字节数组,一次读多个字节,大小为字节数组大小
read(byte[] b, int off, int len)
:off表示偏移量(数组下标),len表示预计读多少字节
对于传入的字节数组会尽量数组填满,但长度可能不会存满,返回值为实际读取的长度。当返回值为-1
的时候表示文件已经读完了。
public class Input {public static void main(String[] args) throws IOException {InputStream inputStream = new FileInputStream("d:/example/test.txt");while (true) {byte[] bytes = new byte[1024];int n = inputStream.read(bytes);if (n == -1) {break;}for (int i = 0; i < n; i++) {System.out.printf("%x ",bytes[i]);}}}
}
1、用while-true死循环是为了能一直读文件(我们并不知道文件的大小),每次创建一个新的字节数组用来存储文件中的内容(分次存储输出)。
2、用循环打印字节数组中的内容来模拟输出(并不是每次数组都是满的,所以for循环一定是循环到n次,只有下标为n及前面下标才有数据)
3、当n == -1的时候,说明文件读取完毕
4、打印结果为:68 65 6c 6c 6f 20 77 6f 72 6c 64 表示hello world的十六进制。20表示空格
关闭文件
对于文件也是一种资源,需要去释放。进程中有一个核心属性:文件描述符表,用来记录进程打开的所有文件等。这张表是一个顺序表,无法自动扩容。当文件用完没有及时释放,导致系统资源被持续占用,最终导致性能下降,资源耗尽甚至崩溃(这也就是文件资源泄露)。
为了关闭文件,需要调用close()
方法。close()
方法也会抛出IOException
的异常
public class Input {public static void main(String[] args) {try{InputStream inputStream = new FileInputStream("d:/example/test.txt");while (true) {byte[] bytes = new byte[1024];int n = inputStream.read(bytes);if (n == -1) {break;}for (int i = 0; i < n; i++) {System.out.printf("%x ",bytes[i]);}}inputStream.close();}catch (IOException e){e.printStackTrace();}}
}
用try-catch的方式处理异常。
但当read操作抛出异常的时候,会直接被捕获到执行catch代码块中的代码,从而不会执行 read 后面的代码,也就并没有执行close释放文件资源的操作。
此时可以使用try-catch-finally
public class Input {public static void main(String[] args) {InputStream inputStream = null;try{inputStream = new FileInputStream("d:/example/test.txt");while (true) {byte[] bytes = new byte[1024];int n = inputStream.read(bytes);if (n == -1) {break;}for (int i = 0; i < n; i++) {System.out.printf("%x ",bytes[i]);}}}catch (IOException e){e.printStackTrace();}finally {if(inputStream != null){try {inputStream.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}
这样代码虽然逻辑没有问题,但显得十分“杂乱”,需要进一步的优化,变得更加美观。
使用try with resources
的形式
public class Input_Output {public static void main(String[] args) {try (InputStream inputStream = new FileInputStream("d:/example/test.txt")){while (true) {byte[] bytes = new byte[1024];int n = inputStream.read(bytes);if (n == -1) {break;}for (int i = 0; i < n; i++) {System.out.printf("%x ",bytes[i]);}}} catch (IOException e) {e.printStackTrace();}}
}
在try代码块执行完毕后自动调用close方法,并处理了异常。
需要注意的是:使用这个要求try()括号中定义的对象,需要实现Closeable接口。
写文件
调用OutputStream
类中的write()
方法。也有三种重载方法,没有返回值:
write(int b)
:一次写一个字节
write(byte[] b)
:一次写多个字节
write(byte[] b, int off, int len)
:写字节数组的一部分
public class Output {public static void main(String[] args) {byte[] bytes = new byte[1024];for (int i = 0; i < 5; i++) {bytes[i] = (byte)(65 + i);}try(OutputStream outputStream = new FileOutputStream("d:/example/test.txt")) {outputStream.write(bytes);}catch (IOException e) {e.printStackTrace();}}
}
此时我们发现之前test文本中的内容先被清空了,再进行写入操作。
OutputStream outputStream = new FileOutputStream("d:/example/test.txt",true)
再传入一个参数,这样就不会清空原文件内容,将新写入的内容添加在文件末尾。
字符流
以字符为单位,一次可以读写1个字符,也可以一次读写100字符等。Java中提供的抽象类:Reader输入、Writer输出。我们使用其实现的子类FileReader
输入、FileWriter
输出。对于读写方式以字符流,适合操作文本文件。
读文件
使用read()
方法,有四种重载方法,返回类型都是int:
read()
:一次读一个字符
read(char[] cbuf)
:读字符数组
read(CharBuffer target)
:读字符数组(CharBuffer是将char[]进一步封装了)
read(char[] cbuf, int off, int len)
:读字符数组的一部分
public class InputChar {public static void main(String[] args) {try(Reader reader = new FileReader("d:/example/test.txt")){/*while (true){int n = reader.read();if(n == -1) {break;}System.out.print((char)n);}*/while (true){char[] chars = new char[1024];int n = reader.read(chars);if(n == -1){break;}for (int i = 0; i < n; i++) {System.out.print(chars[i]);}}}catch (IOException e){e.printStackTrace();}}
}
写文件
使用write()
方法,有五种重载方法,没有返回值:
write(int c)
:写入单个字符(int类型表示字符的Unicode码)
write(char[] cbuf)
:写入字符数组
write(char[] cbuf, int off, int len)
:写入字符数组的一部分
write(String str)
:写入字符串
write(String str, int off, int len)
:写入字符串的一部分
public class OutputChar {public static void main(String[] args) {try(Writer writer = new FileWriter("d:/example/test.txt",true)){writer.write("hello,世界");}catch (IOException e){e.printStackTrace();}}
}
其他方式输入输出
用Scanner类输入
public class ScannerInput {public static void main(String[] args) {try(InputStream inputStream = new FileInputStream("D:/example/test.txt");Scanner scanner = new Scanner(inputStream)) {System.out.println(scanner.next());}catch (IOException e){e.printStackTrace();}}
}
用PrintWriter类输出
public class PrintWriterOutput {public static void main(String[] args) {try(PrintWriter printWriter = new PrintWriter("D:/example/test.txt")) {printWriter.print("你好\n");printWriter.printf("%d + %d = %d\n", 10, 20, 30);//支持格式化输出}catch (IOException e){e.printStackTrace();}}
}