Java IO

作者: 单调的灵魂 | 来源:发表于2021-08-31 15:50 被阅读0次
  • 普通 IO ,面向流,同步阻塞线程。
  • NIO,面向缓冲区,同步非阻塞。

BIO、NIO、AIO的概述

首先,传统的 java.io包,它基于流模型实现,提供了我们最熟知的一些 IO 功能,比如 File 抽象、输入输出流等。交互方式是同步、阻塞的方式,也就是说,在读取输入流或者写入输出流时,在读、写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序。

java.io包的好处是代码比较简单、直观,缺点则是 IO 效率和扩展性存在局限性,容易成为应用性能的瓶颈。

很多时候,人们也把 java.net下面提供的部分网络 API,比如 Socket、ServerSocket、HttpURLConnection 也归类到同步阻塞 IO 类库,因为网络通信同样是 IO 行为。

第二,在 Java 1.4 中引入了 NIO 框架(java.nio 包),提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层的高性能数据操作方式。

第三,在 Java 7 中,NIO 有了进一步的改进,也就是 NIO 2,引入了异步非阻塞 IO 方式,也有很多人叫它 AIO(Asynchronous IO)。异步 IO 操作基于事件和回调机制,可以简单理解为,应用操作直接返回,而不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后续工作。

IO 流

IO流简单来说就是input和output流,IO流主要是用来处理设备之间的数据传输,Java IO对于数据的操作都是通过流实现的,而java用于操作流的对象都在IO包中。

IO流的本质是数据传输,并且流是单向的

img

分类

  • 按照操作数据类型分类

    • 字节流 InputStreamOutputStream
    • 字符流 ReaderWriter

    区别

    • 单位不同:字节流单位8bit长,也就是1byte;字符流单位根据不同编码而变化,

      a、A、中、+、*、の......均表示一个字符, 一般 utf-8 编码下,一个汉字 字符 占用 3 个 字节; 一般 gbk 编码下,一个汉字 字符 占用 2 个 字节;Java 默认编码是uincode

    • 处理对象不同:字节流可以处理任意类型数据,字符流只能处理字符相关数据

  • 按照流向分类

    • 输入流ReaderInputStream
    • 输出流WriterOutputStream
  • 按照流的用途分类3

    • 实体流

      直接连接数据源的流类,如FileInputStream

    • 装饰流

      以其他流对象(实体流或装饰流)为基础建立的流类,如DataInputStream/DataOutputStreamBufferedReader/BufferedWriter

InputStream

  • ByteArrayInputStream
  • StringBufferInputStream // 已经过时
  • FileInputStream
  • PipedInputStream

PipedInputStream 内部维护了一个字节数组 buffer 默认大小为1024

PipedOutputStream 内部指向了一个 PipedInputStream借助于PipedInputStream 内部的循环数组进行数据缓存

read和write会向上抛出异常,不会返回-1,所以需要用 available()方法来进行访问

  • ObjectInputStream 也是装饰流,读取对象

如何判断结束

  1. 把对象全部放入一个对象中,读的时候,读一个就可以了
  2. 捕获EOF异常,对异常进行处理
  3. 写文件的时候规定最后一个为null,或者特殊的字符,来约定结束
  • FilterInputStream 和他的子类都是装饰流
    • DataInputStream
    • BufferedInputStream
  • SequenceInputStream 序列流,作用是合并多个流变成一个流
public class learnIO {


