关闭Scanner而不关闭System.in

12

我正在尝试将应用程序中一个经常使用的大部分重新组织为独立的方法,以便于维护。

其中一些方法需要用户输入并进行输入验证,因此我使用了Scanner和System.in。但是,当我关闭Scanner时,也会关闭System.in。

所以我的问题是,我只能通过CloseShieldInputStream来保护System.in不关闭,还是应该开始向这些方法传递Scanner?


请发布一些代码... - nkukhar
6
你真的需要关闭Scanner吗?我建议让垃圾回收器处理它-没有关闭底层对象的方法。 - ddmps
2
我的方法声明了一个Scanner,读取并返回nextLine(),关闭Scanner并在下一次运行时让我头疼。 如果我不关闭它,Eclipse会麻烦我关于潜在的资源泄漏,那么忽略它是否安全? - deepy
1
在这种情况下,Eclipse是完全错误的 - 或者说,它过于谨慎了。关闭Scanner除了关闭底层InputStream之外没有任何有用的效果,因此如果您不想关闭System.in,则不要在Scanner上调用close() - kaya3
3个回答

19

只需使用自定义的FilterInputStream而不是System.in:

new FilterInputStream(System.in) {
    @Override
    public void close() throws IOException {
        //don't close System.in! 
    }
}

2
这基本上就是commons的CloseShieldInputStream所做的事情,如果你发现自己处于这种位置,你可能需要重新审视你的软件设计。除非你正在使用第三方解决方案,否则不应该需要这样的解决方案。(适用于所有答案) - deepy
@deepy,我不理解你的推理。为什么这不是必要的?如果需要在程序的生命周期中多次使用扫描器,除了将扫描器变量有效地设置为全局变量之外,还有什么其他方法可以处理这种情况? - Emmanuel
@Emmanuel 你问了一个关于8年前问题的问题,但这个问题不是如何重用扫描器,而是如何防止 Scanner 关闭时关闭 System.in - deepy
@deepy,我看到这个问题已经很久了,但我仍然希望你能收到我的问题通知。可惜,你没有收到。 你的评论并不是对这个问题的确切回答。你表达了一个我不理解的观点,即“...如果你最终处于这种位置,你可能需要重新审视你的软件设计”。我不明白如何在该位置结束(想要关闭扫描仪但不关闭System.in)意味着软件存在设计缺陷。我希望你能澄清这个观点。我只是想学习。 - Emmanuel

3
您可以通过实现自定义装饰器来忽略关闭。
public class UnClosableDecorator extends InputStream {

    private final InputStream inputStream;

    public UnClosableDecorator(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public int read() throws IOException {
        return inputStream.read();
    }

    @Override
    public int read(byte[] b) throws IOException {
        return inputStream.read(b);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return inputStream.read(b, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
        return inputStream.skip(n);
    }

    @Override
    public int available() throws IOException {
        return inputStream.available();
    }

    @Override
    public synchronized void mark(int readlimit) {
        inputStream.mark(readlimit);
    }

    @Override
    public synchronized void reset() throws IOException {
        inputStream.reset();
    }

    @Override
    public boolean markSupported() {
        return inputStream.markSupported();
    }

    @Override
    public void close() throws IOException {
        //do nothing
    }
}

在主函数中使用它

public static void main(String[] args) throws Exception {
        System.setIn(new UnClosableDecorator(System.in));
}

这不就相当于编写自己的CloseShieldInputStream版本吗? - deepy
我不太确定你的意思,但如果你需要在关闭输入流时执行某些操作,请在UnClosableDecorator.close()方法中执行这些操作。 - nkukhar
1
CloseShieldInputStream是一个代理流,可以防止底层输入流被关闭。 http://commons.apache.org/io/apidocs/org/apache/commons/io/input/CloseShieldInputStream.html - deepy
实际上,我的实现与CloseShieldInputStream不同。CloseShieldInputStream在关闭时会将输入流替换为虚拟对象,在读取时始终返回-1(表示InputStream已关闭),而我的实现在关闭时什么也不做。我不确定哪种解决方案更适合您。 - nkukhar
2
我怀疑最好还是开始传递扫描器,这似乎是最不痛苦的方式。 - deepy

1

您可以不关闭它,只需将占位变量设置为null即可。


Eclipse会发出有关扫描仪从未关闭的警告,可以安全地忽略吗? - deepy
我认为这是一种可以改进的编程风格。程序员可能最终会留下必须关闭的未关闭资源。 - Raúl Salinas-Monteagudo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接