您的位置:首页 > 编程语言 > Java开发

Java高级编程-NIO

2017-02-05 17:14 429 查看
NIO支持缓冲区和通道,效率非常高,非常好用,代码演示如下

1.NIO的HelloWorld

package cn.zzu.wcj.nio;

import static org.junit.Assert.*;

import java.nio.ByteBuffer;

import org.junit.Test;
/*
* 一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
*
* 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
* ByteBuffer
* CharBuffer
* ShortBuffer
* IntBuffer
* LongBuffer
* FloatBuffer
* DoubleBuffer
*
* 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区
*
* 二、缓冲区存取数据的两个核心方法:
* put() : 存入数据到缓冲区中
* get() : 获取缓冲区中的数据
*
* 三、缓冲区中的四个核心属性:
* capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。
* limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
* position : 位置,表示缓冲区中正在操作数据的位置。
*
* mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置
*
* 0 <= mark <= position <= limit <= capacity
*
* 四、直接缓冲区与非直接缓冲区:
* 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
* 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
*/
public class BufferTest {

@Test
public void test1() {
String str="abcde"   ;
//1.分配一个指定大小的缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024)  ;
System.out.println("-----------------allocate()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//2.利用put()方法存入数据
buf.put(str.getBytes())  ;
System.out.println("-----------------put()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//3.切换到读取模式
buf.flip()   ;
System.out.println("-----------------flip()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//4.利用get()读取缓冲区中的数据
byte[] dst=new byte[buf.limit()]  ;
buf.get(dst,buf.position(),buf.limit())  ;
System.out.println("-----------------get()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//5.rewind()可重复读
buf.rewind()   ;
System.out.println("-----------------rewind()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
//6.clear():清空缓冲区,但是缓冲区中的数据依然存在,但是处于‘被遗忘’状态
buf.clear()   ;
System.out.println("-----------------clear()------------------");
System.out.println("position="+buf.position());
System.out.println("limit="+buf.limit());
System.out.println("capacity="+buf.capacity());
System.out.println((char)buf.get(0));

}

@Test
public void test2(){
String str="abcde"    ;
ByteBuffer buf=ByteBuffer.allocate(1024)   ;
byte[] source=str.getBytes()   ;
buf.put(source, 0,2)   ;
System.out.println("position="+buf.position());
buf.mark()  ;
System.out.println("-------------mark()@2--------------");
buf.put(source, 2, 2)   ;
System.out.println("position="+buf.position());
System.out.println("----------------reset()--------------------");
buf.reset() ;
System.out.println("position="+buf.position());
//       System.out.println("len="+source.length);
if(buf.hasRemaining()){
System.out.println(buf.remaining());
}

}

@Test
public void test3(){
ByteBuffer buf=ByteBuffer.allocateDirect(1024)  ;
assertSame(true, buf.isDirect());
}

}


2.创建Channel的几种方式

package cn.zzu.wcj.nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.junit.Test;
/*
* 一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
*
* 二、通道的主要实现类
*  java.nio.channels.Channel 接口:
*      |--FileChannel
*      |--SocketChannel
*      |--ServerSocketChannel
*      |--DatagramChannel
*
* 三、获取通道
* 1. Java 针对支持通道的类提供了 getChannel() 方法
*      本地 IO:
*      FileInputStream/FileOutputStream
*      RandomAccessFile
*
*      网络IO:
*      Socket
*      ServerSocket
*      DatagramSocket
*
* 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
* 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
*
* 四、通道之间的数据传输
* transferFrom()
* transferTo()
*
* 五、分散(Scatter)与聚集(Gather)
* 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
* 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
*
* 六、字符集:Charset
* 编码:字符串 -> 字节数组
* 解码:字节数组  -> 字符串
*
*/
public class ChannelTest {

@Test
public void testEncDec() throws Exception{
Charset charset = Charset.forName("GBK")   ;
CharsetEncoder encoder = charset.newEncoder();
CharsetDecoder decoder = charset.newDecoder()  ;
CharBuffer charBuf=CharBuffer.allocate(1024)   ;
charBuf.put("HelloWorld,世界你好!");
charBuf.flip()  ;
ByteBuffer byteBuffer = encoder.encode(charBuf);
for(int x=0;x<byteBuffer.limit();x++){
System.out.print(byteBuffer.get()+"、");
}
System.out.println();
byteBuffer.flip()   ;
CharBuffer charBuffer = decoder.decode(byteBuffer);
System.out.println(charBuffer.toString());
System.out.println("-----------------------------");
//          Charset charset2 = Charset.forName("UTF-8")   ;
Charset charset2 = Charset.forName("GBK")   ;
byteBuffer.flip()  ;
CharBuffer charBuffer2 = charset2.decode(byteBuffer)  ;
System.out.println(charBuffer2.toString());
}

@Test
public void testCharset(){
Map<String,Charset> charsets = Charset.availableCharsets()  ;
Set<Entry<String,Charset>> set = charsets.entrySet()  ;
for(Entry<String,Charset> e : set ){
System.out.println(e.getKey()+"="+e.getValue());
}
}

@Test
public void testScatterAndGather()throws Exception{
RandomAccessFile raf=new RandomAccessFile("1.txt", "rw")  ;
FileChannel inChannel = raf.getChannel()  ;

ByteBuffer buf=ByteBuffer.allocate(100)   ;
ByteBuffer buf2=ByteBuffer.allocate(1024)   ;
ByteBuffer bufs[]={buf,buf2}   ;

inChannel.read(bufs)   ;
for(ByteBuffer byteBuf : bufs){
byteBuf.flip()  ;   //切换到读取模式
}
System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
System.out.println("------------------------------------------------");
System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));

RandomAccessFile raf2=new RandomAccessFile("2.txt", "rw")  ;
FileChannel outChannel = raf2.getChannel()  ;
outChannel.write(bufs)  ;

outChannel.close();
inChannel.close();
raf.close();
raf2.close();

}

@Test
public void testChannel() throws Exception{
long start=System.currentTimeMillis()   ;
FileInputStream fis=null ;
FileOutputStream fos=null ;

fis=new FileInputStream("1.jpg");
fos=new FileOutputStream("2.jpg") ;

//1.获取通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();

//2.准备缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024)   ;

//3.读写
while(inChannel.read(buf) != -1){  //读
buf.flip()   ;      //切换到读取模式
outChannel.write(buf)  ;   //写
buf.clear()   ;           //清空缓冲区,准备再次读取
}

//4.关闭流
outChannel.close();
inChannel.close();
fos.close();
fis.close();

long end=System.currentTimeMillis()   ;
System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒");

}

@Test
public void testDirectChannel()throws Exception{
long start=System.currentTimeMillis()   ;
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)       ;
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE )    ;
//内存映射文件
MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size())   ;
MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size())   ;

//直接对缓冲区中的数据进行读写操作
byte[] temp=new byte[1024]   ;
inMappedBuf.get(temp)   ;
outMappedBuf.put(temp)    ;

inChannel.close();
outChannel.close();

long end=System.currentTimeMillis()   ;
System.out.println("拷贝任务耗时:"+(end-start)+" 毫秒");

}

