image.png 传统IO也是IO操作,比如读写文件什么的,只是与传统IO操作方式不一样。
缓冲区像火车一样,在通道中来回传输数据
- 通道:表示打开到IO设备(比如文件)的连接
- 缓冲区:用来容纳数据
- ByteBuffer
- CharBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
2. 使用
缓冲区存取数据的两个核心方法:
put(): 存入数据到缓冲区
get(): 获取缓冲区的数据
缓冲区都继承Buffer缓冲区中的四个核心属性
- capacity:容量,缓冲区中最大存储数据的容量,一旦声明不能改变
- limit:界限,缓冲区中可以操作数据的大小,limit限制缓冲区的部分数据可以读写,超过limit的部分,不能读写。
- position:位置,缓冲区中正在操作的位置。position<=limit<=capacity
- mark:标记,表示记录当前position的位置,可以通过reset()恢复到mark的位置。mark和reset组合使用,可以重复读取某一部分的数据,先mark记录position,然后读完了以后,在reset将position重置到之前mark的位置。
@org.junit.Test
public void test1(){
//1.分配指定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
buffer.put("111".getBytes());
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
//切换到读取模式,后,positon和limit的值都会 变化
buffer.flip();
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
buffer.get();
System.out.println(buffer.position());
System.out.println(buffer.limit());
System.out.println(buffer.capacity());
}
buf.rewind() :可重复读数据,相当于重置。就可以重新读取数据了
buf.clear(): 清空缓冲去,但是缓冲区的数据依然存在,只是把position和limit这些重置成最开始的状态,但是以前的数据还在的。
3 直接缓冲区和非直接缓冲区
- 非直接缓冲区:通过allocate() 方法分配缓冲区,将缓冲区建立在JVM内存中
- 直接缓冲区(只有ByteBuffer支持):通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在操作系统的物理内存中。可以提高效率。
@org.junit.Test
public void test2(){
//分配直接缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
//判断当前是否是直接缓冲区
System.out.println(byteBuffer.isDirect());
}
4 通道
通道的主要实现类
- FileChannel
- SocketChannel
- ServerSocketChannel
- DatagramChannel
获取通道三种方式;
- 针对支持通道的类提供的getChannel方法
-
本地IO:
FileInputStream/FileOutputStream
RandomAccessFile -
网络IO:
Socket
ServerSocket
DatagramSocket
- 针对各个通道提供了静态方法Open(),jdk
1.7
FileChannel.open()
- Files工具类的newByteChannel
Files.newByteChannel()
5. 复制文件示例
复制文件示例(非直接缓冲区)
@org.junit.Test
public void test3(){
//获取流
FileInputStream fis = null;
FileOutputStream fos = null;
//获取通道
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
fis = new FileInputStream("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.jpg");
fos = new FileOutputStream("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\2.jpg");
inChannel = fis.getChannel();
outChannel = fos.getChannel();
//分配指定大小的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//将通道中的数据存入缓冲区中
while(inChannel.read(buffer) != -1){
//切换到读取缓冲区数据的模式
buffer.flip();
//将缓冲区的数据写到通道中
outChannel.write(buffer);
//清空缓冲区
buffer.clear();
}
}catch (IOException e){
e.printStackTrace();
}finally {
if (inChannel == null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outChannel == null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis == null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos == null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
使用直接缓冲区完成文件的复制(效率高)
@org.junit.Test
public void test4(){
try {
//StandardOpenOption.READ这里的读写要和下面的缓冲区的FileChannel.MapMode.READ_ONLY一致
FileChannel inChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
//StandardOpenOption.CREATE_NEW,创建新文件,文件已存在的话就报错,
//StandardOpenOption.CREATE,创建新文件,覆盖老文件
FileChannel outChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\3.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
//这里的FileChannel.MapMode.READ_ONLY要和上面的StandardOpenOption.READ一致
MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
byte[] dst = new byte[inMappedBuf.limit()];
inMappedBuf.get(dst);
outMappedBuf.put(dst);
inChannel.close();
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
复制文件(通道之间的数据传输(直接缓冲区))
@org.junit.Test
public void test5() throws IOException {
//StandardOpenOption.READ这里的读写要和下面的缓冲区的FileChannel.MapMode.READ_ONLY一致
FileChannel inChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
//StandardOpenOption.CREATE_NEW,创建新文件,文件已存在的话就报错,
//StandardOpenOption.CREATE,创建新文件,覆盖老文件
FileChannel outChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\5.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
inChannel.transferTo(0,inChannel.size(),outChannel);
//或者,效果和上一行一样
outChannel.transferFrom(inChannel,0,inChannel.size());
inChannel.close();
outChannel.close();
}
6. 分散与聚集
分散读取:
将通道数据分散到多个缓冲区中
聚集写入:
将多个缓冲区中的数据聚集到通道中
@org.junit.Test
public void test6() throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt", "rw");
FileChannel channel1 = randomAccessFile.getChannel();
ByteBuffer buffer1 = ByteBuffer.allocate(100);
ByteBuffer buffer2 = ByteBuffer.allocate(1024);
ByteBuffer[] byteBuffers = {buffer1,buffer2};
//分散读取,依次读取到buffer中
channel1.read(byteBuffers);
buffer1.flip();
buffer2.flip();
System.out.println(new String(buffer1.array(),0,buffer1.limit()));
System.out.println("------------");
System.out.println(new String(buffer2.array(),0,buffer2.limit()));
RandomAccessFile randomAccessFile2 = new RandomAccessFile("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\6.txt", "rw");
FileChannel channel2 = randomAccessFile2.getChannel();
//聚集写入
channel2.write(byteBuffers);
}
7. 阻塞式NIO(网络IO TCP)
@org.junit.Test
public void client() throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileChannel fileChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
while (fileChannel.read(buffer) != -1){
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
socketChannel.close();
fileChannel.close();
}
@org.junit.Test
public void server() throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(9898));
SocketChannel socketChannel = serverSocketChannel.accept();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while (socketChannel.read(byteBuffer) != -1){
System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
System.out.println("----");
byteBuffer.clear();
}
serverSocketChannel.close();
socketChannel.close();
}
8. 非阻塞式NIO(网络IO TCP)
image.png @org.junit.Test
public void client() throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
//切换成非阻塞模式
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
FileChannel fileChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
while (fileChannel.read(buffer) != -1){
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
}
socketChannel.close();
fileChannel.close();
}
@org.junit.Test
public void server() throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//切换成非阻塞模式
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(9898));
//获取选择器
Selector selector = Selector.open();
//将通道注册到选择器上,并且指定监听事件,可选多个
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
//轮询获取选择器上已经准备就绪的事件
while (selector.select()>0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
//获取准备就绪的事件
SelectionKey key = iterator.next();
System.out.println(key);
if (key.isAcceptable()){
SocketChannel socketChannel = serverSocketChannel.accept();
//切换非阻塞模式
socketChannel.configureBlocking(false);
//注册
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
while (channel.read(byteBuffer) != -1){
System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
System.out.println("----");
byteBuffer.clear();
}
channel.close();
}
iterator.remove();
}
}
serverSocketChannel.close();
}
9 非阻塞式NIO(网络IO UDP)
@org.junit.jupiter.api.Test
public void client2() throws IOException {
DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
String str = scanner.next();
buffer.put(str.getBytes());
buffer.flip();
datagramChannel.send(buffer,new InetSocketAddress("127.0.0.1",9898));
buffer.clear();
}
datagramChannel.close();
}
@org.junit.Test
public void server2() throws IOException {
DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.configureBlocking(false);
datagramChannel.bind(new InetSocketAddress(9898));
Selector selector = Selector.open();
datagramChannel.register(selector, SelectionKey.OP_READ);
while (selector.select()>0){
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()){
SelectionKey sk = keys.next();
if (sk.isReadable()){
ByteBuffer buffer = ByteBuffer.allocate(1024);
datagramChannel.receive(buffer);
buffer.flip();
System.out.println(new String(buffer.array(),0,buffer.limit()));
buffer.clear();
}
}
keys.remove();
}
}
网友评论