ByteBuffer使用

本文详细介绍了Java NIO中的ByteBuffer类,包括其静态工厂方法和实例对象的方法。讲解了如何利用ByteBuffer进行文件读写操作,并给出了具体示例。

一、ByteBuffer类非实例的方法

ByteBuffer类提供了4个静态工厂方法来获得ByteBuffer的实例:

1、ByteBuffer allocate(int capacity) //创建一个指定capacity的ByteBuffer。
2、ByteBuffer allocateDirect(int capacity) //创建一个direct的ByteBuffer,这样的ByteBuffer在参与IO操作时性能会更好
3、ByteBuffer wrap(byte [] array)
4、ByteBuffer wrap(byte [] array, int offset, int length) //把一个byte数组或byte数组的一部分包装成ByteBuffer。(offset指偏移量)
注意:wrap方法是静态函数且只能接收byte类型的数据,任何其他类型的数据想通过这种方式传递,需要进行类型的转换。

ByteBuffer.allocateDirect分配的堆外内存不需要我们手动释放,而且ByteBuffer中也没有提供手动释放的API。

简单实例

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Main{
    public static void main(String[] args) throws IOException {
        try {
            // 文件进口
            FileInputStream fileInputStream = new FileInputStream( "test.txt" );
            FileChannel inChannel = fileInputStream.getChannel();
            // 文件出口
            FileOutputStream fileOutputStream = new FileOutputStream( "test_out.txt" );
            FileChannel outChannel = fileOutputStream.getChannel();
            // buffer(相当于运煤车)
            ByteBuffer byteBuffer = ByteBuffer.allocate(10*1024*1024); //申请堆外内存10MB
            // ByteBuffer byteBuffer = ByteBuffer.allocateDirect(10*1024*1024); // Direct Buffer的效率会更高。

            long start = System.currentTimeMillis(); // 记录系统时间(毫秒)
            while (true) {
                int eof = inChannel.read(byteBuffer); //每次读取块大小的文件内容
                if (eof == -1) break; // 通道已经被运煤车运光
                byteBuffer.flip(); // 将缓冲区准备为数据传出状态
                outChannel.write(byteBuffer);
                byteBuffer.clear(); // 这个方法实际上也不会改变缓冲区的数据,而只是简单的重置了缓冲
                // 区的主要索引值.不必为了每次读写都创建新的缓冲区,那样做会降低性能.相反,要重用现在的
                // 缓冲区,在再次读取之前要清除缓冲区.
            }
            System.out.println("spending : " + (System.currentTimeMillis() - start));
            inChannel.close();  // 山西煤矿停止出售煤炭资源
            outChannel.close(); // 山东发电厂停止接收煤炭资源
        }catch(IOException e){
            System.out.println("IOException");
        }
    }
}

二、ByteBuffer类实例对象的方法

方法说明
limit(), limit(10)其中读取和设置这4个属性的方法的命名和jQuery中的val(),val(10)类似,一个负责get,一个负责set
reset()把position设置成mark的值,相当于之前做过一个标记,现在要退回到之前标记的地方
clear()position = 0;limit = capacity;mark = -1; 有点初始化的味道,但是并不影响底层byte数组的内容
flip()limit = position;position = 0;mark = -1; 翻转,也就是让flip之后的position到limit这块区域变成之前的0到position这块,翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态
rewind()把position设为0,mark设为-1,不改变limit的值
remaining()return limit - position;返回limit和position之间相对位置差
hasRemaining()return position < limit返回是否还有未读内容
compact()把从position到limit中的内容移到0到limit-position的区域内,position和limit的取值也分别变成limit-position、capacity。如果先将positon设置到limit,再compact,那么相当于clear()
get()相对读,从position位置读取一个byte,并将position+1,为下次读写作准备
get(int index)绝对读,读取byteBuffer底层的bytes中下标为index的byte,不改变position
get(byte[] dst, int offset, int length)从position位置开始相对读,读length个byte,并写入dst下标从offset到offset+length的区域
put(byte b)相对写,向position的位置写入一个byte,并将postion+1,为下次读写作准备
put(int index, byte b)绝对写,向byteBuffer底层的bytes中下标为index的位置插入byte b,不改变position
put(ByteBuffer src)用相对写,把src中可读的部分(也就是position到limit)写入此byteBuffer
put(byte[] src, int offset, int length)从src数组中的offset到offset+length区域读取数据并使用相对写写入此byteBuffer

