【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解

目录

1、FileChannel

(1)获取 FileChannel 

(2)读取文件

(3)写入文件

(4)关闭通道

(5)当前位置与文件大小

(6)强制写入磁盘

2、两个 FileChannel 之间的数据传输

(1)使用 transferTo() 进行文件传输

(2)处理大于 2GB 的文件

(3)为什么说 transferTo() 高效?

3、文件的操作

3.1、Path类

(1)创建 Path 实例

(2)目录中的特殊符号:. 和 ..

3.2、Files

(1)检查文件是否存在

(2)创建一级目录

(3)创建多级目录

(4)拷贝文件

(5)移动文件

(6)删除文件或目录

(7)遍历目录文件

(8)统计 .jar 文件数量

(9)删除多级目录

(10)拷贝多级目录

1、FileChannel

        在 Java 的 NIO(New I/O)框架中,FileChannel 是一个强大的工具,它能让我们更高效地操作文件,但它和传统的 I/O 操作方式有很多不同。下面,让我们一起了解如何通过 FileChannel 来高效的进行文件的读写操作。

        注意:一个需要注意的地方是,FileChannel 只能在阻塞模式下工作。虽然 NIO 中的大部分组件都支持非阻塞模式(比如 SocketChannel),但 FileChannel 并不支持。换句话说,它会像传统 I/O 一样在读写时等待操作完成。

(1)获取 FileChannel 

FileChannel 不能被直接创建 ,而是要通过以下几种方式间接获取:

  1. FileInputStream 通过它获取的 FileChannel 只能用于读操作。
  2. FileOutputStream 通过它获取的 FileChannel 只能用于写操作。
  3. RandomAccessFile 这个类比较特殊,它可以同时支持读写,具体取决于你在创建 RandomAccessFile 时的模式。

示例:

FileInputStream fis = new FileInputStream("data.txt");
FileChannel readChannel = fis.getChannel(); // 只读FileOutputStream fos = new FileOutputStream("data.txt");
FileChannel writeChannel = fos.getChannel(); // 只写RandomAccessFile raf = new RandomAccessFile("data.txt", "rw");
FileChannel readWriteChannel = raf.getChannel(); // 读写

(2)读取文件

        从 FileChannel 读取数据需要借助 ByteBuffer。当我们调用 read() 方法时,它会把数据读入 ByteBuffer,并返回读到的字节数。如果返回值是 -1,则表示文件已经读取到末尾。

示例:

ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);if (bytesRead == -1) {System.out.println("文件已读完");
}

(3)写入文件

        写入文件时,我们也需要通过 ByteBuffer。先把数据写入 ByteBuffer,然后再通过 write() 方法把 ByteBuffer 中的数据写入 FileChannel。但有个特别需要注意的地方是,write() 方法不能保证会一次性把整个 ByteBuffer 的内容全部写完如果 ByteBuffer 中的数据量大于操作系统或通道能够处理的最大写入量,write() 方法将只能写入部分数据。,因此需要循环调用 write()

示例:

ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Hello, FileChannel".getBytes());
buffer.flip();  // 切换成读模式while (buffer.hasRemaining()) {channel.write(buffer);
}

        这里的 flip() 很重要(上一篇博客有讲到),因为它把 ByteBuffer 从写模式切换到了读模式,确保我们可以把数据从 ByteBuffer 中读出来并写入文件。

(4)关闭通道

        使用完 FileChannel 后,务必要记得关闭它。但好消息是,如果我们关闭了对应的 FileInputStreamFileOutputStream 或者 RandomAccessFile,它们会自动关闭 FileChannel,我们不用额外再关闭一次。

(5)当前位置与文件大小

        可以通过 position() 方法获取当前的文件指针位置,也可以通过 position(long newPos) 方法来设置它的位置。如果把位置设置到文件末尾,再进行写操作时,新的内容会直接追加在文件末尾。如果位置超过了文件末尾,会在新内容和末尾之间填充空白数据。

long pos = channel.position();  // 获取当前位置
channel.position(pos + 1024);   // 移动到当前位置之后的 1024 字节处

        通过 size() 方法获取文件的大小。

long fileSize = channel.size();
System.out.println("文件大小: " + fileSize);

