二、java.nio.ByteBuffer用法小结

2023-10-27

java.nio.ByteBuffer用法小结

简介

在Java的Socket编程中,若使用阻塞式(BIO),则往往通过ServerSocket的accept()方法获取到客户端Socket之后,再使用客户端Socket的InputStream和OutputStream进行读写。Socket.getInputstream.read(byte[] b)和Socket.getOutputStream.write(byte[] b)的方法中的参数都是字节数组。这种阻塞式的Socket编程显然已经远远不能满足目前的并发式访问需求。
所以最近在项目中学习使用了Java原生NIO,这时则需要通过ServerSocketChannel的accept()方法获取到客户端的SocketChannel,再使用客户端SocketChannel直接进行读写。但SocketChannel.read(ByteBuffer dst)和SocketChannel.write(ByteBuffer src)的方法中的参数则都变为了java.nio.ByteBuffer,该类型就是JavaNIO对byte数组的一种封装,其中包括了很多基本的操作,在此记录一下备忘。
ByteBuffer包含几个基本的属性:
• position:当前的下标位置,表示进行下一个读写操作时的起始位置;
• limit:结束标记下标,表示进行下一个读写操作时的(最大)结束位置;
• capacity:该ByteBuffer容量;
• mark: 自定义的标记位置;
无论如何,这4个属性总会满足如下关系:mark <= position <= limit <= capacity。目前对mark属性了解的不多,故在此暂不做讨论。其余3个属性可以分别通过ByteBuffer.position()、ByteBuffer.limit()、ByteBuffer.capacity()获取;其中position和limit属性也可以分别通过ByteBuffer.position(int newPos)、ByteBuffer.limit(int newLim)进行设置,但由于ByteBuffer在读取和写出时是非阻塞的,读写数据的字节数往往不确定,故通常不会使用这两个方法直接进行修改。

初始化

首先无论读写,均需要初始化一个ByteBuffer容器。如上所述,ByteBuffer其实就是对byte数组的一种封装,所以可以使用静态方法wrap(byte[] data)手动封装数组,也可以通过另一个静态的allocate(int size)方法初始化指定长度的ByteBuffer。初始化后,ByteBuffer的position就是0;其中的数据就是初始化为0的字节数组;limit = capacity = 字节数组的长度;用户还未自定义标记位置,所以mark = -1,即undefined状态。下图就表示初始化了一个容量为16个字节的ByteBuffer,其中每个字节用两位16进制数表示:
在这里插入图片描述

向ByteBuffer写数据

手动写入数据

可以手动通过put(byte b)或put(byte[] b)方法向ByteBuffer中添加一个字节或一个字节数组。ByteBuffer也方便地提供了几种写入基本类型的put方法:putChar(char val)、putShort(short val)、putInt(int val)、putFloat(float val)、putLong(long val)、putDouble(double val)。执行这些写入方法之后,就会以当前的position位置作为起始位置,写入对应长度的数据,并在写入完毕之后将position向后移动对应的长度。下图就表示了分别向ByteBuffer中写入1个字节的byte数据和4个字节的Integer数据的结果:
在这里插入图片描述

BufferByte的put方法
但是当想要写入的数据长度大于ByteBuffer当前剩余的长度时,则会抛出BufferOverflowException异常,剩余长度的定义即为limit与position之间的差值(即 limit - position)。如上述例子中,若再执行buffer.put(new byte[12]);就会抛出BufferOverflowException异常,因为剩余长度为11。可以通过调用ByteBuffer.remaining();查看该ByteBuffer当前的剩余可用长度。

从SocketChannel中读入数据至ByteBuffer

在实际应用中,往往是调用SocketChannel.read(ByteBuffer dst),从SocketChannel中读入数据至指定的ByteBuffer中。由于ByteBuffer常常是非阻塞的,所以该方法的返回值即为实际读取到的字节长度。假设实际读取到的字节长度为 n,ByteBuffer剩余可用长度为 r,则二者的关系一定满足:0 <= n <= r。继续接上述的例子,假设调用read方法,从SocketChannel中读入了4个字节的数据,则buffer的情况如下:
在这里插入图片描述

