1.Buffer类图
新创建的ByteBuffer,四个属性的位置
capacity:容量,指定缓冲区的大小。
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
limit:第一个不应该读取或写入的元素的索引。缓冲区的限制不能为负,并且不能大于其容量。
Position: 下一个要读取或写入的元素的索引。缓冲区的位置不能为负,并且不能大于其限制。
遵循条件:0 <= 标记 <= 位置 <= 限制 <= 容量
不同的方法都是编辑buffer,移动这些标记。有四个方法需要重点关注
Buffer.clear();
Buffer.compact();
Buffer.flip();
Buffer.rewind();
clear();
-
使缓冲区为一系列新的通道读取或相对放置 操作做好准备:它将限制设置为容量大小,将位置设置为 0。 这意味着新数据会覆盖旧数据。
compact(); The compact method moves the elements between the current position and the limit to the begging of the buffer.
flip(); The flip method need to be called before reading the data from the buffer.
-
使缓冲区为一系列新的通道写入或相对获取 操作做好准备:它将限制设置为当前位置,然后将位置设置为 0。
rewind();
-
使缓冲区为重新读取已包含的数据做好准备:它使限制保持不变,将位置设置为 0。
字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。
直接字节缓冲区可以通过调用此类的 allocateDirect
工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。
NonDirect: if you create a buffer that will not interact with native resource (ex. Just to store a String) you should use a NonDirect Buffer.
Adding to a Buffer: When adding data to a buffer you can use the wrap() method. Note that when a buffer is created by wrapping it are never direct.
/* * wraps a string inside an buffer.
*/
String string = "Text to be added";
CharBuffer charBuffer = CharBuffer.allocate(string.length());
charBuffer.wrap(string);
or you could wrap entire blocks of data in a form of an array:
/* * takes a byte array and wraps it into a buffer.
*/
byte[] data = “Text to be added”.getBytes(“UTF-8”);
ByteBuffer buffer1 = ByteBuffer.wrap(data);
Draining a Buffer: Buffers can be drained into any data type:
/* * uses the get() method to fill a string.
*/
String fromBuffer = “”;
while (buffer.hasRemaining()) {
fromBuffer += buffer.get();
}
Data Conversion: Data Conversion is an important aspect of buffers. You can use the factory methods to change a buffer from one type of another:
ByteBuffer byteBuffer = ByteBuffer.allocate(5);
IntBuffer intBuffer = byteBuffer.asIntBuffer();
flip方法:首先将限制设置为当前位置,然后将位置设置为 0。
2.Channel
通道只能在字节缓冲区上操作。
I/O可以分为广义的两大类别:File I/O和Stream I/O。相应地有两种类型的通道文件(file)通道和套接字(socket)通道。FileChannel类和三个socket通道类:SocketChannel、ServerSocketChannel和DatagramChannel。
通道可以以多种方式创建。Socket通道有可以直接创建新socket通道的工厂方法。但是一个FileChannel对象却只能通过在一个打开的RandomAccessFile、FileInputStream或FileOutputStream对象上调用getChannel( )方法来获取。您不能直接创建一个FileChannel对象。
- FileChannel对象是线程安全(thread-safe)的。多个进程可以在同一个实例上并发调用方法而不会引起任何问题,不过并非所有的操作都是多线程的(multithreaded)。影响通道位置或者影响文件大小的操作都是单线程的(single-threaded)。文件锁定模型,锁的对象是文件而不是通道或线程,这意味着文件锁不适用于判优同一台Java虚拟机上的多个线程发起的访问。锁与文件关联,而不是与通道关联。我们使用锁来判优外部进程,而不是判优同一个Java虚拟机上的线程。
- Socket Channel。请注意DatagramChannel和SocketChannel实现定义读和写功能的接口而ServerSocketChannel不实现。ServerSocketChannel负责监听传入的连接和创建新的SocketChannel对象,它本身从不传输数据。
为了加深理解,自己写了一个小示例代码实现本地文件拷贝,类似上传:
package com.zhang.nio;
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;
/**
* 使用NIO上传文件
* <p />
*
* @author Administrator
*/
public class UploadFile {
private final static int DEFAULT_CAPACITY = 8092;
private final static String SOURCE_FILE_PATH = "F:\\开发工具\\spring-framework-3.0.5.RELEASE-with-docs.zip";
private final static String DEST_PATH = "F:\\test\\test.zip";
/**
* @param args
*/
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_CAPACITY);
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
inputStream = new FileInputStream(SOURCE_FILE_PATH);
outputStream = new FileOutputStream(DEST_PATH);
inChannel = inputStream.getChannel();
outChannel = outputStream.getChannel();
while (inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
if (inChannel != null) {
inChannel.close();
}
if (outChannel != null) {
outChannel.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}