系列:IO模块

系列:IO模块

1. BIO

常见数据源:文件、管道、网络连接、内存

1.1 流的分类
  • 输入流和输出流
  • 字节流和字符流
  • 节点流和处理流
1.2 装饰器模式 & 适配器模式
装饰器模式

BufferedReader br = new BufferedReader(new FileReader(path));

适配器模式

转换流:InputStreamReaderOutputStreamWriter

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次,耗费时间。

时间
FileInputStream4.125
BufferedInputStream3.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模型
基础概念:
	用户空间和内核空间
	阻塞和非阻塞
	同步和异步
  1. 同步阻塞IO:阻塞(内核等待足够数据到来——阻塞、数据从内核拷贝到用户空间——阻塞)
  2. 同步非阻塞IO:轮询(内核等待数据时进程不断询问是否到达——轮询、数据从内核拷贝到用户空间——阻塞)
  3. 多路复用IO:轮询+多个网络IO(内核不断轮询所有的socket——轮询+多网络IO、数据从内核拷贝到用户空间——阻塞)
  4. 信号驱动IO:非阻塞(进程注册信号继续运行、收到内核的信号开始阻塞然后开始从内核拷贝数据到用户空间——阻塞)
  5. 异步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

参考

IO流不关闭,为什么引起内存泄漏?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值