34.【Java】I/O流(下):NIO与文件操作

BIO、NIO、AIO实战对比:从线程爆炸到内核代工的I/O演进 I/O模型是高性能网络编程的底层基石,本质是操作系统与用户程序之间数据搬运方式的契约。其核心原理在于阻塞/非阻塞、同步/异步的组合差异:BIO依赖线程一对一等待,资源消耗呈线性增长;NIO通过事件驱动与多路复用(如epoll)实现单线程监控海量连接,大幅提升CPU与内存利用率;AIO则将读写操作完全委托内核异步完成,进一步消除用户态轮询开销。该技术路径直接决定系统在高并发场景下的扩展能力与稳定性,广泛应用于Web服务器(Tomcat/NIO)、通信框架(Netty)、中间件(Redis单线程+epoll)及 阅读详情

摘要: 本文全面介绍Java NIO.2文件操作API,涵盖从传统I/O到现代NIO.2的升级路径。详细讲解Path接口、Files工具类的核心用法,包括文件创建、读写、复制、移动、删除等基础操作,以及目录遍历、文件属性获取、与旧API互操作等高级功能。通过丰富的代码示例和综合工具类展示,帮助开发者掌握更简洁、高效的Java文件处理方式,提升日常开发效率。

关键词: Java NIO.2, Path接口, Files工具类, 文件操作, 目录遍历


在上一篇文章中,我们学习了 Java I/O 流的基础:字节流、字符流、缓冲流和转换流。你应该已经能用 FileInputStream、BufferedReader 等工具读写文件了。

但你可能也隐隐感觉到:那些 API 用起来有点“笨重”。

比如,要读取一个文本文件的所有行,你需要嵌套多层装饰器:new BufferedReader(new InputStreamReader(new FileInputStream("test.txt"), "UTF-8"))——这一串写起来既冗长又容易出错。而且,古老的 File 类在文件操作上也有很多不足:它只能检查文件是否存在、获取文件名,但复制、移动、遍历目录这些常见操作都需要你自己写循环和递归,非常不方便。

好在 Java 早在 2011 年(JDK 7)就引入了一套全新的文件 I/O API,叫做 NIO.2(New I/O 2.0)。它彻底改变了 Java 处理文件和目录的方式,提供了:

  • 更简洁、更直观的 API(Path 接口和 Files 工具类)
  • 支持符号链接、文件属性、目录流等高级特性
  • 更高效的异步 I/O 能力(本篇不涉及)

今天,我们就来全面学习这套现代化的文件操作 API。以后你写文件操作,几乎可以告别传统的 File 和 FileInputStream 了。


1. 从 File 到 Path —— 更现代的路径表示

在 NIO.2 中,文件和目录的路径不再用 File 类表示,而是用 Path 接口。Path 比 File 更灵活,支持多种文件系统(比如 ZIP 文件系统),并且提供了丰富的方法来操作路径。

1.1 创建 Path 对象

使用 Paths.get() 静态方法:

import java.nio.file.Path;
import java.nio.file.Paths;

public class PathDemo {
    public static void main(String[] args) {
        // 用字符串创建 Path
        Path path1 = Paths.get("/home/user/test.txt");
        Path path2 = Paths.get("docs", "readme.md");  // 多参数,自动拼接
        Path path3 = Paths.get(".");  // 当前目录
        
        System.out.println(path1);       // /home/user/test.txt
        System.out.println(path2);       // docs/readme.md
        System.out.println(path3.toAbsolutePath());  // 当前工作目录的绝对路径
    }
}

在 Windows 上,路径分隔符是 \,但你可以在代码里统一使用 /,Paths.get() 会自动适配操作系统。

1.2 Path 的常用方法
Path p = Paths.get("/home/user/projects/demo/config.properties");

System.out.println("文件名:" + p.getFileName());           // config.properties
System.out.println("父路径:" + p.getParent());             // /home/user/projects/demo
System.out.println("根路径:" + p.getRoot());               // /
System.out.println("名称元素个数:" + p.getNameCount());    // 5
System.out.println("第2个元素:" + p.getName(1));           // user

// 拼接路径
Path newPath = p.resolveSibling("backup.properties");
System.out.println("同目录下其他文件:" + newPath);  // /home/user/projects/demo/backup.properties

