【源码解析】Java NIO 包中的 ByteBuffer
文章目录
- 1. 前言
- 2. ByteBuffer 概述
- 3. 属性
- 4. 构造器
- 5. 方法
- 5.1 allocate 分配 Buffer
- 5.2 wrap 映射数组
- 5.3 slice 获取子 ByteBuffer
- 5.4 duplicate 复刻 ByteBuffer
- 5.5 asReadOnlyBuffer 创建只读的 ByteBuffer
- 5.6 get 方法获取字节
- 5.7 put 方法往 ByteBuffer 里面加入字节
- 5.8 array 和 arrayOffset
- 5.9 compact 切换写模式
- 5.10 其他方法
- 6. 大端序和小端序
- 7. 小结
1. 前言
上一篇文章我们介绍了最底层的 Buffer,那么这篇文章就要介绍下 Buffer 的
比较核心的一个实现类 HeapByteBuffer,上一篇文章的地址如下:
- 【源码解析】Java NIO 包中的 Buffer
2. ByteBuffer 概述
上面就是 Buffer 的继承结构,当然 Buffer 的子类肯定不会只有这么点,比如下面的图:
只不过上面图中就给了几个基本 Buffer 的实现类,可以看到几个重要的实现类 MappedByteBuffer
、HeapByteBuffer
、DirectByteBuffer
都是 ByteBuffer
的子类,这几个实现类也是我们要介绍的重点,只不过这篇文章我们先介绍 ByteBuffer。
ByteBuffer 是字节缓存,也是最常见的 Buffer,无论是缓存映射还是文件映射都有 ByteBuffer 的身影。上一篇文章中我们也说过,没有 ByteBuffer 之前,对于字节流一个一个处理都是比较繁琐的,有了 ByteBuffer 之后就可以一次处理一大批的数据,性能更加高效。
下面我们就来看下 ByteBuffer 这个类的庐山真面目。
3. 属性
final byte[] hb;
hb 是 ByteBuffer 中存储字节数据的数组,专门用于 HeapByteBuffer 中数据的存放,如果是直接内存 Buffer,那这个数组就不会存储数据。
final int offset;
offset 是 ByteBuffer 中第一个元素的起始位置,也可以说是存储元素的数组的第一个起始下标,一般都是从 0 开始。
boolean isReadOnly;
这个属性就是表示是否是只读的,如果一个 Buffer 是只读的,那么就不能修改,只能读取。
ByteBuffer 的属性比较简单,是因为指针都封装到底层 Buffer 了,所以到 ByteBuffer 这一层属性就没那么多了。
4. 构造器
ByteBuffer(int mark, int pos, int lim, int cap,byte[] hb, int offset){super(mark, pos, lim, cap);this.hb = hb;this.offset = offset;}ByteBuffer(int mark, int pos, int lim, int cap) {this(mark, pos, lim, cap, null, 0);}
无论是哪个构造器,都绕不过 mark
、pos
、limit
、cap
这几个指标,就是 Buffer 里面的这四个参数。
那么这两个构造器不同的是参数上第一个构造器需要设置数组 hb
和 offset
。这其实就很明显了,调用第一个方法的其实就是创建 HeapByteBuffer
,第二个方法则是 DirectByteBuffer
和 MappedByteBuffer
会调用。
5. 方法
因为 ByteBuffer 是抽象类,所以里面的所有方法几乎都留给了子类去实现,所以我这里就简单介绍下这个 ByteBuffer 里面的一些抽象方法以及这些方法的具体用途。
5.1 allocate 分配 Buffer
这个方法用于分配 HeapByteBuffer 和 DirectByteBuffer,其实就是直接 new 出来。
// 分配 HeapByteBuffer
public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity);
}// 分配 DirectByteBuffer
public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}
5.2 wrap 映射数组
这个方法用于将传入数组中的一部分数据或者是数组的全部数据映射成一个 HeapByteBuffer(转换),为什么不是 DirectByteBuffer 和 MappedByteBuffer 呢?当然是另外两个是直接内存了不受 JVM 管理了,所以传入的数组肯定不能映射成堆外的 Buffer。
public static ByteBuffer wrap(byte[] array,int offset, int length)
{try {return new HeapByteBuffer(array, offset, length);} catch (IllegalArgumentException x) {throw new IndexOutOfBoundsException();}
}public static ByteBuffer wrap(byte[] array) {return wrap(array, 0, array.length);
}
这里其实就是简单的创建一个 HeapByteBuffer。
5.3 slice 获取子 ByteBuffer
public abstract ByteBuffer slice();
slice() 方法用于创建一个新的 ByteBuffer 对象,其内容是当前 ByteBuffer 对象内容的一个共享子序列,啥意思呢?新创建的 ByteBuffer 对象和原始 ByteBuffer 对象之间的内容是共享的,但它们的位置(position)、限制(limit)和标记(mark)值是独立的。换句话说这两个 ByteBuffer 对象的底层数组是一样的,只是 Buffer 的几个标记不一样。
既然新建的 ByteBuffer 和原来的 ByteBuffer 共享一个内存空间,那也就意味新的 ByteBuffer 由下面的性质。
-
共享内存
- 对当前 ByteBuffer 对象内容的任何修改将反映在新创建的 ByteBuffer 对象中,反之亦然
-
状态独立
- 新创建的 ByteBuffer 对象的 position 将被设置为 0
- 新创建的 ByteBuffer 对象的 capacity 和 limit将等于当前 ByteBuffer 对象剩余的字节数
- 新创建的 ByteBuffer 对象的 mark 会被重置为 -1
-
属性继承
- 当前 ByteBuffer 是什么类型(HeapByteBuffer 和 DirectByteBuffer),创建出来的 ByteBuffer 就是什么类型
- 如果当前 ByteBuffer 对象是只读的(read-only),则新创建的 ByteBuffer 对象也将是只读的
可以看到上面图中,slice 获取的 ByteBuffer 视图中 position 重新指向了 0 的位置,而 limit = 6,那么问题来了,既然 slice 之后获取的 ByteBuffer 重新设置了这几个指标,那么如何进行访问呢?
不知道大家还记得 ByteBuffer 中的 offset
吗?这个 offset 上面我们说过了就是 position 的偏移量,所以 slice 创建出来的子 ByteBuffer 可以通过 offset + position
来算出,比如在上面例子中 offset = 4
。
那下面我们还可以看个例子:
public static void byteBufferTest(){ByteBuffer byteBuffer = ByteBuffer.allocate(10);byteBuffer.put((byte) 1);byteBuffer.put((byte) 2);byteBuffer.put((byte) 3);byteBuffer.put((byte) 4);ByteBuffer slice = byteBuffer.slice();System.out.println(slice.arrayOffset()); // 4
}
在上面这个例子中,创建出来的 slice.offset = 4
,那么下面我们接着往里面写入数据。
slice.put((byte) 5);slice.put((byte) 6);slice.put((byte) 7);slice.put((byte) 8);System.out.println(Arrays.toString(byteBuffer.array()));
最后来看下输出的结果:
可以看到最后的输出结果就表明了对创建出来的 slice 添加数据也会影响到原来的 ByteBuffer,同时 slice 是在原来 ByteBuffer 的 position 后面继续操作,也能看到上面输出的 offset 就是调用 slice 方法时候的 position 值。
5.4 duplicate 复刻 ByteBuffer
public abstract ByteBuffer duplicate();
如果说上面的 slice 是从原来的 ByteBuffer 截取一段(共享地址)下来,这个方法就是完整复刻整个 ByteBuffer。也就是说它们的 offset,mark,position,limit,capacity
变量的值全部是一样的。
下面来看个例子,其实主要是看里面的 offset 是多少,可以看到 duplicate
就是完全复制一个 ByteBuffer,在里面可以
public static void byteBufferTest(){ByteBuffer byteBuffer = ByteBuffer.allocate(10);byteBuffer.put((byte) 1);byteBuffer.put((byte) 2);byteBuffer.put((byte) 3);byteBuffer.put((byte) 4);ByteBuffer duplicate = byteBuffer.duplicate();System.out.println(duplicate.arrayOffset()); // 0duplicate.put((byte) 5);duplicate.put((byte) 6);duplicate.put((byte) 7);duplicate.put((byte) 8);System.out.println(Arrays.toString(byteBuffer.array()));
}
5.5 asReadOnlyBuffer 创建只读的 ByteBuffer
public abstract ByteBuffer asReadOnlyBuffer();
这个方法就是创建一个只读的 ByteBuffer,而如果写入数据的话会抛出 ReadOnlyBufferException 异常。
5.6 get 方法获取字节
get 方法就是 ByteBuffer 里面获取字节的方法,可以通过这个方法来获取 ByteBuffer 里面 position 位置的值,当然这个方法也有很多重载方法,其中我们也可以传入一个 byte[] 数组,然后把 ByteBuffer 里面的值传到数组里面。
public abstract byte get();// 获取指定下标下面的值
public abstract byte get(int index)// 从 offset 开始获取 length 个字节放到数组 dst 中
public ByteBuffer get(byte[] dst, int offset, int length) {// 检查指定 index 的边界,确保不能越界checkBounds(offset, length, dst.length);// 检查 ByteBuffer 是否有足够的转移字节if (length > remaining())throw new BufferUnderflowException();int end = offset + length;// 从 offset 开始获取 length 个字节转移到数组 dst 中for (int i = offset; i < end; i++)dst[i] = get();return this;
}// 将 ByteBuffer 全部放到 dst 中
public ByteBuffer get(byte[] dst) {return get(dst, 0, dst.length);
}
5.7 put 方法往 ByteBuffer 里面加入字节
// 往 position 位置设置字节 b,同时设置 position = position + 1
public abstract ByteBuffer put(byte b);// 往 index 设置字节 b,设置之后 position 不会改变
public abstract ByteBuffer put(int index, byte b);// 把 src 的所有字节放到当前的 ByteBuffer 里面
public ByteBuffer put(ByteBuffer src) {if (src == this)throw new IllegalArgumentException();if (isReadOnly())throw new ReadOnlyBufferException();int n = src.remaining();if (n > remaining())throw new BufferOverflowException();for (int i = 0; i < n; i++)put(src.get());return this;
}// 从 offset 开始,将 length 个字节设置到当前 ByteBuffer 中
public ByteBuffer put(byte[] src, int offset, int length) {// 检查指定 index 的边界,确保不能越界checkBounds(offset, length, src.length);// 检查 ByteBuffer 是否能够容纳得下if (length > remaining())throw new BufferOverflowException();int end = offset + length;// 从字节数组得 offset 处,转移 length 个字节到 ByteBuffer 中for (int i = offset; i < end; i++)this.put(src[i]);return this;
}// 传入一个字节数组,设置到当前 ByteBuffer 中
public final ByteBuffer put(byte[] src) {return put(src, 0, src.length);
}
上面几个方法都是 put 方法,就是往当前 ByteBuffer 里面设置数据的,不过要注意下,当调用 put(int index, byte b)
来设置字节,position 不会被修改。
5.8 array 和 arrayOffset
public final byte[] array() {if (hb == null)throw new UnsupportedOperationException();if (isReadOnly)throw new ReadOnlyBufferException();return hb;
}public final int arrayOffset() {if (hb == null)throw new UnsupportedOperationException();if (isReadOnly)throw new ReadOnlyBufferException();return offset;
}
这两个方法就是获取 Buffer 底层的数组和数组的第一个元素的偏移量,这个偏移量其实就是 Buffer 第一个元素的下标。
但是如果 Buffer 是只读的,那么就没办法获取,会抛出异常 ReadOnlyBufferException
。
5.9 compact 切换写模式
ByteBuffer 切换写模式之前已经介绍过一个方法了,就是 clear()
,但是这里面有个问题,就是 clear 这个方法是直接把 position 设置为 0,也就是从头开始写入,如果在调用 clear
之前已经把数据读完了那当然没问题,但是如果还遗留一些数据,这样新写入的数据会把原来剩下那些没读取完的覆盖掉,比如看下面的例子:
public static void clearTest(){ByteBuffer byteBuffer = ByteBuffer.allocate(10);byteBuffer.put((byte) 1);byteBuffer.put((byte) 2);byteBuffer.put((byte) 3);byteBuffer.put((byte) 4);// 切换读模式byteBuffer.flip();System.out.println(byteBuffer.get()); // 1System.out.println(byteBuffer.get()); // 2System.out.println(byteBuffer.get()); // 3// 切换写模式byteBuffer.clear();byteBuffer.put((byte) 1);byteBuffer.put((byte) 2);byteBuffer.put((byte) 3);byteBuffer.put((byte) 5);System.out.println(Arrays.toString(byteBuffer.array()));// [1, 2, 3, 5, 0, 0, 0, 0, 0, 0]
}
在上面例子中,首先我们往 ByteBuffer 里面设置了 1,2,3,4
,然后切换到读模式,接着读取前三个数据,也就是 1,2,3
,接着我们调用 clear()
方法切换到写模式,然后往里面写入 1,2,3,5
,这时候我们就发现原来里面的 4 被 5 覆盖了。但是如果换成 compact 方法就不一样了。
public static void compactTest(){ByteBuffer byteBuffer = ByteBuffer.allocate(10);byteBuffer.put((byte) 1);byteBuffer.put((byte) 2);byteBuffer.put((byte) 3);byteBuffer.put((byte) 4);byteBuffer.flip();System.out.println(byteBuffer.get()); // 1System.out.println(byteBuffer.get()); // 2System.out.println(byteBuffer.get()); // 3byteBuffer.compact();byteBuffer.put((byte) 1);byteBuffer.put((byte) 2);byteBuffer.put((byte) 3);byteBuffer.put((byte) 5);System.out.println(Arrays.toString(byteBuffer.array()));// [1, 2, 3, 5, 0, 0, 0, 0, 0, 0]
}
调用 duplicate
之后,会把没有读取到的 4 放到 ByteBuffer 的前面,然后继续往后写入,所以 compact 这个方法切换写模式并不会覆盖没有读取完的数据。
当切换写模式之后,会先把 [position, limit)
挪到下标 0
开始的位置,然后设置 position = limit - position
,就是设置 position = 4 - 3 = 1
,后面会从 1
开始继续写入。
5.10 其他方法
上面就是比较常用的方法,下面剩下那些就是不常用的,可以看下下面的截图。
6. 大端序和小端序
ByteBuffer 里面还有一个重要的概念就是大端序和小端序,下面就来介绍下这个概念,我们先来随便看一个数字的二进制,比如数字 1234
,二进制为:00000000 00000000 00000100 11010010
。
数字存储到计算机中有两种方式,一种是内存的低地址向高地址存储,一种是高地址向低地址存储,也就是下面的两种方式。
- 大端序(Big-Endian):数据的高位字节存储在内存的低地址,低位字节存储在内存的高地址。
- 小端序(Little-Endian):数据的低位字节存储在内存的低地址,高位字节存储在内存的高地址。
比如下面图中的存储:
在大端序中,数字 1234
的存储从 0 开始,高位存储到 0 的位置,依次类推,小端序则反过来。
在 JVM 中,堆的地址从下往上是从低到高的,对于大端序,读取数据的时候就是从高位开始读取,对于小端序则是从低位开始读取。
在 ByteBuffer 中则是通过一个变量 bigEndian
来表示这个 ByteBuffer 存储数据是大端序还是小端序。
boolean bigEndian = true;
同时也给了一个方法返回时大端序还是小端序。
public final ByteOrder order() {return bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
}
当然除了上面两个方法,ByteBuffer 也提供了方法去设置大端序还是小端序。
public final ByteBuffer order(ByteOrder bo) {bigEndian = (bo == ByteOrder.BIG_ENDIAN);nativeByteOrder =(bigEndian == (Bits.byteOrder() == ByteOrder.BIG_ENDIAN));return this;
}
当然这里 ByteBuffer 只是指定大端序还是小端序,对于不同的字节序,从里面读取数据的时候的操作就不同,因为这里只是 ByteBuffer,如果是 IntBuffer 这种一次性读取四个字节的,就需要根据不同的字节序来判断要如何组成一个 int 了,我举个例子,还是下面这张图。
- 如果是大端序,这时候从下标 0 - 4 存储的就是 int 高到底的字节,那么组合的方法就是:
(arr[0] << 24) | (arr[1] << 16) || (arr[2] << 8) || arr[3]
- 如果是小端序,这时候从下标 0 - 4 存储的就是 int 低到高的字节,那么组合的方法就是:
(arr[0]) | (arr[1] << 8) || (arr[2] << 16) || (arr[3] << 24)
那么这里就简单介绍下这两个概念,因为具体的实现是在子类中去完成的,这篇文章就先不介绍了。
7. 小结
这篇文章就先介绍到这了,这个 ByteBuffer 是比较重要的一个类,为什么要介绍这个类呢?因为后面我将会逐步开始学习并写一些 RocketMQ 的文章,但我们都知道像这种 RocketMQ 的中间件的内存存储都离不开文件映射,其中就离不开 MappedByteBuffer,所以要慢慢从最底层的 Buffer 开始学习,这样才知道当往文件里面写入数据的时候,到底是怎么写入的。
如有错误,欢迎指出!!!
相关文章:
【源码解析】Java NIO 包中的 ByteBuffer
文章目录 1. 前言2. ByteBuffer 概述3. 属性4. 构造器5. 方法5.1 allocate 分配 Buffer5.2 wrap 映射数组5.3 slice 获取子 ByteBuffer5.4 duplicate 复刻 ByteBuffer5.5 asReadOnlyBuffer 创建只读的 ByteBuffer5.6 get 方法获取字节5.7 put 方法往 ByteBuffer 里面加入字节5.…...
vue video重复视频 设置 srcObject 视频流不占用资源 减少资源浪费
// 直接设置srcObject减少获取视频流:通过 captureStream() 方法从下方视频元素获取视频流。 // 设置 srcObject:将获取到的视频流设置为上方视频的 srcObject 减少资源浪费 // 获取到需要复制到的dom元素 const firstVideoElement proxy.$refs.firs…...
LayaAir3.2来了:性能大幅提升、一键发布安装包、支持WebGPU、3D导航寻路、升级为真正的全平台引擎
前言 LayaAir3的每一个分支版本都是一次较大的提升,在3.1彻底完善了引擎生态结构之后,本次的3.2会重点完善全平台发布相关的种种能力,例如,除原有的安卓与iOS系统外,还支持Windows系统、Linux系统、鸿蒙Next系统&#…...
计算机网络(四)网络层
4.1、网络层概述 简介 网络层的主要任务是实现网络互连,进而实现数据包在各网络之间的传输 这些异构型网络N1~N7如果只是需要各自内部通信,他们只要实现各自的物理层和数据链路层即可 但是如果要将这些异构型网络互连起来,形成一个更大的互…...
时空笔记:CBEngine(微观交通模拟引擎)
CBEngine 是一个微观交通模拟引擎,可以支持城市规模的道路网络交通模拟。CBEngine 能够快速模拟拥有数千个交叉路口和数十万辆车辆的道路网络交通。 以下内容基本翻译自CBEngine — CBLab 1.0.0 documentation 1 模拟演示 1.0 模拟演示结构 config.cfg 定义了 roa…...
Apache Sedona和Spark将geojson瓦片化例子
Apache Sedona很方便读取geojson、ShapeFile、geopackage等文件,提供了很多spark sql函数和rdd算子。下面例子主要用于熟悉spark和sedona的使用。 引入的maven包 <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.or…...
5种IO模型
目录 一、认识IO二、5种IO模型三、非阻塞IO代码 一、认识IO 什么是IO? Input(输入)和Output(输出)。 冯诺依曼体系结构中,数据从输入设备拷贝到内存,经过处理后,再从内存拷贝到输出设备。现实情况中,数据并不是那么流…...
ue5 蒙太奇,即上半身动画和下半身组合在一起,并使用。学习b站库得科技
本文核心 正常跑步动画端枪动画跑起来也端枪 正常跑步动画 端枪动画的上半身 跑起来也端枪 三步走: 第一步制作动画蒙太奇和插槽 第二步动画蓝图选择使用上半身动画还是全身动画,将上半身端枪和下半身走路结合 第三步使用动画蒙太奇 1.开始把&a…...
面试题
1、shell 脚本写出检测 /tmp/size.log 文件如果存在显示它的内容,不存在则创建一个文件将创建时间写入。 2、写一个 shel1 脚本,实现批量添加 20个用户,用户名为user01-20,密码为user 后面跟5个随机字符。 3、编写个shel 脚本将/usr/local 目录下大于10M的文件转移到…...
工厂方法模式
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它提供了一个接口,用于创建对象,但允许子类决定实例化的类是哪一个。这样,工厂方法模式将对象的创建逻辑委托给子类,从而实现了创建…...
web前端学习总结(一)
web前端使用三项技术:html、css、javascript. 一、html:超文本标记语言,用于展示网页的框架。 <html> <head><title> </title></head><body><div> </div> <!--用于布局,占1行 --><span&g…...
【深度学习入门_基础篇】概率论
开坑本部分主要为基础知识复习,新开坑中,学习记录自用。 学习目标: 随机事件与概率、随机变量及其分布、多维随机变量及其分布、大数定律与中心极限定理。 强烈推荐此视频: 概率论_麻省理工公开课 废话不多说,直接…...
SpringMVC根据url校验权限,防止垂直越权
思路是加一个拦截器,对除登录接口的所有请求进行拦截。拦截到请求后,查询当前用户都拥有哪些url的权限(这个需要权限表有url字段),然后与当前请求的url对比,如果相同则说明有权限,否则没有。 首…...
istio-proxy oom问题排查步骤
1. 查看cluster数量 cluster数量太多会导致istio-proxy占用比较大的内存,此时需检查是否dr资源的host设置有配置为* 2. 查看链路数据采样率 若采样率设置过高,在压测时需要很大的内存来维护链路数据。可以调低采样率或增大istio-proxy内存。 检查iop中…...
openstack下如何生成centos9 centos10 和Ubuntu24 镜像
如何生成一个centos 10和centos 9 的镜像1. 下载 对应的版本 wget https://cloud.centos.org/centos/10-stream/x86_64/images/CentOS-Stream-GenericCloud-x86_64-10-latest.x86_64.qcow2 wget https://cloud.centos.org/centos/9-stream/x86_64/images/CentOS-Stream-Gener…...
Yolov8训练方式以及C#中读取yolov8+onnx模型进行目标检测.NET 6.0
目录 首先你要定义v8的模型特征文件 下方是完整的模型编写函数 然后你要在控件窗体中定义应用这些方法以及函数 一、定义你的标签 二、下方是定义模块和坐标的方法 三、画框 完整推理函数代码块 效果展示 完整源码 训练其实和yolov10差不多 因为v10就是在v8的基础上做了优…...
C#中序列化的选择:JSON、XML、二进制与Protobuf详解
C#中序列化的选择:JSON、XML、二进制与Protobuf详解 在C#开发中,序列化是将对象转换为可存储或传输的格式的过程,而反序列化则是将存储或传输的数据重新转换为对象的过程。选择合适的序列化方式对应用程序的性能、可维护性和兼容性至关重要。…...
深入理解 Java 设计模式之策略模式
一、引言 在 Java 编程的世界里,设计模式就如同建筑师手中的蓝图,能够帮助我们构建出更加健壮、灵活且易于维护的代码结构。而策略模式作为一种经典的行为型设计模式,在诸多实际开发场景中都发挥着至关重要的作用。它能够让算法的定义与使用…...
如何快速导出Python包和安装?
pip freeze > requirements.txt导出: 安转 : pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple...
记录一个在增量更新工具类
1. 应用场景 比方说我现在有一个商品,这个商品可以上传图片用来展示,之前已经上传过5张图片 ABCDE,现在其中BC这两张图片不想要了要换成FG,这个时候就可以使用这个工具类进行更新。 在最后一步,可以不管ADE直接只管新增…...
VUE3 常用的组件介绍
Vue 组件简介 Vue 组件是构建 Vue 应用程序的核心部分,组件帮助我们将 UI 分解为独立的、可复用的块,每个组件都有自己的状态和行为。Vue 组件通常由模板、脚本和样式组成。组件的脚本部分包含了各种配置选项,用于定义组件的逻辑和功能。 组…...
UML系列之Rational Rose笔记二:包图
rose绘制包图,有简单的有复杂的; 正常图简单的话,直接新建package使用就行,改变package的大小,改变名称当作不同的包就行;基本没有什么学习难度;在此,笔者直接介绍一下回执标准一点…...
第34天:安全开发-JavaEE应用反射机制攻击链类对象成员变量方法构造方法
时间轴: Java反射相关类图解: 反射: 1、什么是 Java 反射 参考: https://xz.aliyun.com/t/9117 Java 提供了一套反射 API ,该 API 由 Class 类与 java.lang.reflect 类库组成。 该类库包含了 Field 、 Me…...
cuda实现flash_attn_mma_share_kv源码分析
一 源码分析 1.1 函数入口 void flash_attn_mma_stages_split_q_shared_kv(torch::Tensor Q, torch::Tensor K, torch::Tensor V, torch::Tensor O, int stages) {CHECK_TORCH_TENSOR_DTYPE(Q, torch::kHalf) // Q [B,H,N,D]CHECK_TORCH_TENSOR_DTYPE(K, torch::kHalf) // K …...
Python操作Excel的库openpyxl使用入门
openpyxl 是一个用于读写 Excel 2010 xlsx/xlsm/xltx/xltm 文件的 Python 库。以下是一些 openpyxl 的基本使用方法: 安装 openpyxl 首先,确保已经安装了 openpyxl。如果没有安装,可以使用以下命令进行安装: pip install openp…...
《拉依达的嵌入式\驱动面试宝典》—计算机网络篇(二)
《拉依达的嵌入式\驱动面试宝典》—计算机网络篇(二) 你好,我是拉依达。 感谢所有阅读关注我的同学支持,目前博客累计阅读 27w,关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客》已经是 Linux驱动 相关内容搜索的推荐首位,感谢大家支持。 《…...
信息网络安全考试gjdw
序号 一级纲要 二级纲要 题目分类 题型 题干 选项 答案 题目依据 试题分数 试题编码 备注 说明 1 信息安规 \ 专业题库 单选题 根据信息安规规定,试验和推广信息新技术,应制定相应的( ),经本单位…...
S变换matlab实现
S变换函数 function [st,t,f] st(timeseries,minfreq,maxfreq,samplingrate,freqsamplingrate) % S变换 % Code by huasir Beijing 2025.1.10 % Reference is "Localization of the Complex Spectrum: The S Transform" % from IEEE Transactions on Signal Proc…...
【OLAP和PLTP】—— 浅谈两者的应用场景和区别
大家好,我是摇光~ OLAP(Online Analytical Processing)和OLTP(Online Transaction Processing)是两种不同的数据处理技术,分别用于不同的业务场景。以下是关于OLAP和OLTP的详细介绍: 一、OLAP&…...
计算机组成原理(1)王道学习笔记
一、 引言 计算机硬件唯一能识别的数据是二进制-----0/1。 用低/高电平表示0/1。 通过很多条电路,可以传递多个二进制数位。 每个二进制数位称为1bit(比特)。 计算机硬件的基本组成 早期的ENIAC计算机是通过手动接线来控制计算。冯诺依曼首次…...
LLaMA模型:自然语言处理的革新者
引言 在人工智能的领域中,自然语言处理(NLP)是一个充满挑战的分支,它的目标是让计算机能够理解和生成人类语言。Transformer模型,作为NLP的基石,已经极大地推动了这一领域的发展。然而,为了进一…...
各种特种无人机快速发展,无人机反制技术面临挑战
随着科技的飞速发展,各种特种无人机在军事、民用等领域得到了广泛应用,其性能不断提升,应用场景也日益丰富。然而,无人机反制技术的发展确实面临一定的挑战,难以完全跟上无人机技术的快速发展步伐。以下是对这一问题的…...
1555银行账户概要_pandas解答
目录 题目链接(无_力扣VIP_略过)一.读题(建议使用这种表结构_数据对比看)题目SQL Schema & Pandas Schema 建表语句_数据 二.答案_MySQL一图解MySQL一图解__可只需看此!!!!!!!!!!!!!!!!答案-----------------------------------------------------------------------------…...
【C++补充】第一弹---位图技术揭秘:内存优化与快速访问
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1 位图 1.1 位图相关面试题 1.2 位图的设计及实现 1.3 C库中的位图 bitset 1.4 位图的模拟实现 1.5 位图的优缺点 1.6 位图相关考察题目 1 …...
node.js中实现token的生成与验证
Token(令牌)是一种用于在客户端和服务器之间安全传输信息的加密字符串。在Web开发中,Token常用于身份验证和授权,确保用户能够安全地访问受保护的资源。 作用与意义 身份验证:Token可以用来验证用户的身份࿰…...
服务器登陆后有java变量
需求:在ssh服务器后,用户root 使用java会报错,没有这个变量,其实环境变量中已经有配置了,在/etc/profile 中有写变量及地址,通过source /etc/profile 命令也可以使环境变量加载上,但是ssh后不会…...
层次模型式的工作流
层次模型式的工作流是一种适合分布式版本控制系统(如 Git、Mercurial)的开发协作方式,它将开发团队分成多个层次,每个层次有明确的角色和职责,代码从底层逐步向上层汇总和集成,最终形成一个完整、稳定的产品…...
Linux 发行版介绍与对比:Red Hat、Ubuntu、Kylin、Debian
Linux 操作系统有众多发行版(Distros),每个发行版的设计目标、目标用户、应用场景和使用方式有所不同。常见的 Linux 发行版包括 Red Hat、Ubuntu、Kylin 和 Debian。以下是这些发行版的详细介绍与对比,以及它们的应用场景和使用方…...
G1原理—3.G1是如何提升垃圾回收效率
大纲 1.G1为了提升GC的效率设计了哪些核心机制 2.G1中的记忆集是什么 3.G1中的位图和卡表 4.记忆集和卡表有什么关系 5.RSet记忆集是怎么更新的 6.DCQ机制的底层原理是怎样的 7.DCQS机制及GC线程对DCQ的处理 提升G1垃圾回收器GC效率的黑科技 G1设计了一套TLAB机制 快速…...
IOS界面传值-OC
1、页面跳转 由 ViewController 页面跳转至 NextViewController 页面 (1)ViewController ViewController.h #import <UIKit/UIKit.h>interface ViewController : UIViewControllerend ViewController.m #import "ViewController.h" …...
C# SQL ASP.NET Web
留学生的课程答疑 按照要求完成程序设计、数据库设计、用户手册等相关技术文档; 要求 1. 计算机相关专业,本科以上学历,至少有1年以上工作经验或实习经历。 2. 熟练掌握WinForm程序开发,或ASP.NET Web编程。 3. 熟悉C#中网络…...
asp.net core webapi 并发请求时 怎么保证实时获取的用户信息是此次请求的?
对于并发请求,每个请求会被分配到一个独立的线程或线程池工作线程上。通过 HttpContext 或 AsyncLocal,每个线程都能独立地获取到它自己的上下文数据。由于这些数据是与当前请求相关的,因此在并发请求时不会互相干扰。 在并发请求时…...
软件23种设计模式完整版[附Java版示例代码]
一、什么是设计模式 设计模式是在软件设计中反复出现的问题的通用解决方案。它们是经过多次验证和应用的指导原则,旨在帮助软件开发人员解决特定类型的问题,提高代码的可维护性、可扩展性和重用性。 设计模式是一种抽象化的思维方式,可以帮助开发人员更好地组织和设计他们…...
FPGA 20 ,FPGA按键消抖功能解析与实现
目录 前言 一. 具体场景 二. 消抖方法...
基于单片机的无线气象仪系统设计(论文+源码)
1系统方案设计 如图2.1所示为无线气象仪系统设计框架。系统设计采用STM32单片机作为主控制器,结合DHT11温湿度传感器、光敏传感器、BMP180气压传感器、PR-3000-FS-N01风速传感器实现气象环境的温度、湿度、光照、气压、风速等环境数据的检测,并通过OLED1…...
OA系统如何做好DDOS防护
OA系统如何做好DDOS防护?在数字化办公蔚然成风的当下,OA(办公自动化)系统作为企业内部管理与协作的神经中枢,其安全性和稳定性直接关系到企业的日常运营效率、信息流通效率以及长远发展。OA系统不仅承载着企业内部的日…...
java_单例设计模式
什么是设计模式 什么是单例设计模式 单例设计模式——饿汉式 虽然你没有使用这个对象实例,但是它也帮你创建了!容易造成对象的浪费 對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用. package com.hspedu.singl…...
比较分析:Windsurf、Cody、Cline、Roo Cline、Copilot 和 通义灵码
随着人工智能技术的快速发展,开发者工具变得越来越智能化,特别是在代码生成、辅助编程等领域,市面上涌现了多种 AI 驱动的工具。本文将从开源性、集成能力、功能覆盖范围、支持的编程语言、生态兼容性、成本、学习曲线、响应速度、离线支持以…...
vue3后台系统动态路由实现
动态路由的流程:用户登录之后拿到用户信息和token,再去请求后端给的动态路由表,前端处理路由格式为vue路由格式。 1)拿到用户信息里面的角色之后再去请求路由表,返回的路由为tree格式 后端返回路由如下: …...
C#版 软件开发6大原则与23种设计模式
开发原则和设计模式一直是软件开发中的圣经, 但是这仅仅适用于中大型的项目开发, 在小型项目的开发中, 这些规则会降低你的开发效率, 使你的工程变得繁杂. 所以只有适合你的才是最好的. 设计模式六大原则1. 单一职责原则(Single Responsibility Principle࿰…...