Java NIO (二)NIO Buffer类的重要方法

1 allocate()方法

在使用Buffer实例前,我们需要先获取Buffer子类的实例对象,并且分配内存空间。需要获取一个Buffer实例对象时,并不是使用子类的构造器来创建,而是调用子类的allocate()方法。

java 复制代码
public class AllocateTest {
    static IntBuffer intBuffer = null;//一个整型的Buffer静态变量
    public static void main(String[] args) {
        //创建一个intbuffer 实例对象
        intBuffer = IntBuffer.allocate(20);
        System.out.println("afer allocate ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
    }
}

本例中,IntBuffer是具体的Buffer子类,通过调用IntBuffer.allocate(20)创建了一个intBuffer实例对象,并且分配了20*4字节的内存空间。运行程序后,通过输出结果,可以查看一个新建缓冲区实例对象的主要属性值。

从上面的运行结果可以看出:一个缓冲区在新建后处于写模式,position属性(代表写入位置)的值是0,缓冲区的capacity值是初始化时allocate方法的参数值,而limit最大可写上限值也为allocate方法的初始化参数值。

2 put()方法

在调用allocate方法分配内存、返回了实例对象后,缓冲区实例对象处于写模式,可以写入对象,如果要把对象写入缓冲区,就需要调用put()方法。put方法很简单,只有一个参数,即需要写入的独享,只不过要求写入的数据类型与缓冲区的类型保持一致。接着上面的例子向刚刚创建的intBuffer缓存实例对象写入5个整数。

java 复制代码
public class AllocateTest {
    static IntBuffer intBuffer = null;//一个整型的Buffer静态变量
    public static void main(String[] args) {
        //创建一个intbuffer 实例对象
        intBuffer = IntBuffer.allocate(20);
        System.out.println("before put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            intBuffer.put(i);
        }
        System.out.println("after put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
    }
}

从结果可以看到,写入了5个元素后,缓冲区的position属性值变成了5,所以指向了第6个(从0开始)可以进行写入的元素位置。limit、capacity两个属性的值没有发生变化。

3 flip()方法

向缓冲区写入数据之后,是否可以直接从缓冲区读取数据呢?答案是否定的。这时缓冲区还处于写模式,如果需要读取数据,要将缓冲区转换成读模式。flip()翻转方法是Buffer类提供的一个模式转变的重要方法,作用是将写模式转换成读模式。接着前面的例子演示flip()方法。

java 复制代码
public class AllocateTest {
    static IntBuffer intBuffer = null;//一个整型的Buffer静态变量
    public static void main(String[] args) {
        //创建一个intbuffer 实例对象
        intBuffer = IntBuffer.allocate(20);
        System.out.println("before put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            intBuffer.put(i);
        }
        System.out.println("after put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.flip();
        System.out.println("after flip ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
    }
}

调用flip方法后,新模式可读上限limit的值变成了之前写模式下的position的值,也就是5;而新模式下的position的值变成了0,表示从头开始读取。

对flip()方法从写入到读取转换的规则,再一次详细介绍:

首先,设置可读上限limit的属性值。将写模式下的缓冲区中内容的最后写入位置position的值作为读模式下的limit上限值。

其次,把读的起始位置position的值设为0,表示从头开始读。

最后,清除之前的mark标记,因为mark保存的是写模式下的临时位置,发生模式转换后,如果继续使用旧的mark标记,就会造成位置混乱。

上面三步可以查看Buffer.flip()方法的源码,具体如下:

java 复制代码
 public Buffer flip() {
        //设置可读上限limit,设置为写模式下的position值
        limit = position;
        //把读的起始位置position的值设为0,表示从头开始读
        position = 0;
        //清除之前的mark标记
        mark = -1;
        return this;
   }

新的问题来了:在读取完后,如果再一次将缓冲区切换成写模式呢?答案是:可以调用Buffer.clear()清空或者Buffer.compact()压缩方法,它们可以将缓冲区切换为写模式。

4 get()方法

调用flip()方法将缓冲区切换成读模式后,就可以开始从缓冲区读取数据了。读取数据的方法很简单,可以调用get()方法从position的位置读取一个数据,并且进行相应的缓冲区属性的调整。接着前面调用flip()方法的实例,演示缓冲区的读取操作。

java 复制代码
public class AllocateTest {
    static IntBuffer intBuffer = null;//一个整型的Buffer静态变量
    public static void main(String[] args) {
        //创建一个intbuffer 实例对象
        intBuffer = IntBuffer.allocate(20);
        System.out.println("before put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            intBuffer.put(i);
        }
        System.out.println("after put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.flip();
        System.out.println("after flip ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //先读取两个数据
        for (int i = 0; i < 2; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第一次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //再读取三个数据
        for (int i = 0; i < 3; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第二次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
    }
}

从输出看到,读取操作会改变可读位置position的值,而可读上限limit的值不会改变。在position值和limit值相等时,表示所有数据读取完成,position指向了一个没有数据的元素位置,已经不能在读了。这里强调一下,在读完之后是否可以立即对缓冲区进行数据写入呢?答案是不能。现在还处于读模式,必须调用clear()或compact()方法,即清空或压缩缓冲区,将缓冲区切换成写模式。

5 rewind()方法

已经读完的数据,如果需要再读一遍,可以调用rewind()方法。rewind()方法也叫倒带,就像播放磁带一样,再重新播放。接着前面的代码,继续rewind()方法使用的演示。

java 复制代码
public class AllocateTest {
    static IntBuffer intBuffer = null;//一个整型的Buffer静态变量
    public static void main(String[] args) {
        //创建一个intbuffer 实例对象
        intBuffer = IntBuffer.allocate(20);
        System.out.println("before put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            intBuffer.put(i);
        }
        System.out.println("after put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.flip();
        System.out.println("after flip ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //先读取两个数据
        for (int i = 0; i < 2; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第一次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //再读取三个数据
        for (int i = 0; i < 3; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第二次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.rewind();
        System.out.println("after rewind -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
    }
}

rewind()方法主要是调整了缓冲区的position属性与mark属性,调整规则如下:

1、position重置为0,所以可以重读缓冲区的所有数据。

2、limit保持不变,数据量还是一样的,仍然可以从缓冲区读取的元素数量。

3、mark被清理,表示之前的临时位置不能再用了。

rewind()源码如下:

java 复制代码
public Buffer rewind() {
        //重置为0,所以可以重读缓冲区中的所有数据
        position = 0;
        //mark被清理,表示之前的临时位置不能再用了
        mark = -1;
        return this;
    }

通过源码可以看到rewind()和flip()方法很像,区别在于:rewind()方法不会影响limit属性值;而flip()方法会重设limit属性值。

6 mark()和reset()方法

mark()和reset()方法是配套使用的:mark()方法将当前position的值保存起来放在mark属性中,让mark属性记住这个临时位置;然后可以调用reset()方法将mark的值恢复到position中。

例如,在前面重复读取的示例代码中,在读到第三个元素(i=2时)时,可以调用mark方法,把当前位置position的值保存到mark属性中,这时mark属性值是2。

接下来可以调用reset()方法,将mark属性的值恢复到position中,这样就可以从位置2(第三个元素)开始重复读取了。

java 复制代码
public class AllocateTest {
    static IntBuffer intBuffer = null;//一个整型的Buffer静态变量
    public static void main(String[] args) {
        //创建一个intbuffer 实例对象
        intBuffer = IntBuffer.allocate(20);
        System.out.println("before put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            intBuffer.put(i);
        }
        System.out.println("after put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.flip();
        System.out.println("after flip ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //先读取两个数据
        for (int i = 0; i < 2; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第一次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //再读取三个数据
        for (int i = 0; i < 3; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第二次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.rewind();
        System.out.println("after rewind -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            if(i == 2){
                intBuffer.mark();
            }
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after mark -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.reset();
        for (int i = 2; i < 5; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after reset -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
    }
}

在上面的代码中,首先调用reset方法把mark中的值恢复到position中,因此读取的位置position就是2,表示可以再次开始从第三个元素开始读取数据。调用reset方法后,position的值是2,此时去读取缓冲区,输出后面的三个元素2 、3 、4。

7 clear()方法

在读模式下,调用clear方法将缓冲区切换成写模式。此方法的作用是:

(1)将position清零。

(2)limit设置为capacity最大容量值,可以一直写入,直到缓冲区写满。

接着上面的示例,调用clear方法。

java 复制代码
public class AllocateTest {
    static IntBuffer intBuffer = null;//一个整型的Buffer静态变量
    public static void main(String[] args) {
        //创建一个intbuffer 实例对象
        intBuffer = IntBuffer.allocate(20);
        System.out.println("before put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            intBuffer.put(i);
        }
        System.out.println("after put ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.flip();
        System.out.println("after flip ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //先读取两个数据
        for (int i = 0; i < 2; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第一次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        //再读取三个数据
        for (int i = 0; i < 3; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after get 第二次 ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.rewind();
        System.out.println("after rewind -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        for (int i = 0; i < 5; i++) {
            if(i == 2){
                intBuffer.mark();
            }
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after mark -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.reset();
        for (int i = 2; i < 5; i++) {
            int j = intBuffer.get();
            System.out.println("j = " + j);
        }
        System.out.println("after reset -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
        intBuffer.clear();
        System.out.println("after clear -- ----------------");
        System.out.println("position = " + intBuffer.position());
        System.out.println("limit = " + intBuffer.limit());
        System.out.println("capacity = " + intBuffer.capacity());
    }
}

在缓冲区处于读模式时,调用clear方法,缓冲区被切换成写模式,可以看到清空了position的值,值被设置为0, 并且limit值为最大容量。

8 使用Buffer类的基本步骤

总体来说,使用Java NIO Buffer类的基本步骤如下:

(1)使用创建子类实例对象的allocate()方法创建一个Buffer类的实例对象。

(2)调用put()方法将数据写入缓冲区。

(3)写入完成后,再开始读取数据之前调用flip()方法,将缓冲区切换成读模式。

(4)调用get()方法,可以从缓冲区读取数据。

(5)读取完成后,调用clear()或compact()方法,将缓冲区从读模式切换成写模式,可以继续写入数据。

相关推荐
拔剑纵狂歌13 分钟前
Golang异常处理机制
开发语言·后端·golang·go
ffyyhh99551125 分钟前
java进行音视频的拆分和拼接
java·音视频
L小李要学习29 分钟前
十一、作业
c语言·开发语言·c++
乐之者v34 分钟前
Spring之 IoC、BeanFactory、ApplicationContext
java·后端·spring
DS_Watson42 分钟前
字符串和正则表达式踩坑
java·开发语言
Wayfreem42 分钟前
Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁
java·开发语言
我焦虑的编程日记43 分钟前
【Java EE】验证码案例
java·java-ee
牧夏。1 小时前
vscode运行java中文乱码,引发的mac配置问题
java·vscode·macos
吃饱很舒服1 小时前
kotlin distinctBy 使用
android·java·开发语言·前端·kotlin