系列:IO模块
文章目录
1. BIO
常见数据源:文件、管道、网络连接、内存
1.1 流的分类
- 输入流和输出流
- 字节流和字符流
- 节点流和处理流
1.2 装饰器模式 & 适配器模式
装饰器模式
如BufferedReader br = new BufferedReader(new FileReader(path));
适配器模式
转换流:InputStreamReader
、OutputStreamWriter
如InputStreamReader isr = new InputStreamReader(new FileInputStream(path));
1.3 常用API
节点流:数据源
类型 | 字节流 | 字符流 |
---|---|---|
文件 | FileInputStream FileOutputStream | FileReader FileWriter |
内存数组 | ByteArrayInputStream ByteArrayOutputStream | CharArrayReader CharArrayWriter |
内存字符串 | - | StringReader StringWriter |
管道 | PipedInputStream PipedOutputStream | PipedReader PipedWriter |
基本类型序列化 | ObjectInputStream ObjectOutputStream |
处理流
字节流,Filter:缓存、对象序列化、Pushback等
字符流:缓存、转换流、filter的Pushback等
1.4 demo
场景:读取一个10M的文件,100次,耗费时间。
流 | 时间 |
---|---|
FileInputStream | 4.125 |
BufferedInputStream | 3.264 |
使用FIleInputStream
public static void test01() throws IOException {
FileInputStream fis = new FileInputStream("10M.txt");
FileOutputStream fos = new FileOutputStream("newFile.txt");
int len = -1;
byte[] buf = new byte[1024];
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.close();
fis.close();
}
使用BufferedInputStream
public static void test02() throws IOException {
FileInputStream fis = new FileInputStream("10M.txt");
FileOutputStream fos = new FileOutputStream("newFile.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int len = -1;
byte[] buf = new byte[1024];
while ((len = bis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
bos.close();
bis.close();
}
序列化:基本类型+String、对象
基本类型和String
public static void test03() throws IOException {
int a1 = 3;
double a2 = 55.55d;
String a3 = "你好a";
{
FileOutputStream fos = new FileOutputStream("serialize.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(a1);
dos.writeDouble(a2);
dos.writeUTF(a3);
dos.flush();
dos.close();
}
{
FileInputStream fis = new FileInputStream("serialize.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
int b1 = dis.readInt();
double b2 = dis.readDouble();
String b3 = dis.readUTF();
System.out.println(b1 + "\n" + b2 + "\n" + b3);
dis.close();
}
}
序列化对象
{
User user = new User(1);
FileOutputStream fos = new FileOutputStream("serialize.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(user);
oos.flush();
oos.close();
}
{
FileInputStream fis = new FileInputStream("serialize.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(bis);
User user = (User)ois.readObject();
System.out.println(user);
ois.close();
}
参考
https://blinkfox.github.io/2018/11/05/hou-duan/java/java-io-zhi-shi-zheng-li/
https://www.jianshu.com/p/933631bc5e20
https://www.jianshu.com/p/937883b6b2e5
2. BIO + 网络编程
2.1 主要API
Socket、ServerSocket
InetAddress
DatagramSocket
URL、URLConnection、HttpClient
2.2 demo
基本通信
略
多客户端
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
int i = 1;
while (true) {
Socket client = serverSocket.accept();
new Thread(new ConnTask(i++, client)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public class ConnTask implements Runnable {
private int i;
private Socket socket;
public ConnTask(int i, Socket socket) {
this.i = i;
this.socket = socket;
}
@Override
public void run() {
boolean end = false;
Scanner scanner = null;
PrintStream out = null;
try (InputStream is = socket.getInputStream()) {
scanner = new Scanner(is);
out = new PrintStream(socket.getOutputStream());
while (!end && scanner.hasNext()) {
String line = scanner.nextLine();
if (line.equalsIgnoreCase("end")) {
end = false;
}
out.println(i + ": " + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
scanner.close();
out.close();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
参考
https://blog.csdn.net/peng_666666/article/details/52959636
3. NIO
3.1 Linux的IO模型
基础概念:
用户空间和内核空间
阻塞和非阻塞
同步和异步
- 同步阻塞IO:阻塞(内核等待足够数据到来——阻塞、数据从内核拷贝到用户空间——阻塞)
- 同步非阻塞IO:轮询(内核等待数据时进程不断询问是否到达——轮询、数据从内核拷贝到用户空间——阻塞)
- 多路复用IO:轮询+多个网络IO(内核不断轮询所有的socket——轮询+多网络IO、数据从内核拷贝到用户空间——阻塞)
- 信号驱动IO:非阻塞(进程注册信号继续运行、收到内核的信号开始阻塞然后开始从内核拷贝数据到用户空间——阻塞)
- 异步IO:调用,数据拷贝到用户空间,进程返回处理
3.2 NIO的JDK三大组件
缓冲区
通道
选择器
缓冲区
属性:容量、上届、位置、标记
关系:0 <= mark <= position <= limit <= capacity
操作:
- 存取:get、put
- 翻转:flip
- 释放:clear
- 压缩:compact
- 标记:mark
- 比较
- 批量put|get
wrap方法使用:将byte数组包装到缓冲区
直接缓冲区:不再通过内核地址空间和用户地址空间的缓存数据的复制传递,而是在物理内存中申请了一块空间,这块空间映射到内核地址空间和用户地址空间,应用程序与磁盘之间的数据存取之间通过这块直接申请的物理内存进行。
视图缓冲区
内存映射缓冲区
通道
定义:用于在字节缓冲区和位于通道另一侧的实体(文件或套接字等)之间有效传输数据
- 双向 + 可异步 + 基于缓冲区
文件通道、文件锁定、内存映射文件
Channel-to-Channel传输
SocketChannel + ServerSocketChannel、DatagramChannel
管道
工具类:Channels
选择器
定义:可检测一或多个通道,并检测到其accept + connect + read + write事件的组件,从而实现一个线程管理多个通道/网络连接
选择器,Selector
可选择通道,SelectableChannel
选择键,SelectionKey
demo
1. 文件复制
FileChannel inChannel = FileChannel.open(Paths.get("data.txt"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("data2.txt"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
if (outChannel != null) {
outChannel.close();
}
if (inChannel != null) {
inChannel.close();
}
2. 简单聊天室
public class Server {
public static void main(String[] args) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int countChannel = selector.select(500);
if (countChannel == 0) {
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) {
acceptHandle(serverSocketChannel, selector);
}
if (key.isReadable()) {
readHandle(key, selector);
}
}
}
}
public static void acceptHandle(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
SocketChannel sc = serverSocketChannel.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
sc.write(Charset.forName("utf-8").encode("Welcome"));
}
public static void readHandle(SelectionKey key, Selector selector) throws IOException {
SocketChannel sc = (SocketChannel)key.channel();
StringBuilder sb = new StringBuilder();
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (sc.read(buffer) > 0) {
buffer.flip();
sb.append(Charset.forName("utf-8").decode(buffer));
}
sc.register(selector, SelectionKey.OP_READ);
if (sb.length() > 0) {
broadcast(selector, sc, sb.toString());
}
}
private static void broadcast(Selector selector, SocketChannel sc, String msg) {
Set<SelectionKey> keys = selector.keys();
keys.forEach(key -> {
Channel c = key.channel();
if (c instanceof SocketChannel && c != sc) {
try {
((SocketChannel) c).write(Charset.forName("utf-8").encode(msg));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
public class Client {
static class ReadTask implements Runnable {
private Selector selector;
public ReadTask(Selector selector) {
this.selector = selector;
}
@Override
public void run() {
while (true) {
try {
int readyChannels = selector.select(500);
if (readyChannels == 0) {
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isReadable()) {
SocketChannel sc = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
StringBuilder sb = new StringBuilder();
while (sc.read(buffer) > 0) {
buffer.flip();
sb.append(Charset.forName("utf-8").decode(buffer));
}
sc.register(selector, SelectionKey.OP_READ);
if (sb.length() > 0) {
System.out.println("收到消息:" + sb.toString());
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
sc.configureBlocking(false);
Selector selector = Selector.open();
sc.register(selector, SelectionKey.OP_READ);
new Thread(new ReadTask(selector)).start();
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String msg = scanner.nextLine();
if (msg.length() > 0) {
sc.write(Charset.forName("utf-8").encode(msg));
}
}
}
}
参考
https://www.jianshu.com/p/486b0965c296
https://zhuanlan.zhihu.com/p/59412955
https://blog.csdn.net/jiahao1186/article/details/102892438?utm_source=app
https://my.oschina.net/oosc/blog/1627362
其他
正则表达式
CharSequence
Pattern + Matcher
String
字符集
Charset
CharsetEncoder + CharsetDecoder
CharsetProviderSPI
文件
Path、Paths、File