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方法即可。

  • 相关阅读:
    求一个二维数组的最大子矩阵
    在一整型数组中找到此数组中子数组和的最大值
    软件工程个人小项目:写一个程序,分析一个文本文件(英文文章)中各个词出现的频率,并且把频率最高的10个词打印出来
    Redis 为什么这么快?
    在netfarmerwork3.5版本的winform下执行string串中的代码
    c# 反射(Reflection)详解
    string,特殊的引用类型
    c#使用HashSet<T>集合去重
    c# .Net重试机制
    观察者模式
  • 原文地址:https://www.cnblogs.com/gjl-blog/p/9108315.html
Copyright © 2011-2022 走看看