问题
Consider :
public static void read(String filename) throws IOException {
String charsetName = "UTF-8";
InputStream file = new FileInputStream(filename); // say no problem
InputStreamReader reader = new InputStreamReader(file, charsetName);
BufferedReader buffer = new BufferedReader(reader);
try {
buffer.readLine();
} finally {
try {
buffer.close();
} catch (IOException e) {
// report at least
e.printStackTrace();
}
}
}
If new InputStreamReader(file, charsetName)
throws UnsupportedEncodingException
, the buffer.close();
line will never be called. The alternative is extra verbose :
InputStream file = new FileInputStream(filename);
try {
InputStreamReader reader = new InputStreamReader(file);
try {
BufferedReader buffer = new BufferedReader(buffer);
try {
buffer.readLine();
} finally {
buffer.close(); // should catch
}
} finally {
reader.close(); // should catch
}
} finally {
file.close(); // should catch
}
and it needlessly closes all streams (while output.close();
should suffice - actually any of them should suffice if successful - see comments in the code by Skeet).
Wrapping the constructors
BufferedReader buffer = new BufferedReader(
new InputStreamReader(new FileInputStream(filename), charsetName));
is essentially just hiding the problem.
Notice I use the try-finally idiom suggested by @TomHawtin-tackline here for instance - but the more common approach :
public static void read(String filename) throws IOException {
String charsetName = "UTF-8";
InputStream file = null;
InputStreamReader reader = null;
BufferedReader buffer = null;
try {
file = new FileInputStream(filename);
reader = new InputStreamReader(file, charsetName);
buffer = new BufferedReader(reader);
buffer.readLine();
} finally {
try {
if(buffer != null) buffer.close();
} catch (IOException e) {
// report at least
e.printStackTrace();
}
// Rinse and repeat for the rest
}
}
is as awkward.
Question :
How would you handle this case ?
Would :
public static void read(String filename) throws IOException {
String charsetName = "UTF-8";
InputStream file = new FileInputStream(filename);
try {
InputStreamReader reader = new InputStreamReader(file, charsetName);
BufferedReader buffer = new BufferedReader(reader); // Eclipse warning
buffer.readLine();
// notice that if these were out put streams we SHOULD FLUSH HERE
} finally {
try {
file.close();
} catch (IOException e) {
// report at least
e.printStackTrace();
}
}
}
do ? In other words closing the innermost stream (contrary to what is usually asked) would be the cleanest solution when there are more than 2 wrapped streams ? Are there cases when finally
the decorator should be also closed() ? See for instance points here. Notice that Eclipse warns :
Resource leak: 'buffer' is never closed
Is Eclipse right ?
This is Java 6 - Android is Java 6 only I remind you. Trying to factor out IO code in some utility classes, once and for all
回答1:
In your last approach , Resource leak
warning as shown by eclipse is correct according to the rules of closing streams. closing the innermost stream is only closing that stream but not the other streams that has wrapped up that stream. But closing the outermost stream is one time operation that will automatically close all the underlying streams.
As specified in the article Always close streams :
If multiple streams are chained together, then closing the one which was the last to be constructed, and is thus at the highest level of abstraction, will automatically close all the underlying streams. So, one only has to call close on one stream in order to close (and flush, if applicable) an entire series of related streams.
So In my opinion following code is solution for your case:
public static void read(String filename) throws IOException {
String charsetName = "UTF-8";
InputStream file = null;
InputStreamReader reader = null;
BufferedReader buffer = null;
try
{
file = new FileInputStream(filename);
reader = new InputStream(file,charsetName);
buffer = new BufferedReader(reader);
buffer.readLine();
}
finally
{
closeQuietly(buffer,reader,file);
}
}
EDIT
As suggested by @jtahlborn the code written within finally block is wrapped within a utility method as follows:
public static void closeQuietly(Closeable... closeables)
{
if(closeables == null)
{
return;
}
for (Closeable c : closeables)
{
doCloseQuietly(c);
}
}
public static void doCloseQuietly(Closeable c)
{
try
{
if (c != null)
{
c.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
回答2:
The second pattern (the check for null one) could be amended as :
public static void readSO2(String filename) throws IOException {
String charsetName = "UTF-8";
InputStream file = null;
InputStreamReader reader = null;
BufferedReader buffer = null;
try {
file = new FileInputStream(filename);
reader = new InputStreamReader(file, charsetName);
buffer = new BufferedReader(reader);
buffer.readLine();
} finally {
try {
if (buffer != null) buffer.close();
else if (reader != null) reader.close();
else if (file != null) file.close();
} catch (IOException e) {
// report at least
e.printStackTrace();
}
}
}
- Pros : one close, one possible exception, no flush needed (for output streams)
- Cons : still inelegant : null dance, one must be careful in the order of the ifs, no way to generalize
So question still stands.
来源:https://stackoverflow.com/questions/16363642/java-6-io-wrapped-streams-closing