// 规范化(去除 . 和 ..)
Path normalized = Paths.get("/home/../user/./test.txt").normalize();
System.out.println(normalized);  // /user/test.txt

Path 的 API 设计得很直观,方法名就能看出用途。


2. Files 工具类 —— 文件操作的“瑞士军刀”

Files 是 NIO.2 的核心工具类,提供了海量的静态方法,用于创建、复制、移动、删除、读取、写入文件,以及操作目录和文件属性。它比传统的 File 强大太多了。

重要:Files 类所有方法在发生 I/O 错误时都会抛出 IOException(受检异常),所以调用时需要用 try-catch 或 throws。

2.1 文件存在性检查和创建
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;

public class FilesCheckDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");

        // 检查文件是否存在
        if (Files.exists(path)) {
            System.out.println("文件存在");
        } else {
            System.out.println("文件不存在");
        }

        // 检查是否为普通文件(不是目录)
        boolean isRegular = Files.isRegularFile(path);
        // 检查是否为目录
        boolean isDir = Files.isDirectory(path);
        // 检查是否可读、可写、可执行
        boolean readable = Files.isReadable(path);
        boolean writable = Files.isWritable(path);
        boolean executable = Files.isExecutable(path);
    }
}
2.2 创建文件、目录和临时文件
// 创建空文件(如果不存在)
Path file = Paths.get("newfile.txt");
Files.createFile(file);   // 如果文件已存在会抛出 FileAlreadyExistsException

// 创建目录(单层)
Path dir = Paths.get("mydir");
Files.createDirectory(dir);  // 如果父目录不存在或目录已存在会抛异常

// 创建多级目录(父目录不存在时自动创建)
Path deepDir = Paths.get("a/b/c/d");
Files.createDirectories(deepDir);  // 推荐使用这个

// 创建临时文件/目录
Path tempFile = Files.createTempFile("prefix", ".tmp");
Path tempDir = Files.createTempDirectory("my-temp");
System.out.println("临时文件:" + tempFile);
System.out.println("临时目录:" + tempDir);

3. 读取和写入文件 —— 最常用的操作

NIO.2 让文件读写变得异常简单。你不再需要手动处理 InputStream、Reader 和循环了。

3.1 读取所有字节/行(小文件)

对于小文件(比如配置文件、日志片段),你可以一次性读取所有内容:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.List;

public class ReadWriteDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");

        // 1. 读取所有字节(返回 byte[])
        byte[] bytes = Files.readAllBytes(path);
        String content = new String(bytes, "UTF-8");
        System.out.println("内容:" + content);

        // 2. 读取所有行(返回 List<String>)
        List<String> lines = Files.readAllLines(path, java.nio.charset.StandardCharsets.UTF_8);
        lines.forEach(System.out::println);

        // 3. 写入字节
        String newContent = "Hello, NIO!";
        Files.write(path, newContent.getBytes("UTF-8"));

        // 4. 写入行(覆盖)
        List<String> newLines = List.of("第一行", "第二行", "第三行");
        Files.write(path, newLines, java.nio.charset.StandardCharsets.UTF_8);

        // 5. 追加到文件
        Files.write(path, "追加的内容".getBytes("UTF-8"), 
                    java.nio.file.StandardOpenOption.APPEND);
    }
}

Files.write() 默认是覆盖模式。如果想追加,传入 StandardOpenOption.APPEND。

3.2 流式读取(大文件)

对于大文件,一次性读取所有行可能会占用大量内存。这时可以用 Files.lines() 方法,它返回一个 Stream<String>,可以逐行处理,不会一次性加载全部内容到内存。

import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

public class StreamLinesDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("large_log.txt");

        // 使用 try-with-resources 确保 Stream 被关闭
        try (var lines = Files.lines(path, StandardCharsets.UTF_8)) {
            // 只处理前 10 行
            lines.limit(10)
                 .filter(line -> line.contains("ERROR"))
                 .forEach(System.out::println);
        }
    }
}

Files.lines() 返回的 Stream 是延迟加载的,并且必须用 try-with-resources 关闭,否则可能造成资源泄漏。

3.3 读写二进制文件

同样用 readAllBytes() 和 write() 即可:

Path src = Paths.get("image.jpg");
Path dest = Paths.get("copy.jpg");

byte[] data = Files.readAllBytes(src);
Files.write(dest, data);  // 二进制复制

4. 复制、移动、删除文件

