1. File类概述
Java中通过 java.io.File 类来对⼀个⽂件(包括⽬录)进⾏抽象的描述。注意,有File对象,
并不代表真实存在该⽂件。
1.1 属性
在Java中提供了这两个常量来获取对应的系统的文件路径分隔符
pathSeparatorChar是一个字符常量,表示系统相关的路径分隔符。它的值由fs.getPathSeparator()方法返回。
pathSeparator是一个字符串常量,方便使用和操作路径分隔符。它的值是将pathSeparatorChar转换为字符串得到的。
1.2 构造方法
在Java中,File类提供了三个构造函数用于创建文件对象:
- File(String pathname):根据指定的路径名创建一个新的文件对象。
参数pathname是要创建的文件的路径名,可以是相对路径或绝对路径。 - File(File parent, String child):根据指定的父路径和子路径创建一个新的文件对象。
参数parent是文件对象的父目录。
参数child是文件对象的子目录或文件名。 - File(String parent, String child):根据指定的父路径名和子路径名创建一个新的文件对象。
参数parent是文件对象的父目录的路径名。
参数child是文件对象的子目录或文件名的路径名。
代码演示:
import java.io.File;
public class IODemo1 {
public static void main(String[] args) {
// 通过路径名创建文件对象
File file1 = new File("C:/Users/22759/Desktop/Ting__JAVA/J20240117-IO/file.txt");//绝对路径
File file2 = new File("./file.txt");//相对路径
/*注意:使用相对路径时,如果是在idea中运行程序,此时工作目录为项目所在目录(即 . 代表的目录)
如果是把代码打包成一个单独的jar包,则工作目录为jar包所在的目录
*/
// 通过路径名创建文件对象
File parentDir = new File("C:/Users/22759/Desktop/Ting__JAVA/J20240117-IO");
File file3 = new File(parentDir, "file.txt");
// 通过父路径名和子路径名创建文件对象
File file4 = new File("C:/Users/22759/Desktop/Ting__JAVA/J20240117-IO", "file.txt");
}
}
1.3 常用方法
返回值类型 | 方法名 | 说明 |
---|---|---|
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 File 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作 会到 JVM 运行结束时才会进行 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
2. 文件的读写
2.1 数据流
数据流(Data Stream)是指在计算机中,数据在输入和输出设备之间传输的流动方式。数据可以以不同的形式流动,如字节流、字符流等。
字节流(Byte Stream)以字节为单位进行数据传输,适用于处理二进制数据。字节流可以分为输入字节流和输出字节流。常见的字节流类有InputStream和OutputStream。
字符流(Character Stream)以字符为单位进行数据传输,适用于处理文本数据。字符流可以分为输入字符流和输出字符流。常见的字符流类有Reader和Writer。
2.2 字节流
2.2.1 InputStream
InputStream是抽象类,用于从输入源(如文件、网络连接等)读取数据。以下是使用InputStream的几个常用步骤:
1. 创建InputStream对象:可以使用FileInputStream、ByteArrayInputStream、Socket.getInputStream()等构造方法创建InputStream对象。
public class IODemo3 {
public static void main(String[] args) throws FileNotFoundException {
InputStream inputStream = new FileInputStream("./file.txt");//相当于打开文件
}
}
2. 读取数据:使用read()方法从输入流中读取数据。read()方法。
int read(): 返回下一个字节的整数值,如果已经到达流的末尾,则返回-1可以使用循环结构来连续读取数据,直到返回-1为止。
InputStream inputStream = new FileInputStream("./file.txt");//相当于打开文件
while(true) {
int n = inputStream.read();
if(n == -1) {
break;
}
System.out.print(n + " ");
}
int read(byte[] b) : 从输入流中读取最多b.length个字节的数据,并将其存储在给定的字节数组b中。该方法返回实际读取的字节数,如果已经到达流的末尾,则返回-1。
InputStream inputStream = new FileInputStream("./file.txt");//相当于打开文件
byte[] b = new byte[10];
int n = inputStream.read(b);
System.out.println("n == " + n);
for(int i = 0 ; i < n; i++) {
System.out.print(b[i] + " ");
}
int read(byte[] b, int off, int len): 从输入流中读取最多len个字节的数据,并将其存储在给定的字节数组b中,存储位置从off索引处开始。该方法返回实际读取的字节数,如果已经到达流的末尾,则返回-1。
InputStream inputStream = new FileInputStream("./file.txt");//相当于打开文件
byte[] b = new byte[10];
int n = inputStream.read(b, 0, 4);
System.out.println("n == " + n);
for(int i = 0 ; i < n; i++) {
System.out.print(b[i] + " ");
}
3. 关闭InputStream:使用close()方法关闭输入流,释放资源。
public class IODemo3 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("./file.txt");//相当于打开文件
while(true) {
int n = inputStream.read();
if(n == -1) {
break;
}
System.out.print(n + " ");
}
inputStream.close();//释放资源
}
}
注意:在实际开发中可能由于各种原因,上述写法中的close可能无法执行到,比如抛出异常了,或者提前返回了,所有我们可以对上述代码做出一些改进:
public class IODemo4 {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;//相当于打开文件
try {
inputStream = new FileInputStream("./file.txt");
while(true) {
int n = inputStream.read();
if(n == -1) {
break;
}
System.out.print(n + " ");
}
}finally {
inputStream.close();//释放资源
}
}
}
我们使用try-catch将close放在finally中,保证close一定会被执行
我们也可以使用try-catch的另一版本 try with resource:
public class IODemo4 {
public static void main(String[] args) throws IOException {
try(InputStream inputStream = new FileInputStream("./file.txt")) {
while(true) {
int n = inputStream.read();
if(n == -1) {
break;
}
System.out.print(n + " ");
}
}
}
}
在代码执行出try的代码块时,try会自动调用close方法
注意:只用实现了Closeable接口的类才会自动调用close方法
2.2.2 OutPutStream
OutputStream也是一个抽象类,它是所有输出流类的基类。
使用方法与InPutStream大致相同:
OutPutStream提供了三个write方法:
void write(int b) :这个方法将指定字节写入输出流。被写入的数据是参数b的低八位。
public class IODemo5 {
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./file.txt")) {
outputStream.write(97);//写入一个a
}
}
}
void write(byte[] b) :这个方法将参数字节数组b中的所有字节写入输出流。
public class IODemo5 {
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./file.txt")) {
byte[] b = {97, 98, 99};
outputStream.write(b); //写入b数组中的所有数据
}
}
}
void write(byte[] b, int off, int len) :这个方法将从参数字节数组b中的偏移量off开始写入len个字节到输出流。
public class IODemo5 {
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./file.txt")) {
byte[] b = {97, 98, 99};
outputStream.write(b, 0, 3);//从b数组的0号下标开始写入3个数据
}
}
}
我们发现每次写入数据时文件中的内容都会被清空再重新写入,要想不清空直接在后面续写,我们只需要在实例化OutPutStream时在后面加上一个参数true即可:
public class IODemo5 {
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./file.txt", true)) {
byte[] b = {97, 98, 99};
outputStream.write(97);//写入一个a
outputStream.write(b);
outputStream.write(b, 0, 3);
}
}
}
void flush() 方法:写数据时会先把数据写入缓冲区,等数据超过某个阈值时才会写入目标文件,这个方法会将输出流的缓冲区内容强制刷新到输出目标中(如文件、网络连接等),以确保所有缓冲数据都被写入目标。
2.3 字符流
2.3.1 Reader
Reader也是一个抽象类,不能直接实例化
使用方法类比InPutStream,只不过从按字节为单位变成了按字符为单位
Reader也是通过read方法读取:
int read():从输入流中读取下一个字符的数据,并将其作为整数返回。如果已经到达流的末尾,即没有更多的数据可读取,则返回-1。
int read(char[] cbuf):从输入流中读取最多cbuf.length个字符的数据,并将其存储在给定的字符数组cbuf中。该方法返回实际读取的字符数,如果已经到达流的末尾,则返回-1
int read(CharBuffer target):从输入流中读取字符,并将其存储到提供的CharBuffer对象中。它返回实际读取的字符数,如果已经到达流的末尾,则返回-1。
int read(char[] cbuf, int off, int len):从输入流中读取最多len个字符的数据,并将其存储在给定的字符数组cbuf中,存储位置从off索引处开始。该方法返回实际读取的字符数,如果已经到达流的末尾,则返回-1。
public class IODemo {
public static void main(String[] args) throws IOException {
try(Reader reader = new FileReader("./file.txt")) {
char[] b = new char[100];
int n = reader.read(b);
System.out.println(new String(b, 0, n));
}
}
}
运行程序就可以输出file.txt 中的内容。
但此处有一个细节:在Java中 char类型占2个字节,而utf8编码的中文占3个字节,b数组是如何存储中文的?
我们换一种打印方式:
public class IODemo {
public static void main(String[] args) throws IOException {
try(Reader reader = new FileReader("./file.txt")) {
char[] b = new char[100];
int n = reader.read(b);
//System.out.println(new String(b, 0, n));
for(int i = 0; i < n; i++) {
System.out.print(b[i] + " ");
}
}
}
}
这是因为字符在被读到字符数组时,是先被转化为Unicode编码,构造成字符串时再被转化为utf8编码
2.3.2 Writer
Writer是Java IO包中的一个抽象类,它是所有字符输出流的超类。它提供了一系列方法,用于将字符数据从内存写入到输出流中。
Writer类的常用方法包括:
write(int c):将指定的字符写入输出流。
write(char[] cbuf):将字符数组中的数据写入输出流。
write(String str):将字符串写入输出流。
write(char[] cbuf, int off, int len):将字符数组中的一部分数据写入输出流。
flush():刷新输出流,确保所有待输出的数据都被写入到输出流中。
close():关闭输出流。
使用方法类比OutPutStream
public class IODemo6 {
public static void main(String[] args) throws IOException {
try(Writer writer = new FileWriter("./file.txt")) {
writer.write("你好Java");
}
}
}