简单实例

package src;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;

/**
 * Created by MilletPu on 2017/7/31.
 */
public class testByteBuffer {
    public static void main(String []args){
        try {
            ByteBuffer buffer = ByteBuffer.allocate(100 * 1024); //申请堆外内存
            //ByteBuffer buffer = ByteBuffer.allocateDirect(100*1024); // Direct Buffer的效率会更高。
            //******************************* string 转 buffer  ******************************/
            String sendString = "你好,服务器. ";
            //wrap:是把一个byte数组或byte数组的一部分包装成ByteBuffer。
            ByteBuffer sendBuffer = ByteBuffer.wrap(sendString.getBytes("UTF-16"));
            // 通道也就是FileChannel,可以由FileInputStream,FileOutputStream,RandomAccessFile,三个类来产生
            // RandomAccessFile支持任意位置读写类
            // RandomAccessFile的功能:1、读取任意位置的数据;2、追加数据;3、任意位置插入数据;
            // 方法名: RandomAccessFile.getFilePointer()   作用: 返回文件记录指针的当前位置
            // 方法名: RandomAccessFile.seek(long pos)      作用: 将文件记录指针定位到pos的位置
            FileChannel socketChannel = (new RandomAccessFile("test.txt","r")).getChannel();
            while(true){
                int eof = socketChannel.read(sendBuffer);
                if(eof==-1) break;
                System.out.println(eof);
            }
        }catch (Exception ignored) {
            System.out.println("IOException");
        }
    }

    private byte[] getBytes (char[] chars) {   //将字符转为字节(编码)
        Charset cs = Charset.forName ("UTF-8");
        CharBuffer cb = CharBuffer.allocate (chars.length);
        cb.put (chars); // 将char[]对象放入CharBuffer中
        cb.flip (); // 将缓冲区准备为数据传出状态
        ByteBuffer bb = cs.encode (cb);
        return bb.array();
    }

