File类

1. 概述

File,是文件和目录路径的抽象表示 File只关注文件本身的信息,而不能操作文件里的内容 。如果需要读取或写入文件内容,必须使用IO流来完成。

在Java中,java.io.File 类用于表示文件或目录的抽象路径名。它提供了一组方法,可以用于创建、访问、重命名、删除文件或目录,以及获取文件或目录的属性等操作。

2. File类的使用

  1. 创建文件对象:
File file = new File("d:/to/file.txt");  // 使用文件路径创建文件对象
  1. 创建目录:
File dir = new File("d:/to/directory");  // 使用目录路径创建文件对象
boolean created = dir.mkdir();  // 创建目录
  1. 检查文件或目录是否存在:
boolean exists = file.exists();  // 检查文件是否存在
boolean isDirectory = file.isDirectory();  // 检查是否为目录
boolean isFile = file.isFile();  // 检查是否为文件
  1. 获取文件或目录的属性:
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();//获取文件的名字,包含了文件的扩展名
  1. 文件或目录的操作:
boolean renamed = file.renameTo(new File("f:/path/to/file.txt"));  // 重命名文件或目录
boolean deleted = file.delete();  // 删除文件或目录
  1. 遍历目录下的文件和子目录:
File[] files = dir.listFiles();  // 获取目录下的文件和子目录列表
for (File f : files) {
    if (f.isFile()) {
        // 处理文件
    } else if (f.isDirectory()) {
        // 处理子目录
    }
}
  1. 绝对路径和相对路径:

绝对路径:带有盘符就是绝对路径

相对路径:相对路径是相对于工程目录进行定位

  1. 文件查找和定位

文件查找和定位一般我们都是先找到父级文件夹,再找到具体的文件 这种定位方式我们一般都是通过两个参数来体现

//第一种:第一个参数是父级文件夹路径,第二个参数是文件名
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());
  1. 列出文件夹下的所有文件(下一级)
File folder = new File("d:/io");
        //列出文件夹下的所有文件(下一级)
        File[] files = folder.listFiles();
        if(files != null){
            Arrays.stream(files).forEach(System.out::println);
        }

结果:

2023年7月25日,File类,IO流_第1张图片

  1. 递归扫描文件夹
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);
                    }
                }
            }
        }
    }
}

结果:

2023年7月25日,File类,IO流_第2张图片

  1. 递归删除文件夹
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位。

2023年7月25日,File类,IO流_第3张图片

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. 字节缓冲流

字节缓冲流(BufferedInputStreamBufferedOutputStream)是Java IO流中的一种类型,它们提供了缓冲功能,可以提高读写操作的效率。

字节缓冲流继承自字节流(InputStreamOutputStream),它们通过在内存中创建一个缓冲区(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();
        }
    }
}

结果:

2023年7月25日,File类,IO流_第4张图片

注意:

在数据流中,读取顺序必须与写入顺序保持一致。这是因为数据流中的数据是按照特定的格式写入的,如果读取顺序与写入顺序不一致,就会导致数据读取错误或解析错误。 当使用数据流进行读取时,数据流会按照先后顺序解析数据,并将其转换为相应的数据类型。如果读取顺序与写入顺序不一致,例如尝试先读取一个整数,然后读取一个字符串,这样会导致读取出的数据类型不匹配,造成解析错误。

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);
        }
    }
}