【Java基础】Java7新特性—Files类,Path类,Paths类的用法

【Java基础】Java7新特性—Files类,Path类,Paths类的用法文章目录 Java7 新增文件 IO 类一 Paths1 创建 Paths 二 Path1 创建 Path1 1 创建 Path 的三种方式 2 Path 常用方法三 Files1 判断方法 2 删除方法 3 复制方法 4 移动和重命名方法 5 创建文件和文件夹方法 6 文件属性方法 7 读取 编辑文件内容方法 8 遍历文件列表方法四 Path 和 Files 使用五 FileTime 对象 Java7 新增文件 IO 类 Java7 中文件 IO

Java7新增文件IO类

Java7中文件IO发生了很大的变化,专门引入了很多新的类:

在java中文件或是目录习惯用java.io.File对象来表示,但是File类有很多缺陷

  • 它的很多方法不能抛出异常
  • 它的delete方法经常莫名其妙的失败等,旧的File类经常是程序失败的根源。
  • 因此在Java7中有了更好的替代:java.nio.file.Pathjava.nio.file.Files
    • Path接口的名字非常恰当,就是表示路径的,API中讲Path对象可以是一个文件,一个目录,或是一个符号链接,也可以是一个根目录。 用法很简单。创建Path并不会创建物理文件或是目录path实例经常引用并不存在的物理对象,要真正创建文件或是目录,需要用到Files类
    • Files类是一个非常强大的类,它提供了处理文件和目录以及读取文件和写入文件的静态方法。 可以用它创建和删除路径。复制文件。检查路径是否存在等。 此外。Files还拥有创建流对象的方法。

一.Paths

Paths类仅由静态方法组成,通过转换路径字符串返回Path或URI

  • 因为就两个方法用来生成Path对象,以供Path和Files使用;而Path也经常由Paths来生成,又或者File类有一个toPath();方法可以使用

1.创建Paths

static Path get(String first, String... more) //将路径字符串或连接到路径字符串的字符串序列转换为 Path,可以get("c:/abc");或者get("c:","abc"),注意这里可以有多个参数String... more代表n个参数,这个比较常用 static Path get(URI uri) //将给定的URI转换为Path对象 

二.Path

1.创建Path

Path toPath() //File类对象方法--返回一个java.nio.file.Path对象 abstract Path getPath(String first, String... more) //FileSystem对象方法--将路径字符串或从路径字符串连接起来的一系列字符串转换为 Path 。  

1.1.创建Path的三种方式

创建Path的三种方式

Path path=FileSystems.getDefault().getPath("d:/users/日记5.txt"); //并没有实际创建路径,而是一个指向d:/users/日记5.txt路径的引用 Path path=Paths.get("d:/users/日记5.txt"); //Paths类提供了这个快捷方法,直接通过它的静态get方法创建path Path path= = new File("d:/users/日记5.txt").toPath(); 

2.Path常用方法

Path接口没什么判断方法,其实更多的判断和操作都在Files工具类里面

