我正在编写一个连接到网站并从中读取一行的应用程序。我是这样做的:
try{
URLConnection connection = new URL("www.example.com").openConnection();
BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String response = rd.readLine();
rd.close();
}catch (Exception e) {
//exception handling
}
好吗?我的意思是,我在最后一行关闭了BufferedReader,但我没有关闭InputStreamReader。我应该从connection.getInputStream创建一个独立的InputStreamReader,还是从独立的InputStreamReader创建一个BufferedReader,而不是关闭所有两个读者?我认为最好将结束方法放在finally块中,如下所示:
InputStreamReader isr = null;
BufferedReader br = null;
try{
URLConnection connection = new URL("www.example.com").openConnection();
isr = new InputStreamReader(connection.getInputStream());
br = new BufferedReader(isr);
String response = br.readLine();
}catch (Exception e) {
//exception handling
}finally{
br.close();
isr.close();
}
但它很难看,因为关闭方法可以抛出异常,所以我必须处理或抛出它。
哪种解决方案更好?或者什么是最好的解决方案?
Java中资源获取和发布的一般习惯是:
final Resource resource = acquire();
try {
use(resource);
} finally {
resource.release();
}
注意:
try
应立即跟进收购。这意味着你不能将它包装在装饰器中并保持安全(并删除空格或将东西放在一行上没有帮助:)。finally
,使用null
。否则你会有杂乱的代码和NPE的潜力。final
块中捕获(Java会让你误入歧途)。你可以用try
抽象这个废话,所以你不必重复自己(只是写了很多样板)。
关闭BufferedReader就足够了 - 这也关闭了底层读者。
Execute Around idiom用于关闭流(关闭可能会抛出另一个异常)。
好吗?我的意思是,我在最后一行关闭了BufferedReader,但我没有关闭InputStreamReader。
除了它应该在Yishai posted a nice pattern完成(以便确保关闭,即使在例外的情况下),它是好的。 Java IO类使用装饰器模式。关闭将被委托给底层流。
但它很难看,因为关闭方法可以抛出异常,所以我必须处理或抛出它。
当关闭抛出异常时,通常只意味着另一方已被关闭或删除,这完全超出您的控制范围。您可以在最高日志或忽略它。在一个简单的应用程序中,我会忽略它。在关键任务应用程序中,我会记录它,只是为了确定。
在一个坚果中,您的代码可以重写为:
finally
在Java 7中,将有自动资源处理,这将使您的代码简洁如下:
BufferedReader br = null;
try {
URLConnection connection = new URL("www.example.com").openConnection();
br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String response = br.readLine();
}catch (Exception e) {
//exception handling
}finally{
if (br != null) try { br.close(); } catch (IOException ignore) {}
}
也可以看看:
try (BufferedReader br = new InputStreamReader(new URL("www.example.com").openStream())) {
String response = br.readLine();
} catch (Exception e) {
//exception handling
}
How to use URLConnection
你在没有赋值的情况下声明一个变量(BufferedReader br = null;
不计算 - 在这种情况下它是无用的赋值)。这是Java中的代码“气味”(ref null
; Effective Java,有关变量声明的更多信息)。
Code Complete
首先,你只需要关闭最顶级的流装饰器(}finally{
br.close();
isr.close();
}
将关闭br
)。其次,如果isr
抛出异常,则不会调用br.close()
,因此这不是声音代码。在某些异常情况下,您的代码将使用isr.close()
隐藏原始异常。
NullPointerException
如果isr = new InputStreamReader(connection.getInputStream());
构造函数抛出任何类型的运行时异常(不可否认)事件,则不会关闭来自连接的流。
利用InputStreamReader
接口来减少冗余。
以下是我编写代码的方法:
Closeable
注意:
前段时间,我花了一些时间思考如何URLConnection connection = new URL("www.example.com").openConnection();
InputStream in = connection.getInputStream();
Closeable resource = in;
try {
InputStreamReader isr = new InputStreamReader(in);
resource = isr;
BufferedReader br = new BufferedReader(isr);
resource = br;
String response = br.readLine();
} finally {
resource.close();
}
。
我认为将结束方法放在finally块中会更好
是的,总是。因为可能发生异常并且资源未正确释放/关闭。
您只需要关闭最外层的阅读器,因为它将负责关闭任何封闭的阅读器。
是的,这很难......现在。我认为有计划在Java中使用avoid leaking resources/data when things go wrong。
正如其他人所建议的那样,我会使用apache commons IO,主要是automatic resource management和IOUtils.toString(InputStream):
IOUtils.closeQuietly(InputStream)
对于java.io中的任何嵌套流和读取器,您不需要多个close语句。很难在一个单独的事件中关闭多个东西 - 大多数构造函数都可以抛出异常,因此您将尝试关闭尚未创建的东西。
如果要关闭流,无论读取是否成功,那么您需要输入到finally。
不要为变量赋值null,然后比较它们以查看事先是否发生了事情;而是构建您的程序,以便只有在不抛出异常时才能到达关闭流的路径。除了用于迭代for循环的变量之外,变量不需要改变值 - 我倾向于将所有内容标记为final,除非有其他要求。在你的程序周围有标记,告诉你如何获得当前正在执行的代码,然后根据这些标志改变行为,这是一种程序性(甚至是结构化的)编程风格。
如何嵌套try / catch / finally块取决于您是否要以不同方式处理不同阶段抛出的异常。
public String readFromUrl(final String url) {
InputStream stream = null; // keep this for finally block
try {
stream = new URL(url).openConnection().getInputStream(); // don't keep unused locals
return IOUtils.toString(stream);
} catch (final IOException e) {
// handle IO errors here (probably not like this)
throw new IllegalStateException("Can't read URL " + url, e);
} finally {
// close the stream here, if it's null, it will be ignored
IOUtils.closeQuietly(stream);
}
}
private static final String questionUrl = "http://stackoverflow.com/questions/3044510/";
public static void main ( String...args )
{
try {
final URLConnection connection = new URL ( args.length > 0 ? args[0] : questionUrl ).openConnection();
final BufferedReader br = new BufferedReader ( new InputStreamReader (
connection.getInputStream(), getEncoding ( connection ) ) );
try {
final String response = br.readLine();
System.out.println ( response );
} catch ( IOException e ) {
// exception handling for reading from reader
} finally {
// br is final and cannot be null. no need to check
br.close();
}
} catch ( UnsupportedEncodingException uee ) {
// exception handling for unsupported character encoding
} catch ( IOException e ) {
// exception handling for connecting and opening reader
// or for closing reader
}
}
需要检查连接的getEncoding
和getContentEncoding()
的结果,以确定网页的编码;您的代码只使用平台的默认编码,这可能是错误的。
你的例子在结构化方面是不寻常的,因为它非常程序化;通常,您会在较大的系统中分离打印和检索,并允许客户端代码处理任何异常(或者有时捕获并创建自定义异常):
getContentType()
正如McDowell指出的那样,如果public static void main ( String...args )
{
final GetOneLine getOneLine = new GetOneLine();
try {
final String value = getOneLine.retrieve ( new URL ( args.length > 0 ? args[0] : questionUrl ) );
System.out.println ( value );
} catch ( IOException e ) {
// exception handling for retrieving one line of text
}
}
public String retrieve ( URL url ) throws IOException
{
final URLConnection connection = url.openConnection();
final InputStream in = connection.getInputStream();
try {
final BufferedReader br = new BufferedReader ( new InputStreamReader (
in, getEncoding ( connection ) ) );
try {
return br.readLine();
} finally {
br.close();
}
} finally {
in.close();
}
}
抛出,你可能需要关闭输入流。
在Java 8的范围内,我会使用相同的:
new InputStreamReader