(6)强制写入磁盘

在写文件时,操作系统通常会先将数据缓存起来,而不是立即写入磁盘。如果你需要确保数据即时写入磁盘,可以调用 force(true) 方法。该方法会将文件内容和元数据(如权限信息)立即写入磁盘,防止数据丢失。

2、两个 FileChannel 之间的数据传输

        当我们需要在文件之间进行拷贝时,NIO 提供了一种高效的方式,利用操作系统底层的零拷贝技术,大大提升了传输速度。就是使用两个 FileChannel 进行文件的拷贝操作。

(1)使用 transferTo() 进行文件传输

        FileChannel 提供了两个非常实用的方法来进行文件传输:transferTo()transferFrom()。这两个方法让我们可以高效地从一个通道复制数据到另一个通道。下面是一个简单的例子,展示了如何通过 transferTo() 方法把一个文件的内容传输到另一个文件。

String FROM = "helloword/data.txt";  // 源文件路径
String TO = "helloword/to.txt";      // 目标文件路径
long start = System.nanoTime();      // 记录开始时间try (FileChannel from = new FileInputStream(FROM).getChannel();  // 获取源文件的通道FileChannel to = new FileOutputStream(TO).getChannel();     // 获取目标文件的通道) {from.transferTo(0, from.size(), to);  // 通过 transferTo 方法传输数据
} catch (IOException e) {e.printStackTrace();
}long end = System.nanoTime();  // 记录结束时间
System.out.println("transferTo 用时:" + (end - start) / 1000_000.0);  // 输出耗时

输出结果会显示传输的时间

transferTo 用时:8.2011

        transferTo() 方法可以让我们在两个 FileChannel 之间直接传输数据。参数 0 表示从文件开头开始传输,from.size() 表示传输的字节数等于源文件的大小。它利用了底层的操作系统机制(如 Linux 中的 sendfile()),因此在大文件传输时具有非常高的效率。

(2)处理大于 2GB 的文件

        对于小文件,上面那样的传输是没有问题的。但是,如果我们要处理超过 2GB 的大文件,transferTo() 可能会遇到一些限制,特别是在 32 位的系统中或者由于操作系统的内部限制。因此,我们需要进行分段传输。

以下代码展示了如何处理超过 2GB 的大文件:

public class TestFileChannelTransferTo {public static void main(String[] args) {try (FileChannel from = new FileInputStream("data.txt").getChannel();FileChannel to = new FileOutputStream("to.txt").getChannel();) {long size = from.size();  // 获取文件大小// left 变量表示剩余未传输的字节数for (long left = size; left > 0; ) {System.out.println("position:" + (size - left) + " left:" + left);// transferTo 支持的最大传输量是 2GB,因此我们用循环分段传输left -= from.transferTo((size - left), left, to);}} catch (IOException e) {e.printStackTrace();}}
}

  1. 由于 transferTo() 的一次传输量限制为 2GB(2147483647 字节),我们通过一个循环进行多次传输,直到传输完所有数据。每次调用 transferTo() 后,会更新剩余的字节数,直到 left 变为 0 表示传输完成。
  2. 这个方法非常高效,因为它利用了操作系统的零拷贝机制,避免了不必要的上下文切换和数据拷贝。

(3)为什么说 transferTo() 高效?

        transferTo() 之所以高效,是因为它通过操作系统的低层 API 直接进行文件的传输,避免了从用户空间到内核空间的多次数据拷贝(零拷贝)。

(一)传统的 I/O 读写操作

        在传统的 I/O 读写操作中,数据从一个文件传输到另一个文件通常需要经过如下步骤:

  1. 从磁盘读取数据到内核缓冲区: 操作系统首先从磁盘中读取数据,存放在操作系统的内核缓冲区(Kernel Buffer)中。

  2. 从内核缓冲区拷贝到用户空间: 应用程序从内核缓冲区中读取数据,并将其拷贝到用户空间的缓冲区(User Buffer),这是一个从操作系统到应用程序层的数据传递。

  3. 从用户空间写回内核缓冲区: 应用程序在获取到数据后,再将数据传回给另一个文件的内核缓冲区,即从用户空间的缓冲区拷贝回内核缓冲区,这里再次发生一次拷贝。

