专栏:JavaEE初阶起飞计划
个人主页:手握风云
目录
一、Java文件操作
1.1. 方法
- 打印路径下的文件
import java.io.File;
import java.util.Arrays;
public class Demo1 {
public static void main(String[] args) {
File file = new File("D:\\java-ee-beginner\\Test8.8.1");
System.out.println(Arrays.toString(file.list())); // 返回当前目录下的文件名
System.out.println(Arrays.toString(file.listFiles())); // 返回当前目录下的文件对象
}
}
- 创建目录
package myFile;
import java.io.File;
public class Demo2 {
public static void main(String[] args) {
File file = new File("./test1/aaa/bbb");
file.mkdir(); //创建File对象的单层目录
file.mkdirs(); // 创建File对象的多层目录
}
}
- 重命名
package myFile;
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Demo3 {
public static void main(String[] args) throws IOException {
File file1 = new File("./test.txt");
if (!file1.exists()) {
file1.createNewFile();
}
Scanner in = new Scanner(System.in);
System.out.println("请输入任意内容:");
in.next();
File file2 = new File("./test2.txt");
file1.renameTo(file2);// 重命名文件名称
}
}
该方法不光可以修改文件名称,还可以修路径。
- 用户权限
package myFile;
import java.io.File;
public class Demo4 {
public static void main(String[] args) {
File file = new File("./test");
file.canRead();//判断文件是否可读
file.canWrite();//判断文件是否可写
}
}
上述两个方法在Windows上面运行结果不太明显,在Linux上运行才明显。
二、Java文件内容读取
2.1. 数据流
文件内容操作大致分为读文件(硬盘的数据读到内存)和写文件(把内存的数据写到内存)。这时我们就需要借助流对象进行文件内容操作。
流对象,我们类比为水流。比如我们要接100ml水,分1次接100ml,或者分2次接50ml。同样对于100byte的数据,分1次读取100byte,或者分2次读取50byte。各种编程语言,围绕文件的操作都统称为“流”。
Java中通过一系列大类表示“流对象”,但总体上可以分为两类:字节流和字符流。字节流(InputStream、OutputStream)以字节为单位,一次最少读一个字节;字符流(Reader、Writer)以字符为单位,一次最少读写一个字符。
2.2. InputStream
public abstract class InputStream implements Closeable
InputStream只是⼀个抽象类,要使用还需要具体的实现类。我们现在只关心从⽂件中读取,所以使用FileInputStream。
InputStream inputStream = new FileInputStream("./test.txt"); //创建一个文件输入流对象
要想实现文件读取的操作,要使用read方法。无参数版本一次读取⼀个字节的数据,返回-1代表已经完全读完了。1个参数版本最多读取b.length字节的数据到b中,返回实际读到的量,-1代表以及读完了。3个参数版本最多读取len-off字节的数据到b中,放在从off开始,返回实际读到的数量,-1代表以及读完了。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo5 {
public static void main(String[] args) throws IOException {
//创建一个文件输入流对象,”打开文件“的过程
InputStream inputStream = new FileInputStream("./test.txt");
while (true) {
int c = inputStream.read();
if (c == -1) {
// 读取完毕
break;
}
System.out.printf("0x%x\n", c);
}
}
}
我们在项目路径下新建记事本,然后内容保存为"Hello world!",运行就会出现如下效果,各个字符对应的ASCII码值。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo6 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("./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("0x%x\n", bytes[i]);
}
}
}
}
此处的字节数组是一个输出型参数,我们实现构造好数组元素全为0的数组,然后填充在read方法内部。这种方式适用于需要返回多个结果或批量数据的场景,避免了方法只能有一个返回值的限制。我们可以想象在食堂打饭时,需要拿着餐盘递给阿姨,装完饭后,阿姨把成盛满饭的餐盘端给我们。
第三个方法与第二个方法逻辑一致,只不过打印顺序是从off下标开始。
2.3. 文件泄露
我们在上面的打开文件和读取文件之后,还有一个重要的操作,就是关闭文件。inputSream.close();进程PCB中有一个属性,文件描述符表(通过顺序表实现),每次打开一个文件,都会在这个表里面插入一个元素。但是文件描述符表是不能扩容的,如果光打开文件不关闭,就会把表里的内容消耗殆尽,再想打开其他文件就会失败。
我们看下面一段代码:下面的代码存在一些问题,当read方法出现了异常,会直接跳过close()方法
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo7 {
private static void readFile() {
try {
InputStream inputStream = new FileInputStream("./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]);
}
System.out.println();
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
readFile();
}
}
为了解决上面的问题,我们可以把close方法放进finally代码块里面。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo7 {
private static void readFile() {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./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]);
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream == null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
readFile();
}
}
虽然可以解决上面的问题,但又引入了新的问题,就是代码的美观。
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Demo7 {
private static void readFile1() {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./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]);
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream == null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void readFile2() {
// try with resource 简化语法
// try语句执行结束之后,会自动调用close方法
try (InputStream inputStream = new FileInputStream("./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();
}
}
public static void main(String[] args) {
readFile2();
}
}