boolean isAbsolute() //告诉这条路是否是绝对的 boolean endsWith(Path other) //测试此路径是否以给定的路径结束 boolean endsWith(String other) //测试此路径是否以给定字符串结束,如"c:/a/banana/cat"可以以"/banana/cat"结尾,但不能以"t"结尾 boolean startsWith(Path other) //测试此路径是否以给定的路径开始。  boolean startsWith(String other) //测试此路径是否以给定字符串开始,跟上面一样规律 Path getFileName() //将此路径表示的文件或目录的名称返回为 Path对象,文件名或文件夹名,不含路径 Path getName(int index) //返回此路径的名称元素作为 Path对象。目录中最靠近root的为0,最远的为(count-1),count由下面的方法获得 int getNameCount() //返回路径中的名称元素的数量。0则只有root Path getParent() //返回 父路径,如果此路径没有父返回null,如/a/b/c返回/a/b,配合下面的方法消除"."或".." Path normalize() //返回一个路径,该路径是冗余名称元素的消除。如消除掉"."、".." Path getRoot() //返回此路径的根组分作为 Path对象,或 null如果该路径不具有根组件。如返回"c:/" Path relativize(Path other) //构造此路径和给定路径之间的相对路径。有点难理解,p1-"Topic.txt",p2-"Demo.txt",p3-"/Java/JavaFX/Topic.txt",p4-"/Java/2011";;那么p1和p2的结果是"../Demo.txt";;p2和p1的结果是"../Topic.txt";;p3和p4的结果是"../../2011";;p4和p3的结果是"../JavaFX/Topic.txt" Path resolve(String other) //将给定的路径字符串转换为 Path。如"c:/a/b"和字符串"c.txt"的结果是"c:/a/b/c.txt";更像是拼接 Path resolveSibling(String other) //将给定的路径字符串转换为 Path。如"c:/a/b.txt"和字符串"c.txt"的结果是"c:/a/c.txt";更像是替换 Path subpath(int beginIndex, int endIndex) //返回一个相对的 Path ,它是该路径的名称元素的子序列,如"d:/a/b/c.txt"参数为(1,3)返回一个"b/c.txt" Path toAbsolutePath() //返回表示此路径的绝对路径的 Path对象。包括盘符和文件名或文件夹名 Iterator<Path> iterator() //返回此路径的名称元素的迭代器。"c:/a/b/c.txt"的迭代器可以next出以下"a""b""c.txt" File toFile() //返回表示此路径的File对象 

三.Files

Files类只包含对文件,目录或其他类型文件进行操作的静态方法。主要和Path接口的对象进行配合使用

1.判断方法:

static boolean exists(Path path, LinkOption... options) //测试文件是否存在。  static boolean notExists(Path path, LinkOption... options) //测试此路径所在的文件是否不存在。  static boolean isDirectory(Path path, LinkOption... options) //测试文件是否是目录。  static boolean isExecutable(Path path) //测试文件是否可执行。  static boolean isHidden(Path path) //告知文件是否被 隐藏 。  static boolean isReadable(Path path) //测试文件是否可读。  static boolean isRegularFile(Path path, LinkOption... options) //测试文件是否是具有不透明内容的常规文件。说实话,我也不太懂常规文件指的是啥 static boolean isSameFile(Path path, Path path2) //测试两个路径是否找到相同的文件。 static boolean isSymbolicLink(Path path) //测试文件是否是符号链接。// static boolean isWritable(Path path) //测试文件是否可写。  

2.删除方法

static boolean deleteIfExists(Path path) //删除文件(如果存在)。  static void delete(Path path) //删除文件。  

3.复制方法

static long copy(InputStream in, Path target, CopyOption... options) //将输入流中的所有字节复制到文件。 //关于CopyOption则是一个被继承的接口主要有枚举类StandardCopyOption和LinkOption // 1.StandardCopyOption // REPLACE_EXISTING(也就是替换覆盖) // COPY_ATTRIBUTES(将源文件的文件属性信息复制到目标文件中) // ATOMIC_MOVE(原子性的复制)都是字面意思 // 2.LinkOption // NOFOLLOW_LINKS static long copy(Path source, OutputStream out) //将文件中的所有字节复制到输出流。  static Path copy(Path source, Path target, CopyOption... options) //将文件复制到目标文件。  

4.移动和重命名方法

static Path move(Path source, Path target, CopyOption... options) //将文件移动或重命名为目标文件。  

5.创建文件和文件夹方法

static Path createDirectories(Path dir, FileAttribute<?>... attrs) //首先创建所有不存在的父目录来创建目录。 static Path createDirectory(Path dir, FileAttribute<?>... attrs) //创建一个新的目录。  static Path createFile(Path path, FileAttribute<?>... attrs) //创建一个新的和空的文件,如果该文件已存在失败。 

6.文件属性方法

