使用try-with-resources和System.in

4

好的,这里是一个可能不是最好的问题,但是我卡住了,找不到在网上的答案。

这段代码第二次不会从标准输入读取:

try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in)))
{
    input = br.readLine();
}
catch (final Exception e)
{
    System.err.println("Read from STDIN failed: " + e.getMessage());
}
// do some processing
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in)))
{
    input = br.readLine();
}
catch (final Exception e)
{
    System.err.println("Read from STDIN failed: " + e.getMessage());
}

我知道Java的try-with-resources递归关闭链中的所有流,因此在第一次读取System.in后,System.in将被关闭。是否有好的解决方法?或者我真的需要自己处理流的关闭?
更新:我尝试手动处理流的关闭(这是Java6风格的)。如果有人感兴趣,这里是一个链接code。但我注意到这种链式关闭行为并不是来自try-with-resources,而是来自close方法的实现。所以我从那次尝试中没有获得任何好处。
我选择了fge的解决方案,因为它是最详细的。它直接为我工作了。
总的来说,我觉得很奇怪,Java没有这样的开箱即用的解决方案,因为有些系统流不应该被关闭。

1
你可以创建自己的 InputStream 类,它不会在 .close() 上关闭底层资源;尽管这违反了 .close() 的契约。 - fge
或者在这种情况下,您可以避免使用try-with-resources,因为您不希望流被关闭,而这正是try-with-resources所做的。 - JB Nizet
4个回答

1

一种解决方法是创建一个自定义的InputStream类,它将委托给另一个类,但在自身关闭时不会关闭它。例如:

public class ForwardingInputStream
    extends InputStream
{
    private final InputStream in;
    private final boolean closeWrapped;

    public ForwardingInputStream(final InputStream in, final boolean closeWrapped)
    {
        this.in = in;
        this.closeWrapped = closeWrapped;
    }

    public ForwardingInputStream(final InputStream in)
    {
        this(in, false);
    }

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

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

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

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

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

    @Override
    public void close()
        throws IOException
    {
        if (closeWrapped)
            in.close();
    }

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

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

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

请注意,在您的情况下,可能更容易的解决方案是扩展InputStreamReader类,因为该类不是final的,只需覆盖.close()方法即可。

1
我认为原帖作者已经意识到了这一点。他正在寻求解决方法。 - JB Nizet

1
这确实是一个小问题。我不知道是否存在一些常见库(如Apache Commons IO,Google Guava等)中的解决方案,但您可以自己编写一个简单的类来处理此问题。
编写一个类,它是一个InputStream并包装了一个InputStream,通过委托调用已包装的流覆盖所有公共方法,除了close方法外,该方法根本不执行任何操作。
public final class NonClosingInputStream extends InputStream {
    private final InputStream wrappedStream;
    public NonClosingInputStream(final InputStream wrappedStream) {
        this.wrappedStream = Objects.requireNonNull(wrappedStream);
    }
    @Override
    public void close() {
        // do nothing
    }
    // all other methods
}

System.in包装在这个类的实例中将解决您的问题。

1

我认为这个问题比使用try-with-resources更普遍,因为其他人可能会使用旧版本的Java并自己关闭BufferedReader。这会让你陷入与现在一样的情况。
我在相关的SO问题中找到了一个解决这个更普遍情况的答案。那里给出的答案是使用Apache Commons IO,它有一个名为CloseShieldInputStream的流代理。如果这是您从Commons IO要使用的唯一内容,则还可以考虑编写代理类而不是依赖于像Commons IO这样的大型库。


0

这是一个老问题,但这里有一个更简洁的解决方案:

try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in)
{public void close() throws IOException {}})) {
    input = br.readLine();
}

这样做可以确保通过将带有空重写的close()方法的InputStreamReader传递给BufferedReader的构造函数,不会关闭System.in


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