zoukankan      html  css  js  c++  java
  • Please Call Me NIO

      与其他语言相比,Java的IO功能显得异常复杂,各种流操作,通过程序员多次封装才可以达到操作文件的目的。自从jdk1.4之后,java提供了一个新的api完成IO操作,人称New IO(NIO),使用java nio包来进行IO操作,相比原先的API相比要方便多了(每次用老的api写IO操作,心中就会有千万的草泥马飘过)。Jdk1.7发布后,java nio又有了新的改进,人称NIO.2。终于java的IO操作可以和其他语言接轨了T^T。

     

    下面就来了解下java的NIO包(注意了啊~这里我使用的jdk版本是1.7)

     

    探索Path与Files

    在java 7 中文件路径和对文件的操作被很清晰地用两个类分开了,路径全部使用Path来处理,而文件的操作则通过Files类来操作。

    Path的创建非常简单,通过Paths.get()方法就可以获得了。

    Path path=Paths.get("F:\javawork\eclipsework");
    System.out.println(path.toAbsolutePath());
    System.out.println(path.getFileName());
    System.out.println(path.getRoot());
    System.out.println(path.getParent());
    System.out.println(path.getNameCount());

    通过这个path对象,就可以获得很多和路径有关的信息了。

    为了兼容java 6 ,File类中特别添加了一个toPath() 方法帮助File类获取Ptah对象,于此同时,Path也拥有一个toFile()方法,方便两个类相互转换。

    有了Path的帮助,接下来就是处理文件的操作了。我们在获取的了相应的路径之后,现在就需要Files类来操作文件了。

    之前提到路径的处理和文件的处理被分的很清楚,那么文件的相关信息,就只有由Files类来完成了。

    Path path=Paths.get("F:\javawork\eclipsework\jdk7Test\src\pro\app\hello2.txt");
    System.out.println(Files.getLastModifiedTime(path));
    System.out.println(Files.size(path));
    System.out.println(Files.isSymbolicLink(path));
    System.out.println(Files.isDirectory(path));
    System.out.println(Files.readAttributes(path,"*"));

    使用Files才完成文件的创建、删除、复制和移动操作也是非常方便。

    //path和target均是Path类的对象
    Files.createFile(path);    //创建文件
    Files.delete(path);    //删除文件
    Files.copy(path, target);    //复制文件
    Files.move(path, target);    //移动文件

    同时copy()和move()方法均有第三个参数(通过import static java.nio.file.StandardCopyOption.*;获得常量)。

    REPLACE_EXISTING  替换已经存在的文件
    COPY_ATTRIBUTES  复制文件属性        
    ATOMIC_MOVE  确保两边操作均成功,否则回滚

    文件的读取与写入

    想到要用java对文件操作就很痛苦,有木有~流过来,输出入~泥煤哒!!jdk1.7已经开始解放大家的痛苦了。(当然java原先的IO操作让程序员有更高的自由度,可以非常容易滴处理IO的细节,但有时候,咱真的没有那个需求啊~)。

     

    Path path=Paths.get("test.txt");
    List<String> lines=Files.readAllLines(path,StandardCharsets.UTF_8);        for(String line:lines){
      System.out.println(line);
    }

     

    很简单有木有,如果想通过byte类型获取数据可以这么操作。

    byte[] bytes=Files.readAllBytes(path);
    for(byte b:bytes){
        String temp=new String(new byte[]{b});
        System.out.print(temp);
    }

    读了文件之后,现在就要开始写入文件了,这里使用Files类的write方法~

    Path path=Paths.get("F:\javawork\eclipsework\jdk7Test\src\pro\app\hello2.txt");
    String temp="hello world~";
    Files.write(path,temp.getBytes(),StandardOpenOption.APPEND);

    不要太轻松啊~啊哈哈哈哈哈。

    异步IO

    NIO给我们带来最大的惊喜就是异步的IO操作了,相比传统的IO方式,NIO通过异步操作极大地提升了IO的通信。异步IO允许程序在结束IO操作之前,处理其他的事情。这样就可以充分地利用CPU了。想象一下在完成一个100G的文件操作时,如果是老版本的api,我们在完成IO操作之前,除了等待还是无奈的等待~~~

    新的异步IO主要有两种形式,将来式回调式(有种学英语的感觉orz)。我们先来看看将来式。

    Path path=Paths.get("F:\javawork\eclipsework\jdk7Test\src\pro\app\hello2.txt");
    //异步打开文件
    try(AsynchronousFileChannel channel=AsynchronousFileChannel.open(path)){
      ByteBuffer buffer=ByteBuffer.allocate(100_000);
       Future<Integer> result=channel.read(buffer, 0);
       //使用isDone()判断result是否结束
         while(!result.isDone()){
         //do something else
       }
       buffer.flip();
       while(buffer.hasRemaining()){
         System.out.print((char) buffer.get());
       }
       //clear()方法会清空整个缓冲区
       buffer.clear();
       Integer byteRead=result.get();
       System.out.println(byteRead);
    }catch(IOException | ExecutionException | InterruptedException e){
      System.out.println(e.getMessage());
    }

    在完成读取之前,我们可以在 while(!result.isDone())中完成其他的事儿~

     

     

    这里可以复习下nio的一些概念,Java NIO 是面向缓存Buffer的IO操作方式。

    Buffer的本质是一块可以读写数据的内存,NIO将其封装成一个对象。可以将它视作一个集装箱,咱把货物装好了,打包就可以从通道送出去了。

    而Channel是IO操作中的通道,我们通过通道获得数据,输出数据,好比是马路,我们想要搬运货物都得通过这个通道。

    前面提到Buffer是一块可以读写的内存,一般我们使用Buffer有这么几个步骤:

    1、  声明Buffer大小
    2、  写入数据到buffer
    3、  调用filp()方法
    4、  从Buffer中读取数据,放到相应的位置
    5、   调用clear()或者compact()方法清理Buffer的内存空间

    Buffer有这么几个类型:ByteBuffer、MappedByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer

    很惊讶有木有!这就是java的基本类型加了个Buffer啊,想要什么类型的数据,咱就用什么类型的Buffer,每次使用之前就通过allocate()方法来给Buffer分配大小。

     

    再有,之前的jdk版本(1.7之前的版本)写IO操作最怕忘记写close()方法了,在新的api中try语句之后可以接上一个括号,在括号中声明需要保护的资源,然后交给java来关闭这个资源。

     

    接下来再看看回调式,比起将来式的写法,这种更加自然一些。有点像ajax的写法。

    Path path=Paths.get("test.txt");
    try(AsynchronousFileChannel channel=AsynchronousFileChannel.open(path);){
      ByteBuffer buffer=ByteBuffer.allocate(100_000);
       channel.read(buffer,0,buffer,new CompletionHandler<Integer,ByteBuffer>(){
         @Override
          public void completed(Integer result,ByteBuffer attachment){
            System.out.println(result);
          }
          @Override
          public void failed(Throwable exc, ByteBuffer attachment) {
            System.out.println(exc.getMessage());
          }
       });
    }catch(IOException e){
      System.out.println(e.getMessage());
    }

    主线程派出一个侦查员CompletionHandler,来观察独立线程的IO操作,如果IO操作成功则调用CompletionHandler中的completed()中的方法,如果失败就调用failed方法,采取行动挽救损失。

    总结

    Path类与Files类

    文件的写入与读取

    IO的异步

  • 相关阅读:
    mysql学习笔记
    MySQL的MySQL 的JDBC的安装与使用
    numpy的使用方法
    Linux命令
    MongoDB数据库
    爬虫请求库之selenium
    解析库beautifulsoup
    Requests属性
    正向代理、反向代理
    爬虫基本原理
  • 原文地址:https://www.cnblogs.com/whthomas/p/3925008.html
Copyright © 2011-2022 走看看