@Test
public void testTransform()throws Exception{
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)  ;
FileChannel outChannel = FileChannel.open(Paths.get("4.jpg"), StandardOpenOption.READ,
StandardOpenOption.WRITE,
StandardOpenOption.CREATE )    ;
//inChannel.transferTo(0, inChannel.size(), outChannel)   ;
outChannel.transferFrom(inChannel, 0, inChannel.size())   ;
inChannel.close();
outChannel.close();
}

}


3.阻塞式NIO

package cn.zzu.wcj.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import org.junit.Test;
/*
* 一、使用 NIO 完成网络通信的三个核心:
*
* 1. 通道(Channel):负责连接
*
*     java.nio.channels.Channel 接口:
*          |--SelectableChannel
*              |--SocketChannel
*              |--ServerSocketChannel
*              |--DatagramChannel
*
*              |--Pipe.SinkChannel
*              |--Pipe.SourceChannel
*
* 2. 缓冲区(Buffer):负责数据的存取
*
* 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
*
*/
public class BlockingNIOTest {

@Test
public void testClient() throws Exception {
//1.创建通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989));
//2.准备缓冲区
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)  ;
ByteBuffer buf=ByteBuffer.allocate(1024)   ;
//3.读取本地文件,发送到客户端
while(inChannel.read(buf) != -1){
buf.flip()  ;
socketChannel.write(buf)   ;
buf.clear()  ;
}
//4.关闭通道
inChannel.close();
socketChannel.close();

}