从ByteBuffer中读数据

复位position

现在ByteBuffer容器中已经存有数据,那么现在就要从ByteBuffer中将这些数据取出来解析。由于position就是下一个读写操作的起始位置,故在读取数据后直接写出数据肯定是不正确的,要先把position复位到想要读取的位置。
首先看一个rewind()方法,该方法仅仅是简单粗暴地将position直接复原到0,limit不变。这样进行读取操作的话,就是从第一个字节开始读取了。如下图:
在这里插入图片描述

该方法虽然复位了position,可以从头开始读取数据,但是并未标记处有效数据的结束位置。如本例所述,ByteBuffer总容量为16字节,但实际上只读取了9个字节的数据,因此最后的7个字节是无效的数据。故rewind()方法常常用于字节数组的完整拷贝。
实际应用中更常用的是flip()方法,该方法不仅将position复位为0,同时也将limit的位置放置在了position之前所在的位置上,这样position和limit之间即为新读取到的有效数据。如下图:
在这里插入图片描述

读取数据

在将position复位之后,我们便可以从ByteBuffer中读取有效数据了。类似put()方法,ByteBuffer同样提供了一系列get方法,从position开始读取数据。get()方法读取1个字节,getChar()、getShort()、getInt()、getFloat()、getLong()、getDouble()则读取相应字节数的数据,并转换成对应的数据类型。如getInt()即为读取4个字节,返回一个Int。在调用这些方法读取数据之后,ByteBuffer还会将position向后移动读取的长度,以便继续调用get类方法读取之后的数据。
这一系列get方法也都有对应的接收一个int参数的重载方法,参数值表示从指定的位置读取对应长度的数据。如getDouble(2)则表示从下标为2的位置开始读取8个字节的数据,转换为double返回。不过实际应用中往往对指定位置的数据并不那么确定,所以带int参数的方法也不是很常用。get()方法则有两个重载方法:
• get(byte[] dst, int offset, int length):表示尝试从 position 开始读取 length 长度的数据拷贝到 dst 目标数组 offset 到 offset + length 位置,相当于执行了

 for (int i = off; i < off + len; i++)
	dst[i] = buffer.get();

• get(byte[] dst):尝试读取 dst 目标数组长度的数据,拷贝至目标数组,相当于执行了

 buffer.get(dst, 0, dst.length);

此处应注意读取数据后,已读取的数据也不会被清零。下图即为从例子中连续读取1个字节的byte和4个字节的int数据:
在这里插入图片描述

此处同样要注意,当想要读取的数据长度大于ByteBuffer剩余的长度时,则会抛出 BufferUnderflowException 异常。如上例中,若再调用buffer.getLong()就会抛出 BufferUnderflowException 异常,因为 remaining 仅为4。

确保数据长度

为了防止出现上述的 BufferUnderflowException 异常,最好要在读取数据之前确保 ByteBuffer 中的有效数据长度足够。在此记录一下我的做法:

private void checkReadLen(
	long reqLen,
	ByteBuffer buffer,
	SocketChannel dataSrc
) throws IOException {
  int readLen;
  if (buffer.remaining() < reqLen) { // 剩余长度不够,重新读取
  	buffer.compact(); // 准备继续读取
    System.out.println("Buffer remaining is less than" + reqLen + ". Read Again...");
    while (true) {
      readLen = dataSrc.read(buffer);
      System.out.println("Read Again Length: " + readLen + "; Buffer Position: " + buffer.position());
      if (buffer.position() >= reqLen) { // 可读的字节数超过要求字节数
        break;
      }
    }
    buffer.flip();
    System.out.println("Read Enough Data. Remaining bytes in buffer: " + buffer.remaining());
  }
}

