Java NIO.2:文件系统API

好的,各位亲爱的程序员朋友们,欢迎来到“Java NIO.2:文件系统API”的探险之旅!准备好了吗?让我们一起扬帆起航,拨开迷雾,揭开Java NIO.2文件系统API的神秘面纱。

开场白:文件,程序员的“老朋友” 🤝

在程序员的世界里,文件就像我们形影不离的老朋友,存储着代码、数据、配置,承载着我们的智慧和汗水。从最初的简单文本文件,到复杂的二进制文件、数据库文件,我们无时无刻不在和它们打交道。然而,传统的Java IO操作,就像老牛拉破车,效率低下,阻塞式操作让我们的程序“卡顿”不堪。

终于,Java NIO.2(New IO 2)带着革命性的文件系统API闪亮登场,它像一辆法拉利跑车,让我们在文件操作的道路上风驰电掣,体验前所未有的速度与激情!🚀

第一站:NIO.2 核心概念速览 🗺️

在深入探索之前,我们先来一张“藏宝图”,了解NIO.2文件系统API中的几个关键概念:

概念 解释 备注
Path 代表文件或目录的路径,是java.nio.file包下的接口,取代了java.io.File。它不仅仅是一个字符串,而是一个对象,包含更多信息和操作。 想象一下,Path就像一个指向宝藏的罗盘,指引我们找到文件或目录。
FileSystem 代表文件系统的抽象,可以通过FileSystems.getDefault()获取默认文件系统。 FileSystem就像一个国家,包含各种文件和目录,而Path就是这个国家的地址。
FileStore 代表文件系统的存储设备,例如硬盘、U盘、网络共享等。 FileStore就像一个仓库,存储着我们的文件。
WatchService 允许我们监控文件或目录的变化,例如创建、删除、修改等。 WatchService就像一个警卫,时刻守护着我们的文件,一旦发生变化,立刻通知我们。
DirectoryStream 用于遍历目录中的文件,可以过滤文件类型。 DirectoryStream就像一个探险家,带着我们深入目录,寻找我们需要的宝藏。
Files 提供各种静态方法,用于文件和目录的操作,例如创建、删除、复制、移动、读取、写入等。 Files就像一个工具箱,里面装满了各种工具,帮助我们轻松完成文件操作。
AsynchronousFileChannel 异步文件通道,用于执行异步的文件读写操作,避免阻塞主线程。 AsynchronousFileChannel就像一个快递员,悄悄地帮我们处理文件读写,不会打扰我们的工作。
FileVisitor 用于遍历文件树,可以自定义遍历逻辑。 FileVisitor就像一个导游,带着我们游览整个文件树,我们可以自定义游览路线和景点。
AclFileAttributeView 用于管理文件的访问控制列表(ACL),控制不同用户的访问权限。 AclFileAttributeView就像一个门卫,控制谁可以进入我们的文件,保障文件的安全。

第二站:Path:文件路径的“新宠” 💖

java.nio.file.Path接口是NIO.2文件系统API的核心,它取代了java.io.FilePath不仅仅是一个字符串,而是一个对象,包含更多信息和操作。

  • 创建Path对象

    • Paths.get(String first, String... more):从字符串创建Path对象。

      Path path = Paths.get("C:", "Users", "username", "Documents", "myfile.txt"); // Windows
      Path path2 = Paths.get("/home", "username", "documents", "myfile.txt"); // Linux/macOS
    • FileSystem.getPath(String first, String... more):从文件系统创建Path对象。

      FileSystem fileSystem = FileSystems.getDefault();
      Path path3 = fileSystem.getPath("myfile.txt");
  • Path的常用方法

    方法 描述 示例
    getFileName() 返回Path表示的文件或目录的名称。 Path path = Paths.get("/home/user/myfile.txt"); String fileName = path.getFileName().toString(); // fileName = "myfile.txt"
    getParent() 返回Path的父路径。 Path path = Paths.get("/home/user/myfile.txt"); Path parent = path.getParent(); // parent = "/home/user"
    getRoot() 返回Path的根路径。 Path path = Paths.get("/home/user/myfile.txt"); Path root = path.getRoot(); // root = "/"
    getNameCount() 返回Path中名称元素的数量。 Path path = Paths.get("/home/user/myfile.txt"); int count = path.getNameCount(); // count = 3
    getName(int index) 返回Path中指定索引位置的名称元素。 Path path = Paths.get("/home/user/myfile.txt"); Path name = path.getName(0); // name = "home"
    subpath(int beginIndex, int endIndex) 返回Path中指定范围的子路径。 Path path = Paths.get("/home/user/myfile.txt"); Path sub = path.subpath(0, 2); // sub = "home/user"
    isAbsolute() 判断Path是否是绝对路径。 Path path = Paths.get("/home/user/myfile.txt"); boolean absolute = path.isAbsolute(); // absolute = true
    toAbsolutePath() 返回Path的绝对路径。 Path path = Paths.get("myfile.txt"); Path absolutePath = path.toAbsolutePath(); // 返回绝对路径
    normalize() 规范化Path,移除冗余的名称元素(例如...)。 Path path = Paths.get("/home/user/.././myfile.txt"); Path normalized = path.normalize(); // normalized = "/home/myfile.txt"
    resolve(Path other) Path解析为另一个Path的相对路径。 Path path = Paths.get("/home/user"); Path other = Paths.get("myfile.txt"); Path resolved = path.resolve(other); // resolved = "/home/user/myfile.txt"
    relativize(Path other) 返回一个Path,它是Path到另一个Path的相对路径。 Path path = Paths.get("/home/user"); Path other = Paths.get("/home/user/myfile.txt"); Path relative = path.relativize(other); // relative = "myfile.txt"
    toUri() Path转换为URI Path path = Paths.get("/home/user/myfile.txt"); URI uri = path.toUri(); // uri = "file:///home/user/myfile.txt"
    toFile() Path转换为File Path path = Paths.get("/home/user/myfile.txt"); File file = path.toFile();

    示例代码

    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class PathExample {
        public static void main(String[] args) {
            Path path = Paths.get("/home/user/documents/myfile.txt");
    
            System.out.println("File Name: " + path.getFileName());
            System.out.println("Parent: " + path.getParent());
            System.out.println("Root: " + path.getRoot());
            System.out.println("Name Count: " + path.getNameCount());
            System.out.println("Absolute Path: " + path.toAbsolutePath());
            System.out.println("URI: " + path.toUri());
        }
    }