    public static void main(String[] args) throws Exception {
        System.out.println("---------------------------byte输入流测试--------------------------------");
        byte[] bytes=new byte[26];
        byte n=0;
        for (int i = 0; i < bytes.length; i++) {
            bytes[i]=n++;
        }
        int c;
        ByteArrayInputStream bIn=new ByteArrayInputStream(bytes);
        while ((c=bIn.read())!=-1){
            System.out.print(c+" ");
        }
        System.out.println("\n---------------------------StringBuffer输入流测试--------------------------------");
        StringBuffer stringBuffer=new StringBuffer("123456789456");
        StringBufferInputStream stringBufferInputStream=new StringBufferInputStream(stringBuffer.toString());
        while ((c=stringBufferInputStream.read())!=-1){
            System.out.print((char)c+" ");
        }
        System.out.println("\n---------------------------FileInputStream输入流测试--------------------------------");

        FileInputStream fileInputStream=new FileInputStream("src/testfile.txt");
        while ((c=fileInputStream.read())!=-1){
            System.out.print((char) c+" ");
        }
        System.out.println("\n---------------------------PipedInputStream 输入流测试--------------------------------");
        PipedInputStream pipedInputStream=new PipedInputStream();
        PipedOutputStream pipedOutputStream=new PipedOutputStream();

        pipedOutputStream.connect(pipedInputStream);
        Thread writeThread= new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println("写了");
                            for (int i=0;i<10;i++) {
                                System.out.print(" "+i);
                                pipedOutputStream.write(i);
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        System.out.println();
                    }
                }
        );
        Thread readThread= new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        int r;
                        System.out.println("读了");
                        while (true){
                            try {
                                if (pipedInputStream.available()==0) break;//read和write会向上抛出异常,不会返回-1,所以需要用 `available()`方法来进行访问
                                r=pipedInputStream.read();
                                System.out.print(" "+r);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                        }
                        System.out.println();
                    }
                }
        );
        writeThread.start();
        Thread.sleep(50);
        readThread.start();
        Thread.sleep(50);
        System.out.println("\n---------------------------ObjectInputStream 输入流测试--------------------------------");
        // 首先写输入
        FileOutputStream fileOutputStream=new FileOutputStream("src/Obj.txt");
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(fileOutputStream);
        for (int i=0;i<1;i++) objectOutputStream.writeObject(new String("你好,写个对象"));

        //开始读
        fileInputStream=new FileInputStream("src/Obj.txt");
        ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
        //
        Object res;
        while (true){
            // 有三种方法判断结束
            /*
                1. 把对象全部放入一个对象中,读的时候,读一个就可以了
                2. 捕获EOF异常,对异常进行处理
                3. 写文件的时候规定最后一个为null,或者特殊的字符,来约定结束
             */
            try {
                res=objectInputStream.readObject();
                System.out.println("读了  "+(String) res);
            }catch (Exception e){
                break;
            }
        }
        System.out.println("\n---------------------------FilterInputStream 输入流测试--------------------------------");
        System.out.println("\n---------------------------DataInputStream 输入流测试--------------------------------");
        fileOutputStream=new FileOutputStream("src/testfile.txt");
        DataOutputStream dataOutputStream=new DataOutputStream(fileOutputStream);
        dataOutputStream.writeUTF("123456");
        dataOutputStream.writeInt(12);
        fileInputStream=new FileInputStream("src/testfile.txt");
        DataInputStream dataInputStream=new DataInputStream(fileInputStream);
        System.out.println(dataInputStream.readUTF());
        System.out.println(dataInputStream.readInt());
        System.out.println("\n---------------------------BufferedInputStream 输入流测试--------------------------------");
        fileInputStream=new FileInputStream("src/testfile.txt");
        BufferedInputStream bufferedInputStream=new BufferedInputStream(fileInputStream);
        while ((c=bufferedInputStream.read())!=-1){
            System.out.print(c+" ");
        }
        System.out.println("\n---------------------------SequenceInputStream 输入流测试--------------------------------");
        FileInputStream fileInputStream1=new FileInputStream("src/Obj.txt");
        fileInputStream=new FileInputStream("src/testfile.txt");
        SequenceInputStream sequenceInputStream=new SequenceInputStream(fileInputStream,fileInputStream1);
        
        while ((c=sequenceInputStream.read())!=-1){
            System.out.print(c+" ");
        }
    }
            System.out.println("\n---------------------------pushBack 输入流测试--------------------------------");
        String str = "www.baidu.com" ;      // 定义字符串
        PushbackInputStream push = null ;       // 定义回退流对象
        ByteArrayInputStream bai = null ;       // 定义内存输入流
        bai = new ByteArrayInputStream(str.getBytes()) ;    // 实例化内存输入流
        push = new PushbackInputStream(bai) ;   // 从内存中读取数据
        int temp = 0 ;
        push.unread('a');

        while((temp=push.read())!=-1){  // 读取内容
            if(temp=='.'){  // 判断是否读取到了“.”
                push.unread(temp) ; // 放回到缓冲区之中
                temp = push.read() ;    // 再读一遍
                System.out.print("(退回"+(char)temp+")") ;
            }else{
                System.out.print((char)temp) ;  // 输出内容
            }
        }
}

Reader

  • FileReader
  • BufferReader
  • StringReader

用法大同小异,只不过读取的单位一个是字节,一个字符

同一个文件读取的对比如下,默认是UTF8解码

[图片上传失败...(image-8d87ad-1630396189504)]

NIO

三个组成部分

Channel

Channel是客户端连接的一个抽象,当每个客户端连接到服务器时,服务器都会为其生成一个Channel对象;

