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+"\n");//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知识点总结

posted @ 2017-07-17 10:47  cello_ist  阅读(185)  评论(0编辑  收藏  举报