diff --git a/docs/home.md b/docs/home.md index e8fa936e0550a6b8296a6256aa5e8d8062bc286f..7dc698afedd1fe5c2f32c7bf06979478da04d4b2 100644 --- a/docs/home.md +++ b/docs/home.md @@ -228,10 +228,11 @@ head: ### Java NIO - [为什么我们要使用 Java NIO?](nio/why.md) +- [如何给女朋友解释什么是 BIO、NIO 和 AIO?](nio/BIONIOAIO.md) - [Java NIO 快速入门(buffer缓冲区、Channel管道、Selector选择器)](nio/rumen.md) - [一文彻底理解Java IO模型(阻塞IO非阻塞IO/IO多路复用)](nio/moxing.md) - [使用Java NIO完成网络通信](nio/network-connect.md) -- [如何给女朋友解释什么是 BIO、NIO 和 AIO?](nio/BIONIOAIO.md) + ### 重要知识点 diff --git a/docs/io/file-path.md b/docs/io/file-path.md index 7bcb5c13be5318b7508eeb1ddcc69db284bafc7c..80ff38c4286389ae551790fd45866ffd6609abaa 100644 --- a/docs/io/file-path.md +++ b/docs/io/file-path.md @@ -12,7 +12,7 @@ head: content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,Java IO,file,java文件,java目录,java文件增删改查,java file --- -# 7.2 关于File +# 7.2 关于 File 在 IO 操作中,文件的操作相对来说是比较复杂的,但也是使用频率最高的部分,我们几乎所有的项目中几乎都躺着一个叫做 FileUtil 或者 FileUtils 的工具类。那么 File 类可以说是其基石,我们必须得先来了解下。 @@ -83,13 +83,13 @@ File 的常用方法主要分为获取功能、获取绝对路径和相对路径 测试代码如下【注意测试以你自己的电脑文件夹为准】: ```java -File f = new File("/Users/username/aaa/bbb.java"); +File f = new File("/Users/username/aaa/bbb.java"); System.out.println("文件绝对路径:"+f.getAbsolutePath()); System.out.println("文件构造路径:"+f.getPath()); System.out.println("文件名称:"+f.getName()); System.out.println("文件长度:"+f.length()+"字节"); -File f2 = new File("/Users/username/aaa"); +File f2 = new File("/Users/username/aaa"); System.out.println("目录绝对路径:"+f2.getAbsolutePath()); System.out.println("目录构造路径:"+f2.getPath()); System.out.println("目录名称:"+f2.getName()); @@ -109,7 +109,6 @@ System.out.println("目录长度:"+f2.length()); - 在 Windows 操作系统中,文件系统默认是不区分大小写的,即在文件系统中,文件名和路径的大小写可以混合使用。例如,"`C:\Users\username\Documents\example.txt`" 和 "`C:\Users\Username\Documents\Example.txt`" 表示的是同一个文件。但是,Windows 操作系统提供了一个区分大小写的选项,可以在格式化磁盘时选择启用,这样文件系统就会区分大小写。 - 在 macOS 和 Linux 等 Unix 系统中,文件系统默认是区分大小写的。例如,在 macOS 系统中,"`/Users/username/Documents/example.txt`" 和 "`/Users/username/Documents/Example.txt`" 表示的是两个不同的文件。 - ```java // 绝对路径示例 File absoluteFile = new File("/Users/username/example/test.txt"); @@ -157,10 +156,10 @@ if (file.isFile()) { #### **4)创建、删除功能的方法** -* `createNewFile()` :文件不存在,创建一个新的空文件并返回`true`,文件存在,不创建文件并返回`false`。 -* `delete()` :删除文件或目录。如果是目录,只有目录为空才能删除。 -* `mkdir()` :只能创建一级目录,如果父目录不存在,则创建失败。返回 true 表示创建成功,返回 false 表示创建失败。 -* `mkdirs()` :可以创建多级目录,如果父目录不存在,则会一并创建。返回 true 表示创建成功,返回 false 表示创建失败或目录已经存在。 +- `createNewFile()` :文件不存在,创建一个新的空文件并返回`true`,文件存在,不创建文件并返回`false`。 +- `delete()` :删除文件或目录。如果是目录,只有目录为空才能删除。 +- `mkdir()` :只能创建一级目录,如果父目录不存在,则创建失败。返回 true 表示创建成功,返回 false 表示创建失败。 +- `mkdirs()` :可以创建多级目录,如果父目录不存在,则会一并创建。返回 true 表示创建成功,返回 false 表示创建失败或目录已经存在。 **开发中一般用**`mkdirs()`; @@ -193,8 +192,8 @@ if (directory.mkdirs()) { #### 5)目录的遍历 -* `String[] list()` :返回一个 String 数组,表示该 File 目录中的所有子文件或目录。 -* `File[] listFiles()` :返回一个 File 数组,表示该 File 目录中的所有的子文件或目录。 +- `String[] list()` :返回一个 String 数组,表示该 File 目录中的所有子文件或目录。 +- `File[] listFiles()` :返回一个 File 数组,表示该 File 目录中的所有的子文件或目录。 ```java File directory = new File("/Users/itwanger/Documents/Github/paicoding"); @@ -221,7 +220,7 @@ for (File fileOrDir : filesAndDirs) { **listFiles**在获取指定目录下的文件或者子目录时必须满足下面两个条件: - 1. **指定的目录必须存在** -- 2. **指定的必须是目录。否则容易引发NullPointerException异常** +- 2. **指定的必须是目录。否则容易引发 NullPointerException 异常** #### 6)递归遍历 @@ -253,6 +252,120 @@ public static void traverseDirectory(File directory) { } ``` +### RandomAccessFile + +RandomAccessFile 是 Java 中一个非常特殊的类,它既可以用来读取文件,也可以用来写入文件。与其他 IO 类(如 FileInputStream 和 FileOutputStream)不同,RandomAccessFile 允许您跳转到文件的任何位置,从那里开始读取或写入。这使得它特别适用于需要在文件中随机访问数据的场景,如数据库系统。 + +下面是一个使用 RandomAccessFile 的示例,包括写入和读取文件: + +```java +import java.io.IOException; +import java.io.RandomAccessFile; + +public class RandomAccessFileDemo { + + public static void main(String[] args) { + String filePath = "logs/javabetter/itwanger.txt"; + + try { + // 使用 RandomAccessFile 写入文件 + writeToFile(filePath, "Hello, 沉默王二!"); + + // 使用 RandomAccessFile 读取文件 + String content = readFromFile(filePath); + System.out.println("文件内容: " + content); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void writeToFile(String filePath, String content) throws IOException { + try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw")) { + // 将文件指针移动到文件末尾(在此处追加内容) + randomAccessFile.seek(randomAccessFile.length()); + + // 写入内容 + randomAccessFile.writeUTF(content); + } + } + + private static String readFromFile(String filePath) throws IOException { + StringBuilder content = new StringBuilder(); + + try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r")) { + // 将文件指针移动到文件开始处(从头开始读取) + randomAccessFile.seek(0); + + content.append(randomAccessFile.readUTF()); + } + + return content.toString(); + } +} +``` + +为了避免中文乱码问题,我们使用 RandomAccessFile 的 writeUTF 和 readUTF 方法,它们将使用 UTF-8 编码处理字符串。大家可以运行一下这段代码,体验一下。 + +![](https://cdn.tobebetterjavaer.com/stutymore/file-path-20230331193604.png) + +接下来,会详细介绍一下 RandomAccessFile 的构造方法和常用的方法。 + +#### 构造方法 + +RandomAccessFile 主要有两个构造方法: + +- `RandomAccessFile(File file, String mode)`:使用给定的文件对象和访问模式创建一个新的 RandomAccessFile 实例。 +- `RandomAccessFile(String name, String mode)`:使用给定的文件名和访问模式创建一个新的 RandomAccessFile 实例。 + +访问模式 mode 的值可以是: + +- "r":以只读模式打开文件。调用结果对象的任何 write 方法都将导致 IOException。 +- "rw":以读写模式打开文件。如果文件不存在,它将被创建。 +- "rws":以读写模式打开文件,并要求对内容或元数据的每个更新都被立即写入到底层存储设备。这种模式是同步的,可以确保在系统崩溃时不会丢失数据。 +- "rwd":与“rws”类似,以读写模式打开文件,但仅要求对文件内容的更新被立即写入。元数据可能会被延迟写入。 + +#### 主要方法 + +- `long getFilePointer()`:返回文件指针的当前位置。 +- `long length()`:返回此文件的长度。 +- `int read()`:从该文件中读取一个字节数据。 +- `int read(byte[] b)`:从该文件中读取字节数据并将其存储到指定的字节数组中。 +- `int read(byte[] b, int off, int len)`:从该文件中读取字节数据并将其存储到指定的字节数组中,从偏移量 off 开始,最多读取 len 个字节。 +- `String readLine()`:从该文件中读取一行文本。 +- `readUTF()`:从文件读取 UTF-8 编码的字符串。此方法首先读取两个字节的长度信息,然后根据这个长度读取字符串的 UTF-8 字节。最后,这些字节被转换为 Java 字符串。这意味着当你使用 readUTF 方法读取字符串时,需要确保文件中的字符串是使用 writeUTF 方法写入的,这样它们之间的长度信息和编码方式才能保持一致。 +- `void seek(long pos)`:将文件指针设置到文件中的 pos 位置。 +- `void write(byte[] b)`:将指定的字节数组的所有字节写入该文件。 +- `void write(byte[] b, int off, int len)`:将指定字节数组的部分字节写入该文件,从偏移量 off 开始,写入 len 个字节。 +- `void write(int b)`:将指定的字节写入该文件。 +- `writeUTF(String str)`:将一个字符串以 UTF-8 编码写入文件。此方法首先写入两个字节的长度信息,表示字符串的 UTF-8 字节长度,然后写入 UTF-8 字节本身。因此,当你使用 writeUTF 写入字符串时,实际写入的字节数会比字符串的 UTF-8 字节长度多两个字节。这两个字节用于在读取字符串时确定正确的字符串长度。 + +再来看一个示例,结合前面的讲解,就会彻底掌握 RandomAccessFile。 + +```java +File file = new File("logs/javabetter/itwanger.txt"); + +try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { + // 写入文件 + raf.writeUTF("Hello, 沉默王二!"); + + // 将文件指针移动到文件开头 + raf.seek(0); + + // 读取文件内容 + String content = raf.readUTF(); + System.out.println("内容: " + content); + +} catch (IOException e) { + e.printStackTrace(); +} +``` + +在这个示例中,我们首先创建了一个名为 itwanger.txt 的文件对象。然后我们使用 RandomAccessFile 以读写模式打开这个文件。 + +接下来,我们使用 writeUTF 方法将字符串"Hello, 沉默王二!"写入文件。然后,我们使用 seek 方法将文件指针移动到文件开头,并使用 readUTF 方法读取文件内容。输出应该是"Hello, 沉默王二!"。 + +最后,我们使用[try-with-resources](https://tobebetterjavaer.com/exception/try-with-resources.html)语句确保 RandomAccessFile 在操作完成后被正确关闭。 + ### Apache FileUtils 类 FileUtils 类是 Apache Commons IO 库中的一个类,提供了一些更为方便的方法来操作文件或目录。 @@ -303,11 +416,11 @@ String extension = FileUtils.getExtension(file.getName()); FileUtil 类是 [Hutool](https://hutool.cn) 工具包中的文件操作工具类,提供了一系列简单易用的文件操作方法,可以帮助 Java 开发者快速完成文件相关的操作任务。 -FileUtil类包含以下几类操作工具: +FileUtil 类包含以下几类操作工具: - 文件操作:包括文件目录的新建、删除、复制、移动、改名等 - 文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等。 -- 绝对路径:针对ClassPath中的文件转换为绝对路径文件。 +- 绝对路径:针对 ClassPath 中的文件转换为绝对路径文件。 - 文件名:主文件名,扩展名的获取 - 读操作:包括 getReader、readXXX 操作 - 写操作:包括 getWriter、writeXXX 操作 @@ -346,11 +459,10 @@ FileUtil.readLines(file, "UTF-8").forEach(System.out::println); 更多方法,可以去看一下 hutool 的源码,里面有非常多实用的方法,多看看,绝对能提升不少编程水平。 ---------- +--- -最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) +最近整理了一份牛逼的学习资料,包括但不限于 Java 基础部分(JVM、Java 集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类 Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是 2022 年全网最全的学习和找工作的 PDF 资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) 微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。 ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) - diff --git a/docs/nio/why.md b/docs/nio/why.md index e0f67850b13dc92bf0490f9dea753c57676cc963..5ad79bcca01a20ed14a54ab725ecec8ebcf6adbf 100644 --- a/docs/nio/why.md +++ b/docs/nio/why.md @@ -1,6 +1,6 @@ --- -title: 为什么我们要使用 Java NIO? -shortTitle: 为什么我们要使用Java NIO? +title: Java 的 NIO 是什么? +shortTitle: Java NIO是什么 category: - Java核心 tag: @@ -9,28 +9,27 @@ description: Java程序员进阶之路,小白的零基础Java教程,为什 head: - - meta - name: keywords - content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,nio + content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,nio,java nio --- -我花了几天去了解**NIO的核心知识点**,期间看了《Java 编程思想》和《疯狂Java 讲义》的nio模块。**但是**,会发现看完了之后还是很**迷**,不知道NIO这是干嘛用的,而网上的资料与书上的知识点没有很好地对应。 +# 12.1 -* 网上的资料很多都以IO的五种模型为基础来讲解NIO,而IO这五种模型其中又涉及到了很多概念:`同步/异步/阻塞/非阻塞/多路复用`,**而不同的人又有不同的理解方式**。 -* 还有涉及到了unix的`select/epoll/poll/pselect`,`fd`这些关键字,没有相关基础的人看起来简直是天书 -* 这就导致了在初学时认为nio远不可及 +我花了几天时间去了解**NIO 的核心知识**,期间看了《Java 编程思想》和《疯狂 Java 讲义》中的 NIO 模块。**但是**,看完之后还是很**迷**,不知道 NIO 是干嘛用的,网上的资料和书上的知识点没有很好地对应上。 -我在找资料的过程中也收藏了好多讲解NIO的资料,这篇文章就是**以初学的角度来理解NIO**。也算是我这两天看NIO的一个总结吧。 +- 网上的资料很多都以 IO 的五种模型为基础来讲解 NIO,而 IO 这五种模型其中又涉及到了很多概念:`同步/异步/阻塞/非阻塞/多路复用`,**而不同的人又有不同的理解方式**。 +- 还有涉及到 Unix 的`select/epoll/poll/pselect`,`fd`这些关键字,没有相关基础的人看起来简直是天书 -* 希望大家可以看了之后知道什么是NIO,NIO的核心知识点是什么,会使用NIO~ +我在找资料的过程中也收藏了好多讲解 NIO 的资料,这篇内容就是**以初学的角度来理解 NIO**。也算是我这两天看 NIO 的一个总结吧。希望大家可以看了之后知道什么是 NIO,NIO 的核心知识点是什么,以及会使用 NIO~ 那么接下来就开始吧,如果文章有错误的地方请大家多多包涵,不吝在评论区指正哦~ -> 声明:本文使用JDK1.8 +> 声明:本文使用 JDK1.8 -JDK 1.4中的`java.nio.*包`中引入新的Java I/O库,其目的是**提高速度**。实际上,“旧”的I/O包已经使用NIO**重新实现过,即使我们不显式的使用NIO编程,也能从中受益**。 +JDK 1.4 中,`java.nio.*包`引入新的 Java I/O 库,其目的是**提高速度**。实际上,“旧”的 I/O 包已经使用 NIO**重新实现过,即使我们不显式的使用 NIO 编程,也能从中受益**。 -* nio翻译成 no-blocking io 或者 new io 都无所谓啦,都说得通~ +- nio 翻译成 no-blocking io 或者 new io 都无所谓啦,都说得通~ -在《Java编程思想》读到**“即使我们不显式的使用NIO编程,也能从中受益”**的时候,我是挺在意的,所以:我们**测试**一下使用NIO复制文件和传统IO复制文件的性能: +在《Java 编程思想》读到“即使我们不显式的使用 NIO 编程,也能从中受益”的时候,我是挺在意的,所以:我们**测试**一下使用 NIO 复制文件和[传统 IO](https://tobebetterjavaer.com/io/file-path.html) 复制文件的性能: ```java import java.io.*; @@ -39,16 +38,18 @@ import java.nio.channels.FileChannel; public class SimpleFileTransferTest { + // 使用传统的 I/O 方法传输文件 private long transferFile(File source, File des) throws IOException { long startTime = System.currentTimeMillis(); if (!des.exists()) des.createNewFile(); + // 创建输入输出流 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source)); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(des)); - //将数据源读到的内容写入目的地--使用数组 + // 使用数组传输数据 byte[] bytes = new byte[1024 * 1024]; int len; while ((len = bis.read(bytes)) != -1) { @@ -59,25 +60,30 @@ public class SimpleFileTransferTest { return endTime - startTime; } + // 使用 NIO 方法传输文件 private long transferFileWithNIO(File source, File des) throws IOException { long startTime = System.currentTimeMillis(); if (!des.exists()) des.createNewFile(); + // 创建随机存取文件对象 RandomAccessFile read = new RandomAccessFile(source, "rw"); RandomAccessFile write = new RandomAccessFile(des, "rw"); + // 获取文件通道 FileChannel readChannel = read.getChannel(); FileChannel writeChannel = write.getChannel(); - ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);//1M缓冲区 + // 创建并使用 ByteBuffer 传输数据 + ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024); while (readChannel.read(byteBuffer) > 0) { byteBuffer.flip(); writeChannel.write(byteBuffer); byteBuffer.clear(); } + // 关闭文件通道 writeChannel.close(); readChannel.close(); long endTime = System.currentTimeMillis(); @@ -86,66 +92,51 @@ public class SimpleFileTransferTest { public static void main(String[] args) throws IOException { SimpleFileTransferTest simpleFileTransferTest = new SimpleFileTransferTest(); - File sourse = new File("F:\\电影\\[电影天堂www.dygod.cn]猜火车-cd1.rmvb"); - File des = new File("X:\\Users\\ozc\\Desktop\\io.avi"); - File nio = new File("X:\\Users\\ozc\\Desktop\\nio.avi"); + File sourse = new File("[电影天堂www.dygod.cn]猜火车-cd1.rmvb"); + File des = new File("io.avi"); + File nio = new File("nio.avi"); + // 比较传统的 I/O 和 NIO 传输文件的时间 long time = simpleFileTransferTest.transferFile(sourse, des); System.out.println(time + ":普通字节流时间"); long timeNio = simpleFileTransferTest.transferFileWithNIO(sourse, nio); System.out.println(timeNio + ":NIO时间"); - } - } ``` -我分别测试了文件大小为13M,40M,200M的: - - - -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nio/why-d5118350-471f-4998-abb2-4e82c7a50344.jpg) - - - - +在我给出实际的结论之前: -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nio/why-ffcb8770-5f0a-41e9-8534-f92a6f931a49.jpg) +- 对于较小的文件,NIO 和普通 IO 之间的性能差异可能不会非常明显,因为文件本身较小,复制过程较快。 +- 对于较大的文件,使用 NIO 的性能可能会明显优于普通 IO。这是因为 NIO 使用了更高效的缓冲区和通道机制,可以在内存中进行更快的数据传输。 +![](https://cdn.tobebetterjavaer.com/stutymore/why-20230331191748.png) +为什么要使用 NIO? +可以看到使用过 NIO 重新实现过的**传统 IO 根本不虚**,在大文件下效果还比 NIO 要好(当然了,个人几次的测试,或许不是很准) -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nio/why-0425087f-7878-466b-b02a-a802444e7405.jpg) +- 而 NIO 要有一定的学习成本,也没有传统 IO 那么好理解。 +那这意味着我们**可以不使用/学习 NIO 了吗**? +答案是**否定**的,IO 操作往往在**两个场景**下会用到: -为什么要使用NIO? +- 文件 IO +- 网络 IO -可以看到使用过NIO重新实现过的**传统IO根本不虚**,在大文件下效果还比NIO要好(当然了,个人几次的测试,或许不是很准) +NIO 的**魅力:在网络中使用 IO 就可以体现出来了**! -* 而NIO要有一定的学习成本,也没有传统IO那么好理解。 +- 后面会说到网络中使用 NIO,不急哈~ -那这意味着我们**可以不使用/学习NIO了吗**? +> 参考链接:[https://www.zhihu.com/question/29005375/answer/667616386](https://www.zhihu.com/question/29005375/answer/667616386),整理:沉默王二 -答案是**否定**的,IO操作往往在**两个场景**下会用到: - -* 文件IO -* 网络IO - -NIO的**魅力:在网络中使用IO就可以体现出来了**! - -* 后面会说到网络中使用NIO,不急哈~ - - ->参考链接:[https://www.zhihu.com/question/29005375/answer/667616386](https://www.zhihu.com/question/29005375/answer/667616386),整理:沉默王二 - ---------- +--- -最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) +最近整理了一份牛逼的学习资料,包括但不限于 Java 基础部分(JVM、Java 集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类 Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是 2022 年全网最全的学习和找工作的 PDF 资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) 微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。 - ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) diff --git "a/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" "b/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" index 58310ae62d165e639a3cc966a391d248e72b119c..03c101567c11668c0cb5e0b440373d80b85e61d6 100644 --- "a/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" +++ "b/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" @@ -19207,13 +19207,13 @@ File 的常用方法主要分为获取功能、获取绝对路径和相对路径 测试代码如下【注意测试以你自己的电脑文件夹为准】: ```java -File f = new File("/Users/username/aaa/bbb.java"); +File f = new File("/Users/username/aaa/bbb.java"); System.out.println("文件绝对路径:"+f.getAbsolutePath()); System.out.println("文件构造路径:"+f.getPath()); System.out.println("文件名称:"+f.getName()); System.out.println("文件长度:"+f.length()+"字节"); -File f2 = new File("/Users/username/aaa"); +File f2 = new File("/Users/username/aaa"); System.out.println("目录绝对路径:"+f2.getAbsolutePath()); System.out.println("目录构造路径:"+f2.getPath()); System.out.println("目录名称:"+f2.getName()); @@ -19233,7 +19233,6 @@ System.out.println("目录长度:"+f2.length()); - 在 Windows 操作系统中,文件系统默认是不区分大小写的,即在文件系统中,文件名和路径的大小写可以混合使用。例如,"`C:\Users\username\Documents\example.txt`" 和 "`C:\Users\Username\Documents\Example.txt`" 表示的是同一个文件。但是,Windows 操作系统提供了一个区分大小写的选项,可以在格式化磁盘时选择启用,这样文件系统就会区分大小写。 - 在 macOS 和 Linux 等 Unix 系统中,文件系统默认是区分大小写的。例如,在 macOS 系统中,"`/Users/username/Documents/example.txt`" 和 "`/Users/username/Documents/Example.txt`" 表示的是两个不同的文件。 - ```java // 绝对路径示例 File absoluteFile = new File("/Users/username/example/test.txt"); @@ -19281,10 +19280,10 @@ if (file.isFile()) { #### **4)创建、删除功能的方法** -* `createNewFile()` :文件不存在,创建一个新的空文件并返回`true`,文件存在,不创建文件并返回`false`。 -* `delete()` :删除文件或目录。如果是目录,只有目录为空才能删除。 -* `mkdir()` :只能创建一级目录,如果父目录不存在,则创建失败。返回 true 表示创建成功,返回 false 表示创建失败。 -* `mkdirs()` :可以创建多级目录,如果父目录不存在,则会一并创建。返回 true 表示创建成功,返回 false 表示创建失败或目录已经存在。 +- `createNewFile()` :文件不存在,创建一个新的空文件并返回`true`,文件存在,不创建文件并返回`false`。 +- `delete()` :删除文件或目录。如果是目录,只有目录为空才能删除。 +- `mkdir()` :只能创建一级目录,如果父目录不存在,则创建失败。返回 true 表示创建成功,返回 false 表示创建失败。 +- `mkdirs()` :可以创建多级目录,如果父目录不存在,则会一并创建。返回 true 表示创建成功,返回 false 表示创建失败或目录已经存在。 **开发中一般用**`mkdirs()`; @@ -19317,8 +19316,8 @@ if (directory.mkdirs()) { #### 5)目录的遍历 -* `String[] list()` :返回一个 String 数组,表示该 File 目录中的所有子文件或目录。 -* `File[] listFiles()` :返回一个 File 数组,表示该 File 目录中的所有的子文件或目录。 +- `String[] list()` :返回一个 String 数组,表示该 File 目录中的所有子文件或目录。 +- `File[] listFiles()` :返回一个 File 数组,表示该 File 目录中的所有的子文件或目录。 ```java File directory = new File("/Users/itwanger/Documents/Github/paicoding"); @@ -19345,7 +19344,7 @@ for (File fileOrDir : filesAndDirs) { **listFiles**在获取指定目录下的文件或者子目录时必须满足下面两个条件: - 1. **指定的目录必须存在** -- 2. **指定的必须是目录。否则容易引发NullPointerException异常** +- 2. **指定的必须是目录。否则容易引发 NullPointerException 异常** #### 6)递归遍历 @@ -19377,6 +19376,120 @@ public static void traverseDirectory(File directory) { } ``` +### RandomAccessFile + +RandomAccessFile 是 Java 中一个非常特殊的类,它既可以用来读取文件,也可以用来写入文件。与其他 IO 类(如 FileInputStream 和 FileOutputStream)不同,RandomAccessFile 允许您跳转到文件的任何位置,从那里开始读取或写入。这使得它特别适用于需要在文件中随机访问数据的场景,如数据库系统。 + +下面是一个使用 RandomAccessFile 的示例,包括写入和读取文件: + +```java +import java.io.IOException; +import java.io.RandomAccessFile; + +public class RandomAccessFileDemo { + + public static void main(String[] args) { + String filePath = "logs/javabetter/itwanger.txt"; + + try { + // 使用 RandomAccessFile 写入文件 + writeToFile(filePath, "Hello, 沉默王二!"); + + // 使用 RandomAccessFile 读取文件 + String content = readFromFile(filePath); + System.out.println("文件内容: " + content); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void writeToFile(String filePath, String content) throws IOException { + try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw")) { + // 将文件指针移动到文件末尾(在此处追加内容) + randomAccessFile.seek(randomAccessFile.length()); + + // 写入内容 + randomAccessFile.writeUTF(content); + } + } + + private static String readFromFile(String filePath) throws IOException { + StringBuilder content = new StringBuilder(); + + try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r")) { + // 将文件指针移动到文件开始处(从头开始读取) + randomAccessFile.seek(0); + + content.append(randomAccessFile.readUTF()); + } + + return content.toString(); + } +} +``` + +为了避免中文乱码问题,我们使用 RandomAccessFile 的 writeUTF 和 readUTF 方法,它们将使用 UTF-8 编码处理字符串。大家可以运行一下这段代码,体验一下。 + +![](https://cdn.tobebetterjavaer.com/stutymore/file-path-20230331193604.png) + +接下来,会详细介绍一下 RandomAccessFile 的构造方法和常用的方法。 + +#### 构造方法 + +RandomAccessFile 主要有两个构造方法: + +- `RandomAccessFile(File file, String mode)`:使用给定的文件对象和访问模式创建一个新的 RandomAccessFile 实例。 +- `RandomAccessFile(String name, String mode)`:使用给定的文件名和访问模式创建一个新的 RandomAccessFile 实例。 + +访问模式 mode 的值可以是: + +- "r":以只读模式打开文件。调用结果对象的任何 write 方法都将导致 IOException。 +- "rw":以读写模式打开文件。如果文件不存在,它将被创建。 +- "rws":以读写模式打开文件,并要求对内容或元数据的每个更新都被立即写入到底层存储设备。这种模式是同步的,可以确保在系统崩溃时不会丢失数据。 +- "rwd":与“rws”类似,以读写模式打开文件,但仅要求对文件内容的更新被立即写入。元数据可能会被延迟写入。 + +#### 主要方法 + +- `long getFilePointer()`:返回文件指针的当前位置。 +- `long length()`:返回此文件的长度。 +- `int read()`:从该文件中读取一个字节数据。 +- `int read(byte[] b)`:从该文件中读取字节数据并将其存储到指定的字节数组中。 +- `int read(byte[] b, int off, int len)`:从该文件中读取字节数据并将其存储到指定的字节数组中,从偏移量 off 开始,最多读取 len 个字节。 +- `String readLine()`:从该文件中读取一行文本。 +- `readUTF()`:从文件读取 UTF-8 编码的字符串。此方法首先读取两个字节的长度信息,然后根据这个长度读取字符串的 UTF-8 字节。最后,这些字节被转换为 Java 字符串。这意味着当你使用 readUTF 方法读取字符串时,需要确保文件中的字符串是使用 writeUTF 方法写入的,这样它们之间的长度信息和编码方式才能保持一致。 +- `void seek(long pos)`:将文件指针设置到文件中的 pos 位置。 +- `void write(byte[] b)`:将指定的字节数组的所有字节写入该文件。 +- `void write(byte[] b, int off, int len)`:将指定字节数组的部分字节写入该文件,从偏移量 off 开始,写入 len 个字节。 +- `void write(int b)`:将指定的字节写入该文件。 +- `writeUTF(String str)`:将一个字符串以 UTF-8 编码写入文件。此方法首先写入两个字节的长度信息,表示字符串的 UTF-8 字节长度,然后写入 UTF-8 字节本身。因此,当你使用 writeUTF 写入字符串时,实际写入的字节数会比字符串的 UTF-8 字节长度多两个字节。这两个字节用于在读取字符串时确定正确的字符串长度。 + +再来看一个示例,结合前面的讲解,就会彻底掌握 RandomAccessFile。 + +```java +File file = new File("logs/javabetter/itwanger.txt"); + +try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { + // 写入文件 + raf.writeUTF("Hello, 沉默王二!"); + + // 将文件指针移动到文件开头 + raf.seek(0); + + // 读取文件内容 + String content = raf.readUTF(); + System.out.println("内容: " + content); + +} catch (IOException e) { + e.printStackTrace(); +} +``` + +在这个示例中,我们首先创建了一个名为 itwanger.txt 的文件对象。然后我们使用 RandomAccessFile 以读写模式打开这个文件。 + +接下来,我们使用 writeUTF 方法将字符串"Hello, 沉默王二!"写入文件。然后,我们使用 seek 方法将文件指针移动到文件开头,并使用 readUTF 方法读取文件内容。输出应该是"Hello, 沉默王二!"。 + +最后,我们使用[try-with-resources](https://tobebetterjavaer.com/exception/try-with-resources.html)语句确保 RandomAccessFile 在操作完成后被正确关闭。 + ### Apache FileUtils 类 FileUtils 类是 Apache Commons IO 库中的一个类,提供了一些更为方便的方法来操作文件或目录。 @@ -19427,11 +19540,11 @@ String extension = FileUtils.getExtension(file.getName()); FileUtil 类是 [Hutool](https://hutool.cn) 工具包中的文件操作工具类,提供了一系列简单易用的文件操作方法,可以帮助 Java 开发者快速完成文件相关的操作任务。 -FileUtil类包含以下几类操作工具: +FileUtil 类包含以下几类操作工具: - 文件操作:包括文件目录的新建、删除、复制、移动、改名等 - 文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等。 -- 绝对路径:针对ClassPath中的文件转换为绝对路径文件。 +- 绝对路径:针对 ClassPath 中的文件转换为绝对路径文件。 - 文件名:主文件名,扩展名的获取 - 读操作:包括 getReader、readXXX 操作 - 写操作:包括 getWriter、writeXXX 操作 @@ -19470,9 +19583,9 @@ FileUtil.readLines(file, "UTF-8").forEach(System.out::println); 更多方法,可以去看一下 hutool 的源码,里面有非常多实用的方法,多看看,绝对能提升不少编程水平。 ---------- +--- -最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) +最近整理了一份牛逼的学习资料,包括但不限于 Java 基础部分(JVM、Java 集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类 Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是 2022 年全网最全的学习和找工作的 PDF 资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) 微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。