IO模型

推荐博客:Java NIO:浅析I/O模型

1.寻常io

 寻常io写入:

public static  void testBasicFileIO() throws Exception {
        byte[] data = "123456789\n".getBytes();
        String path =  "/root/testfileio/out.txt";
        File file = new File(path);
        FileOutputStream out = new FileOutputStream(file);
            out.write(data);
    }

寻常io缓存写入:

  public static void testBufferedFileIO() throws Exception {
        byte[] data = "123456789\n".getBytes();
        String path =  "/root/testfileio/out.txt";
        File file = new File(path);
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
        while(true){
            Thread.sleep(10);
            out.write(data);
        }
    }

这两种写入方式的区别在于第二种使用了Buffered缓存,首先io写入模型如下:

 

 

这种写法的缺点,只能按照顺序读取数据,即当文件读取到某个位置时,只能从头再读或者继续向下读取,且该读取方式为同步阻塞

2.NIO   新的io

传统io是面向流的,nio是面向块的  a.容量(capacity) b.界限(limit) c.位置(position),缓冲区本质上是一个可以写入数据的内存块,然后可以再次读取,该对象提供了一组方法,可以更轻松地使用内存块,使用缓冲区读取和写入数据          通常遵循以下四个步骤:
  1. 写数据到缓冲区;
  2. 调用buffer.flip()方法;
  3. 从缓冲区中读取数据;
  4. 调用buffer.clear()或buffer.compat()方法;
当向buffer写入数据时,buffer会记录下写了多少数据,一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式,在读模式下可以读取之前写入到buffer的所有数据,一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。

 

 

 

 /**创建byte缓存*/
        ByteBuffer byteBuffer=ByteBuffer.allocate(20);
        // ByteBuffer allocateDirect=ByteBuffer.allocateDirect(1024);堆外分配
        /**创建byte[],用于put,当大小大于上面创建的bytebuffer时,报BufferOverflowException*/
        byte [] bytes={'q','w','e','r','t','1','2',3,4,5,6,7,8,9};
        /**打印指针,当为null时,指针指向0的位置*/
        System.out.println("position = " + byteBuffer.position());
        System.out.println(byteBuffer);
        /**将bytes放入ByteBuffer*/
        System.out.println("put = " + byteBuffer.put(bytes));
        byteBuffer.get();
        System.out.println("get = "+byteBuffer);
        byteBuffer.flip();
        System.out.println("flip = " + byteBuffer);
        byteBuffer.compact();
        System.out.println("compact = " + byteBuffer);
        System.out.println("isDirect = " + byteBuffer.isDirect());

输出:

position = 0
java.nio.HeapByteBuffer[pos=0 lim=20 cap=20]
put = java.nio.HeapByteBuffer[pos=14 lim=20 cap=20]
get = java.nio.HeapByteBuffer[pos=15 lim=20 cap=20]
flip = java.nio.HeapByteBuffer[pos=0 lim=15 cap=20]
compact = java.nio.HeapByteBuffer[pos=15 lim=20 cap=20]
isDirect = false

调用get和put方法会将position向后移动,调用flip方法,position移植到0,limit移植到之前position位置,做了一次读写交换,调用compact将position移动到之前有值最大位置,limit移动到capacity位置

 

/**
 * 一、缓冲区(Buffer):在java NIO中负责数据的存取。缓冲区就是数组。用于存储
 * 不同数据类型的数据
 *
 * 根据数据类型的不同(boolean除外),提供对应类型的缓冲区:
 * ByteBuffer - 最常用的
 * CharBuffer
 * ShortBuffer
 * IntBuffer
 * LongBuffer
 * FloatBuffer
 * DoubleBuffer
 *
 * 上述的缓冲区的管理方式几乎是一致的 ,通过allocate()获取缓冲区
 *
 * 二、缓冲区里面的存取数据的两个核心方法:
 * put():存入数据到缓冲区中
 * get():获取缓冲区中的数据
 *
 * 四 、缓冲区中的核心属性
 * capacity:容量,表示缓冲区中最大存储数据的容量,一旦声明了则不能改变。
 * limit:表示缓冲区中可以操作数据的大小。(limit后数据是不可以进行读写的)
 * position:位置,表示缓冲区正在操作数据的位置。
 * mark:标记,表示记录当前position的位置,可以通过reset()恢复到mark的位置
 *
 * 0 <= mark <= position <= limit <= capacity
 */
public class TestBuffer {

