File类
1. 概述
File,是文件和目录路径的抽象表示 File只关注文件本身的信息,而不能操作文件里的内容 。如果需要读取或写入文件内容,必须使用
IO
流来完成。在Java中,
java.io.File
类用于表示文件或目录的抽象路径名。它提供了一组方法,可以用于创建、访问、重命名、删除文件或目录,以及获取文件或目录的属性等操作。
2. File类的使用
- 创建文件对象:
File file = new File("d:/to/file.txt"); // 使用文件路径创建文件对象
- 创建目录:
File dir = new File("d:/to/directory"); // 使用目录路径创建文件对象
boolean created = dir.mkdir(); // 创建目录
- 检查文件或目录是否存在:
boolean exists = file.exists(); // 检查文件是否存在
boolean isDirectory = file.isDirectory(); // 检查是否为目录
boolean isFile = file.isFile(); // 检查是否为文件
- 获取文件或目录的属性:
String name = file.getName(); // 获取文件或目录的名称
String absolutePath = file.getAbsolutePath();//获取文件或目录的绝对路径
String path = file.getPath(); // 获取文件或目录的相对路径
long size = file.length(); // 获取文件的大小(字节数byte)
long lastModified = file.lastModified(); // 获取文件或目录的最后修改时间
String name = file.getName();//获取文件的名字,包含了文件的扩展名
- 文件或目录的操作:
boolean renamed = file.renameTo(new File("f:/path/to/file.txt")); // 重命名文件或目录
boolean deleted = file.delete(); // 删除文件或目录
- 遍历目录下的文件和子目录:
File[] files = dir.listFiles(); // 获取目录下的文件和子目录列表
for (File f : files) {
if (f.isFile()) {
// 处理文件
} else if (f.isDirectory()) {
// 处理子目录
}
}
- 绝对路径和相对路径:
绝对路径:带有盘符就是绝对路径
相对路径:相对路径是相对于工程目录进行定位
- 文件查找和定位
文件查找和定位一般我们都是先找到父级文件夹,再找到具体的文件 这种定位方式我们一般都是通过两个参数来体现
//第一种:第一个参数是父级文件夹路径,第二个参数是文件名
File f1 = new File("d:/io", "test.txt");
System.out.println(f1.exists());
//第二种:第一个参数是父级文件夹对象,第二个参数是文件名
File parent = new File("d:/io");
File f2 = new File(parent, "user.txt");
System.out.println(f2.exists());
- 列出文件夹下的所有文件(下一级)
File folder = new File("d:/io");
//列出文件夹下的所有文件(下一级)
File[] files = folder.listFiles();
if(files != null){
Arrays.stream(files).forEach(System.out::println);
}
结果:
- 递归扫描文件夹
package com.wz.io;
import java.io.File;
public class ScanTest {
public static void main(String[] args) {
String folder = "d:/io"; // 指定要扫描的文件夹路径
scanFolder(new File(folder)); // 调用scanFolder方法开始扫描
}
public static void scanFolder(File folder) {
if (folder.isFile()) { // 如果是文件,直接打印文件路径
System.out.println(folder);
} else {
// 如果是文件夹,列出文件夹的下一级子文件
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) { // 如果是子文件夹,递归调用scanFolder方法
scanFolder(f);
} else { // 如果是文件,打印文件路径
System.out.println(f);
}
}
}
}
}
}
结果:
- 递归删除文件夹
package com.wz.io;
import java.io.File;
public class DeleteTest {
public static void main(String[] args) {
String folder = "d:/test"; // 指定要删除的文件夹路径
deleteFolder(new File(folder)); // 调用deleteFolder方法开始删除
}
public static void deleteFolder(File folder) {
if (folder.isDirectory()) { // 如果是文件夹
File[] files = folder.listFiles(); // 列出文件夹的下一级子文件
if (files != null) {
for (File f : files) {
if (f.isDirectory()) { // 如果是子文件夹,递归调用deleteFolder方法
deleteFolder(f);
} else { // 如果是文件,直接删除
f.delete();
}
}
}
}
folder.delete(); // 删除文件夹本身
}
}
IO流
流:是一抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流。更具体一点,是内存与存储设备之间传输数据的通道。
IO的概念:IO =
input
Output
,也就是输入和输出,参照物就是内存针对内存来说,把数据读入内存称为输入,将内存中的数据写出去就是输出。
IO按照读的方式分为字节流和字符流。字节流每次读取的基本单位是字节,字符流每次读取的单位是一个字符=2个字节,因此字节流每次读取8位,字符流每次读取16位。
1. 字节流
1. OutputStream输出流(写数据)
package com.wz.io01_class;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputStreamTest {
public static void main(String[] args) {
String path = "d:/io/output.txt";
File file = new File(path);
//判断父级目录是否存在
File parentFile = file.getParentFile();
if (!parentFile.exists()){
parentFile.mkdirs();
}
//try-with-resources=>JDK1.7提供的新特性 IO流在使用了之后会自动关闭
//try-with-resources try后面的()中可以写多行代码,但是必须以分号分割开,最后一行代码的
//分号可以省略。能够写入括号中的内容必须是实现了AutoClosable接口的流的构建
try (OutputStream os = new FileOutputStream(file,true)) {
String content = "hello world!!";
final byte[] data = content.getBytes();//获取字符串的byte数据
os.write(data);//写入数据
os.flush();//强制将通道中的数据刷出,写入文件
}catch (IOException e){
e.printStackTrace();
}
}
}
2. InputStream输入流(读数据)
package com.wz.io01_class;
import java.io.*;
public class InputStreamTest {
public static void main(java.lang.String[] args) {
File file = new File("d:/io/output.txt");
try (InputStream in = new FileInputStream(file);){
//如果文件比较大,我们需要构建一个容器,来反复读取文件内容
byte[] buffer = new byte[5];
int len;
while ((len = in.read(buffer)) != -1){
System.out.println(new java.lang.String(buffer,0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
2. 字符流
字符流(Character Stream)是Java中用于以字符为单位进行读写操作的输入输出流。与字节流不同,字符流以字符作为数据处理的基本单位,而不是字节。
Java提供了两个主要的字符流类:Reader和Writer。这些类通常用于处理字符数据,例如文本文件或网络连接中的文本数据。
Reader类是抽象类,它的子类用于从字符源读取字符数据。常用的Reader子类包括FileReader(从文件中读取字符)、InputStreamReader(从字节流中读取字符)等。
package com.wz.char_class;
import java.io.*;
public class ReaderTest {
public static void main(String[] args) {
//字符流的顶层父类,Reader Writer
try(Reader reader = new FileReader("d:/io/output.txt");
Writer writer = new FileWriter("d:/io/output03.txt")){
char[] buffer = new char[2048];
int len;
while ((len=reader.read(buffer))!=-1){
System.out.println(new String(buffer,0,len));
writer.write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
这段代码首先通过
FileReader
创建一个字符输入流reader
,用于读取文件"d:/io/output.txt"的内容。然后,通过FileWriter
创建一个字符输出流writer
,用于写入内容到文件"d:/io/output03.txt"。在代码的主体部分,我们使用一个字符数组
buffer
作为缓冲区,大小设置为2048个字符。reader.read(buffer)
方法会将文件的内容读取到buffer
中,并返回实际读取的字符数(或者返回-1表示已到达文件末尾)。然后,通过
new String(buffer, 0, len)
将buffer
中的字符转换为字符串,并在控制台打印该字符串。最后,使用
writer.write(buffer, 0, len)
将buffer
中的字符写入到输出文件中。try-with-resources语句用于自动关闭字符流。这样可以确保在代码块结束时,无论是否发生异常,都会正确关闭字符流。
3. 字节缓冲流
字节缓冲流(
BufferedInputStream
和BufferedOutputStream
)是Java IO流中的一种类型,它们提供了缓冲功能,可以提高读写操作的效率。字节缓冲流继承自字节流(
InputStream
和OutputStream
),它们通过在内存中创建一个缓冲区(byte数组)来存储数据。当使用字节缓冲流进行读取或写入操作时,数据会先被读取到缓冲区中,然后从缓冲区中读取或写入到目标位置。这样可以减少实际的IO操作次数,提高读写效率。
BufferedInputStream
类提供了以下常用方法:
int read()//从输入流中读取一个字节数据,并返回其整数表示(0-255),如果已经读取到流的末尾,则返回-1。
int read(byte[] buffer)//从输入流中读取一定数量的字节数据,并将其存储到指定的字节数组buffer中,返回实际读取的字节数,如果已经读取到流的末尾,则返回-1。
void close()//关闭输入流。
BufferedOutputStream
类提供了以下常用方法:
void write(int byteValue)// 向输出流中写入一个字节数据。
void write(byte[] buffer)// 将指定的字节数组buffer中的数据写入到输出流中。
void flush()// 刷新输出流,将缓冲区中的数据立即写入到目标位置。
void close()//关闭输出流。
在使用字节缓冲流进行写入操作时,数据并不会立即写入到目标位置,而是先存储在缓冲区中。如果需要立即将数据写入到目标位置,可以调用flush方法刷新输出流。
字节缓冲流在处理大量数据时能够提供较高的读写效率,特别适用于频繁读写小块数据的场景。在进行文件复制、网络传输等操作时,使用字节缓冲流可以显著提升性能。
- 利用字节缓冲流进行文件拷贝
package com.wz.charBufferStream;
import java.io.*;
public class Test01 {
public static void main(String[] args) {
String sourceFile ="d:/io/IO流理解图.png";
String destFile = "d:/io/copy.png";
copyFile(sourceFile,destFile);
}
public static void copyFile(String sourceFile,String destFile){
//创建一个File对象,表示目标文件。
File file = new File(destFile);
//获取目标文件的父目录
File parentFile = file.getParentFile();
//判断父目录是否存在
if (!parentFile.exists()) parentFile.mkdirs();
//创建一个BufferedInputStream对象,并将其初始化为一个FileInputStream对象的包装器,用于读取源文件的数据。
//创建一个BufferedOutputStream对象,并将其初始化为一个FileOutputStream对象的包装器,用于写入目标文件的数据。
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile))) {
//创建一个字节数组作为缓冲区,用于存储从源文件读取的数据。
byte[] buffer = new byte[2048];
while(true){
int len = bis.read(buffer);
if (len==-1)break;
bos.write(buffer,0,len);
bos.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 字符缓冲流
字符缓冲流是Java IO提供的一种高效的字符流,用于处理字符数据。它是基于字符流的装饰器模式实现的,通过在字符流的基础上添加缓冲功能来提高读写性能。 在Java中,字符缓冲流有两个主要的类:BufferedReader和BufferedWriter。
BufferedReader
BufferedReader
是字符缓冲输入流,它提供了一些额外的方法来增强字符输入流的功能。它可以缓冲字符,允许高效的读取字符数据。它的构造方法可以接受一个字符输入流作为参数,然后创建一个带有缓冲功能的字符输入流。
常用方法:
readLine()//读取一行字符数据并返回一个字符串,如果到达文件末尾,则返回null。
read()//读取一个字符。
close()//关闭流,同时会关闭基础的字符输入流。
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
BufferedWriter
BufferedWriter
是字符缓冲输出流,它提供了一些额外的方法来增强字符输出流的功能。它可以缓冲字符并提供高效的写入操作。它的构造方法可以接受一个字符输出流作为参数,然后创建一个带有缓冲功能的字符输出流。
常用方法:
write(String str)//将字符串写入流中。
newLine()//写入一个行分隔符。
flush()//刷新缓冲区,将数据写入基础的字符输出流。
close()//关闭流,同时会关闭基础的字符输出流。
try (BufferedWriter writer = new BufferedWriter(new FileWriter("file.txt"))) {
writer.write("Hello, world!");
writer.newLine();
writer.write("This is a test.");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
通过使用字符缓冲流,可以提高字符输入输出操作的性能,尤其是在处理大量字符数据时,它能减少实际IO操作的次数,从而提高程序的效率。
5. 数据流
在Java的I/O流中,数据流(Data Stream)是一种流类别,用于处理基本数据类型和字符串的输入和输出。数据流提供了一种方便的方式来读取和写入原始数据类型(如int,double,boolean等)以及字符串,而无需手动进行数据类型转换。 Java的数据流操作由两个主要类组成:
- 数据流的部分方法
void writeBoolean(boolean v) throws IOException;//将布尔值作为1个字节写入底层输出通道
void writeByte(int v) throws IOException;//将字节写入底层输出通道
void writeShort(int v) throws IOException;//将短整数作为2个字节(高位在前)写入底层输出通道
void writeChar(int v) throws IOException;//将字符作为2个字节写(高位在前)入底层输出通道
void writeInt(int v) throws IOException;//将整数作为4个字节写(高位在前)入底层输出通道
void writeLong(long v) throws IOException;//将长整数作为8个字节写(高位在前)入底层输出通道
void writeFloat(float v) throws IOException;//将单精度浮点数作为4个字节写(高位在前)入底层输出通道
void writeDouble(double v) throws IOException;//将双精度浮点数作为8个字节写(高位在前)入底层输出通道
void writeUTF(String s) throws IOException;//将UTF-8编码格式的字符串以与机器无关的方式写入底层输出通道。
package com.wz.io01;
import java.io.*;
public class Test01 {
public static void main(String[] args) {
dataStream();
}
private static void dataStream(){
String path = "d:/io/test.txt";
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(path));){
dos.writeBoolean(false);
dos.writeInt(1);
dos.writeByte(2);
dos.writeShort(3);
dos.writeLong(4);
dos.writeFloat(5.0f);
dos.writeDouble(6.0);
dos.writeChar('a');
dos.writeUTF("Hello World");
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
try (DataInputStream dis = new DataInputStream(new FileInputStream(path))){
boolean b = dis.readBoolean();
System.out.println(b);
int i = dis.readInt();
System.out.println(i);
byte b1 = dis.readByte();
System.out.println(b1);
short s = dis.readShort();
System.out.println(s);
long l = dis.readLong();
System.out.println(l);
float v = dis.readFloat();
System.out.println(v);
double v1 = dis.readDouble();
System.out.println(v1);
char c = dis.readChar();
System.out.println(c);
String s1 = dis.readUTF();
System.out.println(s1);
} catch (IOException e) {
e.printStackTrace();
}
}
}
结果:
注意:
在数据流中,读取顺序必须与写入顺序保持一致。这是因为数据流中的数据是按照特定的格式写入的,如果读取顺序与写入顺序不一致,就会导致数据读取错误或解析错误。 当使用数据流进行读取时,数据流会按照先后顺序解析数据,并将其转换为相应的数据类型。如果读取顺序与写入顺序不一致,例如尝试先读取一个整数,然后读取一个字符串,这样会导致读取出的数据类型不匹配,造成解析错误。
6. 序列化
将一个对象从内存中写入磁盘文件中的过程称之为序列化,反之就是反序列化。序列化必须要求该对象所有类型实现序列化的接口
Serializable
注意: 序列化和反序列化的对象版本一致性,即序列化期间的Java类版本与反序列化期间的Java类版本应保持一致。如果版本不一致,可能会导致对象反序列化失败或数据丢失。
Serializable
接口仅仅只用于标识序列化
实现了
Serializable
接口的类可以通过ObjectOutputStream
类进行序列化,通过ObjectInputStream
类进行反序列化。
package com.wz.io02;
import java.io.*;
public class Test {
public static void main(String[] args) {
serialize();
}
public static void serialize(){
Student student = new Student("ZhangSan", 18, '男');
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/io/test.txt"))){
oos.writeObject(student);
oos.flush();
}catch (IOException e){
e.printStackTrace();
}
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/io/test.txt"))) {
Student s = (Student) ois.readObject();
System.out.println(s);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}