Buffer

Buffer从字面上讲是一个缓存,本质上其是一个字节数组,通过Buffer,可以从Channel上读取数据,然后交由下层的处理器进行处理。这里的Buffer的优点在于其封装了一套非常简单的用于读取和写入数据Api。

Selector

Selector则是Java NIO实现高性能的关键,其本质上使用了IO多路复用的原理,通过一个线程不断的监听多个Channel连接来实现多所有这些Channel事件进行处理,这样的优点在于只需要一个线程就可以处理大量的客户端连接,当有客户端事件到达时,再将其分发出去交由其它线程处理;

img imag

实现:

Channel:

FileChannel:从文件中读写数据

public static void main(String[] args) throws Exception {
    RandomAccessFile file=new RandomAccessFile("src/com/learn/test.txt","rw");
    FileChannel inChannel=file.getChannel();
    ByteBuffer buf=ByteBuffer.allocate(48);;
    int bytesRead = inChannel.read(buf);//返回读了多少个数
    while (bytesRead != -1) {

        System.out.println("Read " + bytesRead);
        //buf.flip();// 确认现在缓存区可读内容的大小,即改变指针poison,和最大范围limit
        byte[] buffer = new byte[50];
        int index=0;
        while(buf.hasRemaining()){
            buffer[index++]=buf.get();
        }
        System.out.println(new String(buffer,0,index));// 防止乱码
        buf.clear();// 重置指针poison,和最大范围limit
        bytesRead = inChannel.read(buf);
    }
    file.close();
}

DatagramChannel:能通过UDP读写网络中的数据

  • datagramChannel = DatagramChannel.open();//获取channel
    datagramChannel.socket().bind(new InetSocketAddress("127.0.0.1",9999));//绑定自身端口号
    ByteBuffer buffer = ByteBuffer.allocate(50);//需要缓存区
    datagramChannel.send(buffer, new InetSocketAddress("127.0.0.1",8989));//发送到目标地址
    
    
public class learnNIO {
    public static void main(String[] args) throws Exception {
        DatagramChannel channel = DatagramChannel.open();
        channel.socket().bind(new InetSocketAddress("127.0.0.1",8887));
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("开始监听");
                ByteBuffer buffer = ByteBuffer.allocate(50);
                DatagramChannel datagramChannel = null;
                byte[] b;
                try {
                    datagramChannel = DatagramChannel.open();
                    datagramChannel.socket().bind(new InetSocketAddress("127.0.0.1",8989));
                } catch (IOException e) {
                    e.printStackTrace();
                }
                while (true){
                    // 清空Buffer
                    buffer.clear();
                    // 接受客户端发送数据

                    SocketAddress socketAddress = null;
                    try {
                        socketAddress = datagramChannel.receive(buffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    if (socketAddress != null) {
                        int position = buffer.position();
                        b = new byte[position];
                        buffer.flip();
                        for(int i=0; i<position; ++i) {
                            b[i] = buffer.get();
                        }
                        try {
                            System.out.println("receive remote " +  socketAddress.toString() + ":"  + new String(b, "UTF-8"));
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }).start();
        ByteBuffer buf = ByteBuffer.allocate(50);
        buf.clear();
        String newData = "New String to write to file..." + System.currentTimeMillis();
        buf.clear();
        buf.put(newData.getBytes());
        buf.flip();
        System.out.println("开始发送");
        int bytesSent = channel.send(buf, new InetSocketAddress("127.0.0.1",8989));
    }
}

SocketChannel:能通过TCP读写网络中的数据

https://www.cnblogs.com/lxyit/p/9209407.html

ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

public class learnSocketChannel {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        serverSocketChannel.bind( new InetSocketAddress("127.0.0.1", 8888));
        System.out.println(serverSocketChannel.getLocalAddress());
        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        while (true){
                            try {
                                SocketChannel socketChannel=serverSocketChannel.accept();
                                if (socketChannel!=null){
                                    System.out.println(socketChannel);
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
        ).start();
        SocketChannel socketChannel = SocketChannel.open(
                new InetSocketAddress("127.0.0.1", 8888));
        socketChannel.configureBlocking(false);
        ByteBuffer byteBuffer = ByteBuffer.allocate(16);
        socketChannel.read(byteBuffer);

        socketChannel.close();
        System.out.println("test end!");



    }
}

相关文章

网友评论

      本文标题:Java IO

      本文链接:https://www.haomeiwen.com/subject/pksziltx.html