摘要
NIO的通道类似于流,但有些区别如下:
- 通道可以同时进行读写而流只能读或者只能写
- 通道可以实现异步读写数据。
- 通道可以从缓种读数据,也可以写数据到缓冲:
- BIO中的stream是单向的,例如 FilelnputStream对象只能进行读取数据的操作,NIO中的通道(Channel)是双向的,可以读操作,也可以写操作。
- Channel在NIO中是一个接口public interface Channel extends Closeable0常用的 Channel类有:FileChanng,DatagramChannel、ServerSocketChannel 和SocketChannel。
- FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和 SocketChannel用于TCP的数据读写。
主要用来对本地文件进行IO操作,常见的方法有
- 1) public int read(ByteBuffer dst从通道读取数据并放到缓净中
- 2) public int write(ByteBuffer src),把缓冲区的数据写到通道中
- 3) public long transferFrom(ReadableByteChannel src, long position, long count),目标通道中复制数据到当前通道
- 4) public long transferTo(long position, long count, WritableByteChannel target),把数据从当前通道复制给目标通道
package com.zhuangxiaoyan.nio.channel;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Classname NIOFileChannel
* @Description TODO
* @Date 2021/10/28 23:39
* @Created by xjl
*/
public class NIOFileChannel {
public static void main(String[] args) throws IOException {
String str="hi 庄小焱";
//创建一个输出流->channel
String filepath="D:\\softwaresavfile\\Github\\JAVA_NIO\\NIO\\src\\main\\resources\\test";
FileOutputStream fileOutputStream = new FileOutputStream(filepath);
//通过FileOutputStream获取一个
// FileChannel的真实的类型是FileChannelImpl 类型
FileChannel fileChannel = fileOutputStream.getChannel();
//创建一个buffer
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
//数据放入到buffer
byteBuffer.put(str.getBytes());
//对bytebuffer 进行的flip
byteBuffer.flip();
//将bytebuffer的数据写入到filechannel
fileChannel.write(byteBuffer);
//关闭
fileOutputStream.close();
}
}
将文件数据读取出来
package com.zhuangxiaoyan.nio.channel;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Classname NIOFileChannel2
* @Description 将文件的数据读取到控制台
* @Date 2021/10/30 14:29
* @Created by xjl
*/
public class NIOFileChannel2 {
public static void main(String[] args) throws IOException {
String filepath = "D:\\softwaresavfile\\Github\\JAVA_NIO\\NIO\\src\\main\\resources\\test.txt";
//创建文件的输入流
File file = new File(filepath);
FileInputStream fileInputStream = new FileInputStream(file);
//通过fileinputStream 获取filechannel-->>实际类型的 FilechannelImpl
FileChannel fileChannel = fileInputStream.getChannel();
//创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
//将通道的数据读入到buffer
fileChannel.read(byteBuffer);
//将bytebuffer中的字节数据转为string
System.out.println(new String(byteBuffer.array()));
}
}
利用Buffer将文件读出在将数据写入到文件
package com.zhuangxiaoyan.nio.channel;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Classname NIOFileChannel3
* @Description TODO
* @Date 2021/10/30 14:39
* @Created by xjl
*/
public class NIOFileChannel3 {
public static void main(String[] args) throws IOException {
File file = new File("D:\\softwaresavfile\\Github\\JAVA_NIO\\NIO\\src\\main\\resources\\1.txt");
FileInputStream fileInputStream = new FileInputStream(file);
FileChannel fileChannel1 = fileInputStream.getChannel();
File file1 = new File("D:\\softwaresavfile\\Github\\JAVA_NIO\\NIO\\src\\main\\resources\\2.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file1);
FileChannel fileChanne2 = fileOutputStream.getChannel();
//创建一个bytebuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while (true) {
//这个有一个复位的操作 清空buffer
byteBuffer.clear();
int read = fileChannel1.read(byteBuffer);
if (read == -1) {
//表示读取完成
break;
}
//将buffer中的数据写入到filechannel2---2.txt
byteBuffer.flip();
fileChanne2.write(byteBuffer);
}
//关闭相关的流
fileInputStream.close();
fileOutputStream.close();
}
}
利用Transform完成文件的拷贝
package com.zhuangxiaoyan.nio.channel;
import java.io.*;
import java.nio.channels.FileChannel;
/**
* @Classname NIOFileChannel4
* @Description TODO
* @Date 2021/10/30 14:55
* @Created by xjl
*/
public class NIOFileChannel4 {
public static void main(String[] args) throws IOException {
File file=new File("D:\\softwaresavfile\\Github\\JAVA_NIO\\NIO\\src\\main\\resources\\微信图片_20210422220149.jpg");
FileInputStream fileInputStream=new FileInputStream(file);
File file1=new File("D:\\softwaresavfile\\Github\\JAVA_NIO\\NIO\\src\\main\\resources\\new copy.jpg");
FileOutputStream fileOutputStream = new FileOutputStream(file1);
//获取各个流对应的filechannel
FileChannel source=fileInputStream.getChannel();
FileChannel dastch=fileOutputStream.getChannel();
//使用的transferform完成拷贝
dastch.transferFrom(source,0,source.size());
//关闭相关的流
source.close();
dastch.close();
fileInputStream.close();
fileOutputStream.close();
}
}
ReadOnlyBuffer函数
package com.zhuangxiaoyan.nio.channel;
import java.nio.ByteBuffer;
/**
* @Classname NIOFileChannel5
* @Description TODO
* @Date 2021/10/30 15:05
* @Created by xjl
*/
public class NIOFileChannel5 {
public static void main(String[] args) {
//创建一个buffer
ByteBuffer buffer = ByteBuffer.allocate(64);
for (int i = 0; i < 64; i++) {
buffer.put((byte) i);
}
//读取
buffer.flip();
// 得到一个只读取的buffer
ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
System.out.println(readOnlyBuffer.getClass());
while (readOnlyBuffer.hasRemaining()) {
System.out.println(readOnlyBuffer.get());
}
//报错 将不允许写入数据
readOnlyBuffer.put((byte)100);
}
}
MappedByteBuffer函数
package com.zhuangxiaoyan.nio.channel;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Classname NIOFileChannel6
* @Description mappedbyteBuffer 可以让文件直接修改(堆外内存) 操作系统不需要拷贝
* @Date 2021/10/30 15:11
* @Created by xjl
*/
public class NIOFileChannel6 {
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\softwaresavfile\\Github\\JAVA_NIO\\NIO\\src\\main\\resources\\1.txt", "rw");
//获取对应的文件通道
FileChannel channel = randomAccessFile.getChannel();
/**
参数l:Filechannel.MapMode.READ_WRITE使用的读写模式
参数2:0 :可以直接修改的起始位置
参数3:5:是映射到内存的大小 不是包含5的相当于是【0,5)
*/
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
map.put(0, (byte) 'S');
map.put(3, (byte) 'M');
//关闭文件流
randomAccessFile.close();
//提示
System.out.println("修改成功");
}
}
Buffer gathering 和 scatter函数
package com.zhuangxiaoyan.nio.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
/**
* @Classname NIOFileChannel7
* @Description Scattering :buffer的分散和聚合 将数据写入到buffer时候 可以采用buffer数组 然后在依次写入
* Gathering :从buffer读取数据的时候 可以采用buffer数组 然后在依次读取
* @Date 2021/10/30 15:23
* @Created by xjl
*/
public class NIOFileChannel7 {
public static void main(String[] args) throws IOException {
//使用serversockerChannel 和SocketChannel网络
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
//绑定端口socket 并启动
serverSocketChannel.socket().bind(inetSocketAddress);
//创建buffer数组
ByteBuffer[] byteBuffers = new ByteBuffer[2];
byteBuffers[0] = ByteBuffer.allocate(5);
byteBuffers[1] = ByteBuffer.allocate(3);
//等待客户通知
SocketChannel socketChannel = serverSocketChannel.accept();
int messagelen = 8;
//循环读取
while (true) {
int byteRead = 0;
while (byteRead < messagelen) {
long num = socketChannel.read(byteBuffers);
byteRead += num;
System.out.println("byteRead=" + byteRead);
//使用的流打印 当前的buffer 和position和limit
Arrays.asList(byteBuffers).stream().map(buffer -> "position" + buffer.position() + ", limit-" + buffer.limit()).forEach(System.out::println);
}
//将所有的buffer进行flip
Arrays.asList(byteBuffers).forEach(byteBuffer -> byteBuffer.flip());
//将数据的读取到客户端
long bytewrite = 0;
while (bytewrite < messagelen) {
long num = socketChannel.write(byteBuffers);
bytewrite += num;
}
//将所有的buffer 进行分析clear
Arrays.asList(byteBuffers).forEach(byteBuffer -> byteBuffer.clear());
System.out.println("byteRead" + byteRead + "byteWrite" + bytewrite);
}
}
}
Buffer和 Channel的区别
- 1) ByteBuffer 支持类型化的put和 get, put放入的是什么数据类型,get就应该使用相应的数据类型来取出,否则可能有BufferUnderflowException异常。
- 2)可以将一个普通Buffer转成只读Buffer
- 3)NIO还提供了MappedByteBuffer,可以让文件直接在内存(堆外的内存)中进行修改,而如何同步到文件由NIO 来完成.
- 4)前面我们讲的读写操作,都是通过一个Buffer完成的,NIO还支持通过多个Buffer(即 Buffer数组)完成读写操作,即 Scattering 和 Gatering