关闭构造函数链接期间生成的java scanner对象

txu3uszq  于 2021-06-30  发布在  Java
关注(0)|答案(4)|浏览(349)

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

/**
 * 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() 两个构造函数都调用的函数(不优雅。)
谢谢。

0ejtzxu1

0ejtzxu11#

让我们从这个开始:

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) 构造函数可以在实践中实现防漏。

5w9g7ksd

5w9g7ksd2#

在geomap(scanner)构造函数的末尾调用scanner.close()。
这将关闭在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

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

xpszyzbs

xpszyzbs3#

我假设您的问题是,您只想关闭涉及的扫描仪,如果您已经在您的构造函数中创建了它 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();
    }
}
xmd2e60i

xmd2e60i4#

首先,你们班 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 示例将根本不拥有扫描仪,您不在乎,扫描仪占用的资源是一个便便(其他人的问题)。
好的,当您只需要扫描仪来初始化 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() ,但这在一段时间前被否决了(最后是Java9),这是有充分理由的。
看看这篇关于如何使用 Cleaner , PhantomReference 以及 Closeable 为了你的 GeoMap 班级。一开始看起来有点混乱,但最后却显得相对直截了当。

相关问题