zoukankan      html  css  js  c++  java
  • IO与NIO

    一 . IO

    1 . io流根据处理数据类型的不同分为字节流(InputStream和OutputStream)和字符流(Reader和Writer);根据数据流向的不同分为输入流(InputStream和Reader)和输出流(OutputStream和Writer)。


    2 . 字节流和字符流的区别:
    • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。字节流主要是操作byte类型数据,以byte数组为主要操作对象
    • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。字符流主要是操作char[]或者String。
    • 结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

    3 .随机读取文件:类RandomAccessFile可以随机的读取一个文件中指定位置的数据。因为在文件中,所有得内容都是按照字节存放的,都有固定的保存位置. 实例化RandomAccessFile的时候需要传递File类的对象,以及文件的打开模式,r:读 w:写 rw:读写,使用rw模式时如果文件不存在,则会自动创建.RandomAccessFile不是InputStream和OutputStream继承层次结构中的一部分,它是完全独立的一个类。

    public class UseRandomAccessFile {
    
        public static void store() throws IOException {
            RandomAccessFile raf = new RandomAccessFile("C:/Users/Desktop/output.txt", "rw");
            for (int i = 0; i < 7; i++) {
                raf.writeDouble(i * 1.414);
            }
            raf.writeUTF("----------> the end of file");
            raf.close();
        }
    
        public static void display() throws IOException {
            RandomAccessFile raf = new RandomAccessFile("C:/Users/Desktop/output.txt", "r");
            for (int i = 0; i < 7; i++) {
                System.out.println("value " + i + " : " + raf.readDouble());
            }
            System.out.println(raf.readUTF());
            raf.close();
        }
    
    
    
        public static void main(String[] args) throws IOException {
            store();
            display();
        }
    
    }

    4 .InputStream,OutputStream,Reader,Writer 都是抽象类。

    • ByteArrayInputStreamStringBufferInputStreamFileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。ByteArrayOutputStreamFileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据
    • PipedInputStream 是从与其它线程共用的管道中读取数据,PipedOutputStream是向与其它线程共用的管道中写入数据。
    • ObjectInputStream /ObjectOutputStream 和所有FilterInputStream /FilterOutputStream 的子类都是装饰流(IO流大量使用了设计模式中的装饰器模式)。
    • OutputStreamWriterInputStreamReader用于实现字节流和字符流之间的相互转化。可对读取到的字节/字符数据经过指定编码转换成字符/字节。这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。

    5 .IO流的几个经典用法:

    • 输入流的相关操作
    public class InputFile {
    
        /**
         * 缓冲输入文件
         * @Description 
         * @author niepei
         * @param filename
         * @return
         * @throws IOException
         */
        public static String read(String filename) throws IOException {
            BufferedReader in = new BufferedReader(new FileReader(filename));
            String s;
            StringBuilder sb = new StringBuilder();
            while((s=in.readLine())!=null){
                sb.append(s+"
    ");//readLine()会清除掉换行符,所以这里需要手动添加
            }
            in.close();
            return sb.toString();
        }
    
    
        /**
         * 从内存输入
         * @Description 
         * @author niepei
         * @throws IOException
         */
        public static void memoryInput() throws IOException{
            //使用read()方法返回的String来创建一个StringReader对象
            StringReader in = new StringReader(InputFile.read("D:/workspace/IO/src/io/InputFile.java"));
            int c;
            //使用read()方法一次读取一个字符发送给控制台
            while((c=in.read())!=-1){
                //read()方法返回的是int,为了正常显示需要转为char
                System.out.print((char)c);
            }
        } 
    
        /**
         * 格式化的从内存输入
         * @Description 
         * @author niepei
         * @throws IOException
         */
        public static void memoryInputFormat() throws IOException {
            DataInputStream in = new DataInputStream(new ByteArrayInputStream(read("D:/workspace/IO/src/ioInputFile.java").getBytes()));
            while (in.available() != 0) {
                System.out.print((char) in.readByte());
            }
    
        }
    
        public static void main(String[] args) throws IOException {
            /*System.out.println(read(PATH+"InputFile.java"));*/
    
            /*memoryInput();*/
    
            memoryInputFormat();
        }
    
    }
    • 输出流的相关操作
    public class OutpuFile {
    
    
        /**
         * 将文本写入文件
         * @Description 
         * @author niepei
         * @throws IOException
         */
        public static void basicFileOutPut() throws IOException {
            BufferedReader in = new BufferedReader(new StringReader(InputFile.read("D:/workspace/IO/src/ioOutpuFile.java")));
    //        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(C:/Users/Desktop/output.txt)));
            PrintWriter out = new PrintWriter("C:/Users/Desktop/output.txt");/*本行代码是上一行代码的简写形式,javaSE5新增了这个构造方法替代我们层层的使用装饰器*/
            int lineCount = 1;
            String s;
            while((s=in.readLine())!=null){
                out.println(lineCount++ +" : " + s);
            }
            out.close();
            System.out.println(InputFile.read(FILE));
    
        }
    
    
    
        /**
         * 数据的存储与恢复
         * @Description 
         * @author niepei
         * @throws IOException
         */
        @SuppressWarnings("resource")
        public static void storeAndRecoveryData() throws IOException{
            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("C:/Users/Desktop/output.txt")));
            out.writeUTF("pi is ");
            out.writeDouble(3.14159265357);
            out.writeUTF("Square root of 2 is ");
            out.writeDouble(1.414);
            out.close();
            DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("C:/Users/Desktop/output.txt")));
            System.out.print(in.readUTF());
            System.out.println(in.readDouble());
            System.out.print(in.readUTF());
            System.out.println(in.readDouble());
    
        }
    
    
    
        /**
         * 标准io,System.in,System.ou.System.err这三个都是PrintStream对象<字节流>
         * @Description 
         * @author niepei
         * @throws IOException
         */
        public static void standardIO() throws IOException{
    
            //可以将System.out转换为PrintWriter,true代表开启自动清空功能
            PrintWriter out = new PrintWriter(System.out,true);
            out.println("hello world!");
    
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            String s;
            while((s=in.readLine())!=null){
                System.out.println(s);
            }
    
    
        }
    
    
    
        public static void main(String[] args) throws IOException {
            /*basicFileOutPut();*/
    
            /*storeAndRecoveryData();*/
    
            standardIO();
        }
    
    }

    参考以下博客:

    二 . NIO

    1 . NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。
    2 . NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。Channel 用于源节点与目标节点的连接, 负责缓冲区中数据的传输,Channel 本身不存储数据,因此需要配合缓冲区进行传输; Buffer 负责存储。
    3 . NIO 中的Channel表示打开到 IO 设备(例如:文件、套接字)的连接。 Buffer 主要用于与 NIO 通道进行交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。
    4 . 通道的主要实现类
      java.nio.channels.Channel 接口:
        |--FileChannel
        |--SocketChannel
        |--ServerSocketChannel
        |--DatagramChannel
    Channel的用法示例:

    package cn.com.io.nio;
    /*
     * 1. Java 针对支持通道的类提供了 getChannel() 方法
     *         本地 IO:
     *         FileInputStream/FileOutputStream
     *         RandomAccessFile
     * 
     *         网络IO:
     *         Socket
     *         ServerSocket
     *         DatagramSocket
     *         
     * 2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
     * 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
     */
    public class TestChannel {
    
        /**
         * 1.1 利用通道完成文件的复制(非直接缓冲区)
         * @Description
         * @author niepei
         */
        public void fileCopy1() throws IOException {// 10874-10953
            long start = System.currentTimeMillis();
    
            FileInputStream fis = new FileInputStream("d:/1.mkv");
            FileOutputStream fos = new FileOutputStream("d:/2.mkv");
            // ①获取通道
            FileChannel inChannel = fis.getChannel();
            FileChannel outChannel = fos.getChannel();
    
            // ②分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);
    
            // ③将通道中的数据存入缓冲区中
            while (inChannel.read(buf) != -1) {
                buf.flip(); // 切换读取数据的模式
                // ④将缓冲区中的数据写入通道中
                outChannel.write(buf);
                buf.clear(); // 清空缓冲区
            }
            inChannel.close();
            outChannel.close();
            fos.close();
            fis.close();
            long end = System.currentTimeMillis();
            System.out.println("耗费时间为:" + (end - start));
    
        }
    
    
        /**
         * 1.2  实现将文件复制(非直接缓冲区)
         * @Description 
         * @author niepei
         * @throws IOException
         */
        public void fileCopy2() throws IOException{
    
            FileChannel inchannel  = FileChannel.open(Paths.get("dream.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE);
            FileChannel outchannel = FileChannel.open(Paths.get("drama.txt"),StandardOpenOption.CREATE, StandardOpenOption.READ,StandardOpenOption.WRITE);
            ByteBuffer buff = ByteBuffer.allocate(1024);
            while((inchannel.read(buff))!=-1){
                buff.flip();
                outchannel.write(buff);
                buff.clear();
            }
            inchannel.close();
            outchannel.close();
        }
    
    
        /**
         * 2.使用直接缓冲区完成文件的复制(内存映射文件)
         * @Description
         * @author niepei
         * @throws IOException
         */
        public void fileCopy3() throws IOException {// 2127-1902-1777
            long start = System.currentTimeMillis();
    
            FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE,
                    StandardOpenOption.READ, StandardOpenOption.CREATE);
    
            // 内存映射文件
            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));
        }
    
        /**
         * 3.通道之间的数据传输(直接缓冲区)
         * @Description
         * @author niepei
         * @throws IOException
         */
        public void test3() throws IOException {
            FileChannel inChannel = FileChannel.open(Paths.get("d:/1.mkv"), StandardOpenOption.READ);
            FileChannel outChannel = FileChannel.open(Paths.get("d:/2.mkv"), StandardOpenOption.WRITE,
                    StandardOpenOption.READ, StandardOpenOption.CREATE);
    
            // inChannel.transferTo(0, inChannel.size(), outChannel);
            outChannel.transferFrom(inChannel, 0, inChannel.size());
    
            inChannel.close();
            outChannel.close();
        }
    
        /**
         * 4.分散(Scatter)与聚集(Gather)
         * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
         * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
         * 
         * @Description
         * @author niepei
         * @throws IOException
         */
        @SuppressWarnings("resource")
        public void test4() throws IOException {
            RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw");
    
            // 1. 获取通道
            FileChannel channel1 = raf1.getChannel();
    
            // 2. 分配指定大小的缓冲区
            ByteBuffer buf1 = ByteBuffer.allocate(100);
            ByteBuffer buf2 = ByteBuffer.allocate(1024);
    
            // 3. 分散读取
            ByteBuffer[] bufs = { buf1, buf2 };
            channel1.read(bufs);
    
            for (ByteBuffer byteBuffer : bufs) {
                byteBuffer.flip();
            }
    
            System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
            System.out.println("-----------------");
            System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
    
            // 4. 聚集写入
            RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw");
            FileChannel channel2 = raf2.getChannel();
    
            channel2.write(bufs);
        }
    
        /**
         * 5.从文件读入数据并输出到控制台
         * @Description 
         * @author niepei
         * @throws IOException
         */
        public void readDataFromFile()throws IOException{
            // 建立到文件的通道
            FileChannel inchannel = FileChannel.open(Paths.get("dream.txt"),StandardOpenOption.READ, StandardOpenOption.WRITE);
            // 分配缓冲区
            ByteBuffer buff = ByteBuffer.allocate(1024);
            while(inchannel.read(buff)!=-1){//从通道中读数据到指定的缓冲区
                buff.flip();//翻转缓冲区
            }
            byte[] bt = buff.array();//返回当前缓冲区的字节数组
            System.out.println(new String(bt,0,buff.limit()));
            buff.clear();//清空缓冲区
            inchannel.close();//关闭通道
        }
    }

    5 . 缓冲区(Buffer):在 NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据,根据数据类型不同(boolean 除外),提供了相应类型的缓冲区。
    6 . 缓冲区中的四个核心属性:0 <= mark <= position <= limit <= capacity
      capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。
      limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
      position : 位置,表示缓冲区中正在操作数据的位置。
      mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置
    7 . 直接缓冲区与非直接缓冲区:
    非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
    直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存 中。可以提高效率
    Buffer的用法示例:

    public class LearnBuffer {
    
        @Test
        public void test1(){
    
            String str = "abcde";
    
            //1. 分配一个指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);
            System.out.println("-----------------allocate()----------------");
            System.out.println(buf.position());//0
            System.out.println(buf.limit());//1024
            System.out.println(buf.capacity());//1024
    
            //2. 利用 put() 存入数据到缓冲区中
            buf.put(str.getBytes());
            System.out.println("-----------------put()----------------");
            System.out.println(buf.position());//5
            System.out.println(buf.limit());//1024
            System.out.println(buf.capacity());//1024
    
            //3. 切换读取数据模式
            buf.flip();
            System.out.println("-----------------flip()----------------");
            System.out.println(buf.position());//0
            System.out.println(buf.limit());//5
            System.out.println(buf.capacity());//1024
    
            //4. 利用 get() 读取缓冲区中的数据
            byte[] bytes = new byte[buf.limit()];
            buf.get(bytes);
            System.out.println("-----------------get()----------------");
            System.out.println(new String(bytes, 0, bytes.length));//abcde
            System.out.println(buf.position());//5
            System.out.println(buf.limit());//5
            System.out.println(buf.capacity());//1024
    
            //5. rewind() : 可重复读
            buf.rewind();
            System.out.println("-----------------rewind()----------------");
            System.out.println(buf.position());//0
            System.out.println(buf.limit());//5
            System.out.println(buf.capacity());//1024
    
            //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态
            buf.clear();
            System.out.println("-----------------clear()----------------");
            System.out.println(buf.position());//0
            System.out.println(buf.limit());//1024
            System.out.println(buf.capacity());//1024
            System.out.println((char)buf.get());//a
    
        }
    
        @Test
        public void test2(){
            String str = "abcde";
    
            ByteBuffer buf = ByteBuffer.allocate(1024);
    
            buf.put(str.getBytes());
    
            buf.flip();
    
            byte[] bytes = new byte[buf.limit()];
            buf.get(bytes, 0, 2);
            System.out.println(new String(bytes, 0, 2));//ab
            System.out.println(buf.position());//2
    
            //mark() : 标记
            buf.mark();
    
            buf.get(bytes, 2, 2);
            System.out.println(new String(bytes, 2, 2));//cd
            System.out.println(buf.position());//4
    
            //reset() : 恢复到 mark 的位置
            buf.reset();
            System.out.println(buf.position());//2
    
            //判断缓冲区中是否还有剩余数据
            if(buf.hasRemaining()){
                //获取缓冲区中可以操作的数量
                System.out.println(buf.remaining());//3
            }
        }
    
        @Test
        public void test3(){
            //分配直接缓冲区
            ByteBuffer buf = ByteBuffer.allocateDirect(1024);
    
            System.out.println(buf.isDirect());
        }
    
    }

    参考博客

    java NIO、BIO、AIO全面剖析

    JAVA NIO知识点总结

  • 相关阅读:
    C++入门经典-例5.19-指针的引用与传递参数
    C++入门经典-例5.18-通过引用交换数值
    C++入门经典-例5.17-右值引用的定义
    C++入门经典-例5.16-输出引用
    C++入门经典-例5.15-回收动态内存的一般处理步骤
    C++入门经典-例5.14-丢失的内存,关于内存泄漏
    C++入门经典-例5.13-内存安全,被销毁的内存
    C++入门经典-例5.12-动态内存的销毁
    C++入门经典-例5.11-动态分配空间,堆与栈
    C++入门经典-例5.10-指针作为返回值
  • 原文地址:https://www.cnblogs.com/pepper7/p/7193221.html
Copyright © 2011-2022 走看看