  4. 从内核缓冲区写入目标磁盘: 最后,操作系统将数据从目标文件的内核缓冲区写入到目标磁盘。

在这种情况下,数据从磁盘到磁盘的传输经历了 4 次拷贝2 次上下文切换(应用程序和内核之间的切换),这导致了性能损耗。

(二)使用 transferTo() 或 transferFrom()

        transferTo() 或 transferFrom() 的优化过程主要是依赖于零拷贝技术,省略了不必要的用户空间数据拷贝。使用这两个方法时,操作系统直接将数据从一个文件的内核缓冲区传输到另一个文件的内核缓冲区,过程如下:

  1. 从源文件读取数据到内核缓冲区: 操作系统从源文件中读取数据,直接存放在源文件的内核缓冲区中。

  2. 通过 DMA 将数据从内核缓冲区传输到目标缓冲区: 操作系统使用 DMA(Direct Memory Access) 机制,将数据从源文件的内核缓冲区直接传输到目标文件的内核缓冲区,而无需经过用户空间。这一步通过操作系统内核中的数据传输通道完成。

  3. 将数据从目标缓冲区写入磁盘: 最后,操作系统将目标缓冲区中的数据写入到目标磁盘。

这样,整个过程中只发生了 2 次拷贝1 次上下文切换,并且跳过了用户空间的数据传输,从而大幅减少了 CPU 的参与,降低了 I/O 操作的成本,特别是在处理大文件时,这种优化表现非常显著。

(3)零拷贝的操作系统支持

  1. Linux:在 Linux 中,transferTo()transferFrom() 底层通过 sendfile() 系统调用实现,它允许文件描述符之间直接传输数据,避免了数据拷贝到用户空间的过程。

  2. Windows:在 Windows 操作系统中,类似的功能是通过 TransmitFile() 系统调用来实现的,提供了类似的零拷贝优化。

总结比较:

  • 传统 I/O:4 次拷贝2 次上下文切换
  • transferTo() / transferFrom()2 次拷贝1 次上下文切换

3、文件的操作

3.1、Path类

        Path类是在 JDK 7 中引入的一个新类,它用于替代之前繁琐的 File 类来处理文件路径。相较于 File 类,Path 提供了更灵活的方式来表示和操作文件路径。与之配套的还有 Paths 工具类,它帮助我们更便捷地创建 Path 实例。下面,是一些简单的 Path 用法的例子。

(1)创建 Path 实例

使用 Paths.get() 来获取 Path 实例,可以通过传入不同形式的路径来表示文件的位置:

Path source = Paths.get("1.txt"); 

这个表示相对路径,它使用的是当前项目目录作为起点(可以通过 System.getProperty("user.dir") 查看当前项目所在目录)。

如果你想指定一个绝对路径,直接写绝对路径即可:

Path source = Paths.get("d:\\1.txt"); // 代表 d:\1.txt
Path source = Paths.get("d:/1.txt");  // 斜杠也可以正常使用

另外,Paths.get() 也可以通过多个参数来拼接路径:

Path projects = Paths.get("d:\\data", "projects");  // 这里的代码相当于 d:\data\projects。

(2)目录中的特殊符号:. 和 ..

  • . 表示当前目录。
  • .. 表示上一级目录。

假设你有以下目录结构:

d:|- data|- projects|- a|- b

如果你想表示 b 目录,但从 a 目录“退回”一步再进入 b,你可以写成:

Path path = Paths.get("d:\\data\\projects\\a\\..\\b");
System.out.println(path);

这段代码输出的是 d:\data\projects\a\..\b,看起来还不是很直观。不过,Path 提供了一个 normalize() 方法,它可以将路径中的 .. 等符号“正常化”:

System.out.println(path.normalize());

经过 normalize() 处理后,输出就变成了 d:\data\projects\b,清晰地展示了最终的路径。

3.2、Files

        Files 类是 NIO 中一套强大的文件操作工具,无论是检查文件是否存在、创建目录、拷贝文件,还是删除文件,Files 类都可以轻松搞定。

(1)检查文件是否存在

        使用 Files.exists() 可以轻松判断某个文件或目录是否存在。

Path path = Paths.get("helloword/data.txt");
System.out.println(Files.exists(path)); // true 表示文件存在,false 表示文件不存在

(2)创建一级目录