4.1 复制文件
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");

// 复制文件(如果目标已存在会抛出 FileAlreadyExistsException)
Files.copy(source, target);

// 覆盖已存在的目标
Files.copy(source, target, java.nio.file.StandardCopyOption.REPLACE_EXISTING);

// 复制到目录(保持文件名)
Path destDir = Paths.get("backup/");
Files.copy(source, destDir.resolve(source.getFileName()));

Files.copy() 也支持复制目录(但不会递归复制内容,需要自己遍历)。

4.2 移动文件(或重命名)
Path source = Paths.get("data.txt");
Path target = Paths.get("archive/data.txt");

// 移动文件(如果目标已存在会抛异常)
Files.move(source, target);

// 覆盖已有文件,并原子地执行移动(如果文件系统支持)
Files.move(source, target, 
           java.nio.file.StandardCopyOption.REPLACE_EXISTING,
           java.nio.file.StandardCopyOption.ATOMIC_MOVE);

移动和复制都可以设置选项:REPLACE_EXISTING(覆盖)、ATOMIC_MOVE(原子移动)、COPY_ATTRIBUTES(复制文件属性)。

4.3 删除文件或目录
Path file = Paths.get("temp.txt");

// 删除文件(如果文件不存在抛出 NoSuchFileException)
Files.delete(file);

// 安全删除:如果文件不存在,不抛出异常,返回 boolean
boolean deleted = Files.deleteIfExists(file);
System.out.println("是否删除成功:" + deleted);

// 删除目录(必须为空)
Path emptyDir = Paths.get("mydir");
Files.deleteIfExists(emptyDir);

要删除非空目录,需要递归遍历删除所有子文件和子目录(后面会讲如何遍历)。


5. 目录遍历 —— 探索文件树

NIO.2 提供了强大的目录遍历能力,可以很方便地列出目录内容、搜索文件、递归处理整个目录树。

5.1 列出目录内容(浅层)
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.util.stream.Stream;

public class ListDirDemo {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get(".");

        // 方式一:Files.list() 返回 Stream<Path>(只包含一层)
        try (Stream<Path> entries = Files.list(dir)) {
            entries.forEach(System.out::println);
        }

        // 方式二:只遍历目录(过滤)
        try (Stream<Path> entries = Files.list(dir)) {
            entries.filter(Files::isDirectory)
                   .forEach(System.out::println);
        }
    }
}

Files.list() 不是递归的,只列出当前目录的直接子项。

5.2 递归遍历整个目录树

如果需要递归遍历所有子目录,使用 Files.walk()。它返回一个 Stream<Path>,深度优先遍历整个目录树。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;

public class WalkDemo {
    public static void main(String[] args) throws IOException {
        Path start = Paths.get("src");

        // 遍历 src 目录下的所有文件(包括子目录)
        try (var paths = Files.walk(start)) {
            paths.filter(Files::isRegularFile)
                 .forEach(System.out::println);
        }

        // 限制遍历深度(比如只遍历两层)
        try (var paths = Files.walk(start, 2)) {
            paths.forEach(System.out::println);
        }
    }
}
5.3 搜索文件(Find)

Files.find() 方法可以结合深度和谓词(条件)来搜索文件。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.io.IOException;
import java.nio.file.attribute.BasicFileAttributes;

public class FindDemo {
    public static void main(String[] args) throws IOException {
        Path start = Paths.get(".");

        // 搜索所有扩展名为 .txt 或 .log 的文件
        try (var paths = Files.find(start, Integer.MAX_VALUE,
                (path, attrs) -> {
                    String name = path.getFileName().toString();
                    return name.endsWith(".txt") || name.endsWith(".log");
                })) {
            paths.forEach(System.out::println);
        }

        // 搜索最近 7 天内修改过的文件(需要 BasicFileAttributes)
        long sevenDaysAgo = System.currentTimeMillis() - 7 * 24 * 3600 * 1000;
        try (var paths = Files.find(start, Integer.MAX_VALUE,
                (path, attrs) -> attrs.lastModifiedTime().toMillis() > sevenDaysAgo)) {
            paths.forEach(System.out::println);
        }
    }
}

Files.find() 比 walk() + filter() 更高效,因为它可以在遍历过程中就进行筛选,且能访问文件属性。

5.4 用 FileVisitor 进行更精细的控制