字节序处理

基本类型的值在内存中的存储形式还有字节序的问题,这种问题在不同CPU的机器之间进行网络通信时尤其应该注意。同时在调用ByteBuffer的各种get方法获取对应类型的数值时,ByteBuffer也会使用自己的字节序进行转换。因此若ByteBuffer的字节序与数据的字节序不一致,就会返回不正确的值。如对于int类型的数值8848,用16进制表示,大字节序为:0x 00 00 22 90;小字节序为:0x 90 22 00 00。若接收到的是小字节序的数据,但是却使用大字节序的方式进行解析,获取的就不是8848,而是-1876819968,也就是大字节序表示的有符号int类型的 0x 90 22 00 00。
JavaNIO提供了java.nio.ByteOrder枚举类来表示机器的字节序,同时提供了静态方法ByteOrder.nativeOrder()可以获取到当前机器使用的字节序,使用ByteBuffer中的order()方法即可获取该buffer所使用的字节序。同时也可以在该方法中传递一个ByteOrder枚举类型来为ByteBuffer指定相应的字节序。如调用buffer.order(ByteOrder.LITTLE_ENDIAN)则将buffer的字节序更改为小字节序。
一开始并不知道还可以这样操作,比较愚蠢地手动将读取到的数据进行字节序的转换。不过觉得还是可以记下来,也许在别的地方用得到。JDK中的 Integer 和 Long 都提供了一个静态方法reverseBytes()来将对应的 int 或 long 数值的字节序进行翻转。而若想读取 float 或 double,也可以先读取 int 或 long,然后调用 Float.intBitsToFloat(int val) 或 Double.longBitsToDouble(long val) 方法将对应的 int 值或 long 值进行转换。当ByteBuffer中的字节序与解析的字节序相反时,可以使用如下方法读取:

int i = Integer.reverseBytes(buffer.getInt()); 
float f = Float.intBitsToFloat(Integer.reverseBytes(buffer.getInt()));
long l = Long.reverseBytes(buffer.getLong());
double d = Double.longBitsToDouble(buffer.getLong());

继续写入数据

由于ByteBuffer往往是非阻塞式的,故不能确定新的数据是否已经读完,但这时候依然可以调用ByteBuffer的compact()方法切换到读取模式。该方法就是将 position 到 limit 之间还未读取的数据拷贝到 ByteBuffer 中数组的最前面,然后再将 position 移动至这些数据之后的一位,将 limit 移动至 capacity。这样 position 和 limit 之间就是已经读取过的老的数据或初始化的数据,就可以放心大胆地继续写入覆盖了。仍然使用之前的例子,调用 compact() 方法后状态如下:
在这里插入图片描述

总结-特别重要

总之ByteBuffer的基本用法就是:
初始化(allocate)–> 写入数据(read / put)–> 转换为写出模式(flip)–> 写出数据(get)–> 转换为写入模式(compact)–> 写入数据(read / put)…

ByteBuffer使用示例

具体详细使用示例请看:多路复用器-selector单线程版本
/**
 * 客户端数据读取处理类
 * @param selectionKey
 */