        如果目录已经存在,会抛出 FileAlreadyExistsException,同时 createDirectory() 方法不能一次创建多级目录。如果父目录不存在,将抛出 NoSuchFileException

Path path = Paths.get("helloword/d1");
Files.createDirectory(path); // 创建单一的一级目录

(3)创建多级目录

        与 createDirectory() 不同,createDirectories() 方法会自动创建不存在的父目录,非常方便。

Path path = Paths.get("helloword/d1/d2");
Files.createDirectories(path); // 创建多级目录

(4)拷贝文件

Path source = Paths.get("helloword/data.txt");
Path target = Paths.get("helloword/target.txt");
Files.copy(source, target); // 拷贝文件,如果目标文件已存在,会抛 FileAlreadyExistsException

        如果希望在目标文件已存在时进行覆盖,可以使用 StandardCopyOption.REPLACE_EXISTING

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); // 覆盖已有文件

(5)移动文件

Path source = Paths.get("helloword/data.txt");
Path target = Paths.get("helloword/data_moved.txt");
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE); // 原子性文件移动

        StandardCopyOption.ATOMIC_MOVE 选项确保文件移动的操作是原子的,也就是说,要么移动成功,要么不做任何更改。

(6)删除文件或目录

删除文件

Path target = Paths.get("helloword/target.txt");
Files.delete(target); // 删除文件,如果文件不存在,会抛 NoSuchFileException

        注意:如果文件不存在,Files.delete() 会抛出 NoSuchFileException。如果不确定文件是否存在,可以使用 Files.deleteIfExists() 来避免异常抛出。

删除目录

Path target = Paths.get("helloword/d1");
Files.delete(target); // 删除空目录

        注意:如果目录不为空,会抛出 DirectoryNotEmptyException,需要先删除目录中的文件。

(7)遍历目录文件

        使用 Files.walkFileTree() 可以递归遍历目录中的所有文件。以下代码将遍历指定目录,并统计文件和目录的数量:

public static void main(String[] args) throws IOException {Path path = Paths.get("C:\\Program Files\\Java\\jdk1.8.0_91"); // 要遍历的根目录AtomicInteger dirCount = new AtomicInteger(); // 记录目录数量AtomicInteger fileCount = new AtomicInteger(); // 记录文件数量// 遍历目录和文件Files.walkFileTree(path, new SimpleFileVisitor<Path>() {@Overridepublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {System.out.println(dir); // 打印目录dirCount.incrementAndGet(); // 统计目录数量return super.preVisitDirectory(dir, attrs);}@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {System.out.println(file); // 打印文件fileCount.incrementAndGet(); // 统计文件数量return super.visitFile(file, attrs);}});System.out.println("目录数:" + dirCount); // 输出目录总数System.out.println("文件数:" + fileCount); // 输出文件总数
}

(8)统计 .jar 文件数量

        假设我们想要统计某个目录中 .jar 文件的数量,可以在 visitFile 方法中添加对文件后缀的判断:

Path path = Paths.get("C:\\Program Files\\Java\\jdk1.8.0_91");
AtomicInteger fileCount = new AtomicInteger();Files.walkFileTree(path, new SimpleFileVisitor<Path>() {@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {if (file.toFile().getName().endsWith(".jar")) { // 判断文件是否为 .jar 结尾fileCount.incrementAndGet();}return super.visitFile(file, attrs);}
});System.out.println("JAR 文件数:" + fileCount); // 输出 .jar 文件总数

(9)删除多级目录

        我们可以使用 Files.walkFileTree() 来递归删除目录及其内容。以下代码演示了如何递归删除指定路径下的所有文件和目录:

Path path = Paths.get("d:\\a");
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {@Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {Files.delete(file); // 删除文件return super.visitFile(file, attrs);}@Overridepublic FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {Files.delete(dir); // 删除空目录return super.postVisitDirectory(dir, exc);}
});

        注意删除操作的风险:删除是一个非常危险的操作,特别是递归删除目录时,务必要确保该目录中的文件没有重要数据。

(10)拷贝多级目录

想要递归地拷贝整个多级目录,以下代码演示了如何遍历目录并进行拷贝操作:

long start = System.currentTimeMillis();
String source = "D:\\Snipaste-1.16.2-x64"; // 源目录
String target = "D:\\Snipaste-1.16.2-x64_copy"; // 目标目录// 遍历源目录
Files.walk(Paths.get(source)).forEach(path -> {try {String targetName = path.toString().replace(source, target); // 将源路径转换为目标路径if (Files.isDirectory(path)) { // 如果是目录Files.createDirectory(Paths.get(targetName)); // 创建目录} else if (Files.isRegularFile(path)) { // 如果是文件Files.copy(path, Paths.get(targetName)); // 拷贝文件}} catch (IOException e) {e.printStackTrace();}
});

推荐: 

【NIO基础】NIO(非阻塞 I/O)和 IO(传统 I/O)的区别,以及 NIO 的三大组件详解-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/m0_65277261/article/details/142662308?spm=1001.2014.3001.5501【Redis】Redis中的 AOF(Append Only File)持久化机制-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/m0_65277261/article/details/142661193?spm=1001.2014.3001.5501

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/440697.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode-42. 接雨水 单调栈

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表…

微软发布Windows 11 2024更新,新型Copilot+ AI PC功能亮相

前言 微软在Windows 11的2024更新中加强了对人工智能的应用&#xff0c;推出了新功能Copilot。 此次更新的版本号为26100.1742&#xff0c;Copilot将首先在Windows Insider中推出&#xff0c;计划于11月向特定设备和市场推广&#xff0c;用户需开启“尽快获取最新更新”选项以…

RESTful风格接口+Swagger生成Web API文档

RESTful风格接口Swagger生成Web API文档 文章目录 RESTful风格接口Swagger生成Web API文档1.RESTful风格接口RESTful简介RESTful详细图示常见http状态码springboot实现RESTfulRESTful springboot设计实例demo 2.Swagger生产Web API文档Swagger简介使用Swagger1.加入依赖2.配置S…

基于STM32的超声波测距仪设计

引言 本项目将基于STM32微控制器设计一个超声波测距仪&#xff0c;通过超声波传感器实现距离测量&#xff0c;并将结果显示在液晶屏上。该项目展示了STM32微控制器与超声波传感器、LCD显示器的接口通信&#xff0c;以及信号处理和距离计算的过程。 环境准备 1. 硬件设备 ST…

技术速递|Python in Visual Studio Code 2024年9月发布

排版&#xff1a;Alan Wang 我们很高兴地宣布将于 2024 年 9 月发布适用于 Visual Studio Code 的 Python 和 Jupyter 扩展&#xff01; 此版本包括以下公告&#xff1a; Django 单元测试支持使用 Pylance 从 inlay 提示转到定义 如果您有兴趣&#xff0c;可以在我们的 Pyth…

提升 CI/CD 稳定性:Jenkins 开机自检与推送通知

简介&#xff1a;Jenkins 是一个广泛使用的开源自动化服务器&#xff0c;常用于持续集成和持续交付。在某些情况下&#xff0c;服务器重启可能导致 Jenkins 构建任务中断或失败。为了解决这个问题&#xff0c;可以使用一个自检服务&#xff0c;定期检查系统的启动时间&#xff…

算法题总结(十)——二叉树上

#二叉树的递归遍历 // 前序遍历递归LC144_二叉树的前序遍历 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result new ArrayList<Integer>(); //也可以把result 作为全局变量&#xff0c;只需要一个函数即可。…

Open3D实现点云数据的序列化与网络传输

转载自个人博客&#xff1a;Open3D实现点云数据的序列化与网络传输 在处理点云数据的时候&#xff0c;有时候需要实现点云数据的远程传输。当然可以利用传输文件的方法直接把点云数据序列化成数据流进行传输&#xff0c;但Open3D源码在实现RPC功能时就提供了一套序列化及传输的…

今日指数day8实战补充用户管理模块(下)

ps : 由于前端将userId封装为BigInt类型 , 导致有精度损失, 传入的userId不正确 , 部分功能无法正确实现 , 但是代码已经完善 1.4 更新用户角色信息接口说明 1&#xff09;原型效果 2&#xff09;接口说明 功能描述&#xff1a;更新用户角色信息 服务路径&#xff1a;/user/…

vue-scrollto实现页面组件锚点定位

文章目录 前言背景操作指南安装及配置步骤vue组件中使用 参考文章 前言 博主介绍&#xff1a;✌目前全网粉丝3W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容&#xff1a;Java后端、大数据…

php获取远程https内容时提示 PHP Warning: copy(): Unable to find the wrapper “https“ 解决方法

异常信息&#xff1a; php -r "copy(https://getcomposer.org/installer, composer-setup.php);" PHP Warning: copy(): Unable to find the wrapper "https" - did you forget to enable it when you configured PHP? in Command line code on line 1 P…

鸿蒙harmonyos next flutter混合开发之开发plugin(获取操作系统版本号)

创建Plugin为my_plugin flutter create --org com.example --templateplugin --platformsandroid,ios,ohos my_plugin 创建Application为my_application flutter create --org com.example my_application flutter_application引用flutter_plugin&#xff0c;在pubspec.yam…

万界星空科技MES数据集成平台

制造执行系统MES作为连接企业上层ERP系统和现场控制系统的桥梁&#xff0c;承担了实时数据采集、处理、分析和传递的重要任务。MES数据集成平台是一个集成各类数据源&#xff0c;将数据进行整合和统一管理的系统&#xff0c;通过提供标准化接口和协议&#xff0c;实现数据的无缝…

图像分割恢复方法

传统的图像分割方法主要依赖于图像的灰度值、纹理、颜色等特征&#xff0c;通过不同的算法将图像分割成多个区域。这些方法通常可以分为以下几类&#xff1a; 1.基于阈值的方法 2.基于边缘的方法 3.基于区域的方法 4.基于聚类的方法 下面详细介绍这些方法及其示例代码。 1. 基…

《黑神话:悟空》像素版 v0.1b [PC+安卓]

游戏简介 《黑神话&#xff1a;悟空》像素版是一款由火山哥哥与林学学LinkLin合作开发的游戏。这款游戏采用了像素化的艺术风格&#xff0c;巧妙地简化并再现了《黑神话&#xff1a;悟空》中的核心玩法和经典场景。游戏不仅成功复刻了原作中的战斗系统和角色动画&#xff0c;还…

解锁 Python 嵌套字典的奥秘:高效操作与实战应用指南

文章目录 前言&#x1f340;一、 什么是 Python 字典&#xff1f;1.1 字典的语法 &#x1f340;二、 字典的基本操作2.1 字典的创建2.2 访问字典中的值2.3 添加或修改键值对2.4 删除字典中的键值对 &#x1f340;三、 字典的遍历操作3.1 遍历字典的键3.2 遍历字典的值3.3 同时遍…

【springboot】使用代码生成器快速开发

接上一项目&#xff0c;使用mybatis-plus-generator实现简易代码文件生成 在fast-demo-web模块中的pom.xml中添加mybatis-plus-generator、freemarker和Lombok依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator&…

劳动与科技、艺术结合更好提高劳动教育意义

在中小学教育中&#xff0c;劳动教育是培养学生基本生活技能和劳动习惯的重要环节。但当代的劳动教育不在单纯的劳动&#xff0c;而是劳动技能的提升与学习&#xff0c;通过学习劳动技能与实践活动&#xff0c;强化劳动教育与其他课程的融合&#xff0c;学生深刻理解劳动的意义…

draw.io 设置默认字体及添加常用字体

需求描述 draw.io 是一个比较好的开源免费画图软件。但是其添加容器或者文本框时默认的字体是 Helvetica&#xff0c;一般的期刊、会议论文或者学位论文要求的英文字体是 Times New Roman&#xff0c;中文字体是 宋体&#xff0c;所以一般需要在文本字体选项里的下拉列表选择 …

【2024】前端学习笔记13-JavaScript修改网页样式

学习笔记 1.修改网页样式1.1.修改内联样式(`style`属性)1.2.使用`cssText`属性:2.修改样式类(`classList`属性)2.1.添加和移除类名2.2.切换类名(`toggle`方法)1.修改网页样式 1.1.修改内联样式(style属性) 直接修改元素的style属性: 可以通过获取元素对象,然后直…