如果需要更复杂的遍历逻辑(比如在遍历中删除文件、处理符号链接等),可以使用 Files.walkFileTree() + SimpleFileVisitor。这是传统方式,适合需要精细控制的场景。

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

public class DeleteDirVisitor extends SimpleFileVisitor<Path> {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        Files.delete(file);  // 删除文件
        System.out.println("删除文件:" + file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        Files.delete(dir);   // 删除目录(此时已空)
        System.out.println("删除目录:" + dir);
        return FileVisitResult.CONTINUE;
    }

    public static void main(String[] args) throws IOException {
        Path toDelete = Paths.get("temp");
        Files.walkFileTree(toDelete, new DeleteDirVisitor());
        System.out.println("删除完成!");
    }
}

这种方式的优势是你能精确控制访问每个文件/目录时的行为(继续、跳过、终止),还能处理异常。


6. 文件属性 —— 获取更多元数据

NIO.2 提供了统一的文件属性访问接口,支持不同操作系统的特定属性。

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

public class AttrDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("test.txt");

        // 获取基础属性(所有文件系统都支持)
        BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
        System.out.println("创建时间:" + attrs.creationTime());
        System.out.println("最后修改时间:" + attrs.lastModifiedTime());
        System.out.println("文件大小:" + attrs.size());
        System.out.println("是否是目录:" + attrs.isDirectory());

        // 获取 Posix 属性(Linux/macOS 等 Unix 系统)
        if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
            java.nio.file.attribute.PosixFileAttributes posixAttrs = 
                Files.readAttributes(path, java.nio.file.attribute.PosixFileAttributes.class);
            System.out.println("权限:" + java.nio.file.attribute.PosixFilePermissions.toString(posixAttrs.permissions()));
            System.out.println("所有者:" + posixAttrs.owner());
        }

        // 设置文件权限(仅在 Posix 系统有效)
        if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
            java.nio.file.attribute.PosixFilePermission ownerRead = java.nio.file.attribute.PosixFilePermission.OWNER_READ;
            java.util.Set<java.nio.file.attribute.PosixFilePermission> perms = 
                java.nio.file.attribute.PosixFilePermissions.fromString("rw-r--r--");
            Files.setPosixFilePermissions(path, perms);
        }
    }
}

在 Windows 下,你可以通过 DosFileAttributes 获取隐藏、只读等属性。


7. 与旧 I/O 的互操作

NIO.2 与传统的 File 和流可以互相转换:

  • Path → File:path.toFile()
  • File → Path:file.toPath()
  • Path → InputStream/OutputStream:Files.newInputStream(path) 和 Files.newOutputStream(path)
  • Path → Reader/Writer:Files.newBufferedReader(path) 和 Files.newBufferedWriter(path)
// 传统 File 转 Path
File oldFile = new File("test.txt");
Path path = oldFile.toPath();

// 使用 Files 工具类创建流
try (InputStream in = Files.newInputStream(path);
     BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
    // 混用 NIO 和传统流 API
}

你可以逐步迁移代码,不用一次性重写所有旧的 I/O 代码。


8. 综合示例 —— 一个完整的文件工具类

我们把今天学到的常用操作封装成一个工具类,方便以后复用:

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
import java.util.stream.Stream;

public class FileUtil {
    
    /**
     * 读取文件所有行(小文件)
     */
    public static List<String> readAllLines(String path) throws IOException {
        return Files.readAllLines(Paths.get(path), StandardCharsets.UTF_8);
    }
    
    /**
     * 写入文件(覆盖)
     */
    public static void writeAllLines(String path, List<String> lines) throws IOException {
        Files.write(Paths.get(path), lines, StandardCharsets.UTF_8);
    }
    
    /**
     * 追加一行到文件末尾
     */
    public static void appendLine(String path, String line) throws IOException {
        Path p = Paths.get(path);
        // 确保父目录存在
        if (p.getParent() != null) {
            Files.createDirectories(p.getParent());
        }
        String content = line + System.lineSeparator();
        Files.write(p, content.getBytes(StandardCharsets.UTF_8), 
                    StandardOpenOption.APPEND, StandardOpenOption.CREATE);
    }
    
    /**
     * 复制文件,自动创建目标目录
     */
    public static void copyFile(String source, String target) throws IOException {
        Path src = Paths.get(source);
        Path dest = Paths.get(target);
        // 确保目标目录存在
        if (dest.getParent() != null) {
            Files.createDirectories(dest.getParent());
        }
        Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
    }
    