private void readHandler(SelectionKey selectionKey){
    /**
     * 由于在之前<p>acceptHandler </p>方法中已经把SocketChannel(客户端连接)
     *  <p> SelectionKey clientSelectionKey = clientSocketChannel.register(selector,SelectionKey.OP_READ,byteBuffer);</p>
     *   已经注册到多路复用器(selector)中,因此现阶段可以直接把客户端连接从选择器中取出
     */
    SocketChannel clientSocketChannel = (SocketChannel)selectionKey.channel();

    /**
     * 附加选项---byteBuffer
     * 由于在之前<p>acceptHandler </p>方法中已经把ByteBuffer(缓冲区)
     *          *  <p> SelectionKey clientSelectionKey = clientSocketChannel.register(selector,SelectionKey.OP_READ,byteBuffer);</p>
     *          *   已经注册到多路复用器(selector)中,因此现阶段可以直接把客户端连接对应的byteBuffer(缓冲区)从选择器中取出。
     */
    ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
    byteBuffer.clear();//清空缓冲区
    try {
        int read = 0;
        while (true){
            read = clientSocketChannel.read(byteBuffer);//从客户端中读取数据到缓冲区
            if(read > 0){//读取到数据
                byteBuffer.flip();//准备开始从缓冲区中读取数据,指针反转
                byte[] bytes = new byte[read];
                byteBuffer.get(bytes);//从缓冲区中获取数据到字节数组
                String clientStr = new String(bytes);
                //打印客户传入的数据信息
                System.out.println("客户端【" + clientSocketChannel.getRemoteAddress() + "】有数据传入:" + clientStr);

                /**
                 * 数据读取完毕,开始准备写入数据
                 */
                byteBuffer.clear(); //清空缓冲区
                String returnClientStr = "---server port---" + serverPort +"---accept client port---" + clientSocketChannel.getRemoteAddress() + "---data---" + clientStr;
                byteBuffer.put(returnClientStr.getBytes());
                byteBuffer.flip();//准备开始从缓冲区中读取数据,指针反转
                while (byteBuffer.hasRemaining()){//判断当前缓冲区中是否有数据
                    clientSocketChannel.write(byteBuffer);//把当前缓冲区中数据写回客户端。
                }
                byteBuffer.clear();
            }else if(read == 0){//没有数据传入跳过
                break;
            }else { //-1 close_wait  bug  客户端可能断开
                System.out.println("---client port---" + clientSocketChannel.getRemoteAddress() + "---offline---");
                /**
                 * 检测到客户端关闭,删除selectionKey监听事件,
                 * 否则会一直受到这个selectionKey的动作。
                 */
                selectionKey.cancel();
                clientSocketChannel.close();
            }
        }

    } catch (IOException e) {
        e.printStackTrace();
        try {
            /**
             * 如果这里不做处理  在 read读取数据的时候就可能会报错
             * <p>
             *       java.io.IOException: 远程主机强迫关闭了一个现有的连接
             *
             *       java.io.IOException: 远程主机强迫关闭了一个现有的连接。
             *         at sun.nio.ch.SocketDispatcher.read0(Native Method)
             *         at sun.nio.ch.SocketDispatcher.read(Unknown Source)
             *         at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source)
             *         at sun.nio.ch.IOUtil.read(Unknown Source)
             *         at sun.nio.ch.SocketChannelImpl.read(Unknown Source)
             * </p>
             * 主要原因是客户端强制关闭了连接(没有调用SocketChannel的close方法),服务端还在read事件中,此时读取客户端的信息时会报错。
             * 代码不够健壮导致的;
             * 可以把这个catch中的代码注释掉,重启serversocket,然后使用nc命令连接后再强制关闭,看一下错误日志信息打印。
             */
            System.out.println("---client port---" + clientSocketChannel.getRemoteAddress() + "---offline---");
            /**
             * 检测到客户端关闭,删除selectionKey监听事件,
             * 否则会一直受到这个selectionKey的动作。
             */
            selectionKey.cancel();
            clientSocketChannel.socket().close();
            clientSocketChannel.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

参考资料

  1. java字节序、主机字节序和网络字节序扫盲贴:https://blog.csdn.net/aitangyong/article/details/23204817
  2. 原文链接:https://blog.csdn.net/mrliuzhao/article/details/89453082
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二、java.nio.ByteBuffer用法小结 的相关文章

  • 将用户库添加到 Ant Builder 类路径

    我在为 Eclipse 项目设置 Ant Builder 时遇到问题 我确实在 Eclipse 中将几个第 3 方库配置为用户库 这些库已添加到我的项目的构建路径中 一切正常 我的问题是 如果我想从 Eclipse 使用 Ant Build
  • 春天。使用java配置解决循环依赖而不使用@Autowired

    我有循环依赖和java配置 虽然使用 xml 配置解决它非常简单 但如果没有 Autowired 我无法使用 java 配置解决它 豆子 public class A private B b public B getB return b p
  • H2 - 多个应用程序访问同一个 H2 数据库

    我正在使用嵌入式数据库H2在 2 个网络应用程序中说WebApp1 and WebApp2 我运行 WebApp1 并执行一些查询来访问 H2 数据库 同时我运行 WebApp2 但它抛出异常H2 当前已被另一个进程使用 我的需求是 我应该
  • 在 Java 中将系统属性设置为 Null

    在我的单元测试中 我需要将 workingDir 系统属性设置为 Null 但我不能这样做 因为它给了我 NullPointerException System setProperty workingDir null 我该怎么做 您不能将属
  • 如何解析比 Java 中 NumberFormat 更严格的数字?

    我正在验证表单中的用户输入 我解析输入NumberFormat http docs oracle com javase 7 docs api java text NumberFormat html 但它是邪恶的 几乎允许任何事情 有没有办法
  • Spring Batch 多线程

    我正在编写一个 Spring Batch 并希望在需要时对其进行扩展 我的 ApplicationContext 看起来像这样 Configuration EnableBatchProcessing EnableTransactionMan
  • Eclipse 说“更新 Android Developer Toolkit”

    我不知何故弄乱了我的 Eclipse 和 Android 设置 我不知道如何修复它 问题症状如下 在 首选项 gt Android 中 我尝试选择 android sdk linux 的位置 选择时出现错误 此 Android SDK 需要
  • Spring 可以理解 @Inject 替换 Weld 作为 JSR-299 实现吗?

    我从几个网页中注意到 Spring 3 0 显然支持来自 JSR 330 的 Inject 由于我们确实希望在 Web 应用程序和独立应用程序的库中使用 JSR 299 语法进行依赖项注入 并且有 Weld 的替代方案 因此如果 Sprin
  • 使用Optional作为类中的属性是一个好习惯吗? [复制]

    这个问题在这里已经有答案了 我读过一些关于目的的内容Optional 不幸的是我不记得在哪里 在Java 8中 我很惊讶作者没有提到使用Optional作为类中的属性 由于我在课堂上经常使用选项 我想知道这是否是一个好的做法 或者我可以更好
  • Maven:缺少工件 org.springframework:spring:jar:4.2.6

    我在 SpringToolSuite 中有一个动态 Web 项目 它被转换为 Maven 项目 我遇到问题 缺少工件 org springframework spring jar 4 2 6 我已经尝试清理 重建和运行该项目 它给 读取文件
  • 用dagger 2查看依赖注入

    我有一个自定义视图扩展TextView 我应该在哪里调用我的组件来注入视图 component inject customTextView 因此 我发现我需要在自定义视图的构造函数中添加注入 在所有视图中 或者使一个调用另一个 Exampl
  • 如何更改tomcat jmx密码的文件权限

    我正在尝试保护 Windows 平台上托管的本地 tomcat 实例上的 JMX 访问 我已经创建了访问权限和密码文件 并使用以下 VM 参数插入这些文件 Dcom sun management jmxremote password fil
  • Spring Data elasticsearch @Query 注解嵌套对象

    我有两节课 Document public class PracticeQuestion private int userId private List
  • 我可以从同一个 jar 文件执行两个不同的类吗?

    我有一个项目 在一个包中我制作了服务器 在第二个包中我制作了客户端 它运行良好 我想创建一个 Jar 文件 是否可以使用同一个 jar 文件分别运行客户端和服务器 我使用了只有一个 main 的 jar 文件 当我运行 jar 文件时 它会
  • 警告:无法加载 sqljdbc_auth.dll 原因:java.library.path 中没有 sqljdbc_auth

    我正在使用 Ubuntu 12 05 并尝试连接到 Windows Server 2012 来获取数据库 我的数据库名称是 jobs 电脑的IP地址是192 160 1 33 托管在1433 但是当我尝试连接时出现以下错误 WARNING
  • 如果可能,将 jFrame 输出到第二台显示器

    我在 Java 中的 Swing 上有一个 jFrame 我希望它输出到第二个监视器 如果该监视器存在 我尝试过这个 通过this http download oracle com javase 6 docs api java awt Gr
  • Selenium Webdriver 中的 IF 语句

    我想知道是否有人可以帮助我解决我正在尝试解决的问题以及 Java 中 Webdriver 的 If 语句 当登录到我正在测试的应用程序时 可以在主页之前进入安全问题页面 如果是新用户等 我希望测试中的代码做的是 如果出现安全问题页面 请填写
  • 确保对象实现 Comparable

    我有一个小问题 想知道如何解决它 我有一个通用类Tuple
  • 如何更改MultipartFile的originalFilename

    我在服务器端有一个 MultipartFile 文件 我想更改该文件的原始文件名 但该类仅支持 getOriginalFilename 谁能帮我这个 PS 上传的是图片文件 多谢 您可以使用 MockMultipartFile 类更改名称
  • 在私有 guice 模块中公开 Map

    我在 guice 中有一个 PrivateModule 我想从该模块公开一个 Map public class TestInjectionModule extends PrivateModule expose Map class annoa

随机推荐

  • 针对TextView更换字体颜色同时设置不同的点击事件的设计方案

    在需求中我们需要将下面的文字放到一起 并更改不同的颜色进行区分 还需要将用 用户协议 和 隐私政策 添加不同的点击事情 去跳转进行展示不同的说明 首先这是一个弹框 我们需要先去定义它的布局文件
  • 【科普贴】USB_ID介绍

    一 功能介绍 1 当前很多SOC 都支持OTG功能 所以mini micro USB的接口上通常会拓展一个USB ID引脚 如下图 2 USB ID pin 为低电平时 则设备为host模式 比如PC和支持OTG设备做主设备时 USB ID
  • platformio添加外部库文件方法

    方法1 在这里搜索所需要的库函数进行添加到工程里 方法2 常用 1 准备好所需要的库文件 2 打开工程文件夹 完成以上4步后 重启VScode 即可完成库文件添加 方法3 常用 当创建完工程后 在该工程中添加库 方法2是添加全局库的方法 添
  • QT中qrect的使用介绍,超级详细

    QT中qrect的使用 QT是跨平台的GUI应用程序开发框架 使用C 编写 其中 qrect是QT中一个常用的类 用于描述矩形 qrect的函数原型参数介绍 在使用qrect时 我们需要了解以下几个函数原型参数 QRect 构造函数 创建一
  • 基于Python和mysql开发的BBS问答社区管理系统(源码+数据库+程序配置说明书+程序使用说明书)

    一 项目简介 本项目是一套基于Python和mysql开发的BBS问答社区管理系统 主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者 包含 项目源码 项目文档 数据库脚本等 该项目附带全部源码可作为毕设使用 项
  • Spring、SpringMVC、Spring Boot、Spring Cloud 概念、关系及区别

    注 此文章转载于其他大神 一 正面解读 Spring主要是基于IOC反转Beans管理Bean类 主要依存于SSH框架 Struts Spring Hibernate 这个MVC框架 所以定位很明确 Struts主要负责表示层的显示 Spr
  • MFC扩展库BCGControlBar Pro v33.6亮点 - 流程图、Ribbon Bar功能升级

    BCGControlBar库拥有500多个经过全面设计 测试和充分记录的MFC扩展类 我们的组件可以轻松地集成到您的应用程序中 并为您节省数百个开发和调试时间 BCGControlBar专业版 v33 6已正式发布了 此版本包含了对图表组件
  • anaconda、cuda、pytorch相关

    文章目录 0 00写在最前面 0 Anaconda安装 配置 使用的详细教程 1 MobaXterma远程连接服务器 2 linux系统下配置cuda及pytorch 3 linux操作系统下在conda环境中配置cuda和pytorch
  • Powershell快速入门(三) 实战应用

    好像关于Powershell说的已经差不多了 所以最后一篇文章就来使用Powershell写一些脚本 帮助我们完成一些日常工作 文件管理 常用命令 先来看看常用的文件管理命令 Set Location命令用于切换工作目录 它的别名是cd G
  • 基于SSM+Vue的乐购游戏商城系统

    末尾获取源码 开发语言 Java Java开发工具 JDK1 8 后端框架 SSM 前端 采用Vue技术开发 数据库 MySQL5 7和Navicat管理工具结合 服务器 Tomcat8 5 开发软件 IDEA Eclipse 是否Mave
  • 解放生产力!chatGPT接入Excel与Word教程(需要魔法上网,不用的都是骗人的)

    解放生产力 chatGPT接入Excel与Word教程 需要魔法上网 不用的都是骗人的 默认你注册过openAI账号了并使用过chatGPT了 本文就不教如何注册GPT啦 网上全是教程 这里贴一条亲测可用的教程网址 一 创建你的chatGP
  • centos7安装killall命令

    centos7精简安装后 使用中发现没有killall命令 经查找 可以通过以下命令解决 yum install psmisc y 简单介绍一下 psmisc Psmisc软件包包含三个帮助管理 proc目录的程序 安装下列程序 fuser
  • 模拟ic设计和数字ic设计的区别,含薪资表

    前言 本文主讲 数字IC设计与模拟IC设计的区别 初衷在于希望对新入行或想入行的同学在方向选择时提供有效的参考 正文 IC设计是半导体集成电路产品设计的统称 又称芯片设计 按功能可分为 数字IC 模拟IC 微波IC 其他IC 本文主要介绍数
  • sql根据指定字段查找表中重复记录,并统计重复次数

    查找表中重复记录 重复记录是根据单个字段来判断 并统计重复次数 SELECT 重复字段 COUNT 0 AS num FROM 表名 GROUP BY 重复字段 HAVING COUNT 重复字段 gt 1
  • npm link 引发的 Invalid hook call 问题

    项目场景 开发React组件库 假设路径为 workspace ui 时 为了避免每次修改都发布到 npm 在本地的测试项目 假设路径为 tmp test 中使用 npm link 为组件库建立软连接 方便本地调试 问题描述 不幸的是 发生
  • 对比学习:MoCo :Momentum Contrast for Unsupervised Visual Representation Learning

    参考链接 论文链接 https link zhihu com target https 3A arxiv org abs 1911 05722 代码链接 https github com facebookresearch moco Intr
  • QtCreator调试传入运行参数

    QtCreator是非常不错的IDE 最近在做的Qt命令行应用 因为调试的环境不同等问题 需要在调试的时候为 main 传入参数 度娘了半天 没找到方法 只能自力更生 后来在 项目 构建和运行 运行 下找到了 如果想在调试时为 main 传
  • 学习问题的总结

    1 该不该坚持学习MachineLearning 赵天琪 程序媛 INTJ 我觉得你不必过于担心 上了那几门公开课后完全可以胜任机器学习的工作 不必和Ph D比 他们是搞学术的 将来留在高校任教 发文章居多 我实习的主管原来在linkedi
  • 将form表单序列化成json格式

    将form表单序列化成json格式 fn serializeObject function var o var a this serializeArray each a function if o this name undefined i
  • 二、java.nio.ByteBuffer用法小结

    java nio ByteBuffer用法小结 简介 在Java的Socket编程中 若使用阻塞式 BIO 则往往通过ServerSocket的accept 方法获取到客户端Socket之后 再使用客户端Socket的InputStream