@Test
public void testServer() throws Exception{
//1.创建通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()  ;
//2.绑定端口号
serverSocketChannel.bind(new InetSocketAddress(8989))   ;
//3.准备Channel和Buffer
FileChannel outChannel=FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.CREATE)  ;
ByteBuffer buf=ByteBuffer.allocate(1024)   ;
//4.接收客户端请求
SocketChannel socketChannel = serverSocketChannel.accept()  ;
while(socketChannel.read(buf) != -1){
buf.flip()    ;
outChannel.write(buf)   ;
buf.clear()   ;
}
//5.关闭流
socketChannel.close();
outChannel.close();
serverSocketChannel.close();
}

}


package cn.zzu.wcj.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import org.junit.Test;

public class BlockingNIOTestPlus {

@Test
public void testClent() throws Exception{
SocketChannel client = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999))   ;
FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ)   ;
ByteBuffer buf=ByteBuffer.allocate(1024)     ;
while(inChannel.read(buf) != -1){
buf.flip()    ;
client.write(buf)    ;
buf.clear()    ;
}
client.shutdownOutput()    ;    //结束输出

//接收服务器反馈
while(client.read(buf) != -1){
buf.flip()    ;
System.out.println(new String(buf.array(),0,buf.limit()));
buf.clear()   ;
}

inChannel.close()  ;
client.close()  ;

}

@Test
public void testServer() throws Exception{
ServerSocketChannel server = ServerSocketChannel.open()   ;
server.bind(new InetSocketAddress(9999))   ;
FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"),
StandardOpenOption.WRITE,
StandardOpenOption.CREATE   )   ;
ByteBuffer buf=ByteBuffer.allocate(1024)  ;
SocketChannel client = server.accept();
while(client.read(buf)!=-1){
buf.flip()    ;
outChannel.write(buf)    ;
buf.clear()   ;
}
//发送反馈给客户端
buf.put("乖儿子,爸爸接收到黄图啦!!!".getBytes())  ;
buf.flip()   ;
client.write(buf)   ;

client.close();
outChannel.close();
server.close();
}

}


4.非阻塞式NIO

package cn.zzu.wcj.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

import org.junit.Test;

