有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

JAVAutil。扫描器关闭构造函数链接期间生成的Java扫描器对象

我正在使用构造函数链接,我担心它会导致资源泄漏。以下是我的两个构造函数:

/**
 * Constructor to build the map based off of a file. Redirects to the Scanner-based constructor
 * @param fileName the name of the file to open
 */
public GeoMap(String fileName) throws FileNotFoundException {
    this(new Scanner(new File(fileName)));
}

/**
 * Constructor to build the map based off of a Scanner. (Probably from an open file.)
 * @param scanner the Scanner to read
 */
public GeoMap(Scanner scanner) {
    // goes on to read the string data and make an object...

从任何类型的Scanner(键盘、文件等)创建对象都很重要,尽管它通常是从文件创建的。问题是,我认为这里正在发生资源泄漏。每当我阅读一个文件时,我喜欢在完成后关闭它。问题是,构造函数链接意味着this()调用必须是第一行。我倾向于这样做:

this(Scanner scannerToClose = new Scanner(new File(fileName)));

在我看来,这会给我一个Scanner的名字,然后我就可以结束了。但这似乎真的让编译器感到困惑——我从中得到了大约5个编译时错误,包括许多“找不到符号”的问题,这意味着编译器不适合这种类型的东西。Java支持这个吗?或者我需要创建两个构造函数调用的完全不同的initFromScanner()函数吗?(不优雅。)

谢谢


共 (4) 个答案

  1. # 1 楼答案

    我假设您的问题是,如果您在构造函数中创建了相关的扫描仪,并且该扫描仪采用fileName,那么您只想关闭它。我认为您的想法没有任何问题,即使用两个构造函数都调用的init方法。我不认为那是不雅的

    我想我要做的是创建第三个私有构造函数,而不是init方法。这两种方法实际上都是一样的,尽管可能在某个时候,您希望能够传入一个预构建的扫描程序,您希望在构造函数调用结束时关闭该扫描程序,在这种情况下,您可以将此新构造函数公开,以便从外部调用它

    在这两种情况下,我要做的是向新的构造函数/方法传递一个布尔“closeScanner”参数,该参数指示是否应该关闭扫描仪。以下是我在代码方面的想法:

    import java.io.File;
    import java.io.FileNotFoundException;
    import java.util.Scanner;
    
    class GeoMap {
    
        /**
         * Constructor to build the map based off of a file. Redirects to the Scanner-based constructor
         *
         * @param fileName the name of the file to open
         */
        public GeoMap(String fileName) throws FileNotFoundException {
            this(new Scanner(new File(fileName)), true);
        }
    
        /**
         * Constructor to build the map based off of a Scanner. (Probably from an open file.)
         *
         * @param scanner the Scanner to read
         */
        public GeoMap(Scanner scanner) {
            this(scanner, false);
        }
    
        private GeoMap(Scanner scanner, boolean closeScanner) {
            // goes on to read the string data and make an object...
            if (closeScanner)
                scanner.close();
        }
    }
    
  2. # 2 楼答案

    让我们从以下内容开始:

    public GeoMap(Scanner scanner) {
        ...
    }
    

    这里有资源泄漏吗?嗯,这取决于关闭Scanner的责任在哪里

    • 如果责任在于建造商,那么我们可以像这样堵住漏洞:

      public GeoMap(Scanner scanner) {
          try (Scanner s = scanner) {
              // original body
          }
      }
      

      这是理想的解决方案,但它假定Scanner的作用寿命是构造函数

    • 如果这是呼叫者的责任,那么呼叫者需要处理泄漏预防。这是可行的。。。但超出了这个问题的范围

    • 如果这既不是构造函数的责任,也不是调用方的责任,那么您需要将GeoMap本身视为一种资源,以及所有需要的资源

    现在我们考虑这个问题:

    public GeoMap(String fileName) throws FileNotFoundException {
        this(new Scanner(new File(fileName)));
    }
    

    首先,是否存在潜在的资源泄漏

    理论上,是的。Scanner构造函数可以打开文件的流,然后失败,使流保持打开状态

    实际上,这是极不可能的。如果我们忽略类库中的错误,以及应用程序错误,比如使用无法识别的字符集名称,那么new Scanner的自发故障可能会泄漏文件描述符等的唯一原因就是您得到了OOME。但无论如何,这可能会触发一个完整的GC

    那之后呢

    答案取决于前面关于GeoMap(Scanner)构造函数的职责所在的答案

    • 如果责任在于建造商,我们知道如何避免泄漏;见上文

    • 否则。。。我们有一个问题:

      • 有可能的解决方案,但可能涉及改变Scanner的使用方式
      • 还可能存在涉及直接使用构造函数的的泄漏

    总之,根据您指定和实现GeoMap(Scanner)的方式,GeoMap(String)构造函数可以实现为实际的防漏

  3. # 3 楼答案

    首先,类GeoMap应该定义它如何处理构造函数中提供给它的扫描器;通常,当允许它创建自己的Scanner实例(如示例中所示)时,策略是GeoMap实例可以使用该扫描仪执行任何操作,包括关闭它–这意味着它拥有它,并且所有权在相应的构造函数中转移

    如果不是这种情况(它不拥有扫描器),则必须删除GeoMap(String)构造函数(因为,当GeoMap实例不拥有它时,还有谁会拥有它并在以后处理它呢?),或者,您必须采用与以下类似的设计:

    class GeoMap 
    {
        private final Scanner m_Scanner;
        private final boolean m_MayCloseScanner;
    
        /**
         *  Creates a GeoMap from a file.
         *
         *  @param fileName The name of the file to open.
         */
        public GeoMap( String fileName ) throws FileNotFoundException 
        {
            this( new Scanner( new File( fileName ) ), true );
        }  // GeoMap()
    
        /**
         *  Creates a GeoMap from a Scanner instance.
         *
         *  @param scanner The Scanner to read
         */
        public GeoMap( Scanner scanner ) 
        {
            this( scanner, false );
        }  // GeoMap()
    
        /**
         *  Internal constructor.
         *  
         *  @param scanner The scanner to read.
         *  @param mayClose true, if this instance of GeoMap may close the
         *      given Scanner instance, false otherwise.
         */
        private GeoMap( Scanner scanner, boolean mayClose ) 
        {
            m_Scanner = scanner;
            m_MayCloseScanner = mayClose;
        }  // GeoMap()
    
        …
    }
    // class GeoMap
    

    这里,所有权由标志m_MayCloseScanner跟踪。 不幸的是,这还不能解决您的资源泄漏问题:当不再使用GeoMap实例时,扫描器仍然不会关闭

    当你的GeoMap实例根本不拥有扫描器时,你不在乎,扫描器占用的资源是一个废物(aproblemofotherppeople)

    好的,当您将只需要扫描器来初始化GeoMap实例时,您可以使用一个init()方法在完成后关闭扫描器:

    …
    public void init()
    {
        // Do something with the scanner ...
        …
    
        // Close the scanner when done.
        m_Scanner.close()
    }  // init()
    …
    

    当然,当GeoMap可能拥有或不拥有扫描仪时,结束行需要如下所示:if( m_MayCloseScanner ) m_Scanner.close;

    但是如果init选项不起作用,那么GeoMap实例需要一个析构函数。Java中不存在析构函数的概念,最接近它的是实现finalize(),但这一概念在一段时间前就被弃用了(最后是Java 9),理由很充分

    看看this post如何为GeoMap类使用CleanerPhantomReferenceCloseable。一开始看起来有点混乱,但最后显示出相对直截了当

  4. # 4 楼答案

    呼叫扫描仪。在GeoMap(扫描仪)构造函数的末尾关闭()

    这将关闭在GeoMap(字符串文件名)中创建的扫描仪,因为对它的引用作为扫描仪传递到GeoMap(扫描仪)中

    实际上,scanner变量指向创建的新扫描仪,因此调用scanner。close()在任何方法中的任何位置,对它可能在范围内的任何和所有其他方法关闭它

    下面是一个演示扫描仪面向对象特性的程序:

    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.util.Scanner;
    public class Main
    {
        static class Test
        {
            String name;
            public Test(String filename) throws FileNotFoundException
            {
                this(new Scanner(new File(filename)));
            }
            public Test(Scanner scanner)
            {
                name = scanner.nextLine();//body of constructor
                scanner.close();
                System.out.println("Your name is "+ name);
                scanner.close();
                
                /*These next lines of code show that the Scanner is closed */
                String throwsException = scanner.nextLine();
                System.out.println(throwsException + "here");//unreachable
            }
        }
        public static void main(String[] args)
        {
            try
            {
                Test temp = new Test("input.txt");
            }
            catch(Exception e)
            {
                System.out.println(e);
            }
    
        }
        
    
    }
    
    

    输入。txt: Smitty

    输出:

    Your name is Smitty
    java.lang.IllegalStateException: Scanner closed
    

    本质上,扫描仪创建在哪里并不重要,如果它在任何一点关闭,它在范围内的任何地方都会关闭