一、IO与NIO的区别
IO:面向流的、阻塞的
NIO:面向缓冲区的、非阻塞的、有选择器
二、NIO的核心:通道(Channel)和缓冲区(Buffer)
1、使用NIO:需要获取连接IO设备(如:文件、套接字)的通道和容纳数据的缓冲区;然后操作缓冲区,对数据进行处理。
Channel负责传输;Buffer负责存储;
三、缓冲区Buffer
1、定义:缓冲区主要是用来与通道交互的;从通道读取数据,向通道写入数据;缓冲区存储的是相同的基本数据类型,类似与数组;java.nio包下的类都是抽象类Buffer的子类;
ByteBuffer, ShortBuffer,IntBuffer, CharBuffer,FloatBuffer,DoubleBuffer,LongBuffer
2、基本属性:
容量 capacity:缓冲区的大小
限制 limit:从limit开始后的数据不可读写
位置 position:准备要读取或写入的索引
标记 mark 和重置 reset:mark是一个索引,通过 mark()方法指定一个特殊的position位置,之后可以通过调用 reset()方法 回到 该 position
注意: 0<=mark<=position<=limit<=capacity
3、主要方法:
static xxxBuffer allocate(int capacity): 创建xxx类型的Buffer
Buffer clear() :清空缓冲区
Buffer flip():将limit设置为当前位置,并将position置为0
int capacity() :返回Buffer容量大小
boolean hasRemaining() : 判断position和limit之间是否还有元素
int limit() :返回界限的位置
Buffer limit (int n) :将缓冲区界限设置为 n
Buffer mark() :在该位置设置标记
int position () :返回缓冲区当前位置
Buffer position () :将缓冲区当前位置设置为 n
int remaining () :返回position 和limit之间的元素个数
Buffer reset() :将缓冲区当前位置设置为之前mark标记处的位置
Buffer rewind() :将当前位置position设置为0,并清空设置的mark标记
获取Buffer中的数据:
get()
get (int[] arr)
get (int index) (不会移动position)
放入数据到Buffer中:
put()
put(int[] arr)
put(int index,int x)(不会移动position)
public class TestBuffer {
public static void main(String[]args) {
int[] array={1,2,3,4,5}; //创建一个数组
IntBuffer buffer=IntBuffer.allocate(5); //创建只能储存int类型的大小为5的缓冲区
System.out.println(buffer.position()); //缓冲区创建时position位置为0
System.out.println(buffer.limit()); //缓冲区创建时limit位置为5
buffer.put(array); //将数组里的值放入缓冲区
System.out.println(buffer.position()); //此时position位置为5
System.out.println(buffer.limit()); //此时limit仍然是5
buffer.rewind(); //将缓冲区位置重新设置为0
System.out.println(buffer.position());
while(buffer.hasRemaining()) //判断position和limit之间是否有元素
System.out.println(buffer.get());
//注意:在position和limit处于同一位置时,调用get()会抛出异常BufferUnderflowException
System.out.println("--"+buffer.get(1));
}
}
四、直接缓冲区和非直接缓冲区
1、通过allocate()方法建立非直接缓冲区,该缓冲区建立在JVM内存上
2、通过allocateDirect()方法建立直接缓冲区,该缓冲区建立在物理内存中
五、通道(Channel)
用于原节点与目标节点的连接;需要配合Buffer来进行数据传输;
主要的实现类: java.nio.channels.Channel 接口
FileChannel:用于读取、写入、映射、和操作文件的通道
SocketChannel :通过TCP来读取网络中数据的通道
ServerSocketChannel:通过监听新进来的TCP连接,对每一个新进来的连接都会创建一个ServerSocketChannel
DatagramChannel :通过UDP来读取网络中数据的通道
获取通道的方式:
1、java针对支持通道的类提供了getChannel () 方法
本地IO:FileInputStream 、 FileOutputStream 、RandomAcessFile
网络IO:Socket 、 ServerSocket 、 DatagramSocket
2、在JDK1.7中的NIO.2 针对各个方法提供了静态方法 open()
3、在JDK1.7中的NIO.2 的Files工具类的newByteChannel()
例如: 用FileInputStream 和 FileOutputStream 来创建只能输入或输出的通道
//创建输入输出流
FileInputStream in = new FileInputStream("./libs/6.jpg");
FileOutputStream out = new FileOutputStream("./libs/1.jpg");
//用getChannel()创建通道
FileChannel inchannel = in.getChannel();
FileChannel outchannel = out.getChannel();
//创建非直接缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024); //使用allocateDiret()方法来创建直接缓冲区
while (inchannel.read(buffer) != -1) { //将通道中的数据存入缓冲区
buffer.flip(); //将buffer切换成读取的模式
outchannel.write(buffer); //将缓冲区的数据写入通道
buffer.clear(); //清空缓冲区
使用直接缓冲区俩复制文件(内存映射,缓冲区直接建立在物理内存上)
//用FileChannel的静态方法来创建通道
FileChannel inChannel=FileChannel.open(Paths.get("./libs/1.txt"), StandardOpenOption.READ);
FileChannel outChannel=FileChannel.open(Paths.get("./libs/3.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ);
//创建通道大小的直接缓冲区
MappedByteBuffer inmapbuffer=inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
MappedByteBuffer outmapbuffer=outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());
byte[] array=new byte[inmapbuffer.limit()];
inmapbuffer.get(array);
outmapbuffer.put(array);
inChannel.close();
outChannel.close();
五、分散与聚集
分散读取:将通道中的数据存入多个缓冲区中
聚集写入:将多个缓冲区中的数据聚集到通道中
六、字符集CharSet
编码:字符串->字节数组
解码:字节数组 ->字符串
1、打印所有的支持的字符集:
Map<String,Charset> map=Charset.availableCharsets();
Set<String> set=map.keySet();
for(String name:set)
System.out.println(map.get(name));
2、编码:
Charset charset=Charset.forName("UTF-8"); //使用UTF-8字符集
CharsetEncoder encoder=charset.newEncoder(); //获取编码器
CharBuffer charBuffer= CharBuffer.allocate(100);
charBuffer.put("字符集");
charBuffer.flip();
ByteBuffer byteBuffer=encoder.encode(charBuffer); //编码
for (int i=0;i<byteBuffer.limit();i++){
System.out.println(byteBuffer.get()); //遍历并打印
}
3、解码:
Charset charset=Charset.forName("GBK"); //使用GBK字符集
CharsetDecoder decoder=charset.newDecoder(); //解码器
ByteBuffer byteBuffer=ByteBuffer.allocate(5);
byte[] arr={9,2,3,4,5};
byteBuffer.put(arr);
byteBuffer.flip(); //将limit设置为当前位置,然后将position置为0
CharBuffer charBuffer=decoder.decode(byteBuffer); //解码
for(int i=0;i<charBuffer.limit();i++)
System.out.println(charBuffer.get());