static <V extends FileAttributeView> V getFileAttributeView(Path path,<V> type, LinkOption... options) //返回给定类型的文件属性视图。指定六个视图其中一种,上面一开始有点到。拿到的xxxAttributeView会有一个跟下面一样名字的readAttributes方法来得到一个xxxAttributes真正的获取操作就全是在这个xxxAttributes类的对象里get啦 static <A extends BasicFileAttributes> A readAttributes(Path path,<A> type, LinkOption... options) //读取文件的属性作为批量操作。指定一个xxxAttributes,得到一个实例,通过里面的方法得到时间等基本属性 static Object getAttribute(Path path, String attribute, LinkOption... options) //读取文件属性的值。这个 String attributes 参数的语法固定是以 view-name:comma-separated-attributes 的形式;view-name指定视图名如basic,posix,acl等,不写默认为basic;有写默认要加":";可以用"basic:*"或"*"读取所有,又或者用"basic:size,lastModifiedTime"读取大小和修改时间。具体还有那些属性可以看具体指定的类,比如basic视图就看BasicFileAttributes这个接口都有哪些方法,可以读取哪些文件属性。同理,下面的 String attributes 一样是这个理 static Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options) //读取一组文件属性作为批量操作。 static Path setAttribute(Path path, String attribute, Object value, LinkOption... options) //设置文件属性的值。  /* 下面这些也是获取属性的方法,不过还没研究到是怎么用的 */ static FileTime getLastModifiedTime(Path path, LinkOption... options) //返回文件的上次修改时间。  static UserPrincipal getOwner(Path path, LinkOption... options) //返回文件的所有者。  static Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption... options) //返回文件的POSIX文件权限。  static Path setLastModifiedTime(Path path, FileTime time) //更新文件上次修改的时间属性。  static Path setOwner(Path path, UserPrincipal owner) //更新文件所有者。  static Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms) //设置文件的POSIX权限。  static long size(Path path) //返回文件的大小(以字节为单位)。  

7.读取、编辑文件内容方法

static BufferedReader newBufferedReader(Path path) //打开一个文件进行阅读,返回一个 BufferedReader以高效的方式从文件读取文本。  static BufferedReader newBufferedReader(Path path, Charset cs) //打开一个文件进行阅读,返回一个 BufferedReader ,可以用来以有效的方式从文件读取文本。  static BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption... options) //打开或创建一个写入文件,返回一个 BufferedWriter ,可以用来以有效的方式将文本写入文件。  static BufferedWriter newBufferedWriter(Path path, OpenOption... options) //打开或创建一个写入文件,返回一个 BufferedWriter以高效的方式写入文件。  static SeekableByteChannel newByteChannel(Path path, OpenOption... options) //打开或创建文件,返回可访问的字节通道以访问该文件。  static SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) //打开或创建文件,返回可访问的字节通道以访问该文件。  static InputStream newInputStream(Path path, OpenOption... options) //打开一个文件,返回输入流以从文件中读取。  static OutputStream newOutputStream(Path path, OpenOption... options) //打开或创建文件,返回可用于向文件写入字节的输出流。  static byte[] readAllBytes(Path path) //读取文件中的所有字节。  static List<String> readAllLines(Path path) //从文件中读取所有行。  static List<String> readAllLines(Path path, Charset cs) //从文件中读取所有行。 static Path write(Path path, byte[] bytes, OpenOption... options) //将字节写入文件。  static Path write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options) //将文本行写入文件。  static Path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options) //将文本行写入文件。  

以上方法适用于处理中等长度的文本文件,如果要处理的文件长度比较大,或者是二进制文件,那么还是应该使用流

8.遍历文件列表方法

  • newDirectoryStream只是遍历当前Path的子目录列表,或者写一个方法里面递归调用实现遍历到底;
  • walk则是可以通过maxDepth参数来决定遍历的深度,后面的FileVisitOption参数可有可无;
  • list类似于newDirectoryStream,区别是walk和newDirectoryStream是递归的,list是非递归的