第三站:Files:文件操作的“瑞士军刀” 🧰

java.nio.file.Files类提供了大量的静态方法,用于文件和目录的操作,就像一个百宝箱,里面装满了各种工具,帮助我们轻松完成文件操作。

  • 文件和目录的创建与删除

    • createDirectory(Path dir, FileAttribute<?>... attrs):创建目录。
    • createDirectories(Path dir, FileAttribute<?>... attrs):创建多级目录。
    • createFile(Path file, FileAttribute<?>... attrs):创建文件。
    • delete(Path path):删除文件或目录(如果目录为空)。
    • deleteIfExists(Path path):如果文件或目录存在,则删除。
  • 文件的复制与移动

    • copy(Path source, Path target, CopyOption... options):复制文件或目录。
    • move(Path source, Path target, CopyOption... options):移动文件或目录。

    CopyOptionStandardCopyOption

    • REPLACE_EXISTING:如果目标文件已存在,则替换它。
    • COPY_ATTRIBUTES:复制文件属性(例如最后修改时间)。
    • ATOMIC_MOVE:原子性地移动文件(如果文件系统支持)。
  • 文件的读取与写入

    • readAllBytes(Path path):将文件内容读取到字节数组中。
    • readAllLines(Path path, Charset charset):将文件内容读取到字符串列表中。
    • write(Path path, byte[] bytes, OpenOption... options):将字节数组写入文件。
    • write(Path path, Iterable<? extends CharSequence> lines, Charset charset, OpenOption... options):将字符串列表写入文件。

    OpenOptionStandardOpenOption

    • APPEND:追加到文件末尾。
    • CREATE:如果文件不存在,则创建它。
    • CREATE_NEW:如果文件已存在,则抛出异常。
    • TRUNCATE_EXISTING:将文件截断为0字节。
  • 文件属性的访问

    • getAttribute(Path path, String attribute, LinkOption... options):获取文件属性。
    • setAttribute(Path path, String attribute, Object value, LinkOption... options):设置文件属性。
    • getFileAttributeView(Path path, Class<V> type, LinkOption... options):获取文件属性视图。
  • 其他常用方法

    • exists(Path path, LinkOption... options):判断文件或目录是否存在。
    • notExists(Path path, LinkOption... options):判断文件或目录是否不存在。
    • isDirectory(Path path, LinkOption... options):判断是否是目录。
    • isRegularFile(Path path, LinkOption... options):判断是否是普通文件。
    • isSymbolicLink(Path path):判断是否是符号链接。
    • isReadable(Path path):判断是否可读。
    • isWritable(Path path):判断是否可写。
    • isExecutable(Path path):判断是否可执行。
    • size(Path path):返回文件大小。
    • getLastModifiedTime(Path path, LinkOption... options):返回最后修改时间。
    • setLastModifiedTime(Path path, FileTime time):设置最后修改时间。
    • getOwner(Path path, LinkOption... options):返回文件所有者。
    • setOwner(Path path, UserPrincipal owner):设置文件所有者。

示例代码

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

