0

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

/**
 * 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

4 回答 4

2

在 GeoMap(Scanner scanner) 构造函数的末尾调用scanner.close()。

这将关闭在 GeoMap(String filename) 中创建的 Scanner,因为对它的引用作为扫描仪传递到 GeoMap(Scanner 扫描仪)。

本质上,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

本质上,扫描器的创建位置无关紧要,如果它在任何时候关闭,它在范围内的任何地方都会关闭。

于 2020-12-07T03:06:47.827 回答
1

让我们从这个开始:

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)));
}

首先,是否new Scanner(new File(fileName))存在潜在的资源泄漏?

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

在实践中,这是极不可能的。如果我们忽略类库中的错误,以及使用无法识别的字符集名称等应用程序错误,那么导致自发失败的唯一原因new Scanner可能会泄漏文件描述符等,那就是您是否有 OOME。但这很可能会触发完整的 GC。

那么之后会发生什么?

答案取决于先前对构造函数的责任所在的答案GeoMap(Scanner)

  • 如果责任在于那个构造函数,我们知道如何避免泄漏;看上面。

  • 否则......我们有问题:

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

总之,根据你如何指定和实现GeoMap(Scanner)GeoMap(String)构造函数可以在实践中实现为防漏。

于 2020-12-12T05:44:13.533 回答
1

我假设您的问题是,如果您在构造函数中创建了涉及的扫描仪,您只想关闭它fileName。我不认为你有一个init你的两个构造函数都调用的方法的想法有什么问题。我不认为这不优雅。

我想我要做的是创建第三个私有构造函数而不是init方法。无论哪种方式,实际上都是一样的,尽管在某些时候您可能希望能够传入一个您希望在构造函数调用结束时关闭的预构建 Scanner,在这种情况下,您可以将这个新的构造函数设为 public所以你可以从外面调用它。

在任何一种情况下,我都会将一个布尔“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();
    }
}
于 2020-12-07T03:26:18.983 回答
0

首先,你的类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

在这里,所有权由 flag 跟踪m_MayCloseScannerGeoMap不幸的是,这还没有解决您的资源泄漏问题:当不再使用实例时,扫描仪仍然不会关闭。

当您的GeoMap实例根本不拥有扫描仪时,您不在乎,扫描仪占用的资源是POOP 其他的问题)

好的,当您只需要扫描仪来初始化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 选项不起作用,则需要实例的析构函数。GeoMapJava 中不存在析构函数的概念,最接近它的是实现finalize(),但前段时间(最终在 Java 9 中)已弃用,这是有充分理由的。

看看这篇关于如何使用的帖子CleanerPhantomReference以及Closeable你的GeoMap班级。一开始看起来有点混乱,但最终显示出相对直截了当。

于 2020-12-07T08:46:53.240 回答