static DirectoryStream<Path> newDirectoryStream(Path dir) //打开一个目录,返回一个DirectoryStream以遍历目录中的所有条目。最好用 try-with-resources 构造,可以自动关闭资源。返回的 DirectoryStream 
   
     其实可以直接使用 Iterator或者for循环 遍历每一个 dir 下面的文件或目录 
    static DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter) //上面方法的重载,通过实现参数二(有一个 boolean accept(Path p) 方法来判断文件是否符合需要)来达到过滤的目的。如accept方法中写"return (Files.size(p) > 8192L);"来匹配大于8k的文件 static DirectoryStream<Path> newDirectoryStream(Path dir, String glob) //上面方法的重载,可以通过参数二作为过滤匹配出对应的文件。如 newDirectoryStream(dir, "*.java") 用于遍历目录里所有java后缀的文件 static Stream<Path> walk(Path start, FileVisitOption... options) //深度优先遍历。返回一个 Stream ,它通过 Path根据给定的起始文件的文件树懒惰地填充 Path 。  static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options) //深度优先遍历。返回一个 Stream ,它是通过走根据给定的起始文件的文件树懒惰地填充 Path 。 static Stream<Path> list(Path dir) //返回一个懒惰的填充 Stream ,其元素是 Stream中的条目。返回的 Stream 里封装了一个 DirectoryStream 用于遍历。 

四.Path和Files使用

