对scanner.close方法的误解以及没法补救的错误

scanner错误关闭致使的异常

public class test2 {

  public static void main(String[] args) {
    Scanner scanner1 = new Scanner(System.in);
    System.out.println("run scanner1.close()");
    scanner1.close();

    Scanner scanner2 = new Scanner(System.in);
    System.out.println("run scanner2.nextLine()");
    scanner2.nextLine();

  }
}

可是会在scanner2.nextLine()调用时抛出异常
java.util.NoSuchElementException: No line foundjava

缘由

下面是scanner的源码程序员

// The input source
    private Readable source;
public void close() {
        if (closed)
            return;
        if (source instanceof Closeable) {
            try {
                ((Closeable)source).close();
            } catch (IOException ioe) {
                lastException = ioe;
            }
        }
        sourceClosed = true;
        source = null;
        closed = true;
    }

因此调用close方法并不单单关闭scanner类,同时关闭了初始化时做为参数传入的Readable对象。
在示例代码中scanner1.close();关闭了System.in,因此虽然初始化scanner2没有问题,可是readLine()会报错。安全

是否能够经过从新开启System.in的方式补救呢?

遗憾的是,至少做为java的使用者来讲是不能够的,除非咱们能控制jvm运行。
这涉及到System.in是如何开启的,简单来讲由于System.in是特殊的系统资源,由jvm负责开启,没法经过java代码从新初始化System.in。
若是查看System.in的代码咱们就能发现它经过native方法实现初始化,在native方法中将控制台或文件句柄传输给System.in来完成。
一样的,Systerm.out System.err也是没法从新被开启的资源,对于它们,close方法应该被谨慎的调用。
(ps:实际上有什么缘由关闭呢?)eclipse

让咱们把问题拓展开来

实际上,全部的可以以System.in/out/err为构造器参数的包装器类的close方法都应该被考虑,
能够看到jdk的设计者并无区别对待普通的流和System.in/out/err,
而包装器类的close方法关闭底层流对于普通流来讲是很合理的,所以咱们能够推测事实上其余的包装器的close方法也可能致使关闭System.in/out/err。
经常使用的bufferinputstream就是如此。jvm

jdk7引入的带资源的try语法糖产生隐蔽的问题

jdk7引入的带资源的try语法糖隐式的帮助程序员调用close方法,
遗憾的是该方法也会产生上述问题,甚至更难被发现。ide

咱们有必要积极的关闭再也不须要的流嘛?如何对待io.close?如何预防该问题?

从性能的角度来讲,积极关闭流是必须的,实际上若是咱们使用findbugs等代码规范工具,能发现关闭io是被强烈推荐的。
若是你使用idea的话,建议在close以前,使用快捷键ctrl+b进入close方法的源代码来查看其关闭机制。这种方法很是简便,固然eclipse应该也有插件能够实现相似功能。
咱们也能够经过包装System.in/out/err来安全的使用它们,实际上利用装饰器模式,覆盖System.in/out/err的close方法便可。工具

相关文章
相关标签/搜索