    /**
     * 移动文件,自动创建目标目录
     */
    public static void moveFile(String source, String target) throws IOException {
        Path src = Paths.get(source);
        Path dest = Paths.get(target);
        if (dest.getParent() != null) {
            Files.createDirectories(dest.getParent());
        }
        Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING);
    }
    
    /**
     * 删除目录及所有内容(递归)
     */
    public static void deleteDirectory(String dirPath) throws IOException {
        Path root = Paths.get(dirPath);
        if (!Files.exists(root)) return;
        
        try (Stream<Path> walk = Files.walk(root)) {
            // 必须先删除子文件,再删除目录本身(逆序)
            walk.sorted((a, b) -> b.toString().compareTo(a.toString()))
                .forEach(p -> {
                    try {
                        Files.deleteIfExists(p);
                    } catch (IOException e) {
                        throw new RuntimeException("删除失败:" + p, e);
                    }
                });
        }
    }
    
    /**
     * 搜索目录下所有匹配扩展名的文件
     */
    public static List<Path> searchByExtension(String rootPath, String extension) throws IOException {
        Path start = Paths.get(rootPath);
        try (Stream<Path> paths = Files.find(start, Integer.MAX_VALUE,
                (p, attrs) -> p.getFileName().toString().endsWith(extension))) {
            return paths.toList();
        }
    }
    
    /**
     * 获取文件大小(人性化显示)
     */
    public static String getHumanReadableSize(String path) throws IOException {
        long size = Files.size(Paths.get(path));
        String[] units = {"B", "KB", "MB", "GB", "TB"};
        int idx = 0;
        double s = size;
        while (s >= 1024 && idx < units.length - 1) {
            s /= 1024;
            idx++;
        }
        return String.format("%.2f %s", s, units[idx]);
    }
    
    public static void main(String[] args) throws IOException {
        // 测试各个方法
        appendLine("logs/app.log", "程序启动于 " + java.time.LocalDateTime.now());
        copyFile("config.properties", "backup/config_backup.properties");
        System.out.println("文件大小:" + getHumanReadableSize("config.properties"));
        
        List<Path> txtFiles = searchByExtension(".", ".txt");
        System.out.println("找到 " + txtFiles.size() + " 个 txt 文件");
    }
}

9. 传统 I/O vs NIO.2 —— 何时用哪个?

场景推荐
新项目、新代码NIO.2 + Files 工具类,更简洁、更现代
需要处理大文件、高性能NIO.2(支持内存映射、异步 I/O)
需要与旧的 File 生态对接NIO.2 可互操作
需要手动控制缓冲、底层细节传统流可能更灵活,但通常不必要
简单读写小文件Files.readAllLines() / Files.write()

总结:从现在开始,优先使用 NIO.2 的 Path 和 Files,它们会让你的文件操作代码更短、更清晰、更不容易出错。


10. 今天的总结

今天我们全面学习了 NIO.2 的文件操作:

  • Path:代表文件或目录路径,提供了丰富的路径操作方法。
  • Files:包含了几乎所有文件操作的静态方法:exists、createFile、createDirectories、copy、move、delete、readAllBytes、readAllLines、write 等。
  • Files.lines():流式读取大文件,逐行处理。
  • 目录遍历:Files.list()(一层)、Files.walk()(递归)、Files.find()(带条件搜索)、walkFileTree()(精细控制)。
  • 文件属性:通过 Files.readAttributes() 获取创建时间、修改时间、大小、权限等。
  • 与传统 I/O 互操作:toFile() / toPath() / Files.newInputStream() 等。

NIO.2 让 Java 的文件操作从“繁琐”变成了“简洁”。现在你已经掌握了最现代、最推荐的文件处理方式。


动手试试

  1. 写一个程序,把当前目录下所有 .java 文件复制到 backup 目录下,保持原有的目录结构(提示:用 Files.walk() 和 Files.copy())。

  2. 写一个 FileSizeCounter 方法,遍历一个目录,计算所有文件的总大小,并输出每个子目录的大小(类似 Linux 的 du 命令)。

  3. 写一个程序,从用户输入一个目录路径,然后定期(每隔 10 秒)检查该目录下是否有新增的 .tmp 文件,如果有则打印文件名(提示:用 WatchService,这是 NIO.2 的文件监听机制,可以选做)。

  4. 把你之前的学生管理系统中的数据存储功能改为用 Files.write() 和 Files.readAllLines() 保存和加载数据(比如保存到 students.txt 中),体会用 NIO.2 做持久化是多么简单。

