【NIO番外篇】之组件 Channel
目录
- 一、什么是NIO Channel?
- 二、常见的Channel组件及其用法
- 1. FileChannel
- 2. SocketChannel
- 3. ServerSocketChannel
- 4. DatagramChannel
🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!
🌟了解 Redis主从复制 请看 : Redis主从复制:告别单身Redis!
其他优质专栏: 【🎇SpringBoot】【🎉多线程】【🎨Redis】【✨设计模式专栏(已完结)】…等
如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning
这篇文章让我来详细讲解一下Java NIO中的Channel
(通道)组件。
一、什么是NIO Channel?
看本篇文章之前可以先看看NIO是啥:NIO,看完你就懂了!
在Java NIO(New I/O)模型中,Channel
扮演着核心角色。可以将其理解为一个连接到能够执行I/O操作的实体(如文件、网络套接字)的开放连接或通道。它类似于传统Java I/O中的Stream
(流),但有几个关键区别:
- 双向性:传统的
InputStream
只能读,OutputStream
只能写。而Channel
通常是双向的,可以同时进行读和写操作(具体取决于通道类型,例如FileChannel
)。 - 与Buffer交互:
Channel
本身不直接存储数据。所有数据读写都必须通过Buffer
对象进行。数据总是先从Channel
读入Buffer
,或者从Buffer
写入Channel
。 - 异步/非阻塞支持:
Channel
可以工作在阻塞(Blocking)模式或非阻塞(Non-Blocking)模式下。非阻塞模式是NIO实现高并发、高性能网络应用的关键,通常与Selector
(选择器)一起使用。 - 底层连接:
Channel
代表了与操作系统底层I/O服务的直接连接。
可以把Channel
想象成数据传输的管道,而Buffer
是运载数据的卡车。卡车(Buffer)在管道(Channel)中来回运输数据。
二、常见的Channel组件及其用法
Java NIO提供了多种Channel
实现,用于处理不同类型的I/O。以下是一些最常见的:
FileChannel
: 用于文件I/O。SocketChannel
: 用于TCP网络通信的客户端。ServerSocketChannel
: 用于TCP网络通信的服务器端,监听并接受连接。DatagramChannel
: 用于UDP网络通信。
下面我们逐一详细讲解:
1. FileChannel
FileChannel
是用于读、写、映射和操作文件的通道。它是唯一保证始终阻塞,不能配置为非阻塞模式的常用通道。
特点:
- 可以从文件的任何位置读写数据(随机访问)。
- 可以将文件区域直接映射到内存(MappedByteBuffer),实现更快的I/O(内存映射文件)。
- 可以实现通道之间的数据直接传输(零拷贝,Zero-Copy),效率很高。
- 线程安全。
如何获取FileChannel
:
- 通过
FileInputStream
的getChannel()
方法。 - 通过
FileOutputStream
的getChannel()
方法。 - 通过
RandomAccessFile
的getChannel()
方法。 - (推荐) 使用
FileChannel.open(Path path, OpenOption... options)
静态工厂方法。
常用方法:
read(ByteBuffer dst)
: 从通道读取数据到Buffer。返回读取的字节数,或-1表示到达文件末尾。write(ByteBuffer src)
: 将Buffer中的数据写入通道。返回写入的字节数。position()
: 返回此通道的文件位置。position(long newPosition)
: 设置此通道的文件位置。size()
: 返回此通道关联文件的当前大小。truncate(long size)
: 将此通道的文件截断为给定大小。force(boolean metaData)
: 强制将通道的所有更新写入存储设备。map(FileChannel.MapMode mode, long position, long size)
: 将此通道的文件区域直接映射到内存中。transferTo(long position, long count, WritableByteChannel target)
: 将字节从此通道的文件传输到给定的可写字节通道(高效的文件复制)。transferFrom(ReadableByteChannel src, long position, long count)
: 将字节从给定的可读字节通道传输到此通道的文件(高效的文件复制)。
示例:使用FileChannel
读取文件
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class FileChannelReadExample {public static void main(String[] args) {Path filePath = Paths.get("example.txt"); // 假设存在一个 example.txt 文件// 使用 try-with-resources 确保通道被关闭try (FileChannel fileChannel = FileChannel.open(filePath, StandardOpenOption.READ)) {ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配一个1KB的缓冲区System.out.println("Reading from file: " + filePath);int bytesRead;while ((bytesRead = fileChannel.read(buffer)) != -1) { // 从通道读数据到缓冲区System.out.println("Read " + bytesRead + " bytes.");// 切换Buffer到读模式buffer.flip();// 处理缓冲区中的数据 (这里简单地打印出来)// 使用 Charset 将字节解码为字符System.out.print(StandardCharsets.UTF_8.decode(buffer));// 清空/重置Buffer,为下一次读做准备buffer.clear(); // 或者 buffer.compact(); 如果想保留未读数据}System.out.println("\nEnd of file reached.");} catch (IOException e) {System.err.println("Error reading file: " + e.getMessage());e.printStackTrace();}}
}
// 需要先创建一个 example.txt 文件,并写入一些内容,例如:
// Hello, Java NIO FileChannel!
// This is a test file.
示例:使用FileChannel
写入文件
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class FileChannelWriteExample {public static void main(String[] args) {Path filePath = Paths.get("output.txt");String dataToWrite = "Writing using Java NIO FileChannel.\nLine 2.";// 使用 try-with-resourcestry (FileChannel fileChannel = FileChannel.open(filePath,StandardOpenOption.WRITE, // 允许写入StandardOpenOption.CREATE, // 如果文件不存在则创建StandardOpenOption.TRUNCATE_EXISTING)) { // 如果文件存在则清空内容ByteBuffer buffer = ByteBuffer.allocate(1024);// 将字符串数据放入缓冲区buffer.put(dataToWrite.getBytes(StandardCharsets.UTF_8));// 切换Buffer到读模式(写出模式)buffer.flip();System.out.println("Writing to file: " + filePath);while (buffer.hasRemaining()) {int bytesWritten = fileChannel.write(buffer); // 将缓冲区数据写入通道System.out.println("Wrote " + bytesWritten + " bytes.");}System.out.println("Finished writing.");} catch (IOException e) {System.err.println("Error writing file: " + e.getMessage());e.printStackTrace();}}
}
示例:使用transferTo
高效复制文件
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class FileChannelTransferExample {public static void main(String[] args) {Path sourcePath = Paths.get("example.txt"); // 源文件Path destPath = Paths.get("example_copy.txt"); // 目标文件try (FileChannel sourceChannel = FileChannel.open(sourcePath, StandardOpenOption.READ);FileChannel destChannel = FileChannel.open(destPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {long position = 0;long count = sourceChannel.size();System.out.println("Starting file transfer...");long bytesTransferred = sourceChannel.transferTo(position, count, destChannel);// 或者使用 destChannel.transferFrom(sourceChannel, position, count);System.out.println("Transferred " + bytesTransferred + " bytes from " + sourcePath + " to " + destPath);} catch (IOException e) {System.err.println("Error during file transfer: " + e.getMessage());e.printStackTrace();}}
}
2. SocketChannel
SocketChannel
用于TCP网络连接的客户端。它可以工作在阻塞或非阻塞模式下。
特点:
- 用于发起TCP连接到服务器。
- 可以进行读写操作。
- 可以配置为非阻塞模式,与
Selector
配合使用,实现单线程处理多个连接。
如何获取SocketChannel
:
SocketChannel.open()
: 创建一个未连接的SocketChannel
。SocketChannel.open(SocketAddress remote)
: 创建并直接连接到指定地址。
常用方法:
connect(SocketAddress remote)
: 连接到远程地址(非阻塞模式下立即返回,可能未完成连接)。finishConnect()
: 完成非阻塞连接过程。如果连接成功返回true
,如果仍在进行中返回false
,如果失败则抛出异常。isConnected()
: 检查是否已连接。read(ByteBuffer dst)
: 从通道读取数据到Buffer。write(ByteBuffer src)
: 将Buffer中的数据写入通道。configureBlocking(boolean block)
: 设置阻塞/非阻塞模式。true
为阻塞(默认),false
为非阻塞。register(Selector sel, int ops)
: 将通道注册到Selector
上,监听指定事件(如SelectionKey.OP_CONNECT
,OP_READ
,OP_WRITE
)。close()
: 关闭连接。
示例:阻塞模式的SocketChannel
客户端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;public class BlockingSocketClient {public static void main(String[] args) {String hostname = "localhost"; // 服务器地址int port = 9090; // 服务器端口try (SocketChannel socketChannel = SocketChannel.open()) {// 连接服务器 (阻塞模式下会等待连接成功或失败)System.out.println("Connecting to " + hostname + ":" + port);socketChannel.connect(new InetSocketAddress(hostname, port));System.out.println("Connection established.");// 发送数据String message = "Hello from NIO Client!";ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8));System.out.println("Sending message: " + message);while (writeBuffer.hasRemaining()) {socketChannel.write(writeBuffer);}// 接收数据ByteBuffer readBuffer = ByteBuffer.allocate(1024);int bytesRead = socketChannel.read(readBuffer);if (bytesRead > 0) {readBuffer.flip();byte[] receivedBytes = new byte[readBuffer.remaining()];readBuffer.get(receivedBytes);String response = new String(receivedBytes, StandardCharsets.UTF_8);System.out.println("Received response: " + response);} else if (bytesRead == -1) {System.out.println("Server closed connection.");}System.out.println("Closing connection.");} catch (IOException e) {System.err.println("Client error: " + e.getMessage());e.printStackTrace();}}
}
// 这个例子需要一个监听在 localhost:9090 的服务器来响应。
示例:非阻塞模式SocketChannel
(概念,通常与Selector配合)
非阻塞模式的SocketChannel
通常与Selector
一起使用,这里只展示配置和连接部分:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;public class NonBlockingSocketClientConcept {public static void main(String[] args) {String hostname = "localhost";int port = 9090;try {// 1. 创建 Selector 和 SocketChannelSelector selector = Selector.open();SocketChannel socketChannel = SocketChannel.open();// 2. 配置为非阻塞模式socketChannel.configureBlocking(false);// 3. 发起连接 (非阻塞,立即返回)boolean connected = socketChannel.connect(new InetSocketAddress(hostname, port));System.out.println("Initiating connection (non-blocking)...");if (connected) {System.out.println("Connection established immediately.");// 立即连接成功(少见),可以直接注册读写事件socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);} else {// 连接尚未完成,注册 OP_CONNECT 事件,等待连接完成System.out.println("Connection pending, registering OP_CONNECT.");socketChannel.register(selector, SelectionKey.OP_CONNECT);}// --- 之后进入 Selector 循环 ---// while (true) {// selector.select(); // 阻塞等待事件// Set<SelectionKey> selectedKeys = selector.selectedKeys();// Iterator<SelectionKey> keyIterator = selectedKeys.iterator();//// while (keyIterator.hasNext()) {// SelectionKey key = keyIterator.next();// if (key.isConnectable()) {// // 连接完成事件// handleConnect(key, selector);// } else if (key.isReadable()) {// // 可读事件// handleRead(key);// } else if (key.isWritable()) {// // 可写事件// handleWrite(key);// }// keyIterator.remove();// }// }// --- Selector 循环结束 ---// (实际应用中,Selector 循环会处理连接、读写,并在适当时候关闭 channel 和 selector)System.out.println("Selector loop would start here (conceptual).");// 在实际应用中关闭资源// socketChannel.close();// selector.close();} catch (IOException e) {System.err.println("Client error: " + e.getMessage());e.printStackTrace();}}// 辅助方法 (仅为演示)private static void handleConnect(SelectionKey key, Selector selector) throws IOException {SocketChannel channel = (SocketChannel) key.channel();if (channel.finishConnect()) {System.out.println("Connection successfully established.");// 连接成功,现在可以注册读写事件了key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);} else {System.err.println("Failed to finish connection.");key.cancel(); // 取消注册channel.close();}}// handleRead 和 handleWrite 方法省略
}
3. ServerSocketChannel
ServerSocketChannel
用于TCP网络连接的服务器端。它负责监听指定端口上的连接请求,并为每个接受的连接创建一个SocketChannel
。它也可以配置为阻塞或非阻塞模式。
特点:
- 监听入站TCP连接。
- 为每个接受的连接创建一个
SocketChannel
。 - 可以配置为非阻塞模式,与
Selector
配合实现高并发服务器。
如何获取ServerSocketChannel
:
ServerSocketChannel.open()
: 创建一个ServerSocketChannel
。
常用方法:
bind(SocketAddress local)
: 将通道的套接字绑定到本地地址并开始监听。accept()
: 接受一个连接。在阻塞模式下,此方法会阻塞直到有连接进来。在非阻塞模式下,如果没有待处理的连接,它会立即返回null
。返回的是代表客户端连接的SocketChannel
。configureBlocking(boolean block)
: 设置阻塞/非阻塞模式。register(Selector sel, int ops)
: 将通道(通常是自身,监听OP_ACCEPT
事件)注册到Selector
。close()
: 关闭监听。
示例:阻塞模式的ServerSocketChannel
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;public class BlockingServerSocket {public static void main(String[] args) {int port = 9090;try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {// 绑定端口并开始监听serverSocketChannel.bind(new InetSocketAddress(port));System.out.println("Server started. Listening on port " + port + " (Blocking Mode)");while (true) { // 无限循环接受连接System.out.println("Waiting for a new connection...");// 阻塞,直到有客户端连接进来SocketChannel clientChannel = serverSocketChannel.accept();System.out.println("Accepted connection from: " + clientChannel.getRemoteAddress());// 为每个客户端创建一个新线程处理(简单示例,非最佳实践)new Thread(() -> handleClient(clientChannel)).start();}} catch (IOException e) {System.err.println("Server error: " + e.getMessage());e.printStackTrace();}}private static void handleClient(SocketChannel clientChannel) {try (SocketChannel channel = clientChannel) { // 使用 try-with-resourcesByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = channel.read(buffer); // 读取客户端数据if (bytesRead > 0) {buffer.flip();byte[] receivedBytes = new byte[buffer.remaining()];buffer.get(receivedBytes);String message = new String(receivedBytes, StandardCharsets.UTF_8);System.out.println("Received from client " + channel.getRemoteAddress() + ": " + message);// 回显给客户端String response = "Server received: " + message;ByteBuffer writeBuffer = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));while (writeBuffer.hasRemaining()) {channel.write(writeBuffer);}System.out.println("Sent response to client " + channel.getRemoteAddress());} else if (bytesRead == -1) {System.out.println("Client " + channel.getRemoteAddress() + " closed connection.");}} catch (IOException e) {try {System.err.println("Error handling client " + clientChannel.getRemoteAddress() + ": " + e.getMessage());} catch (IOException ignored) {System.err.println("Error handling client (address unavailable): " + e.getMessage());}} finally {System.out.println("Finished handling client " ); // 地址可能已不可用}}
}
示例:非阻塞模式ServerSocketChannel
(概念,通常与Selector配合)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;public class NonBlockingServerSocketConcept {public static void main(String[] args) {int port = 9090;try {// 1. 创建 Selector 和 ServerSocketChannelSelector selector = Selector.open();ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 2. 配置为非阻塞模式serverSocketChannel.configureBlocking(false);// 3. 绑定端口serverSocketChannel.bind(new InetSocketAddress(port));System.out.println("Server started. Listening on port " + port + " (Non-Blocking Mode)");// 4. 将 ServerSocketChannel 注册到 Selector,监听 OP_ACCEPT 事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Waiting for events...");// --- 进入 Selector 循环 ---while (true) {int readyChannels = selector.select(); // 阻塞等待事件if (readyChannels == 0) continue; // 没有事件,继续等待Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {// 有新的连接请求ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept(); // 接受连接 (非阻塞)if (clientChannel != null) {clientChannel.configureBlocking(false); // 将接受的客户端Channel也设为非阻塞// 将新的客户端Channel注册到Selector,监听读事件clientChannel.register(selector, SelectionKey.OP_READ);System.out.println("Accepted new connection from: " + clientChannel.getRemoteAddress());}} else if (key.isReadable()) {// 有客户端Channel可读// handleRead(key); // 调用处理读取的方法System.out.println("Readable event for channel: " + key.channel());// 在这里读取数据...// 如果读取完毕或连接关闭,需要 key.cancel() 和 channel.close()} else if (key.isWritable()) {// 有客户端Channel可写// handleWrite(key); // 调用处理写入的方法System.out.println("Writable event for channel: " + key.channel());// 在这里写入数据...// 写完数据后,可能需要取消 OP_WRITE 监听: key.interestOps(SelectionKey.OP_READ);}keyIterator.remove(); // 处理完后移除Key,防止重复处理}}// --- Selector 循环结束 ---} catch (IOException e) {System.err.println("Server error: " + e.getMessage());e.printStackTrace();}// 实际应用中需要正确关闭 serverSocketChannel 和 selector}// handleRead 和 handleWrite 方法省略
}
4. DatagramChannel
DatagramChannel
用于UDP(用户数据报协议)网络通信。UDP是无连接的,所以DatagramChannel
可以向任何IP地址和端口发送数据报,也可以接收来自任何IP地址和端口的数据报。
特点:
- 支持无连接的UDP数据报发送和接收。
- 可以配置为阻塞或非阻塞模式。
- 可以“连接”(
connect
)到一个特定地址,这之后就可以像TCP一样使用read
和write
方法,只与该特定地址通信。
如何获取DatagramChannel
:
DatagramChannel.open()
: 创建一个DatagramChannel
。
常用方法:
bind(SocketAddress local)
: 将通道的套接字绑定到本地地址以接收数据。send(ByteBuffer src, SocketAddress target)
: 向指定目标地址发送数据报。receive(ByteBuffer dst)
: 接收一个数据报,并将数据放入Buffer。返回发送方的地址。在阻塞模式下,会等待直到收到数据报。在非阻塞模式下,如果没有数据报会立即返回null
。connect(SocketAddress remote)
: 将通道“连接”到特定的远程地址。连接后,只能向/从此地址发送/接收数据报,并且可以使用read
/write
方法。read(ByteBuffer dst)
: (仅在连接后使用)从连接的对等方读取数据报。write(ByteBuffer src)
: (仅在连接后使用)向连接的对等方发送数据报。disconnect()
: 断开连接(如果已连接)。configureBlocking(boolean block)
: 设置阻塞/非阻塞模式。register(Selector sel, int ops)
: (非阻塞模式)注册到Selector
。
示例:DatagramChannel
发送数据
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;public class DatagramSender {public static void main(String[] args) {String targetHost = "localhost";int targetPort = 9876;String message = "UDP Packet from NIO DatagramChannel";try (DatagramChannel channel = DatagramChannel.open()) {// 不需要 bind,操作系统会自动分配端口发送SocketAddress targetAddress = new InetSocketAddress(targetHost, targetPort);ByteBuffer buffer = ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8));System.out.println("Sending UDP packet to " + targetAddress);int bytesSent = channel.send(buffer, targetAddress); // 发送数据报System.out.println("Sent " + bytesSent + " bytes.");} catch (IOException e) {System.err.println("Datagram sender error: " + e.getMessage());e.printStackTrace();}}
}
示例:DatagramChannel
接收数据(阻塞模式)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.charset.StandardCharsets;public class DatagramReceiver {public static void main(String[] args) {int listenPort = 9876;try (DatagramChannel channel = DatagramChannel.open()) {// 绑定到指定端口接收数据channel.bind(new InetSocketAddress(listenPort));System.out.println("UDP Receiver started. Listening on port " + listenPort);ByteBuffer buffer = ByteBuffer.allocate(1024);while (true) { // 循环接收buffer.clear(); // 准备接收System.out.println("Waiting to receive UDP packet...");// 阻塞,直到收到数据报SocketAddress senderAddress = channel.receive(buffer);if (senderAddress != null) {buffer.flip(); // 切换到读模式byte[] receivedBytes = new byte[buffer.remaining()];buffer.get(receivedBytes);String message = new String(receivedBytes, StandardCharsets.UTF_8);System.out.println("Received " + buffer.limit() + " bytes from " + senderAddress + ": " + message);// (可选) 可以向发送方回显消息// String response = "Receiver got: " + message;// ByteBuffer sendBuffer = ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8));// channel.send(sendBuffer, senderAddress);}}} catch (IOException e) {System.err.println("Datagram receiver error: " + e.getMessage());e.printStackTrace();}}
}
// 需要先运行 Receiver,再运行 Sender。
总结
Channel
是NIO中数据传输的管道,连接到I/O源(文件、套接字)。Channel
与Buffer
紧密配合,读写操作通过Buffer
进行。FileChannel
用于文件操作,支持随机访问、内存映射和零拷贝。SocketChannel
和ServerSocketChannel
用于TCP网络通信,支持阻塞和非阻塞模式。DatagramChannel
用于UDP网络通信,支持阻塞和非阻塞模式。- 网络相关的
Channel
(SocketChannel
,ServerSocketChannel
,DatagramChannel
)可以配置为非阻塞模式,并与Selector
一起使用,是构建高性能、高并发NIO应用(如Netty框架的基础)的关键。
希望这篇文章的讲解和示例能帮助你理解Java NIO中的Channel
组件!
相关文章:
【NIO番外篇】之组件 Channel
目录 一、什么是NIO Channel?二、常见的Channel组件及其用法1. FileChannel2. SocketChannel3. ServerSocketChannel4. DatagramChannel 🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下&a…...
探秘 Ruby 与 JavaScript:动态语言的多面风采
1 语法特性对比:简洁与灵活 1.1 Ruby 的语法优雅 Ruby 的语法设计旨在让代码读起来像自然语言一样流畅。它拥有简洁而富有表现力的语法结构,例如代码块、符号等。 以下是一个使用 Ruby 进行数组操作的简单示例: # 定义一个数组 numbers [1…...
高频面试题(含笔试高频算法整理)基本总结回顾21
干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版和持续更新见高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言…...
深入浅出一下Python函数的核心概念与进阶应用
本篇技术博文摘要 🌟 本文系统梳理了Python函数的核心知识点,从基础概念到高级特性,构建了完整的函数编程知识体系。内容涵盖:变量作用域的局部与全局划分、函数注释的规范写法、参数传递中值与引用的区别、匿名函数的灵活应用&am…...
【漫话机器学习系列】198.异常值(Outlier)
异常值(Outlier)全面指南 —— 检测、分析与处理 作者:Chris Albon(图源) 场景:数据清洗与特征工程必备技能 一、什么是异常值(Outlier) 定义 异常值(Outlier࿰…...
React 记账本项目实战:多页面路由、Context 全局
在本文中,我们将分享一个使用 React 开发的「记账本」项目的实战经验。该项目通过 VS Code 完成,包含首页、添加记录页、编辑页等多个功能页面,采用了 React Router 实现路由导航,使用 Context API 管理全局的交易记录状态,并引入数据可视化组件呈现不同月份的支出情况。项…...
[React] 如何用 Zustand 构建一个响应式 Enum Store?附 RTKQ 实战与 TS 架构落地
[React] 如何用 Zustand 构建一个响应式 Enum Store?附 RTKQ 实战与 TS 架构落地 本文所有案例与数据为作者自行构建,所有内容均为技术抽象示例,不涉及任何实际商业项目 自从之前尝试了一下 zustand 之后,就发现 zustand 是一个轻…...
DeepSeek在职场办公中的高效指令运用与策略优化
摘要 随着人工智能技术的飞速发展,大型语言模型在各个领域的应用日益广泛。DeepSeek作为一款具有影响力的AI产品,为职场办公带来了新的变革与机遇。本文深入剖析DeepSeek在职场办公场景下的提示词指令运用,通过对提示词概念、作用、设计原则的…...
mysql事务脏读 不可重复读 幻读 事务隔离级别关系
看了很多文档,发现针对事务并发执行过程中的数据一致性问题,即脏读、不可重复读、幻读的解释一塌糊涂,这也不能说什么,因为官方SQL标准中的定义也模糊不清。 按照mysql中遵循的事务隔离级别,可以梳理一下其中的关系 隔…...
Fork/Join框架与线程池对比分析
Fork/Join框架与线程池对比分析 1. 概述 线程池(如ThreadPoolExecutor)是Java并发编程中用于管理线程生命周期的通用工具,适用于处理大量独立任务。Fork/Join框架(基于ForkJoinPool)是Java 7引入的专用框架ÿ…...
docker 安装 Gitlab
GitLab 安装 #创建容器数据卷映射目录 mkdir -p /usr/docker/gitlab/config mkdir -p /usr/docker/gitlab/logs mkdir -p /usr/docker/gitlab/data #目录授权 chmod 777 -R /usr/docker/gitlab/*#直接复制可用(记得改下宿主机ipv4,不知道怎么看,输入i…...
【贪心之摆动序列】
题目: 分析: 这里我们使用题目中给的第二个实例来进行分析 题目中要求我们序列当中有多少个摆动序列,摆动序列满足一上一下,一下一上,这样是摆动序列,并且要输出摆动序列的最长长度 通过上面的图我们可以…...
kubectl修改资源时添加注解
kubectl修改资源时添加注解 kubectl修改资源时添加注解老版本的注解(变化注解)删除Annotations查看Annotations信息 查看发布记录回滚 kubectl修改资源时添加注解 参考: 为什么我们要使用kubectl apply 修改资源时,在命令行后添加 --save-configtrue ,就会自动添加此次修改的…...
【C++初学】课后作业汇总复习(四) 复数类与运算符重载
1、复数类输出 如题,要求实现: 1、复数类含两个参数的构造函数,一个为实部,一个为虚部 2、用Show()现实复数的值。 输出 (23i) //如题,要求实现: // //1、复数类含两个参数的构造函数&…...
十四、C++速通秘籍—函数式编程
目录 上一章节: 一、引言 一、函数式编程基础 三、Lambda 表达式 作用: Lambda 表达式捕获值的方式: 注意: 四、函数对象 函数对象与普通函数对比: 五、函数适配器 1、适配普通函数 2、适配 Lambda 表达式 …...
复刻系列-星穹铁道 3.2 版本先行展示页
复刻星穹铁道 3.2 版本先行展示页 0. 视频 手搓~星穹铁道~展示页~~~ 1. 基本信息 作者: 啊是特嗷桃系列: 复刻系列官方的网站: 《崩坏:星穹铁道》3.2版本「走过安眠地的花丛」专题展示页现已上线复刻的网…...
阿里云备案有必要选择备案管家服务吗?自己ICP备案可以吗?
阿里云备案有必要选择备案管家服务吗?新手可以选择备案管家,备案管家不需要自己手动操作,可以高效顺利通过ICP备案。自己ICP备案可以吗?自己备案也可以的,也很简单,适合动手能力强的同学。 阿里云备案管家…...
SQL语言基础(二)--以postersql为例
上次教程我们讲述了数据库中的增,删,改语句,今天我们来学习最后一个–‘改’的语句。 1.select语法 数据库查询只有select一个句子,但select语法相对复杂,其功能丰富,使用方式也很灵活 SELECT [ALL|Dist…...
探索 Rust 语言:高效、安全与并发的完美融合
在当今的编程语言领域,Rust 正以其独特的魅力吸引着越来越多开发者的目光。它诞生于 Mozilla 实验室,旨在解决系统编程中长久以来存在的难题,如今已成为构建可靠、高效软件的有力工具。 1 内存安全 Rust 通过所有权(ownership&a…...
最大公约数和最小倍数 java
在Java中,计算两个数的最大公约数(Greatest Common Divisor, GCD)和最小公倍数(Least Common Multiple, LCM)是常见的编程问题。以下是具体的实现方法和代码示例。 --- ### **1. 最大公约数 (GCD)** 最大公约数是指…...
OpenHarmony Camera开发指导(三):相机设备输入输出(ArkTS)
相机应用可通过创建相机输入流调用并控制相机设备,创建不同类型的输出流,进而实现预览、拍照、录像等基础功能。 开发步骤 在创建相机设备输入之前需要先完成相机设备管理,详细开发步骤可参考上一篇文章。 创建相机输入流 通过cameraMana…...
通过分治策略解决内存限制问题完成大型Hive表数据的去重的PySpark代码实现
在Hive集群中,有一张历史交易记录表,要从这张历史交易记录表中抽取一年的数据按某些字段进行Spark去重,由于这一年的数据超过整个集群的内存容量,需要分解成每个月的数据,分别用Spark去重,并保存为Parquet文…...
融媒体中心智能语音识别系统设计与实现
县级融媒体中心智能语音识别系统设计与实现 序言 随着融媒体时代的快速发展,新闻采编、专题节目制作对语音转写效率的要求日益提高。作为基层融媒体中心的技术工程师,我们在实际工作中常面临以下痛点: 采访录音整理耗时:传统人…...
学习笔记九——Rust所有权机制
🦀 Rust 所有权机制 📚 目录 什么是值类型和引用类型?值语义和引用语义?什么是所有权?为什么 Rust 需要它?所有权的三大原则(修正版)移动语义 vs 复制语义:变量赋值到底…...
计算机视觉算法实现——电梯禁止电瓶车进入检测:原理、实现与行业应用(主页有源码)
✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ 1. 电梯安全检测领域概述 近年来,随着电动自行车(以下简称"电瓶车"&…...
扩散模型 Diffusion Model 整体流程详解
🧠 Diffusion Model 思路、疑问和代码 文章目录 🧠 Diffusion Model 思路、疑问和代码🔄 一、核心思想:从噪声到图像📦 二、正向过程:加噪🧠 三、反向过程:学习去噪🎯 目…...
[Spark]深入解密Spark SQL源码:Catalyst框架如何优雅地解析你的SQL
本文内容组织形式 总结具体例子执行语句解析层优化层物理计划层执行层 猜你喜欢PS 总结 先写个总结,接下来会分别产出各个部分的源码解析,Spark SQL主要分为以下五个执行部分。 具体例子 接下来举个具体的例子来说明 执行语句 SELECT name, age FR…...
【数据结构_7】栈和队列(上)
一、概念 栈和队列,也是基于顺序表和链表实现的 栈是一种特殊的线性表,其只允许在固定的一段进行插入和删除元素操作。 遵循后进先出的原则 此处所见到的栈,本质上就是一个顺序表/链表,但是,实在顺序表/链表的基础…...
Linux中的cat命令常见用法
在 Linux 中,cat 命令是 concatenate(连接)的缩写,意思是“连接”或“串联”。 基本功能 cat 命令的主要功能是用于查看、合并和创建文件。它会将一个或多个文件的内容输出到标准输出设备(通常是终端屏幕)…...
css - 实现三角形 div 容器,用css画一个三角形(提供示例源码)简单粗暴几行代码搞定!
效果图 如下图所示,让一个 div 变成三角形,并且可随意更改大小, 本文提供了可运行示例源码,直接复制即可。 实现源码 建议创建一个 demo.html 文件,一键复制代码运行。 <style> .div{width: 0px;height: 0px…...
springboot 项目 jmeter简单测试流程
测试内容为 主机地址随机数 package com.hainiu.example;import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotat…...
设计模式实践:模板方法、观察者与策略模式详解
目录 1 模板方法1.1 模板方法基本概念1.2 实验1.2.1 未使用模板方法实现代码1.2.2 使用模板方法的代码 2 观察者模式2.1 观察者模式基本概念2.2 实验 3 策略模式3.1 策略模式基本概念3.2 实验 1 模板方法 1.1 模板方法基本概念 定义:一个操作中的算法的骨架 &…...
Google的AI模型Gemini和Gemini网络协议
粉丝私信问我:gemini如何访问? "Gemini如何访问"需明确区分两种完全不同的技术体系:Google的AI模型Gemini和Gemini网络协议。以下是两者的访问方式详解: 一、访问Google的Gemini AI模型 1. 通过Web应用 地址…...
HTTP实现心跳模块
HTTP实现心跳模块 使用轻量级的cHTTP库cpp-httplib重现实现HTTP心跳模块 头文件HttplibHeartbeat.h #ifndef HTTPLIB_HEARTBEAT_H #define HTTPLIB_HEARTBEAT_H#include <string> #include <thread> #include <atomic> #include <chrono> #include …...
基于web的民宿信息系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 随着信息时代的来临,民宿过去的民宿信息方式的缺点逐渐暴露,对过去的民宿信息的缺点进行分析,采取计算机方式构建民宿信息系统。本文通过阅读相关文献,研究国内外相关技术,提出了一种民宿信息管理、民宿信息管理…...
使用OpenSceneGraph (osg)实现一个星系漫游
简介 使用OpenSceneGraph (osg)实现了一个太阳系漫游的程序,具有以下特点: 1.通过按键控制飞行器前进后退、空间姿态; 2.星系渲染; 3.背景星空渲染; 效果 提供了一张超大的星空背景图 代码示例 int main(int a…...
笔试专题(九)
文章目录 十字爆破(暴力)题解代码 比那名居的桃子(滑动窗口/前缀和)题解代码 分组(暴力枚举 优化二分)题解代码 十字爆破(暴力) 题目链接 题解 1. 暴力 预处理 2. 如果单纯的暴…...
sklearn决策树 待更新
注意:sklearn中所有的决策树模型包括回归决策树实现的是CART决策树算法,在官方文档中有介绍。sklearn中的决策树模型最终得到的树结构都是二叉树,因为CART算法生成的就是二叉树。 DecisionTreeClassifier类 如果待预测样本有多个类别具有相同…...
eino v0.3.21 重磅发布!节点中断控制+空值映射支持,AI应用开发再添神器!
CloudWeGo/eino v0.3.21 作为最新补丁版本,聚焦流程控制与数据映射两大核心场景,为AI应用与微服务开发者提供更灵活的调试能力与容错设计! 1. 节点中断控制(Feat: Node Interrupt) • 功能亮点:新增 node …...
力扣每日打卡 50. Pow(x, n) (中等)
[TOC](力扣 50. Pow(x, n) 中等) 前言 这是刷算法题的第十一天,用到的语言是JS 题目:力扣 50. Pow(x, n) (中等) 一、题目内容 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。 示例 1࿱…...
Mac M1管理多个Node.js版本
目录 1. 使用 nvm (Node Version Manager) 1.1.安装 nvm 1.2.安装Node.js版本 1.3.查看已安装的node版本列表 1.4.使用特定版本的Node.js 1.5.查看当前使用的版本 2. 使用 fnm (Fast Node Manager) 2.1.安装 fnm 2.2.安装Node.js版本 2.3.查看已安装的版本 2.4.使用…...
arm_math.h、arm_const_structs.h 和 arm_common_tables.h
在 FOC(Field-Oriented Control,磁场定向控制) 中,arm_math.h、arm_const_structs.h 和 arm_common_tables.h 是 CMSIS-DSP 库的核心组件,用于实现高效的数学运算、预定义结构和查表操作。以下是它们在 FOC 控…...
每天五分钟深度学习:非线性激活函数的导数
本文重点 本文探讨了神经网络中几种常见非线性激活函数(Sigmoid、Tanh、ReLU、Leaky ReLU、ELU、Softmax)的导数特性。通过对各激活函数导数的数学推导与实际应用分析,揭示了不同激活函数在梯度传播、收敛速度及模型表达能力方面的差异。研究发现,ReLU及其变体在计算效率与…...
OpenHarmony5.0.2 USB摄像头适配
开发环境 OpenHarmony5.0.2 RK3568 USB摄像头 遇到问题 编译后插上USB摄像头后打开相机无图像 解决思路 5.0.2版本是支持USB摄像头的,需要修改默认的板载相机配置即可。 修改代码 1、修改配配置 文件地址:vendor/hihope/rk3568/hdf_config/uhdf…...
vue: router基础用法
router基础用法 1.安装router2.配置router3.路由编程1.编程式导航2.声明式导航 1.安装router 在node环境下,直接运行 npm install router42.配置router 创建文件夹并命名为router 在router文件夹中创建index.js index.js示例配置如下: import { creat…...
IDE中使用Spring Data Redis
步骤一:导入Spring Data Redis的maven坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency> 步骤二:配置Redis数据源 步骤三&…...
【计网】网络交换技术之报文交换(复习自用,了解,重要3)
复习自用的,处理得比较草率,复习的同学或者想看基础的同学可以看看,大佬的话可以不用浪费时间在我的水文上了 另外两种交换技术可以直接点击链接访问相关笔记: 电路交换 分组交换 一、报文交换的定义 报文交换(Me…...
GitLab 17.x 配置 https
文章目录 使用外部 nginx 参考:https://docs.gitlab.com/omnibus/settings/nginx.html 使用内置 nginx 参考:https://docs.gitlab.com/omnibus/settings/ssl/index.html#configure-https-manually // 使用自己手工申请证书 $ mkdir /etc/gitlab/ssl $ m…...
中间件--ClickHouse-1--基础介绍(列式存储,MPP架构,分布式计算,SQL支持,向量化执行,亿万级数据秒级查询)
1、概述 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。它由俄罗斯的互联网巨头Yandex为解决其内部数据分析需求而开发,并于2016年开源。专为大规模数据分析,实时数据分析和复杂查询设计,具有高性能、实时数据和可扩展性等…...
【编写Node接口;接口动态获取VUE文件并异步加载, 并渲染impoort插件使用】
编写Node接口;接口动态获取VUE文件并异步加载, 并渲染impoort插件使用; vue3-sfc-loader主要特征: 编写Node接口:Vue2项目使用:Vue3项目使用:(页面按需加载插件、图片等)主要使用&am…...