/*
* 一、使用 NIO 完成网络通信的三个核心:
*
* 1. 通道(Channel):负责连接
*
*     java.nio.channels.Channel 接口:
*          |--SelectableChannel
*              |--SocketChannel
*              |--ServerSocketChannel
*              |--DatagramChannel
*
*              |--Pipe.SinkChannel
*              |--Pipe.SourceChannel
*
* 2. 缓冲区(Buffer):负责数据的存取
*
* 3. 选择器(Selector):是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况
*
*/
public class TestNonBlockingChannel {

@Test
public void testClient() throws Exception{
//创建客户端通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",6666))   ;
//配置为非阻塞式NIO
sChannel.configureBlocking(false)   ;
//准备缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024)   ;
Scanner sc=new Scanner(System.in)   ;
while(sc.hasNext()){
buffer.put((new Date().toString()+" : "+sc.next()).getBytes())    ;
buffer.flip()  ;   //切换成读模式
//向服务器端发送消息
sChannel.write(buffer)  ;
buffer.clear()    ;
}
//关闭通道
sChannel.close();

}

@Test
public void testServer() throws Exception{
//1.创建服务器端通道
ServerSocketChannel ssChannel = ServerSocketChannel.open()   ;
//2.绑定端口号
ssChannel.bind(new InetSocketAddress(6666))    ;
//3.设置非阻塞模式
ssChannel.configureBlocking(false)    ;
//4.获取选择器
Selector selector = Selector.open()  ;
//5.将选择器注册到通道上
ssChannel.register(selector,SelectionKey.OP_ACCEPT)  ;
//6.以轮训的方式获取选择器上已经准备就绪的事件
while(selector.select() > 0){
//7.接收全部选择键
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
//8.接收选择键
SelectionKey selectionKey = iterator.next();
//9.根据键值判断具体是什么事件
if(selectionKey.isAcceptable()){
//10.接收就绪,获取客户端连接
SocketChannel sChannel = ssChannel.accept()  ;
//11.切换到非阻塞模式
sChannel.configureBlocking(false)   ;
//12.将通道注册到选择器上
sChannel.register(selector, SelectionKey.OP_READ)   ;
}else if(selectionKey.isReadable()){
//13.获取读状态的通道
SocketChannel sChannel=(SocketChannel) selectionKey.channel()   ;
ByteBuffer dst=ByteBuffer.allocate(1024)  ;
//14.读取数据
Integer length=0  ;
while( (length=sChannel.read(dst))> 0){
dst.flip()    ;
System.out.println(new String(dst.array(),0,length));
dst.clear()   ;
}

}
//15.取消选择键
iterator.remove();
}

}
}

}


package cn.zzu.wcj.nio;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;

import org.junit.Test;

public class TestNonBlockingNIOPlus {

@Test
public void testSend() throws Exception{
DatagramChannel dc=DatagramChannel.open()   ;
dc.configureBlocking(false)    ;
ByteBuffer buf=ByteBuffer.allocate(1024)    ;
Scanner sc=new Scanner(System.in)  ;
while(sc.hasNext()){
String msg=sc.next()    ;
buf.put((new Date().toString()+" : "+msg).getBytes())  ;
buf.flip()   ;
dc.send(buf, new InetSocketAddress("127.0.0.1",9999))  ;
buf.clear()    ;
}
dc.close();
}

@Test
public void testReceive() throws Exception{
DatagramChannel dc = DatagramChannel.open()  ;
dc.configureBlocking(false)    ;
dc.bind(new InetSocketAddress(9999))   ;
Selector selector = Selector.open()  ;
dc.register(selector, SelectionKey.OP_READ)   ;
while(selector.select()>0){
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator()  ;
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
ByteBuffer buf=ByteBuffer.allocate(1024)  ;
dc.receive(buf)   ;
buf.flip()   ;
System.out.println(new String(buf.array(),0,buf.limit()));
buf.clear()  ;
}
}
iterator.remove();
}
}

}


5.管道

package cn.zzu.wcj.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;
import java.nio.channels.Pipe.SinkChannel;
import java.nio.channels.Pipe.SourceChannel;

import org.junit.Test;

public class PipeTest {

@Test
public void testPipe() throws Exception{
//1.获取管道
Pipe pipe = Pipe.open()  ;
SinkChannel sinkChannel = pipe.sink();
//2.准备缓冲区
ByteBuffer buf=ByteBuffer.allocate(1024)   ;
buf.put("单向管道发送数据".getBytes())   ;
buf.flip()   ;
//4.发送数据
sinkChannel.write(buf)   ;
buf.clear()    ;
//5.接收数据
SourceChannel sourceChannel = pipe.source()  ;
sourceChannel.read(buf)   ;
buf.flip()  ;
System.out.println(new String(buf.array(),0,buf.limit()));
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java