zoukankan      html  css  js  c++  java
  • 对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 found

    原因

    下面是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:实际上有什么原因关闭呢?)

    让我们把问题拓展开来

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

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

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

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

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

  • 相关阅读:
    Global 文件中挂接HttpModule事件的方法列表参考
    百行代码打造一个DI容器(支持瞬时生命周期、单利生命周期、构造函数自动注入、属性自动注入、字段自动注入)
    松耦合服务调用利器服务分发器
    架构视角面面观之: WebPage能支持DI注入那该多好
    架构视角面面观之: WebPage能像MVC的ViewPage那样支持泛型节约不少代码量的?
    安装 Nuget 插件过程以及注意事项
    给Web Api 再加把柴让它更火起来
    Mini 容器泛型类型的使用
    JAVA虚拟机08垃圾回收HotSpot的算法实现细节
    Web UI 设计(网页设计)命名规范
  • 原文地址:https://www.cnblogs.com/gjl-blog/p/9108315.html
Copyright © 2011-2022 走看看