欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道

程序员文章站 2022-05-07 21:47:15
...

参考课程

PS: 之前一直想了解这个NIO到底是什么东西,奈何目前用不到,听说现在许多框架都在用,而且面试的时候也有被问道,感觉还是去多了解了解底层怎么实现的~~这是我的第100篇博客!!!

一. NIO与IO区别

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector

二. 缓冲区

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道

缓冲区(Buffer) :一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类。
Java NIO 中的 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。

2.1 直接缓冲区与非直接缓冲区

  • 非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中
  • 直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道
这里探究验证了一下底层Buffer的原理

  //分配指定大小的缓冲区
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        String s=new String("abcde");
        //缓冲区存数据
        System.out.println("PUT操作");
        byteBuffer.put(s.getBytes());
        System.out.println(byteBuffer.position()); //5
        System.out.println(byteBuffer.limit());  //1024
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        System.out.println("filp操作");
        byteBuffer.flip();
        System.out.println(byteBuffer.position()); //0
        System.out.println(byteBuffer.limit());    //5
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        System.out.println("get操作");
        byte[] b=new byte[byteBuffer.limit()];
        byteBuffer.get(b,0,4);
        if(byteBuffer.hasRemaining()){ //判断缓冲区是否有剩余
            System.out.println("缓冲区还有几个剩余的: "+byteBuffer.remaining());
        }
        System.out.println(byteBuffer.position()); //5
        System.out.println(byteBuffer.limit());    //5
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        System.out.println("rwind操作可重复读"); //又把position置于开头
        byteBuffer.rewind();
        System.out.println(byteBuffer.position()); //0
        System.out.println(byteBuffer.limit());    //5
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");


        System.out.println("mark操作记录当前的position位置"); //又把position置于开头
        byteBuffer.mark();
        byte[] bytes=new byte[1024];
        byteBuffer.get(bytes,0,2); //取走前两个,position为2
        System.out.println("取走前两个,position为"+byteBuffer.position()); //2
        byteBuffer.reset(); //恢复到mark位置的position 为0
        System.out.println("恢复到mark位置的position 为"+byteBuffer.position()); //0
        System.out.println("***************************");

        System.out.println("clear操作清空(非真清空,相当于把position和limit恢复开始状态)数组中的数据依然存在"); //又把position置于开头
        byteBuffer.clear();
        System.out.println(byteBuffer.position()); //0
        System.out.println(byteBuffer.limit());    //1024
        System.out.println(byteBuffer.capacity()); //1024
        System.out.println("***************************");

        //获取第一个bytez转化为char还是能输出
        System.out.println("获取第一个bytez转化为char还是能输出"+(char)byteBuffer.get());

        if(byteBuffer.hasRemaining()){ //判断缓冲区是否有剩余
            System.out.println("缓冲区还有几个剩余的: "+byteBuffer.remaining());
        }

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道

ByteBuffer byteBuffer=ByteBuffer.allocateDirect(1024);//创建一个直接缓冲区
System.out.println(byteBuffer.isDirect());//判断是否为直接缓冲区

三. 通道

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道

通道:由java.nio.channels包定义。
Channel表示IO源与目标打开的连接。
Channel类似于传统的“流”。但其自身不能直接访问数据,Channel只能与Buffer进行交互。

java.nio.channels.Channel 接口

  • FileChannel:用于读取、写入、映射和操作文件的通道。
    
  • SocketChannel:通过 TCP 读写网络中的数据。
    
  • ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
    
  • DatagramChannel:通过 UDP 读写网络中的数据通道。
    

3.1 java针对支持通道的类提供了getChannel()方法

  • 本地IO:
FileInputStream/FileOutputStream
long start=System.currentTimeMillis();

        FileInputStream fis=null;
        FileOutputStream fos=null;
        //获取通道
        FileChannel inchannel = null;
        FileChannel outchannel = null;

        try {
            fis = new FileInputStream("E:\\image/1.jpg");
            fos = new FileOutputStream("E:\\image/3.jpg");
            inchannel = fis.getChannel();
            outchannel = fos.getChannel();
            //设置非直接缓冲区大小
            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
            while(inchannel.read(byteBuffer)!=-1){ //将通道里的数据存入缓冲区
                byteBuffer.flip(); //切换读取模式;
                outchannel.write(byteBuffer); // //将缓冲区中的数据写入通道中
                byteBuffer.clear(); ////清空缓冲区
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if(outchannel!=null){
                try {
                    outchannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(inchannel!=null){
                try {
                    inchannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        long end=System.currentTimeMillis();
        System.out.println("耗费时间:"+(end-start));//耗费时间:1094
RandomAccessFile

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道

      RandomAccessFile raf=new RandomAccessFile("E:\\image/1234.txt","rw");
        //建立通道
        FileChannel inChannel = raf.getChannel();
        //创建缓存区数组 ->分散读取
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
        ByteBuffer byteBuffer1=ByteBuffer.allocate(100);
        ByteBuffer[] buffers={byteBuffer,byteBuffer1};
        //读取到缓存区
        inChannel.read(buffers);
        for (ByteBuffer buffer : buffers) {
            buffer.flip(); //切换为读模式
        }
        //查看各自通道有多少
        System.out.println(new String(buffers[0].array(),0,buffers[0].limit()));
        System.out.println("****************************************************");
        System.out.println(new String(buffers[1].array(),0,buffers[1].limit()));

        //聚集写入
        RandomAccessFile raf2=new RandomAccessFile("E:\\image/12345.txt","rw");
        FileChannel outChannel = raf2.getChannel();
        outChannel.write(buffers);
        inChannel.close();
        outChannel.close();
  • 网络IO:
    Socket
    ServerSocket
    DatagramSocket

3.2 在JDK 1.7 中的NIO.2 针对各个通道提供了静态方法 open()

   		long start=System.currentTimeMillis();
        //通过静态方法 open()创建通道
        FileChannel inChannel=FileChannel.open(Paths.get("E:\\image/1.jpg"), StandardOpenOption.READ); //读模式
        FileChannel outChannel=FileChannel.open(Paths.get("E:\\image/4.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //读写模式;存在就报错,不存在就创建
        //内存映射文件
        MappedByteBuffer inMappedBuf=inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
        MappedByteBuffer outMappedBuf=outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());
        //进行写操作
        byte[] bytes=new byte[inMappedBuf.limit()];
        inMappedBuf.get(bytes);
        outMappedBuf.put(bytes);
        //关闭通道
        inChannel.close();
        outChannel.close();
        long end=System.currentTimeMillis();
        System.out.println("耗时: "+(end-start));

直接通过通道复制

 	    long start=System.currentTimeMillis();
        FileChannel inChannel=FileChannel.open(Paths.get("E:\\image/1.jpg"), StandardOpenOption.READ); //读模式
        FileChannel outChannel=FileChannel.open(Paths.get("E:\\image/4.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); //读写模式;存在就覆盖,不存在就创建
        inChannel.transferTo(0,inChannel.size(),outChannel);
        inChannel.close();
        outChannel.close();
        long end=System.currentTimeMillis();
        System.out.println("耗时: "+(end-start));

3.3 在JDK 1.7 中的NIO.2 的Files工具类的newByteChannel()

3.4 测试结果

Java〖NIO上篇〗看这一篇就够了 缓冲区 通道

制作不易,转载请标注~

相关标签: java类 NIO类