    @Test
    public void test2() {
        String str = "abcde";
        ByteBuffer buf = ByteBuffer.allocate(1024);
        buf.put(str.getBytes());

        buf.flip();

        byte[] dst = new byte[buf.limit()];
        buf.get(dst,0,2);
        System.out.println(new String(dst, 0, 2));

        System.out.println(buf.position());

        //mark():标记
        buf.mark();
        buf.get(dst,2,2);
        System.out.println(new String(dst,2,2));
        System.out.println(buf.position());

        //reset()
        buf.reset();
        System.out.println(buf.position());

        //判断缓冲区中是否还有剩余数据
        if (buf.hasRemaining()) {
            //如果有的话,那么获取缓冲区中可以操作的数量
            System.out.println(buf.remaining());
        }
    }

    @Test
    public void test1() {
        String str = "abcde";
        //1.分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        System.out.println("-------allocate()-------");
        System.out.println("正在操作的位置 "+buf.position());
        System.out.println("缓冲区中可操作数据的大小 "+buf.limit());
        System.out.println("容量 "+buf.capacity());

        //2.利用put()方法存入数据到缓冲区
        buf.put(str.getBytes());
        System.out.println("-------put()-------");
        System.out.println("正在操作的位置 "+buf.position());
        System.out.println("缓冲区中可操作数据的大小 "+buf.limit());
        System.out.println("容量 "+buf.capacity());
        //3.切换成读取数据的模式,利用flip()方法来进行读取数据
        buf.flip();
        System.out.println("-------flip()-------");
        System.out.println("正在操作的位置 "+buf.position());
        System.out.println("缓冲区中可操作数据的大小 "+buf.limit());
        System.out.println("容量 "+buf.capacity());
        //4.利用get()读取缓冲区中的数据
        byte[] dst = new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst,0,dst.length));
        System.out.println("-------get()-------");
        System.out.println("正在操作的位置 "+buf.position());
        System.out.println("缓冲区中可操作数据的大小 "+buf.limit());
        System.out.println("容量 "+buf.capacity());
        //5.rewind():表示可以重复读取
        buf.rewind();
        System.out.println("-------rewind()-------");
        System.out.println("正在操作的位置 "+buf.position());
        System.out.println("缓冲区中可操作数据的大小 "+buf.limit());
        System.out.println("容量 "+buf.capacity());
        //6.清空缓冲区,但是缓冲区中的数据依然存在,只不过数据是处于被遗望的状态
        buf.clear();
        System.out.println("-------clear()-------");
        System.out.println("正在操作的位置 "+buf.position());
        System.out.println("缓冲区中可操作数据的大小 "+buf.limit());
        System.out.println("容量 "+buf.capacity());

        System.out.println((char)buf.get());
    }
}

原文链接:https://blog.csdn.net/weixin_37778801/article/details/86699341

一、通道(Channel):用于源节点和目标节点的连接。在Java nio中负责缓冲区中数据的传输。
Channel本身不存储数据,因此需要配合缓冲区进行传输。

二、通道的主要实现类
java.nio.channels.Channel 接口
|--FileChannel
|--SocketChannel
|--ServerSocketChannel
|--DatagramChannel
三、获取通道
1.Java针对支持通道的类提供了getChannel()方法
本地IO:
FileInputStream、FileOutputStream
RandomAccessFile
网络IO:
Socket
ServerSocket
DatagramSocket
2、在JDK1.7中的NIO.2针对各个通道提供了静态方法open()
3、在JDK1.7中的NIO.2的File工具类的newByteChannel()

public class TestChannel {
    //1.利用通道来完成文件的复制
    @Test
    public void test1() {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            fis = new FileInputStream("1.jpg");
            fos = new FileOutputStream("3.jpg");
            //2.获取通道
            inChannel = fis.getChannel();
            outChannel = fos.getChannel();
            //3.分配一个指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //4.将通道中的数据存入缓冲区中读取数据
            while (inChannel.read(buf) != -1) {
                buf.flip();//切换成读取数据的模式
                //5.将缓冲区中的数据再写入到通道
                outChannel.write(buf);
                //清空缓冲区
                buf.clear();
            }
        } catch (IOException 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();
                }
            }
        }
    }
}

原文链接:https://blog.csdn.net/weixin_37778801/article/details/86699341
//2.使用直接缓冲区完成文件的复制(内存映射文件的方式)
    @Test
    public void test2() throws IOException {
        long start = System.currentTimeMillis();
        FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("8.jpg"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE_NEW);

        //内存映射文件
        MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());

        //直接对缓冲区进行数据的读写操作
        byte[] dst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(dst);
        outMappedBuf.put(dst);

        inChannel.close();
        outChannel.close();
        long end = System.currentTimeMillis();
        System.out.println("内存映射文件所花时间:"+(end-start));
    }

 

posted @ 2021-04-04 16:16  小虎。。。。  阅读(71)  评论(0)    收藏  举报