Buffer工作原理:capacity - 缓冲区容量,缓冲区满了以后,必须清空后才能继续写入数据。position - 写数据到缓冲区时,position代表写入数据的当前位置,初始值为0,当一个字节数据写入到缓冲区后,position会移动到写一个可插入数据的缓冲区单元;当从缓冲区中读取数据时,position表示读取数据的当前位置,调用flip方法切换到读模式时,position会被重置为零。limit - 写数据时表示最多可以写入多少个数据;读数据时,表示还有多少个数据可读取。
//IntBufferDemo.java
import java.nio.IntBuffer;
public class IntBufferDemo {
public static void main(String[] args) {
//每个Buffer类都有一个allocate方法
IntBuffer buffer = IntBuffer.allocate(8);
for (int i = 0; i < buffer.capacity(); i++) {
int j = 2 * (i+1);
//可以使用通道或者put方法写入数据到缓冲区
buffer.put(j);
}
//clear方法清空缓冲区,compact方法只清除已读取过的数据
//mark方法可以标记缓冲区中的一个特定position,然后可以通过reset恢复到这个位置
//rewind方法是把position设置为0,可以重读缓冲区中的数据
//flip方法把缓冲区从写模式切换到读模式,把limit设置成position的当前值,然后把position清零
buffer.flip();
while(buffer.hasRemaining()) {
//从缓冲区读取数据,可以使用通道读取或者使用get方法
System.out.println(buffer.get() + ",");
}
}
}
//BufferDemo1.java
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class BufferDemo1 {
public static void main(String[] args) throws Exception {
RandomAccessFile file = new RandomAccessFile("01.txt", "rw");
//文件通道从缓冲区读取数据
FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
while(bytesRead != -1){
buffer.flip();
while(buffer.hasRemaining()){
System.out.print((char)buffer.get());
}
buffer.clear();
bytesRead = channel.read(buffer);
}
file.close();
}
}
//BufferDemo2.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class BufferDemo2 {
public static void main(String[] args) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(10);
for(int i=0;i<buffer.capacity();i++){
buffer.put((byte)i);
}
//创建子缓冲区
buffer.position(3);
buffer.limit(7);
ByteBuffer sliceBuffer = buffer.slice();
for(int i=0;i<sliceBuffer.capacity();i++){
byte b = sliceBuffer.get(i);
b *= 10;
sliceBuffer.put(i, b);
}
buffer.position(0);
buffer.limit(buffer.capacity());
while(buffer.hasRemaining()){
System.out.println(buffer.get());
}
//创建只读缓冲区,只读缓冲区随着原缓冲区发生变化
IntBuffer intBuffer = IntBuffer.allocate(10);
for(int i=0;i<intBuffer.capacity();i++){
intBuffer.put(i);
}
IntBuffer readOnlyBuffer = intBuffer.asReadOnlyBuffer();
for(int i=0;i<intBuffer.capacity();i++){
int b = intBuffer.get(i);
b *= 10;
intBuffer.put(i, b);
}
readOnlyBuffer.position(0);
readOnlyBuffer.limit(intBuffer.capacity());
while(readOnlyBuffer.hasRemaining()){
System.out.println(readOnlyBuffer.get());
}
//直接缓冲区
//ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
//内存映射文件I/O
RandomAccessFile file = new RandomAccessFile("02.txt", "rw");
FileChannel fileChannel = file.getChannel();
MappedByteBuffer mbb =
fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
mbb.put(0, (byte)97);
mbb.put(1023, (byte)122);
file.close();
}
}
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NioBufferInstance {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
//获取流的位置
service.submit(() -> {
IntBuffer buffer = IntBuffer.allocate(10);
System.out.println("The capacity of ints buffer:" + buffer.capacity());
Arrays.asList(0, 1, 2, 3, 4)
.forEach(p -> buffer.put(new SecureRandom().nextInt(20)));
System.out.println("before flip limit:" + buffer.limit());
buffer.flip();
System.out.println("after flip limit:" + buffer.limit());
while(buffer.hasRemaining()){
System.out.println("position:" + buffer.position());
System.out.println("limit:" + buffer.limit());
System.out.println("capacity:" + buffer.capacity());
System.out.println(buffer.get());
}
});
//文件通道读写
service.submit(() -> {
try {
FileOutputStream os = new FileOutputStream("output.txt");
FileInputStream is = new FileInputStream("input.txt");
FileChannel inputChannel = is.getChannel();
FileChannel outputChannel = os.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(4);
while(true){
buffer.clear();
int read = inputChannel.read(buffer);
System.out.println("read:" + read);
if(read == -1){
break;
}
buffer.flip();
outputChannel.write(buffer);
}
inputChannel.close();
outputChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
});
//随机存取,使用堆外内存
service.submit(() -> {
try {
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
FileChannel channel = file.getChannel();
MappedByteBuffer buffer =
channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
buffer.put(0, (byte) 'a');
buffer.put(3, (byte) 'b');
file.close();
} catch (IOException e) {
e.printStackTrace();
}
});
//allocate wrap HeapByteBuffer 在JVM堆中创建缓冲区
//directBuffer DirectByteBuffer 在堆外创建缓冲区,直接与IO设备交互,零拷贝
service.submit(() -> {
ByteBuffer buffer = ByteBuffer.allocate(64);
buffer.putInt(15);
buffer.putLong(500000000L);
buffer.putChar('你');
buffer.putShort((short) 2);
buffer.putChar('我');
buffer.flip();
System.out.println(buffer.getInt() + ","
+ buffer.getLong() + "," + buffer.getChar()
+ ',' + buffer.getShort() + "," + buffer.getChar());
//重置position、limit后重新赋值
buffer.clear();
for(int i=0; i<buffer.capacity(); i++){
buffer.put((byte) i);
}
//指定position和limit
buffer.position(2);
buffer.limit(6);
//slice创建一个新的ByteBuffer,新的ByteBuffer操作的结果影响原ByteBuffer
ByteBuffer sliceBuffer = buffer.slice();
for(int i=0;i<sliceBuffer.capacity();i++){
byte b = sliceBuffer.get(i);
b *= 2;
sliceBuffer.put(i, b);
}
buffer.position(0);
buffer.limit(buffer.capacity());
while(buffer.hasRemaining()){
System.out.print(buffer.get() + "\t");
}
System.out.println();
buffer.clear();
System.out.println(buffer.getClass());
ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
System.out.println(readOnlyBuffer.getClass());
});
//通道写
service.submit(() -> {
try {
FileOutputStream fos = new FileOutputStream("NioChannelDemo.txt");
FileChannel channel = fos.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(512);
byte[] message = "Hello world, welcome!".getBytes();
for(byte b:message){
buffer.put(b);
}
buffer.flip();
channel.write(buffer);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
});
//通道读
service.submit(() -> {
try {
FileInputStream fis = new FileInputStream("NioChannelDemo.txt");
FileChannel fileChannel = fis.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
fileChannel.read(byteBuffer);
byteBuffer.flip();
while(byteBuffer.remaining() > 0){
byte b = byteBuffer.get();
System.out.println("Character:" + (char)b);
}
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
SocketChannel是用来连接到TCP网络套接字的通道,主要用来处理网络IO通道,基于TCP传输,实现了可选择通道,可以被多路复用。对于已经存在的套接字不能创建SocketChannel。没有进行连接的SocketChannel执行IO操作时会抛异常。SocketChannel的open方法创建的通道没有进行网络级联,需要使用connect方法连接到指定地址。SocketChannel支持异步关闭、设定参数。ServerSocketChannel是一个基于通道的socket监听器,调用accept方法返回SocketChannel。
//SocketChannelDemo.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class SocketChannelDemo {
public static void main(String[] args) throws IOException {
//创建SocketChannel
SocketChannel socketChannel =
SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(16);
socketChannel.read(buffer);
socketChannel.close();
System.out.println("read over");
}
}
//ServerSocketChannelDemo.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class ServerSocketChannelDemo {
public static void main(String[] args) throws IOException {
int port = 8888;
//创建Buffer
ByteBuffer buffer = ByteBuffer.wrap("hello world".getBytes());
//打开ServerSocketChannel,绑定端口号
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(port));
//设置非阻塞模式
ssc.configureBlocking(false);
//监听新连接
for(;;){
System.out.println("Waiting for connecting...");
SocketChannel sc = ssc.accept();
if(sc == null){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("Incoming connection from:"
+ sc.socket().getRemoteSocketAddress());
buffer.rewind();
sc.write(buffer);
sc.close();
}
}
}
}
//PipeDemo.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;
//管道是两个线程之间的单项数据连接,数据会被写到sink通道,从source通道读取
public class PipeDemo {
public static void main(String[] args) throws IOException {
//获取通道
Pipe pipe = Pipe.open();
Pipe.SinkChannel sinkChannel = pipe.sink();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("hello".getBytes());
buffer.flip();
sinkChannel.write(buffer);
Pipe.SourceChannel sourceChannel = pipe.source();
buffer.flip();
int len = sourceChannel.read(buffer);
System.out.println(new String(buffer.array(), 0,len));
sourceChannel.close();
sinkChannel.close();
}
}
//DatagramChannelDemo.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
//DatagramChannel是无连接的
public class DatagramChannelDemo {
public static void main(String[] args) throws IOException {
System.out.println("程序启动...");
}
private void sendDatagram() throws IOException {
DatagramChannel sendChannel = DatagramChannel.open();
InetSocketAddress sendAddress = new InetSocketAddress("localhost", 9999);
while(true){
ByteBuffer buffer = ByteBuffer.wrap("Hello World".getBytes());
sendChannel.send(buffer, sendAddress);
System.out.println("已经发送成功");
}
}
private void receiveDatagram() throws IOException {
DatagramChannel receiveChannel = DatagramChannel.open();
InetSocketAddress receiveAddress = new InetSocketAddress(9999);
receiveChannel.bind(receiveAddress);
ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
while(true){
receiveBuffer.clear();
SocketAddress socketAddress = receiveChannel.receive(receiveBuffer);
receiveBuffer.flip();
System.out.println(socketAddress.toString());
System.out.println(receiveBuffer);
}
}
}
//FileChannelDemo1.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* 通过FileChannel将数据写入缓冲区
*/
public class FileChannelDemo1 {
public static void main(String[] args) throws IOException {
//创建FileChannel
RandomAccessFile file =
new RandomAccessFile("C:\\Mermaid使用.md", "rw");
FileChannel channel = file.getChannel();
//创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取数据到buffer中
int bytesRead = channel.read(buffer);
while(bytesRead != -1){
System.out.println("读取了:" + bytesRead);
//读写模式转换
buffer.flip();
while(buffer.hasRemaining()){
System.out.println((char)buffer.get());
}
//清除缓冲区内容
buffer.clear();
bytesRead = channel.read(buffer);
}
file.close();
System.out.println("程序结束");
}
}
//FileChannelDemo2.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* 通过FileChannel从缓冲区读取数据
*/
public class FileChannelDemo2 {
public static void main(String[] args) throws IOException {
RandomAccessFile file = new RandomAccessFile("C:\\LICCD.txt", "rw");
FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
String data = "Test 12345";
buffer.clear();
buffer.put(data.getBytes());
buffer.flip();
//需要循环写入
while(buffer.hasRemaining()){
channel.write(buffer);
}
channel.close();
}
}
//FileChannelDemo3.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
/**
* 通道之间数据传输
*/
public class FileChannelDemo3 {
public static void main(String[] args) throws IOException {
RandomAccessFile aFile = new RandomAccessFile("C:\\01.txt", "rw");
RandomAccessFile bFile = new RandomAccessFile("C:\\02.txt", "rw");
FileChannel fromChannel = aFile.getChannel();
FileChannel toChannel = bFile.getChannel();
long position = 0;
long size = fromChannel.size();
//position() - 返回此通道文件位置, position(long) - 设置此通道的文件长度
//transferFrom - 把数据从源通道传输到FileChannel中;
//transferTo - 将数据从FileChannel传输到其他的FileChannel中
toChannel.transferFrom(fromChannel, position, size);
//fromChannel.transferTo(position, size, toChannel);
aFile.close();
bFile.close();
}
}
//FileLockDemo.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
//文件锁的使用
public class FileLockDemo {
public static void main(String[] args) throws IOException {
String input = "Hello World!";
ByteBuffer buffer = ByteBuffer.wrap(input.getBytes());
String filePath = "01.txt";
Path path = Paths.get(filePath);
FileChannel channel = FileChannel.open(path, StandardOpenOption.WRITE,
StandardOpenOption.APPEND);
channel.position(channel.size() - 1);
FileLock lock = channel.lock();
System.out.println("是否共享锁:" + lock.isShared());
channel.write(buffer);
channel.close();
FileReader fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = bufferedReader.readLine();
System.out.println("读取出内容:");
while(line != null){
System.out.println(line);
line = bufferedReader.readLine();
}
fileReader.close();
bufferedReader.close();
}
}
//AsyncFileChannelDemo.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
/**
* 文件通道异步读取
*/
public class AsyncFileChannelDemo {
public static void main(String[] args) throws IOException {
Path path = Paths.get("01.txt");
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 使用Future处理结果集
// Future future = fileChannel.read(buffer, 0);
// //判断是否完成
// while(!future.isDone());
// buffer.flip();
// while(buffer.hasRemaining()){
// System.out.print((char)buffer.get());
// }
// buffer.clear();
//使用CompletionHandler接口处理结果
fileChannel.read(buffer, 0, buffer,
new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
byte[] data = new byte[attachment.limit()];
attachment.get(data);
System.out.print(new String(data));
attachment.clear();
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println(exc.getMessage());
}
});
}
}
Selector选择器也叫做多路复用器,用于检查一个或多个通道的状态是否可读写。使用Selector的好处在于使用更少的线程处理通道,避免多线程上下文切换带来的开销。不是所有的Channel都可以被Selector复用的,继承可选择通道SelectableChannel就可以复用。一个通道可以被注册到多个选择器上,而每个选择器只能被注册一次。
//SelectorDemo.java
public class SelectorDemo {
public static void main(String[] args) throws IOException {
//与Selector多路复用器一起使用时,Channel必须是非阻塞的
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(8000));
ssc.register(selector, SelectionKey.OP_ACCEPT);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
System.out.println("accept");
} else if (key.isConnectable()) {
System.out.println("connect");
} else if (key.isReadable()) {
System.out.println("read");
} else if (key.isWritable()) {
System.out.println("write");
}
iterator.remove();
}
}
}
//SelectorTest.java
public class SelectorTest {
@Test
public void send() throws IOException {
//获取通道、绑定主机和端口,切换到非阻塞模式,
//创建Buffer写入数据,模式切换,写入通道,关闭
SocketChannel sc = SocketChannel.open(new InetSocketAddress(8888));
sc.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(new Date().toString().getBytes());
buffer.flip();
sc.write(buffer);
buffer.close();
}
@Test
public void recevie() throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8888));
ssc.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(selector.select() > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isAcceptable()) {
//注册关心的事件
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
int len = 0;
while((len = channel.read(buffer)) > 0) {
buffer.flip();
System.out.println(new String(buffer.array(), 0, len));
buffer.clear();
}
}
iterator.remove();
}
}
}
}
//CharsetDemo.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
public class CharsetDemo {
public static void main(String[] args) throws IOException {
Charset charset = Charset.forName("UTF-8");
CharsetEncoder encoder = charset.newEncoder();
CharBuffer charBuffer = CharBuffer.allocate(1024);
charBuffer.put("醉里挑灯看剑");
charBuffer.flip();
ByteBuffer buffer = encoder.encode(charBuffer);
System.out.println("编码之后的结果:");
for(int i=0;i<buffer.limit();i++){
System.out.print(buffer.get());
}
System.out.println();
buffer.flip();
CharsetDecoder decoder = charset.newDecoder();
System.out.println("解码之后的结果:");
System.out.println(decoder.decode(buffer));
}
}
//PathDemo.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathDemo {
public static void main(String[] args) throws IOException {
Path path = Paths.get("C:\\2022\\01.txt");
System.out.println(path);
path = Paths.get("C:\\2022", "01");
System.out.println(path);
System.out.println(path.normalize());
Path directory = Files.createDirectory(path);
System.out.println(directory);
Files.copy(path, directory);
}
}