是从 Java 1.4 版本开始引入的一个新的IO API,可以替代标准的 Java IO API。NIO 支持面向缓冲区的、基于通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作。
| 方法 | 介绍 |
|---|---|
int read(ByteBuffer dst) | 从Channel中读取数据到ByteBuffer |
long read(ByteBuffer[] dsts) | 将Channel中的数据“分散”到ByteBuffer[] |
int write(ByteBuffer src) | 将ByteBuffer中的数据写入到Channel |
| long position() | 返回次通道的文件位置 |
| FileChannel position(long p) | 设置次通道的文件位置 |
| long size() | 返回此通道的文件的当前大小 |
| FileChannel truncate(long s) | 将此通道的文件截取为给定大小 |
| void fore(boolean metaData) | 强制将所有对此通道的文件更新写入到存储设备中 |
@Test
public void read() throws Exception {
//打开一个通道(InputStream/OutputStream/RandomAccessFile)来获取一个FileChannel的实例
RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\aa.txt","rw");
FileChannel inchannel = randomAccessFile.getChannel();
//new一个缓冲区,设置大小
ByteBuffer buffer = ByteBuffer.allocate(1024);
//将inchannel中的数据读取到buffer(代表由多少字节被读到了buffer中,-1表示到了文件末尾)
int bytesRead = inchannel.read(buffer);
//当返回-1的时候代表读完
while (bytesRead!=-1){
System.out.println("读取"+bytesRead);
//反转此缓冲区,读写转换
buffer.flip();
//判断缓冲区是否还有剩余
while (buffer.hasRemaining()){
System.out.print((char)buffer.get());
}
//缓冲区清空
buffer.clear();
//清空后返回bytesRead=-1,退出循环,不写这行死循环
bytesRead = inchannel.read(buffer);
}
randomAccessFile.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QY2F2WtH-1669130362008)(https://gitee.com/jerrygrj/img/raw/master/img/image-20211002110822392.png)]
@Test
public void write() throws Exception{
//打开一个通道(InputStream/OutputStream/RandomAccessFile)来获取一个FileChannel的实例
RandomAccessFile aFile = new RandomAccessFile("D:\\bb.txt","rw");
FileChannel inChannel = aFile.getChannel();
//要准备写入的数据
String newData = "今天天气不错"+System.currentTimeMillis();
//new一个缓冲区,设置大小
ByteBuffer buffer = ByteBuffer.allocate(1024);
//清空缓冲区
buffer.clear();
buffer.put(newData.getBytes());
//反转读写模式
buffer.flip();
//判断缓冲区是否还有剩余
while (buffer.hasRemaining()){
//写入数据
inChannel.write(buffer);
}
inChannel.close();
}
public static void main(String[] args) throws Exception {
//端口号
int port = 8080;
//把数据放到一个buffer缓冲区
ByteBuffer buf = ByteBuffer.wrap("hello jerry".getBytes());
//ServerSocketChannel
//打开服务器套接字通道
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(port));
//设置非阻塞模式
ssc.configureBlocking(false);
while (true){
SocketChannel sc = ssc.accept();
//监听是否有新连接接入
if(sc == null){
System.out.println("Waiting fro connections");
Thread.sleep(2000);
}else {
System.out.println("Incoming connection from:"+sc.socket().getRemoteSocketAddress());
buf.rewind();//指针0
sc.write(buf);
sc.close();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-euXcbhiC-1669130362009)(https://gitee.com/jerrygrj/img/raw/master/img/image-20211003140446514.png)]
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
关闭ServerSocketChannel
通过调用
ServerSocketChannel.close() 方法来关闭 ServerSocketChannel.
监听新的连接
ServerSocketChannel 可以设置成非阻塞模式。在非阻塞模式下,accept() 方法会立刻返回,如果还没有新进来的连接,返回的将是 null。 因此,需要检查返回的 SocketChannel 是否是 null.如:
while (true){
SocketChannel sc = ssc.accept();
//监听是否有新连接接入
if(sc == null){
System.out.println("Waiting fro connections");
Thread.sleep(2000);
}else {
}
}
SocketChannel socketChannel = SocketChannel.open(new
InetSocketAddress("www.baidu.com", 80));
//或者
SocketChannel socketChanne2 = SocketChannel.open();
socketChanne2.connect(new InetSocketAddress("www.baidu.com", 80));
socketChannel.isOpen(); // 测试 SocketChannel 是否为 open 状态
socketChannel.isConnected(); //测试 SocketChannel 是否已经被连接
socketChannel.isConnectionPending(); //测试 SocketChannel 是否正在进行
连接
socketChannel.finishConnect(); //校验正在进行套接字连接的 SocketChannel
是否已经完成连接
socketChannel.configureBlocking(false);
SocketChannel socketChannel = SocketChannel.open(
new InetSocketAddress("www.baidu.com", 80));
ByteBuffer byteBuffer = ByteBuffer.allocate(16);
socketChannel.read(byteBuffer);
socketChannel.close();
System.out.println("read over");
public static void main(String[] args) throws Exception {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("www.baidu.com",80));
//设置阻塞和非阻塞
socketChannel.configureBlocking(false);//非阻塞 //阻塞的话会执行不了下面的代码,阻塞状态
ByteBuffer buf = ByteBuffer.allocate(16);
socketChannel.read(buf);
socketChannel.close();;
System.out.println("readOver");
}
DatagramChannel server = DatagramChannel.open();
server.socket().bind(new InetSocketAddress(10086))
ByteBuffer receiveBuffer = ByteBuffer.allocate(64);
receiveBuffer.clear();
SocketAddress receiveAddr = server.receive(receiveBuffer);
//SocketAddress 可以获得发包的 ip、端口等信息,用 toString 查看,格式如下/127.0.0.1:57126
DatagramChannel server = DatagramChannel.open();
ByteBuffer sendBuffer = ByteBuffer.wrap("client send".getBytes());
server.send(sendBuffer, new InetSocketAddress("127.0.0.1",10086));
client.connect(new InetSocketAddress("127.0.0.1",10086));
int readSize= client.read(sendBuffer);
server.write(sendBuffer);
//连接read和write
@Test
public void testConnect() throws Exception{
DatagramChannel connChannel = DatagramChannel.open();
//绑定
connChannel.bind(new InetSocketAddress(9999));
//连接
connChannel.connect(new InetSocketAddress("127.0.0.1",9999));
//write方法
connChannel.write(ByteBuffer.wrap("发送:jerry".getBytes("UTf-8")));
//buffer
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
while (true){
readBuffer.clear();
connChannel.read(readBuffer);
readBuffer.flip();
System.out.println(Charset.forName("UTF-8").decode(readBuffer));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CmwAW3Vi-1669130362010)(https://gitee.com/jerrygrj/img/raw/master/img/image-20211003145627582.png)]
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装
成 NIO Buffer 对象,并提供了一组方法,用来方便的访问该块内存。缓冲区实际上是
一个容器对象,更直接的说,其实就是一个数组,在 NIO 库中,所有数据都是用缓冲
区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到
缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流 I/O
系统中,所有数据都是直接写入或者直接将数据读取到 Stream 对象中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3PhfzpQw-1669130362016)(https://gitee.com/jerrygrj/img/raw/master/img/image-20211003145713660.png)]
当向 buffer 写入数据时,buffer 会记录下写了多少数据。一旦要读取数据,需要通过 flip()方法将 Buffer 从写模式切换到读模式。在读模式下,可以读取之前写入到 buffer 的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有 两种方式能清空缓冲区:调用 clear()或 compact()方法。clear()方法会清空整个缓冲 区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起 始处,新写入的数据将放到缓冲区未读数据的后面。
@Test
public void bufferRead() throws Exception{
RandomAccessFile aFile = new RandomAccessFile("E:\\学习笔记包\\尚硅谷NIO\\src\\NIO_01Channels\\aa.txt","rw");
FileChannel fileChannel = aFile.getChannel();
//创建buffer,大小
ByteBuffer buf = ByteBuffer.allocate(1024);
//读
int bytesRead = fileChannel.read(buf);
while (bytesRead != -1){
//read模式
buf.flip();
while (buf.hasRemaining()){
System.out.println((char)buf.get());
}
buf.clear();
bytesRead = fileChannel.read(buf);
}
aFile.close();
}
@Test
public void write() throws Exception{
IntBuffer buf = IntBuffer.allocate(8);
//buffer放
for (int i = 0; i < buf.capacity(); i++) {
int j = 2*(i+1);
buf.put(j);
}
//重置缓冲区
buf.flip();
//获取
while (buf.hasRemaining()){
int value = buf.get();
System.out.println(value);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PsOtXCpj-1669130362019)(https://gitee.com/jerrygrj/img/raw/master/img/image-20211004103452699.png)]
ByteBuffer
MappedByteBuffer
CharBuffer DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
要想获得一个 Buffer 对象首先要进行分配。 每一个 Buffer 类都有一个 allocate 方 法。
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead=inChannel.read(buf);//读到buffer
buf.put(127);
int bytesWritten = inChannel.write(buf)
byte aByte = buf.get();
| 方法名 | 说明 |
|---|---|
| rewind() | Buffer.rewind()将 position 设回 0,所以你可以重读 Buffer 中的所有数据。limit 保 持不变,仍然表示能从 Buffer 中读取多少个元素(byte、char 等)。 |
| clear() | position 将被设回 0,limit 被设置成 capacity 的值。换 句话说,Buffer 被清空了。Buffer 中的数据并未清除,只是这些标记告诉我们可以从 哪里开始往 Buffer 里写数据。 |
| compact() | compact()方法将所有未读的数据拷贝到 Buffer 起始处。然后将 position 设到最后一 个未读元素正后面。limit 属性依然像 clear()方法一样,设置成 capacity。现在 Buffer 准备好写数据了,但是不会覆盖未读的数据。 |
| mark() | 可以标记 Buffer 中的一个特定 position |
| reset() | 可以通过调用 Buffer.reset()方法恢复到这个 position |
@Test
public void sliceBuf(){
ByteBuffer buf = ByteBuffer.allocate(10);
for (int i = 0; i < buf.capacity(); i++) {
buf.put((byte)i);
}
//创建子缓冲区
buf.position(3);
buf.limit(7);
//创建子缓冲区
ByteBuffer slice = buf.slice();
//改变子缓冲区内容
for (int i = 0; i < slice.capacity(); i++) {
byte b = slice.get(i);
b*=10;
slice.put(i,b);
}
//回到原位
buf.position(0);
buf.limit(buf.capacity());
while (buf.remaining() > 0){
System.out.print(buf.get()+",");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2EyOba2Y-1669130362021)(https://gitee.com/jerrygrj/img/raw/master/img/image-20211004105208645.png)]
@Test
public void asReadOnlyBuf(){
ByteBuffer buf = ByteBuffer.allocate(10);
for (int i = 0; i < buf.capacity(); i++) {
byte b = buf.get(i);
b = (byte) (i * 10);
buf.put(i,b);
}
//创建制度缓冲区
ByteBuffer asReadOnlyBuffer = buf.asReadOnlyBuffer();
asReadOnlyBuffer.position(0);
asReadOnlyBuffer.limit(buf.limit());
while (asReadOnlyBuffer.remaining() > 0){
System.out.println(asReadOnlyBuffer.get());
}
}
直接缓冲区是为加快 I/O 速度,使用一种特殊方式为其分配内存的缓冲区,JDK 文档 中的描述为:给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作。也就是说,它会在每一次调用底层操作系统的本机 I/O 操作之前(或之后), 尝试避免将缓冲区的内容拷贝到一个中间缓冲区中 或者从一个中间缓冲区中拷贝数 据。要分配直接缓冲区,需要调用 allocateDirect()方法,而不是 allocate()方法,使 用方式与普通缓冲区并无区别。
拷贝一个文件
@Test
public void allocateDirectBuf() throws Exception{
String inFile = "E:\\学习笔记包\\尚硅谷NIO\\src\\NIO_01Channels\\aa.txt";
FileInputStream fin = new FileInputStream(inFile);
FileChannel finChannel = fin.getChannel();
String outFile = "E:\\学习笔记包\\尚硅谷NIO\\src\\NIO_01Channels\\dd.txt";
FileOutputStream fou = new FileOutputStream(outFile);
FileChannel fouChannel = fou.getChannel();
//创建直接缓冲区
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while (true){
buffer.clear();
int r = finChannel.read(buffer);
if (r == -1){
break;
}
buffer.flip();
fouChannel.write(buffer);
}
}
static private final int start = 0;
static private final int size = 1024;
static public void main(String args[]) throws Exception {
RandomAccessFile raf = new RandomAccessFile("d:\\atguigu\\01.txt",
"rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE,
start, size);
mbb.put(0, (byte) 97);
mbb.put(1023, (byte) 122);
raf.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1vWfRXmV-1669130362022)(https://gitee.com/jerrygrj/img/raw/master/img/image-20211005100637487.png)]