Java NIO Channel与Buffer的概述

1.缓冲区(Buffer)

Java NIO中数据的读写操作始终是与缓冲区相关联的.数据是从通道读入缓冲区,从缓冲区写入到通道中的。缓冲区是定长的,基本上它只是一个列表,它的所有元素都是基本数据类型.

缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。

Buffer的基本用法

ByteBuffer是最常用的缓冲区,它提供了读写其他数据类型的方法,且信道的读写方法只接收ByteBuffer.因此ByteBuffer的用法是有必要牢固掌握的.

1.创建ByteBuffer
1.1 使用allocate()静态方法
ByteBuffer buffer=ByteBuffer.allocate(1024);
以上方法将创建一个容量为1024字节的ByteBuffer,如果发现创建的缓冲区容量太小,唯一的选择就是重新创建一个大小合适的缓冲区.

1.2使用put()方法来填充ByteBuffer
通过put()方法可以给ByteBuffer填充一个或者多个字节或者基本数据类型的值.

 public void readAllData(byte[] bytes){
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put(bytes);
        }
  • 1
  • 2
  • 3
  • 4

1.3 通过包装一个已有的数组来创建
如下,通过包装的方法创建的缓冲区保留了被包装数组内保存的数据.

ByteBuffer buffer=ByteBuffer.wrap(byteArray);

如果要将一个字符串存入ByteBuffer,可以如下操作:
ByetBuffer buffer = ByteBuffer.warp(“Hello World NIO”.getBytes());
2.回绕缓冲区与清除缓冲区

  buffer.flip();
  buffer.clear();
  • 1
  • 2

一旦调用read()来告知FileChannel向ByteBuffer存储字节,就必须调用缓存器上的flip()方法,这个方法用来将缓冲区准备为数据传出状态,执行以上方法后,输出通道会从数据的开头而不是末尾开始.回绕保持缓冲区中的数据不变,只是准备写入而不是读取。通俗的理解就是让它做好被读取的准备(看起来很麻烦,但是为了最大的传输速度)。如果我们打算使用缓冲器执行进一步的read()操作,我们也必须得调用clear()来为每个read()做准备。

在前面的一片文章中我们只是使用ByteBuffer来读取指定字节的大小的文件,现在我们通过ByteBuffer来读取一个文本的全部内容并写入到另一个文件中:

 try {
            FileChannel inputFileChannel = new FileInputStream("/home/wang/bigchat.sql").getChannel();
            //newBigChat.sql可以不存在。系统会自动创建一个
            FileChannel  outputFileChannel = new FileOutputStream("/home/wang/newBigchat.sql").getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int len = 0;
            //当read返回-1时表示读到了文件末尾
            while ((len=inputFileChannel.read(byteBuffer))!=-1){
                byteBuffer.flip();//准备好写
                outputFileChannel.write(byteBuffer);
                byteBuffer.clear();//准备好读
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

flip()则是准备缓冲器以便它的信息可以由write()提取。write()之后数据依然在缓冲器中,接着clear()方法则对所有内部指针重新安排,以便缓存器在另一个read()操作期间能够做好接受数据的准备。

其实上面的代码并不是最理想,我们可以使用transferTo()和transferFrom()则允许我们将一个通道和另一个通道直接相连:

 try {
            FileChannel inputFileChannel = new FileInputStream("/home/wang/bigchat.sql").getChannel();
            //newBigChat.sql可以不存在。系统会自动创建一个
            FileChannel outputFileChannel = new FileOutputStream("/home/wang/newBigchat1.sql").getChannel();
            inputFileChannel.transferTo(0,inputFileChannel.size(),outputFileChannel);
            outputFileChannel.transferFrom(inputFileChannel,0,inputFileChannel.size());
        }catch (IOException e){}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这种方法了解了解即可。

ByteBuffer俗称缓冲器, 是将数据移进移出通道的唯一方式,并且我们只能创建一个独立的基本类型缓冲器。ByteBuffer 中存放的是字节,如果要将它们转换成字符流则需要使用 Charset , Charset 是字符编码,它提供了把字节流转换成字符流( 解码 ) 和将字符流转换成字节流 ( 编码) 的方法。

private byte[] getBytes (char[] chars) {//将字符转为字节(编码)
   Charset cs = Charset.forName ("UTF-8");
   CharBuffer cb = CharBuffer.allocate (chars.length);
   cb.put (chars);
   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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

通过ByteBuffer读取文件后转化为CharBuffer并输出:

 FileChannel fc = new FileInputStream(PATH).getChannel();
                ByteBuffer buffer = ByteBuffer.allocate(SIZE);
                fc.read(buffer);
                //重值ByteBuffer中的数组,调用方法后输出通道会从数据的开头而不是末尾开始
                buffer.flip();
                //使用UTF-8编码为CharBuffer
                Charset charset = Charset.forName("UTF-8");
                CharBuffer cb = charset.decode(buffer);
                System.out.println(cb);//正确输出数据
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.通道(Channel)

Java NIO的通道比缓冲区好理解多了,它就类似于流,但又有些不同:

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
  • 通道可以异步地读写。
  • 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

正如之前所说,从通道读取数据到缓冲区,从缓冲区写入数据到通道。

前面我们通过三个基础流调用getChannel()方法获取了一个Channel的实现FileChannel。下面我们来看NIO中的其它Channel的实现:

  • FileChannel:从文件中读写数据。
  • DatagramChannel:能通过UDP读写网络中的数据。
  • SocketChannel:能通过TCP读写网络中的数据。
  • ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值