I/O 流和 NIO 的内容到这里就结束了。下一篇文章,我们将学习 日期与时间 API(java.time)——这是 Java 8 引入的一套全新的、设计优秀的日期时间库,它将彻底取代旧的 Date 和 Calendar。

我们下一篇见。😃

📌 获取本系列示例代码请访问 GitCode。

JAVA中的I/O模型-多路复用 环境相关介绍: 1.8 - JDK (1.6前后有版本变化) CentOS Linux release 7.8.2003 (Core) 多路复用 及 Reactor模式 为何需要   在上一节中我们讲解到NIO中如何解决阻塞以及更好的进行客户端数据的读取数据。   但是同样也依旧会存在以下问题: 代码中维护客户端连接。 服务器在不断的将客户端FD传递进行轮询判断是否有事件(涉及线程上下文切换)。 Demo 11 public class Multiplexing { 12 13 阅读详情

相关推荐

34、Java NIO 包全面解析

本文全面解析了Java NIO包的核心功能和使用场景,包括快速文件读写、异步I/O、缓冲区操作、通道机制、字符编码转换等内容。通过示例代码和流程图,详细介绍了NIO在处理大文件、网络通信和字符编码转换方面的优势。同时总结了NIO的主要特性和实际应用注意事项,帮助开发者提升程序性能和并发处理能力。

3a9bq4r8t2y的博客 72

Java中的NIO与高性能I/O操作

Java中的NIO与高性能I/O操作 大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿! 在Java中,传统的I/O操作基于阻塞模型,在处理大量并发连接时性能往往会成为瓶颈。Java NIO(New Input/Output)引入了一种非阻塞的I/O操作方式,能够显著提高高并发场景下的性能。本文...

weixin_42320036的博客 159

长期换衣行人重识别(Long-Term Clothes-Changing Person Reid)数据集汇总

长期换衣行人重识别任务中目前常用的数据集总览目前换衣数据集的主要问题数据集换衣/总体情况集统计DeepChange(2021)LaST(2021)COCAS(2020)VC-Clothes(2020)LTCC(2020)PRCC(2019)Celebrities-reID(2019) 总览 在长期换衣行人重识别的任务当中,数据集的获取(尤其是对于学术环境下)是一个难点,主要的困难有以下几点: 采集大规模的数据集比较困难。 采集不同的衣服的数据集比较困难。 采集不同衣服不同角度的数据集比较困难。 目前也有

KingsMan666的博客 8378

Java中的异步非阻塞I/O操作

Java中的异步非阻塞I/O操作大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们来探讨一下Java中的异步非阻塞I/O操作。一、异步非阻塞I/O简介异步非阻塞I/O(Asynchronous Non-Blocking I/O)是指在执行I/O操作时,不会阻塞线程。传统的阻塞I/O操作会...

weixin_36882596的博客 123

【Kingbase人大金仓】授权文件更换

本文介绍了金仓数据库授权文件更换的步骤:1.通过ksql或客户端工具查询数据库版本;2.在官网下载对应版本的license.bat文件;3.替换前先备份原授权文件(使用tar命令压缩),再用新文件覆盖原文件。注意事项包括:版本号需严格对应,授权文件通常位于安装目录下,可使用find命令查找具体位置。操作需谨慎,建议先备份原文件以便恢复。

qq_34463511的博客 3451

Playwright与MCP协议结合:AI驱动的智能Web自动化测试实践

Web自动化测试是现代软件开发流程中保障产品质量的关键环节,其核心原理是通过程序模拟用户操作,对Web应用进行功能验证。传统自动化测试工具如Selenium和Playwright虽然提供了强大的API,但脚本编写和维护仍需较高的编程技能。随着AI技术的发展,智能测试成为新的趋势,它通过自然语言理解和自动化执行,显著降低了测试创建门槛。Model Context Protocol(MCP)作为一种连接AI模型与外部工具的开放协议,为自动化测试带来了“智能大脑”。通过将MCP与Playwright结合,测试人员

aoe41606的博客 604

NIO是什么

为了实现Selector管理多个SocketChannel,必须将具体的SocketChannel对象注册到Selector,并声明需要监听的事件(这样Selector才知道需要记录什么数据),一共有4种事件: 1、connect:客户端连接服务端事件,对应值为SelectionKey.OPCONNECT(8) 2、accept:服务端接收客户端连接事件,对应值为SelectionKey.OPA...

Top5软件工程硕士,先后在京东、字节从事多年Java后端开发、实时和离线大数据开发 2224

用MFRC522+STM32打造智能门禁:从硬件选型到云端控制全流程

本文详细介绍了基于STM32微控制器和MFRC522射频模块构建智能门禁系统的完整流程。从硬件选型、电路设计、底层驱动开发,到系统软件架构与状态机设计,并最终实现通过ESP8266模块接入云端平台(如OneNET)进行远程控制和数据可视化。项目深入探讨了MFRC522的SPI通信、卡片操作流程、天线调试以及云端MQTT集成等关键技术,为嵌入式开发者提供了一个从本地感知到云端管控的物联网项目实战参考。

raspberrypi5的博客 740

Java bio nio aio 总结

1 Unix 5种 I/O 编程模型 http://www.open-open.com/lib/view/open1371349692734.html 2、 BIO 经典的InputStream OutputStream 3、 NIO jdk 1.4 引入的非阻塞式I/O http://www.iteye.com/magazines/132-Java-NIO

行者无疆 1009

Vue3 - vite 项目报错:Uncaught ReferenceError: require is not defined(解决 vue3 vite 项目无法识别 require 语法导致报错)

vue3,vite,报错解决,vue3+vite报错requireisnotdefined,vue3vite引插件不识别require,老插件必须用require怎么办,vite项目插件要求使用require引入但报错,vue3不兼容require导致第三方包插件无法使用,vue3vite中使用require报错怎么办,解决Vite+vue3使用第三方依赖报错“requireisnotdefined”问题,vite项目require语法兼容问题解决requireisnotdefine

🏆 前端领域优质创作者 2万+

QNX侧触摸屏(TP)Bringup流程与源码深度解析

QNX侧TP Bringup的核心在于构建。

QQ_778132974的博客 477

Java中的异步I/O与并行计算

Java中的异步I/O与并行计算 大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨Java中的异步I/O与并行计算,这两个概念在提高应用程序性能和响应速度方面扮演了重要角色。 一、异步I/O 异步I/O允许应用程序在执行I/O操作时不阻塞线程,这样线程可以处理其他任务,提高应...

weixin_41076920的博客 91

基于图神经网络强化学习解决车辆路径规划问题(完整代码+报告)

基于图神经网络强化学习解决车辆路径规划问题(完整代码+报告)

3055

34_NIO网络编程与Netty

在JavaSE的学习中,我们了解了如何使用IO进行数据传输,Java IO是阻塞的,如果在一次读写数据调用时数据还没有准备好,或者目前不可写,那么读写操作就会被阻塞直到数据准备好或目标可写为止。Java NIO则是非阻塞的,每一次数据读写调用都会立即返回,并将目前可读(或可写)的内容写入缓冲区或者从缓冲区中输出,即使当前没有可用数据,调用仍然会立即返回并且不对缓冲区做任何操作。NIO框架是在JDK1.4推出的,它的出现就是为了解决传统IO的不足,这一期视频,我们就将围绕着NIO开始讲解。

成长需要沉淀。 1007

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

【从入门到放弃-Java】并发编程-NIO使用

前言 上文【从入门到放弃-SpringBoot】SpringBoot源码分析-请求过程中我们了解到,tomcat接收、返回请求的过程都是基于NIO实现的。日常工作中有很多基于NIO的使用,我们知道NIO可以提高系统的并发度,接下来的系列我们来深入学习下NIO,本文先从使用上简单概述。 ...

chikuai9995的博客 213

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

深入理解JAVA I/O系列一:File

I/O简介 I/O问题可以说是当今web应用中所面临的的主要问题之一,大部分的web应用系统的瓶颈都是I/O瓶颈。这个系列主要介绍JAVA的I/O类库基本架构、磁盘I/O工作机制、网络I/O工作机制以及NIO的工作方式。  从1.4版本开始JAVA引入了NIO,用来提升I/O性能。I/O操作类在包java.io下,大概有将近80个类,这些类可以分为如下四组:  基于字节操作的I/O接口:Input...

changhenshui1990的博客 217

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Java之I/O流io和nio

I/O流 最近在学习java io 和java nio 提到io,我们常用的类和其作用如下图: io的核心方法是read和write 以字节流为例: 1 package io; 2 3 import java.io.*; 4 import java.util.Arrays; 5 6 public class IO_Test { 7 8 ...

weixin_30666753的博客 97

重新认识一下,从BIO到NIO,再到AIO,响应式编程

Netty 的高性能架构,是基于一个网络编程设计模式 Reactor 进行设计的。现在,大多数与 I/O 相关的组件,都会使用 Reactor 模型,比如 Tomcat、Redis、Nginx 等,可见 Reactor 应用的广泛性。Reactor 是 NIO 的基础。为什么 NIO 的性能就能够比传统的阻塞 I/O 性能...

cnzzs的博客 1474

java nio AsynchronousChannel

Java NIO 提供了 AsynchronousChannel 接口,用于支持异步 I/O 操作。与传统的阻塞和非阻塞 I/O 模型不同,AsynchronousChannel 实现了异步非阻塞的 I/O 模型,这意味着 I/O 操作可以在后台进行,当操作完成后,系统会通知应用程序或执行回调。主要的 Asynchrono...

weixin_30143813的博客 232

Java I/O的工作机制

前言:当前大量的数据在网络上极其活跃,随处流动,这个流动的过程中都会涉及到I/O问题,可以说大部分web应用的瓶颈都是在I/O上。 注:文章的“详情请看Pxx”指的是《深入分析 Java web技术内幕》的某一页 这篇文章主要讲解:Java I/O 类库的基本架构,磁盘I/O,网络I/O基本机制,以及一些I/O优化技巧。 1.Java I/O类库的基本架构 Java 1.4版本开始引入NI...

qq_41846007的博客 230

netty 4.1.34 源码浅析1

netty 4.1.34 源码浅析1 概述 参考 https://netty.io/wiki/user-guide-for-4.x.html https://github.com/netty/netty https://www.infoq.cn/article/netty-threading-model#mainLogin netty权威指南(第二版) 背景知识 单线程、多线程、线程...

undergrowth的专栏 938

Netty之NIO入门(二)

BIO 下图的通信模型图来熟悉下BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的 Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。 该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程是Java虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发

FJJJJJ 1662

【Java基础】从Java语言层面理解BIO,NIO,AIO(二)

- **BIO方式**: 适用于`连接数目比较小且固定的架构`,这种方式对服务器资源要求比较高,并发局限于应用中,`JDK1.4以前的唯一选择`,但程序直观简单易理解。 - **NIO方式**: 适用于`连接数目多且连接比较短(轻操作)的架构`,比如`聊天服务器`,并发局限于应用中,编程比较复杂,`JDK1.4开始支持`。 - **AIO方式**: 使用于`连接数目多且连接比较长(重操作)的架构`,比如`相册服务器`,充分调用OS参与并发操作,编程比较复杂,`JDK7开始支持`。

墩墩分墩 604

java nio directmemory_JAVA NIO之浅谈内存映射文件原理与DirectMemory

转至:http://www.cnblogs.com/ixenos/p/5863921.htmlJava NIO 内存映射文件@author ixenos文件操作的四大方法前提:内存的访问速度比磁盘高几个数量级,但是基本的IO操作是直接调用native方法获得驱动和磁盘交互的,IO速度限制在磁盘速度上由此,就有了缓存的思想,将磁盘内容预先缓存在内存上,这样当供大于求的时候IO速度基本就是以内存的访问...

weixin_36333534的博客 216

IO流(下)

1、DataInputStream:基本类型 2、ByteArray*:字节数组、CharArray*:字符数组、String*:字符串 3、PrintStream、PrintWrite可以传参File、String、OutputStream,可以指定字符集,可以指定自动刷新,还可以写入任意类型数据。 4、ObjectInputStream:序列化流 5、Properties:属性集合类 6、RandomAccessFile:随机访问流、SequenceInputStream:合并流

weixin_40017062的博客 247
上一篇: 33. 【Java】I/O流(上):字节流与字符流
下一篇: 35. 【Java】日期与时间:java.time 包
gugucoding
博客等级 码龄140天 7粉丝 102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值