ByteBuffer
是 Java NIO(非阻塞输入输出)包中的一个类,用于在字节缓冲区中处理原始字节数据。它提供了一个方法来存储和操作字节数据,特别适用于需要在文件、网络操作或直接操作内存中处理二进制数据的场景。
主要特点
- Direct和Non-direct缓冲区:
ByteBuffer
可以是直接缓冲区(直接与操作系统的内存交互,通常速度更快)或非直接缓冲区(在Java堆中分配)。用户可以通过ByteBuffer.allocateDirect(int capacity)
方法创建直接缓冲区。 - 多种数据类型的支持:除了基本的字节操作,
ByteBuffer
还提供了处理其他数据类型的方法,如putInt()
,putFloat()
,getDouble()
等。 - 位置、限制和容量:
ByteBuffer
具有三个重要的属性:位置(position)、限制(limit)和容量(capacity),通过这些属性可以控制读写的流动和状态。
基本方法
以下是一些常用的 ByteBuffer
方法:
-
allocate(int capacity):创建一个指定容量的非直接缓冲区。
ByteBuffer buffer = ByteBuffer.allocate(10);
-
allocateDirect(int capacity):创建一个指定容量的直接缓冲区。
ByteBuffer directBuffer = ByteBuffer.allocateDirect(10);
-
put(byte b):在当前位置插入一个字节。
buffer.put((byte) 1);
-
putInt(int value):在当前位置插入一个整型值。
buffer.putInt(42);
-
get():读取当前位置的字节,并将位置向后移动一位。
byte b = buffer.get();
-
getInt():读取当前位置的整型值,并将位置向后移动四位。
int value = buffer.getInt();
-
wrap(byte[] array):将一个字节数组包装为
ByteBuffer
,使其可以直接操作该数组的内容。ByteBuffer buffer = ByteBuffer.wrap(new byte[10]);
-
array():返回当前缓冲区的基础数组,可以直接操作。
byte[] byteArray = buffer.array();
总结
ByteBuffer
是处理原始字节数据的强大工具,提供了多种方法以支持各种数据类型的存取,为 Java 程序在高性能 I/O 操作中提供了灵活性和简便性。
Direct(直接)缓冲区和 Non-direct(非直接)缓冲区在 Java NIO 中有以下主要区别:
1. 内存分配
-
直接缓冲区(Direct ByteBuffer):
- 直接缓冲区在 Java 堆外内存中分配。这意味着它的内存并不在 Java 的垃圾回收(GC)管理范围内。
- 由于直接缓冲区与操作系统的内存直接交互,通常在进行 I/O 操作时可以减少复制开销,提高性能。
-
非直接缓冲区(Non-direct ByteBuffer):
- 非直接缓冲区在 Java 堆内存中分配。它存活于 Java 的垃圾回收机制之下。
- 由于需要进行额外的内存复制操作(例如,从内核缓冲区到 Java 堆的复制),在进行 I/O 操作时,性能可能相对较低。
2. 性能
-
直接缓冲区:
- 因为减少了对 Java 堆和直接内存之间的复制,直接缓冲区在高性能 I/O 操作,比如文件和网络通信时,通常具有更好的性能表现。
-
非直接缓冲区:
- 由于需要额外的复制步骤,性能相对较低,特别是当进行大量数据传输时。
3. 生命周期管理
-
直接缓冲区:
- 直接缓冲区的内存管理不受 Java 垃圾回收机制的控制,需通过
ByteBuffer.allocateDirect()
创建,直到 JVM 关闭前不能保证释放。
- 直接缓冲区的内存管理不受 Java 垃圾回收机制的控制,需通过
-
非直接缓冲区:
- 非直接缓冲区的内存由 Java 垃圾回收管理,创建后可以直接通过
ByteBuffer.allocate()
创建,使用完毕后可以被自动回收。
- 非直接缓冲区的内存由 Java 垃圾回收管理,创建后可以直接通过
4. 使用场景
-
直接缓冲区:
- 适合于高性能需求、需要频繁进行 I/O 操作的场景,如大文件操作、网络数据传输等。
-
非直接缓冲区:
- 适合于相对小的、偶发的 I/O 操作或者普通的应用程序日常内存使用。
总结
选择直接缓冲区或非直接缓冲区主要取决于应用的性能需求和内存管理策略。在需要高效的 I/O 操作时,直接缓冲区是更好的选择;而在其他常规内存操作时,非直接缓冲区可能更为简单和方便。
大小端(Endianess)是计算机系统中存储数据时的字节序列定义。它主要有两种类型:大端(Big Endian)和小端(Little Endian)。
以下是它们的定义和图示说明:
大端(Big Endian)
在大端模式中,数据的高位字节存储在低地址,低位字节存储在高地址。这意味着在内存中,数据的顺序是从左到右,按照从最重要的字节到最不重要的字节排列。
示例: 假设我们有一个整数 0x12345678
(十六进制表示),它在内存中的存储方式如下:
地址: 0x00 0x01 0x02 0x03
内容: 0x12 0x34 0x56 0x78 (大端)
小端(Little Endian)
在小端模式中,数据的低位字节存储在低地址,高位字节存储在高地址。这意味着在内存中,数据的顺序是从右到左,按照从最不重要的字节到最重要的字节排列。
示例: 同样以整数 0x12345678
为例,它在内存中的存储方式如下:
地址: 0x00 0x01 0x02 0x03
内容: 0x78 0x56 0x34 0x12 (小端)
视觉图示
以下是用图示表现大小端存储的一个简单示例:
大端(Big Endian):
+-------+-------+-------+-------+
| 0x12 | 0x34 | 0x56 | 0x78 |
+-------+-------+-------+-------+
地址: 0x00 0x01 0x02 0x03小端(Little Endian):
+-------+-------+-------+-------+
| 0x78 | 0x56 | 0x34 | 0x12 |
+-------+-------+-------+-------+
地址: 0x00 0x01 0x02 0x03
总结
- 大端(Big Endian):高位字节在前,低位字节在后,适合人类阅读顺序。
- 小端(Little Endian):低位字节在前,高位字节在后,适合一些特定计算机架构。
每种字节序都有其特定的使用场景,网络协议往往使用大端,而某些处理器(如 x86 架构)则倾向于使用小端。