public class FilesExample {
    public static void main(String[] args) {
        Path file = Paths.get("myfile.txt");

        try {
            // 创建文件
            Files.createFile(file);

            // 写入数据
            List<String> lines = Arrays.asList("Hello", "World!");
            Files.write(file, lines, StandardCharsets.UTF_8);

            // 读取数据
            List<String> readLines = Files.readAllLines(file, StandardCharsets.UTF_8);
            System.out.println("Read lines: " + readLines);

            // 获取文件大小
            long size = Files.size(file);
            System.out.println("File size: " + size);

            // 删除文件
            Files.delete(file);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

第四站:WatchService:文件监控的“千里眼” 👁️

java.nio.file.WatchService允许我们监控文件或目录的变化,例如创建、删除、修改等。它就像一个警卫,时刻守护着我们的文件,一旦发生变化,立刻通知我们。

  • 创建WatchService

    WatchService watchService = FileSystems.getDefault().newWatchService();
  • 注册PathWatchService

    Path directory = Paths.get("/home/user/documents");
    directory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);

    StandardWatchEventKinds

    • ENTRY_CREATE:文件或目录被创建。
    • ENTRY_DELETE:文件或目录被删除。
    • ENTRY_MODIFY:文件或目录被修改。
    • OVERFLOW:事件可能丢失或被丢弃。
  • 监听事件

    while (true) {
        WatchKey key;
        try {
            key = watchService.take();
        } catch (InterruptedException e) {
            return;
        }
    
        for (WatchEvent<?> event : key.pollEvents()) {
            WatchEvent.Kind<?> kind = event.kind();
    
            if (kind == StandardWatchEventKinds.OVERFLOW) {
                continue;
            }
    
            WatchEvent<Path> ev = (WatchEvent<Path>) event;
            Path filename = ev.context();
    
            System.out.println(kind.name() + ": " + filename);
        }
    
        boolean valid = key.reset();
        if (!valid) {
            break;
        }
    }

示例代码

import java.io.IOException;
import java.nio.file.*;

public class WatchServiceExample {
    public static void main(String[] args) throws IOException, InterruptedException {
        Path directory = Paths.get("/tmp/watchtest");
        if (!Files.exists(directory)) {
            Files.createDirectory(directory);
        }

        WatchService watchService = FileSystems.getDefault().newWatchService();
        directory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);

        System.out.println("Watching directory: " + directory);

        while (true) {
            WatchKey key;
            try {
                key = watchService.take();
            } catch (InterruptedException e) {
                return;
            }

            for (WatchEvent<?> event : key.pollEvents()) {
                WatchEvent.Kind<?> kind = event.kind();

                if (kind == StandardWatchEventKinds.OVERFLOW) {
                    continue;
                }

                WatchEvent<Path> ev = (WatchEvent<Path>) event;
                Path filename = ev.context();

                System.out.println(kind.name() + ": " + filename);
            }

            boolean valid = key.reset();
            if (!valid) {
                break;
            }
        }
    }
}

第五站:AsynchronousFileChannel:异步IO的“闪电侠” ⚡

java.nio.channels.AsynchronousFileChannel允许我们执行异步的文件读写操作,避免阻塞主线程。它就像一个快递员,悄悄地帮我们处理文件读写,不会打扰我们的工作。

  • 创建AsynchronousFileChannel

    Path file = Paths.get("myfile.txt");
    AsynchronousFileChannel channel = AsynchronousFileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
  • 异步读取数据

    ByteBuffer buffer = ByteBuffer.allocate(1024);
    channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            System.out.println("Read " + result + " bytes");
            attachment.flip();
            byte[] data = new byte[attachment.limit()];
            attachment.get(data);
            System.out.println(new String(data));
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
            System.err.println("Read failed: " + exc.getMessage());
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
  • 异步写入数据

    ByteBuffer buffer = ByteBuffer.wrap("Hello, Asynchronous World!".getBytes());
    channel.write(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
        @Override
        public void completed(Integer result, ByteBuffer attachment) {
            System.out.println("Written " + result + " bytes");
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void failed(Throwable exc, ByteBuffer attachment) {
            System.err.println("Write failed: " + exc.getMessage());
            try {
                channel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });

示例代码

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class AsynchronousFileChannelExample {
    public static void main(String[] args) throws IOException, InterruptedException {
        Path file = Paths.get("asyncfile.txt");

        AsynchronousFileChannel channel = AsynchronousFileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        ByteBuffer buffer = ByteBuffer.wrap("Hello, Asynchronous World!".getBytes());

        channel.write(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                System.out.println("Written " + result + " bytes");

                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                channel.read(readBuffer, 0, readBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer attachment) {
                        System.out.println("Read " + result + " bytes");
                        attachment.flip();
                        byte[] data = new byte[attachment.limit()];
                        attachment.get(data);
                        System.out.println(new String(data));
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        System.err.println("Read failed: " + exc.getMessage());
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });

            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                System.err.println("Write failed: " + exc.getMessage());
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        // 保持程序运行,直到异步操作完成
        Thread.sleep(2000);
    }
}

总结:NIO.2,文件操作的未来! 🚀

Java NIO.2文件系统API为我们带来了全新的文件操作体验,它更加灵活、高效、易用,让我们能够更好地管理和操作文件。从PathFiles,从WatchServiceAsynchronousFileChannel,NIO.2提供了丰富的工具和API,帮助我们轻松应对各种文件操作场景。

希望通过这次探险之旅,你已经对Java NIO.2文件系统API有了更深入的了解。现在,拿起你的键盘,开始使用NIO.2,让你的文件操作飞起来吧!

记住,NIO.2不仅仅是一个API,更是一种思想,一种拥抱未来的态度。让我们一起学习、探索、进步,共同迎接更加美好的编程未来! 💻🎉

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注