Netty学习笔记1
Netty学习笔记(一)
在的互联网环境下,分布式系统大行其道,而分布式系统的根基在于网络编程,而 Netty 恰恰是 Java 领域网络编程的王者。如果要致力于开发高性能的服务器程序、高性能的客户端程序,必须掌握 Netty。
视频链接:黑马程序员Netty全套教程,全网最全Netty深入浅出教程,Java网络编程的王者
NIO基础
non-blocking io 非阻塞IO
1、三大组件
Channel、Buffer、Selector
Channel 有一点类似于 stream,它就是读写数据的双向通道
常见的Channel有:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
Buffer则用来缓冲读写数据,常见的Buffer有:
- ByteBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
- CharBuffer
服务器设计——多线程版
❗ 缺点:
- 内存占用高
- 线程上下文切换成本高
- 只适合连接少的场景
服务器设计——线程池版
❗ 缺点:
- 阻塞模式下,线程仅能处理一个socket连接
- 仅适合短连接场景
服务器设计——Selector版
Selector的作用就是配合一个线程来管理多个channel,获取这些channel上发生的事件,这些channel工作在非阻塞模式下,不会让线程吊死在一个channel上,适合连接数特别多,但流量低的场景(low traffic)
调用selector的select()会阻塞直到channel发生了读写就绪事件,这些事件发生,select方法就会返回这些事件交给thread来处理
2、ByteBuffer
正确使用姿势:
- 向buffer写入数据,例如调用
channel.read(buffer)
- 调用
flip()
切换至读模式 - 从buffer读取数据,例如调用
buffer.get()
- 调用
clear()
或compact()
切换至写模式 - 重复1-4步骤
@Slf4j
public class TestByteBuffer {public static void main(String[] args) {// FileChannel// 1、输入输出流 2、RandomAccessFiletry (FileChannel channel = new FileInputStream("data.txt").getChannel()) {// 准备缓存区ByteBuffer buffer = ByteBuffer.allocate(10);while (true) {// 从channel读取数据,向buffer写入int len = channel.read(buffer);log.debug("读取到的字节数 {}", len);if (len == -1) { // 没有内容break;}// 打印buffer的内容buffer.flip(); // 切换读模式while (buffer.hasRemaining()) { // 是否还有剩余未读数据byte b = buffer.get();log.debug("读取到的字节 {}", (char) b);}// 切换写模式buffer.clear();}} catch (IOException e) {e.printStackTrace();}}
}
ByteBuffer有以下重要属性:
- capacity
- position
- limit
写模式下,position是写入位置,limit等于容量
flip动作发生后,position切换为读取位置,limit切换为读取限制
常见方法:
分配空间
可以使用allocate方法为ByteBuffer分配空间
/** class java.nio.HeapByteBuffer java堆内存,读写效率较低,受到gc影响* class java.nio.DirectByteBuffer 直接内存,读写效率高(少一次拷贝),不受gc影响,分配效率低*/
System.out.println(ByteBuffer.allocate(16).getClass());
System.out.println(ByteBuffer.allocateDirect(16).getClass());
写入数据
- 调用channel的read方法
- 调用buffer自己的put方法
读取数据
- 调用channel的write方法
- 调用buffer自己的get方法
get方法会让position读指针向后走,如果想重复读取数据
- 可以调用rewind方法将position重新置为0
- 或者调用get(int i)方法获取索引i的内容,它不会移动读指针
public class TestByteBufferRead {public static void main(String[] args) {ByteBuffer buffer = ByteBuffer.allocate(16);buffer.put(new byte[]{'a', 'b', 'c', 'd'});buffer.flip();// 从头开始读buffer.get(new byte[4]);ByteBufferUtil.debugAll(buffer);buffer.rewind();System.out.println((char) buffer.get());buffer.rewind();// mark & reset// mark 做一个标记,记录position位置,reset是将position重置到mark的位置System.out.println((char) buffer.get());System.out.println((char) buffer.get());buffer.mark(); // 加标记,索引为2的位置System.out.println((char) buffer.get());System.out.println((char) buffer.get());buffer.reset(); // 将position重置到上次标记的位置System.out.println((char) buffer.get());System.out.println((char) buffer.get());// get(i) 不会改变读索引的位置System.out.println((char) buffer.get(1));ByteBufferUtil.debugAll(buffer);}
}
字符串与ByteBuffer互转
public class TestByteBufferString {public static void main(String[] args) {// 1. 字符串转为ByteBufferByteBuffer buffer = ByteBuffer.allocate(16);buffer.put("hello".getBytes());ByteBufferUtil.debugAll(buffer);// 2. CharsetByteBuffer buffer1 = StandardCharsets.UTF_8.encode("hello");ByteBufferUtil.debugAll(buffer1);// 3. wrapByteBuffer buffer2 = ByteBuffer.wrap("hello".getBytes(StandardCharsets.UTF_8));ByteBufferUtil.debugAll(buffer2);// 转为字符串String s = StandardCharsets.UTF_8.decode(buffer2).toString();System.out.println(s);buffer.flip();String s1 = StandardCharsets.UTF_8.decode(buffer).toString();System.out.println(s1);}
}
Scattering Reads
分散读取,有一个文本文件3parts.txt
public class TestScatteringReads {public static void main(String[] args) {try (FileChannel channel = new RandomAccessFile("3parts.txt", "r").getChannel()) {ByteBuffer b1 = ByteBuffer.allocate(3);ByteBuffer b2 = ByteBuffer.allocate(3);ByteBuffer b3 = ByteBuffer.allocate(5);channel.read(new ByteBuffer[]{b1, b2, b3});b1.flip();b2.flip();b3.flip();ByteBufferUtil.debugAll(b1);ByteBufferUtil.debugAll(b2);ByteBufferUtil.debugAll(b3);} catch (IOException e) {e.printStackTrace();}}
}
Gathering Writes
public class TestGatheringWrites {public static void main(String[] args) {ByteBuffer b1 = StandardCharsets.UTF_8.encode("hello");ByteBuffer b2 = StandardCharsets.UTF_8.encode("world");ByteBuffer b3 = StandardCharsets.UTF_8.encode("您好");try (FileChannel channel = new RandomAccessFile("words.txt", "rw").getChannel()) {channel.write(new ByteBuffer[]{b1, b2, b3});} catch (IOException e) {e.printStackTrace();}}
}
ByteBuffer 黏包 半包
需求:
/*
网络上有多条数据发送给服务端,数据之间使用 \n 进行分隔
但由于某种原因这些数据在接收时,被进行了重新组合,例如原始数据有3条为
Hello,world\n
I'm zhangsan\n
How are you?\n
变成了下面的两个ByteBuffer(黏包,半包)
Hello,world\nI'm zhangsan\nHo
w are you?\n
现在要求你编写程序,将错乱的数据恢复成原始的按 \n 分隔的数据
*/
具体实现:
package com.example.netty.ch1;import com.example.netty.utils.ByteBufferUtil;import java.nio.ByteBuffer;public class TestByteBufferCase {public static void main(String[] args) {ByteBuffer source = ByteBuffer.allocate(32);source.put("Hello,world\nI'm zhangsan\nHo".getBytes());split(source);source.put("w are you?\n".getBytes());split(source);}private static void split(ByteBuffer source) {source.flip();for (int i = 0; i < source.limit(); i++) {// 找到一条完整的消息if (source.get(i) == '\n') {int length = i + 1 - source.position();// 把完整消息存入新的ByteBufferByteBuffer target = ByteBuffer.allocate(length);// 从source读,向target写for (int j = 0; j < length; j++) {byte b = source.get();target.put(b);}ByteBufferUtil.debugAll(target);}}source.compact();}
}
3、文件编程
FileChannel
⚠注意
FileChannel 只能工作在阻塞模式下
获取FileChannel
- 通过FileInputStream获取的channel只能读
- 通过FileOuputStream获取的channel只能写
- 通过RandomAccessFile是否能读写根据构造RandomAccessFile时的读写模式决定
读取
会从channel读取数据填充ByteBuffer,返回值表示读到了多少字节,-1表示到达了文件的末尾
int len = channel.read(buffer);
写入
ByteBuffer buffer = ...;
buffer.put(...); // 写入数据
buffer.flip(); // 切换读模式while (buffer.hasRemaining()) {channel.write(buffer);
}
关闭
channel必须关闭
channel.close();
两个Channel传输数据
public class TestFileChannelTransferTo {public static void main(String[] args) {try (FileChannel from = new FileInputStream("data.txt").getChannel();FileChannel to = new FileOutputStream("to.txt").getChannel()) {// 效率高,底层使用操作系统的零拷贝进行优化,2g 数据long size = from.size();// i 代表还剩余多少个字节for (long i = size; i > 0;) {i -= from.transferTo((size - i), i, to);}} catch (IOException exception) {exception.printStackTrace();}}
}
Path
JDK7 引入了Path和Paths类
- Path 用来表示文件路径
- Paths 是工具类,用来获取Path实例
Path source = Paths.get("test.txt");
Path source = Paths.get("d:\\test.txt");
Path source = Paths.get("d:/test.txt");
Path source = Paths.get("d:\\data", "test.txt");
正常化路径:path.normalize()
Files
检查文件是否存在:Files.exists(path)
创建一级目录:Files.createDirectory(path)
创建多级目录:Files.createDirectories(path)
拷贝文件:Files.copy(source, target)
移动文件:Files.move(source, target, StandardCopyOption.ATOMIC_MOVE)
删除文件:Files.delete(target)
遍历目录
public class TestFilesWalkFileTree {public static void main(String[] args) throws IOException {AtomicInteger dirCount = new AtomicInteger();AtomicInteger fileCount = new AtomicInteger();Files.walkFileTree(Paths.get("d:/env/jdk11"), 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("dir count: " + dirCount);System.out.println("file count: " + fileCount);}
}
4、网络编程
阻塞
非阻塞
多路复用
单线程可以配合Selector完成对多个Channel可读写事件的监控,这称之为多路复用
- 多路复用仅针对网络IO、普通文件IO没法利用多路复用
- 如果不用Selector的非阻塞模式,线程大部分时间都在做无用功,而Selector能够保证
- 有连接事件时才去连接
- 有可读事件才去读取
- 有可写事件才去写入(限于网络传输能力,Channel未必时时可写,一旦Channel可写,会触发Selector的可写事件)
监听Channel事件
可以通过下面三种方法来监听是否有事件发送,方法的返回值代表有多少channel发生了事件
方法一,阻塞直到绑定事件发生
int count = selector.select();
方法二,阻塞直到绑定事件发生,或是超时(时间单位为ms)
int count = selector.select(long timeout);
方法三,不会阻塞,也就是不管有没有事件,立刻返回,自己根据返回值检查是否有事件
int count = selector.selectNow();
👀select何时不阻塞?
事件发生时
- 客户端发起连接请求,会触发accept事件
- 客户端发送数据过来,客户端正常、异常关闭时,都会触发read事件,另外如果发送地 数据大于buffer缓冲区,会触发多次读取事件
- channel可写,会触发write事件
- 在Linux下 nio bug 发生时
调用selector.wakeup()
调用selector.close()
selector 所在线程interrupt
利用多线程优化
现在都是多核CPU,设计时要充分考虑如何发挥多核CPU的优势
package com.example.netty.ch4;import com.example.netty.utils.ByteBufferUtil;
import lombok.extern.slf4j.Slf4j;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;@Slf4j
public class MultiThreadServer {public static void main(String[] args) throws IOException {Thread.currentThread().setName("boss");ServerSocketChannel ssc = ServerSocketChannel.open();ssc.configureBlocking(false);Selector boss = Selector.open();SelectionKey bossKey = ssc.register(boss, 0, null);bossKey.interestOps(SelectionKey.OP_ACCEPT);ssc.bind(new InetSocketAddress(8080));Worker[] workers = new Worker[Runtime.getRuntime().availableProcessors()];for (int i = 0; i < workers.length; i++) {workers[i] = new Worker("worker-" + i);}AtomicInteger index = new AtomicInteger();while (true) {boss.select();Iterator<SelectionKey> iter = boss.selectedKeys().iterator();while (iter.hasNext()) {SelectionKey key = iter.next();iter.remove();if (key.isAcceptable()) {SocketChannel sc = ssc.accept();sc.configureBlocking(false);log.debug("connected...{}", sc.getRemoteAddress());log.debug("before register...{}", sc.getRemoteAddress());// 负载均衡(轮询)workers[index.getAndIncrement() % workers.length].register(sc);log.debug("after register...{}", sc.getRemoteAddress());}}}}static class Worker implements Runnable {private Thread thread;private Selector selector;private String name;private volatile boolean start = false;private ConcurrentLinkedQueue<Runnable> queue = new ConcurrentLinkedQueue<>();public Worker(String name) {this.name = name;}/*** 初始化线程和selector*/public void register(SocketChannel sc) throws IOException {if (!start) {selector = Selector.open();thread = new Thread(this, name);thread.start();start = true;}// 向队列添加了任务,但这个任务并没有立刻执行queue.add(() -> {try {sc.register(selector, SelectionKey.OP_READ, null);} catch (ClosedChannelException e) {e.printStackTrace();}});selector.wakeup(); // 唤醒 select 方法}@Overridepublic void run() {while (true) {try {selector.select();Runnable task = queue.poll();if (task != null) {// 执行了 sc.register(selector, SelectionKey.OP_READ, null);task.run();}Iterator<SelectionKey> iter = selector.selectedKeys().iterator();while (iter.hasNext()) {SelectionKey key = iter.next();iter.remove();if (key.isReadable()) {ByteBuffer buffer = ByteBuffer.allocate(16);SocketChannel channel = (SocketChannel) key.channel();channel.read(buffer);buffer.flip();ByteBufferUtil.debugAll(buffer);}}} catch (IOException e) {e.printStackTrace();}}}}
}
5、NIO vs BIO
stream vs channel
- stream 不会自动缓冲数据,channel会利用系统提供的发送缓冲区,接收缓冲区(更为底层)
- stream仅支持阻塞API,channel同时支持阻塞、非阻塞API,网络channel可配合selector实现多路复用
- 两者均为全双工,即读写可以同时进行
IO模型
同步阻塞、同步非阻塞、多路复用、异步阻塞(没有此情况)、异步非阻塞
同步:线程自己去获取结果(一个线程)
异步:线程自己不去获取结果,而是由其它线程送结果(至少两个线程)
当调用一个channel.read 或 stream.read 后,会切换至操作系统内核态来完成真正数据读取,而读取又分为两个阶段,分别为:
- 等待数据阶段
- 复制数据阶段
IO模型:
- 阻塞IO
- 非阻塞IO
- 多路复用
- 信号驱动
- 异步IO
零拷贝
仅只发生一次用户态到内核态的切换,数据拷贝了2次。所谓的【零拷贝】,并不是真正的无拷贝,而是在不会拷贝重复数据到JVM内存中,零拷贝的优点有:
- 更少的用户态与内核态的切换
- 不利用CPU计算,减少CPU缓存伪共享
- 零拷贝适合小文件传输
AIO
AIO 用来解决数据复制阶段的阻塞问题
- 同步意味着,在进行读写操作时,线程需要等待结果,还是相当于闲置
- 异步意味着,在进行读写操作时,线程不必等待结果,而是将来由操作系统通过回调方式由另外的线程来获得结果
异步模型需要底层操作系统(Kernel)提供支持
Windows 系统通过IOCP实现了真正得异步IO
Linux 系统异步IO在2.6版本引入,但其底层实现还是用多路复用模拟了异步IO,性能没有优势
参考资料
https://www.bilibili.com/video/BV1py4y1E7oA
相关文章:
Netty学习笔记1
Netty学习笔记(一) 在的互联网环境下,分布式系统大行其道,而分布式系统的根基在于网络编程,而 Netty 恰恰是 Java 领域网络编程的王者。如果要致力于开发高性能的服务器程序、高性能的客户端程序,必须掌握…...
RISK-V品牌的中国化历程(中)
目录 1.技术优势 出道即巅峰 2.生态布道 品牌根植中国 3.应用场景 加速品牌的商业化运作 生态布道 品牌根植中国 2015年成立非盈利组织RISC-V基金会,目前已吸引全球28个国家327家会员,包括英伟达、联发科、苹果、特斯拉、谷歌、高通、IBM、三星、麻省理…...

2023.02.19 学习周报
文章目录摘要文献阅读1.题目2.摘要3.介绍4.本文贡献5.方法5.1 Local Representation Learning5.2 Global Representation Learning5.3 Item Similarity Gating6.实验6.1 数据集6.2 结果7.结论深度学习1.对偶问题1.1 拉格朗日乘数法1.2 强对偶性2.SVM优化3.软间隔3.1 解决问题3.…...

枚举类的使用方法
一、理解枚举类型 枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。下面先来看看如何写…...

.NET3.5安装步骤及相关问题。
.NET3.5全称 Microsoft.NETFramework3.5 最新版本-.NET4.8 第一步打开控制面板 windows系统打开控制面板 选择程序 选择.NET3.5安装。 可能会出现问题。 解决方案: 报错代码80240438的常用解决办法: 方法一:检测windows update servic…...

联想M7268激光打印机开机红绿灯双闪报错不打印
故障现象: 一台联想M7268激光打印机开机后电源键、复印键一起双闪,电源键闪红灯、复印键闪绿灯; 检测维修: 根据闪灯故障判断如果无卡纸异常情况下可能是激光器故障,因为以前曾经维修过一台一模一样的机器故障基本相同,先打开机器吧,把硒鼓拿出来先看看有没有卡纸,进纸…...
产品经理知识体系:7.web和app产品需求设计
web和app产品需求设计 思考 笔记 web产品设计 一、交互设计 1.以用户为中心的设计:功能、体验、用户; 将产品功能转化成用户的体验,功能和体验的结合。 2.交互设计模式 交互逻辑 信息结构 信息内容 界面结构 导航设计 二、视觉设计 元素的…...
强化学习概述
一、Modelfree 和 Modelbased Modelfree:不需要理解环境 Modelbased:需要理解环境,并且为环境建立模型 Model-free 中, 机器人只能按部就班, 一步一步等待真实世界的反馈, 再根据反馈采取下一步行动. 而 model-based, 他能通过想象来预判断接…...

NO.1嵌入式入门笔记:常用命令记录
一、前言 Linux文件目录: Linux Shell: 它负责接收用户的输入,根据用户的输入找到其它程序并运行。比如我们输入“ls”并回车时,shell 程序找到“ls”程序并运行,把结果打印出来。Shell有多种实现,我们常用…...
Shell编程
typora-copy-images-to: pictures typora-root-url: pictures 文章目录typora-copy-images-to: pictures typora-root-url: pictures本节课程目标语法和选项语法和选项3. sort工具语法和选项5.tee工具6.diff工具语法和选项7. paste工具8. tr工具语法和选项小试牛刀二、bash的特…...

网络模型OSI
网络模型OSI定义模型分布数据封装、解封过程数据链路层1.LLC逻辑链路控制子层(Logic Link Control Sub Layer)2.MAC媒介访问控制子层(Medium Acess Control Sub Layer)CSMA/CARST-CST原理OSI定义 OSI:Open Systems Interconnection Reference Model,开放…...

RT-Thread初识学习-01
1. RT-Thread 简介 1.1 RT-Thread 是什么 据不完全统计,世界有成千上万个 RTOS(Real-time operating system,实时操作系统),RT-Thread 就是其中一个优秀的作品。 RT-Thread 内核的第一个版本是熊谱翔先生在 2006 年…...

二阶段提交事务的实现和缺点
背景 说起分布式事务,我们最绕不开的一个话题就是该不该使用分布式事务,而要理解为什么做出使用与否的决定,就必须要提到分布式事务中的最经典的实现:两阶段提交事务,本文我们就简答介绍下这个两阶段提交事务以及它的优缺点 技术…...

定点数的表示和运算
文章目录真值(有正负号)和机器数(0正1负)原码整数小数补码负数的补数正数的补数[y]~补~ > [-y]~补~反码小结移码移位运算加减法运算溢出判断真值(有正负号)和机器数(0正1负) 无符…...

java虚拟机内存分布
java虚拟机内存分布 Java虚拟机在执行java程序的过程中会把它所管理的内存划分为如下若干个不同的数据区域。 1.程序计数器 程序计数器是线程私有的,它占用的空间相对较小,用来记录当前线程字节码执行到哪一步。字节码解释器通过改变这个计数器的值来获…...

最完整的小红书带货笔记——垂直模式
最完整的小红书带货笔记——垂直模式,小红书直播复盘怎么做?#直播带货笔记 第1篇,带你解锁直播复盘5大要点! #小红书店铺#小红书运营 小红书怎么发带货笔记? 做小红书带货的同学注意了,我们带货一定要发笔…...

SpringBoot实战——个人博客项目
目录 一、项目简介 二、项目整体架构 数据库模块 后端模块 前端模块 三、项目具体展示 四、项目的具体实现 1、一些准备工作 🍎数据库、数据表的创建 🍎设置数据库和MyBatis的配置 🍎将前端项目引入到当前项目中 2、登录注册模块 &…...

浅谈Spring中事务管理器
由于事务部分代码在设计上整体比较简单,我自己觉得它在设计上没有什么特别让我眼前一亮的东西,所以下文更多的是侧重执行流程,能理解事务管理器等一众概念以及相关的变量含义,真正遇到Bug会调试,知道在什么地方打断点就…...

Python练习系统
用python给自己做个练习系统刷题吧! #免费源码在文末公众号哈# 选择题 def xuanze():global flag2if flag21:def insert():numvar1.get()questionvar2.get()choicevar3.get()answervar4.get()with open(d:\\练习系统\\练习三3.1.pickle,rb) as file:lst1pickle.lo…...

Transformer学习笔记1
Transformer学习笔记1 翻译中,每个词翻译时更看重哪个原词? ## 注意力机制的一般性原理 典型的注意力机制 hard one-hot形式,但是太过专一 soft 都分布,但是太过泛滥 local attention 自注意力机制:self-atte…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...

抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...