import org.junit.Test; import java.io.*; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Stream; public class PathAndFilesTest { 
    / * 创建Path */ @Test public void createPath() throws URISyntaxException, MalformedURLException { 
    //1.Paths Path path = Paths.get("F:/测试数据.csv"); System.out.println(path.getFileName()); Path path1 = Paths.get(new URI("file:///f:/测试数据.csv")); //2.FileSystems Path path2 = FileSystems.getDefault().getPath("F:/测试数据.csv"); //3.File Path path3 = new File("F:/测试数据.csv").toPath(); } / * 创建一个空文件/文件夹 */ @Test public void create() throws IOException { 
    //文件夹 Path path = Paths.get("F:/hello"); if (!Files.exists(path)) { 
   //如果不存在 Files.createDirectory(path); //创建多个目录 //Files.createDirectories(path); } //文件 Path path1 = Paths.get("F:/helloFile.txt"); if (!Files.exists(path1)) { 
   //如果不存在 Files.createFile(path1); } } / * 文件属性 */ @Test public void getFileProperties() throws IOException { 
    Path path = Paths.get("F:/测试数据.csv"); System.out.println(Files.getLastModifiedTime(path));//最后修改时间:2019-05-22T02:52:45.Z System.out.println(Files.getOwner(path));//拥有者:DESKTOP-GE36VVD\87772 (User) //System.out.println(Files.getPosixFilePermissions(path));//权限,非admin可能会报错 System.out.println(Files.size(path));//文件大小:  } / * 读取一个文本文件 */ @Test public void readText() throws IOException { 
    Path path = Paths.get("F:/test.txt"); //通过bufferedReader读取 BufferedReader bufferedReader = Files.newBufferedReader(path, StandardCharsets.UTF_8);///该文件编码是什么newBufferedReader就必须指定什么字符集,否则报错 StringBuilder sb = new StringBuilder(); String tempString = null; while ((tempString = bufferedReader.readLine()) != null) { 
    sb = sb.append(tempString + "\n"); } System.out.println(sb); //通过Files方法readAllLines List<String> strings = Files.readAllLines(path); strings.forEach(System.out::println); } / * 拿到文件输入流 * * @throws IOException */ @Test public void getInputStream() throws IOException { 
    Path path = Paths.get("F:/test.txt"); InputStream inputStream = Files.newInputStream(path); //转换字符流后在包装成缓冲流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); StringBuilder sb = new StringBuilder(); String tempString = null; while ((tempString = bufferedReader.readLine()) != null) { 
    sb = sb.append(tempString + "\n"); } System.out.println(sb); } / * 文件写操作 */ @Test public void writeFile() throws IOException { 
    Path path = Paths.get("F:/writeFile.txt"); //获取写入流 BufferedWriter bufferedWriter = Files.newBufferedWriter(path); //执行写入操作 String str = "write file test"; bufferedWriter.write(str); //关闭资源 bufferedWriter.flush(); bufferedWriter.close(); } / * 遍历一个文件夹 */ @Test public void traverseDirectory() throws IOException { 
    Path path = Paths.get("F:/test"); Stream<Path> list = Files.list(path); list.forEach(p -> { 
    System.out.println(p.getFileName()); }); } / * 遍历文件树 */ @Test public void traverseTree() throws IOException { 
    Path path = Paths.get("F:/test/"); Stream<Path> walk = Files.walk(path); walk.forEach(path1 -> { 
    // System.out.println(path1.getRoot());//根目录 System.out.println(path1.getFileName());//文件名 // System.out.println(path1.getParent());//上级目录 // System.out.println(path1.getFileSystem());//文件系统 }); //还有种方式Files.walkFileTree() } / * 文件复制 */ @Test public void copyFile() throws IOException { 
    Path src = Paths.get("F:/测试数据.csv"); Path dest = Paths.get("F:/test/Copy测试数据.csv"); Files.copy(src, dest); } / * 读取权限见上面示例,设置权限 */ @Test public void writePermission() throws IOException { 
    Path path = Paths.get("F:/test/导出测试数据.xlsx"); Set<PosixFilePermission> permissionSet = new HashSet<>(); permissionSet.add(PosixFilePermission.GROUP_WRITE); permissionSet.add(PosixFilePermission.OWNER_EXECUTE); Files.setPosixFilePermissions(path, permissionSet); } / * 判断方法 * @throws IOException */ @Test public void judge() throws IOException { 
    Path path1 = Paths.get("f:\\test", "Copy测试数据.csv"); Path path2 = Paths.get("f:\\测试数据.csv"); // boolean exists(Path path, LinkOption … opts) : 判断文件是否存在 System.out.println(Files.exists(path2, LinkOption.NOFOLLOW_LINKS));//true // boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录 //不要求此path对应的物理文件存在。 System.out.println(Files.isDirectory(path1, LinkOption.NOFOLLOW_LINKS));//false // boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件 // boolean isHidden(Path path) : 判断是否是隐藏文件 //要求此path对应的物理上的文件需要存在。才可判断是否隐藏。否则,抛异常。 // System.out.println(Files.isHidden(path1)); // boolean isReadable(Path path) : 判断文件是否可读 System.out.println(Files.isReadable(path1));//true // boolean isWritable(Path path) : 判断文件是否可写 System.out.println(Files.isWritable(path1));//true // boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在 System.out.println(Files.notExists(path1, LinkOption.NOFOLLOW_LINKS));//false } } / * StandardOpenOption.READ:表示对应的Channel是可读的。 * StandardOpenOption.WRITE:表示对应的Channel是可写的。 * StandardOpenOption.CREATE:如果要写出的文件不存在,则创建。如果存在,忽略 * StandardOpenOption.CREATE_NEW:如果要写出的文件不存在,则创建。如果存在,抛异常 */ @Test public void ioStream() throws IOException { 
    Path path1 = Paths.get("f:\\test", "copyTest.txt"); // InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象 InputStream inputStream = Files.newInputStream(path1, StandardOpenOption.READ); // OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象 OutputStream outputStream = Files.newOutputStream(path1, StandardOpenOption.WRITE, StandardOpenOption.CREATE); // SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。 SeekableByteChannel channel = Files.newByteChannel(path1, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE); // DirectoryStream 
   
     newDirectoryStream(Path path) : 打开 path 指定的目录 
    Path path2 = Paths.get("f:\\test"); DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path2); Iterator<Path> iterator = directoryStream.iterator(); while (iterator.hasNext()) { 
    System.out.println(iterator.next()); } } 

五.FileTime对象

表示文件时间戳属性的值,可能会在设置文件最后更新属性时使用到:

static FileTime fromMillis(long value) //返回一个 FileTime以 FileTime单位表示给定值。  long toMillis() //返回以毫秒为单位的值。 String toString() //返回此 FileTime的字符串表示 FileTime 。  

栗子:

/ * 可能你要从文件属性中的FileTime或者到一个Date对象 */ Path pathObj = Paths.get("C:/a/b/c.txt"); BasicFileAttributes attrs = Files.readAttributes(pathObj, BasicFileAttributes.class); Data date = new Date(attrs.lastModifiedTime().toMillis()); / * 又或者可能你要人为地修改这个文件时间属性,需要一个FileTime */ Path path = Paths.get("C:/a/b/c.txt"); long time = System.currentTimeMillis(); FileTime fileTime = FileTime.fromMillis(time); try{ 
    Files.setAttribute(path, "basic:lastModifiedTime", fileTime,LinkOption.NOFOLLOW_LINKS); }catch (IOException e) { 
    System.err.println(e); } 

在这里插入图片描述


添加微信,一起讨论Java、健身、养猫知识,哈哈哈

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/215661.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午1:44
下一篇 2026年3月18日 下午1:44


相关推荐

  • Java两整数相除向上取整

    Java两整数相除向上取整前言:Java中两个整数相除,如果不能整除,默认是向下取整的。例如:11除以3的结果是3。然而,某些情况下(eg.把11个糖果,每3个分一堆,不足三个也分成一堆,可以分几堆?),我们需要向上取整,这样的情况该如果处理呢?方式一:添加三目运算符逻辑代码x/y+(x%y!=0?1:0);这种方法逻辑上很简单,如果x可以整除y,就将x/y的结果加0,不能整除y就将x/y的结果加1。方式二:使用ceil函数(int)Math.ceil((double.

    2022年6月21日
    113
  • 个人电脑从零配置 Clawdbot/Moltbot/OpenClaw

    个人电脑从零配置 Clawdbot/Moltbot/OpenClaw

    2026年3月13日
    4
  • NotebookLM 出了视频生成,但「被动学习」本质上是低效的——这是我的替代方案

    NotebookLM 出了视频生成,但「被动学习」本质上是低效的——这是我的替代方案

    2026年3月16日
    1
  • Java实现扫雷小游戏【优化版】

    Java实现扫雷小游戏【优化版】游戏的设计类似windows扫雷,用户在图形化用户界面内利用鼠标监听事件标记雷区,左上角表示剩余雷的数量,右上角动态显示使用的时间。用户可选择中间组件按钮重新游戏。在使用Java编写扫雷小游戏时遇到了很多问题,在解决问题时,确实对java的面向对象编程有了更加深入的理解。虽然GUI现在并没有很大的市场,甚至好多初学者已经放弃了学习GUI,但是利用GUI编程的过程对于培养编程兴趣,深入理解Java编程有很大的作用。

    2022年7月15日
    17
  • 广义估计方程估计方法_广义估计方程简介

    广义估计方程估计方法_广义估计方程简介广义估计方程估计方法 Akeyassumpti whichlinearr istheindepen Inlongitudin Observatio

    2026年3月19日
    2
  • 炸裂!手摸手教你如何吃透一个 Java 项目,yyds

    炸裂!手摸手教你如何吃透一个 Java 项目,yyds先说一下大多数新手的情况 就是对着视频敲 Java 项目 其中遇到的 BUG 还能解决 但就是每次敲完一个项目 就感觉很空虚 项目里面的知识点感觉懂了但又好像没懂 应该怎样才能掌握一个项目所用的知识点呢 先分享一位好朋友丁威的经验吧 他是 RocketMQ 技术内幕 一书的作者 他在尝试学习 RocketMQ 之前未曾接触过消息中间件 但硬是通过自己提炼的学习方法 最终成为 RocketMQ 社区的优秀布道师 这让他有了一个非常亮眼的标签 极大提高了职场竞争力 他的总结有以下四点 了解这个项目的使用场

    2026年3月17日
    2

发表回复

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

关注全栈程序员社区公众号