    private char[] getChars (byte[] bytes) {  //将字节转为字符(解码)
        Charset cs = Charset.forName ("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate (bytes.length);
        bb.put (bytes);
        bb.flip ();
        CharBuffer cb = cs.decode (bb);
        return cb.array();
    }
}
<think>好的,我现在需要帮助用户了解如何使用Java NIO的ByteBuffer类。用户明确要求教程、示例和详解,所以我得先理清ByteBuffer的基本概念和核心组件。首先,我记得ByteBuffer是Java NIO中的一个关键类,用于高效处理字节数据。根据引用[2],Java NIO提供了非阻塞、高效的I/O操作,而ByteBuffer是其核心之一。 首先,我应该从ByteBuffer的创建方法开始。用户可能需要知道如何分配一个缓冲区,所以需要提到allocate和allocateDirect的区别。引用[3]提到需要弄清楚position、limit、capacity这些变量,所以我得详细解释这些属性。 接下来是缓冲区的读写操作。用户可能需要示例代码来演示如何写入和读取数据。比如put()和get()方法的不同用法,包括绝对定位和相对定位。引用[1]提到put方法有很多版本,可以写入不同数据,这里需要具体说明如何操作。 然后是关于flip()、rewind()、clear()等方法的作用,这些是切换读写模式的关键步骤,容易混淆,必须详细解释。例如,flip()方法用于切换为读模式,调整limit和position的位置,而clear()则重置缓冲区但不清数据,这些都需要明确说明。 还需要涵盖标记(mark)和重置(reset)的用法,以及如何复制缓冲区,比如slice()和duplicate()方法。引用[4]提到的arrayOffset()方法也很重要,如果缓冲区有后备数组的话,用户可能需要知道如何访问底层数组的偏移量。 此外,应该提供一些完整的示例代码,展示从创建缓冲区到读写数据的完整流程,帮助用户理解各个步骤如何衔接。比如,写入字符串到ByteBuffer,然后读取出来,这样的例子比较直观。 最后,要确保回答结构清晰,分步骤讲解,并附上相关问题供用户进一步学习。需要检查所有LaTeX格式是否正确,行内公式用$...$,独立公式用$$...$$,但在这个回答中可能公式较少,主要关注代码块和引用标识。同时引用提供的四个参考文献,适当在相关部分添加引用标记,比如在提到NIO核心概念时引用[^2],在讲解position等变量时引用[^3]等。 总结一下,回答的结构应该是:概述、创建缓冲区、核心属性、读写操作、模式切换、其他方法、示例代码、常见问题,最后是相关问题。这样用户能够逐步学习ByteBuffer使用方法,并通过示例加深理解。</think>### Java NIO ByteBuffer 使用教程详解 #### 1. ByteBuffer 概述 Java NIO 的 $ByteBuffer$ 类是处理字节数据的核心工具,用于高效读写二进制数据(如文件、网络传输)。它通过直接内存访问和缓冲区管理优化 I/O 性能[^2]。 --- #### 2. 创建 ByteBuffer ```java // 分配堆内存缓冲区(JVM 堆内) ByteBuffer heapBuffer = ByteBuffer.allocate(1024); // 分配直接内存缓冲区(操作系统堆外,减少拷贝开销) ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024); // 包装现有数组(数据修改会同步到数组) byte[] arr = new byte[1024]; ByteBuffer wrappedBuffer = ByteBuffer.wrap(arr); ``` --- #### 3. 核心属性与关系 - **capacity**:缓冲区的最大容量(固定值) - **position**:当前读写位置(初始为 0) - **limit**:可读/写的最大位置(初始等于 capacity) - **mark**:临时标记位置(通过 `mark()` 设置) 转换关系如图: ```plaintext 0 ≤ mark ≤ position ≤ limit ≤ capacity ``` --- #### 4. 读写操作 **写入数据**(写模式): ```java byte[] data = "Hello".getBytes(); heapBuffer.put(data); // 相对写入(position 自动后移) heapBuffer.put(2, (byte)0x41); // 绝对写入(指定索引位置) ``` **读取数据**(读模式): ```java heapBuffer.flip(); // 切换为读模式(limit=原position,position=0) while (heapBuffer.hasRemaining()) { byte b = heapBuffer.get(); // 相对读取 } ``` --- #### 5. 模式切换方法 - **`flip()`**:写模式 → 读模式 $$limit = position,\quad position = 0$$ - **`clear()`**:重置缓冲区(不清数据) $$position = 0,\quad limit = capacity$$ - **`rewind()`**:重新读取数据 $$position = 0$$ --- #### 6. 其他重要方法 - **`slice()`**:创建共享数据的子缓冲区 - **`duplicate()`**:创建完全副本(共享底层数据) - **`arrayOffset()`**:获取缓冲区在底层数组的偏移量[^4] ```java if (heapBuffer.hasArray()) { int offset = heapBuffer.arrayOffset() + heapBuffer.position(); } ``` --- #### 7. 完整示例 ```java // 创建并写入数据 ByteBuffer buffer = ByteBuffer.allocate(128); buffer.put("NIO ByteBuffer".getBytes(StandardCharsets.UTF_8)); // 切换读模式 buffer.flip(); // 读取数据到数组 byte[] readData = new byte[buffer.remaining()]; buffer.get(readData); System.out.println(new String(readData)); // 输出 "NIO ByteBuffer" ``` --- #### 8. 常见问题 **Q:堆缓冲区与直接缓冲区的区别?** A:堆缓冲区由 JVM 管理,直接缓冲区通过操作系统本地内存分配(减少拷贝次数,适合高频 I/O 操作)。 **Q:如何避免缓冲区溢出?** A:操作前检查剩余空间:`if (buffer.remaining() >= neededSize) { ... }` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值