简体   繁体   中英

Variants of stream closing pattern in Java

I often see the following pattern both in Java documentation and in other people's code when dealing with streams:

FileInputStream fis = null;
try {
  fis = new FileInputStream("some.file");
  // do something useful with fis
} finally {
  if (fis != null) {
    fis.close();
  }
}

However I personally prefer a different pattern:

FileInputStream fis = new FileInputStream("some.file");
try {
  // do something useful with fis
} finally {
  fis.close();
}

I like the brevity of the latter and I think it's correct. But am I right about correctness? Is there an objective reason to prefer one over another?

Update

Even if I write the examples pretty much how I would write such code in real life my pattern is still more concise. Compare the documentation approach:

public Object processFile(String fn) throws MyException {
  FileInputStream fis = null;
  try {
    fis = new FileInputStream(fn);
    return new Object(); // somehow derived from fis
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  } finally {
    if (fis != null) {
      IOUtils.closeQuietly(fis);
    }
  }
}

with my approach:

public Object processFile(String fn) throws MyException {
  try {
    FileInputStream fis = new FileInputStream(fn);
    try {
      return new Object(); // somehow derived from fis
    } finally {
      IOUtils.closeQuietly(fis);
    }
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  }
}

Mine is one line shorter! :D

Interestingly my code does not change if you decide to use fis.close() instead of IOUtils.closeQuietly() while the "official" code will grow another 4 lines.

The second example that you have posted would not try/catch the possible errors that ... = new FileInputStream("..."); will return if it fails loading the file. In the first example though you automatically also catch the errors that the FileInputStream gives.

I would personally go with the first one to have better error handling.

The second example does not compile unless you throw the exception from the method, because FileNotFoundException is a checked exception.

Compile.java:5: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
        FileInputStream fis = new FileInputStream("some.file");

The first example also does not compile, because you are missing the appropriate catch block. Here is a compiling example.

import java.io.*;
public class Compile {

    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("some.file");
            // do something useful with fis

            if (fis != null) {
                fis.close();
            }
        }
        catch (FileNotFoundException fnf) {
        }
        catch (IOException ioe) {
        }
        finally {
        }
    }
}

Use try-with-resources :

try (FileInputStream fis = new FileInputStream("some.file")) {
    // some code here
}

To answer the question of why the recommended pattern is the way it is, consider the fact that new FileInputStream(String) throws a checked exception that you should probably catch; The

FileInputStream fis = null;
try {
    fis = new FileInputStream("somefile.txt");
} catch (FileNotFoundException e) {
    return false;
} finally {
    if (fis != null) {
        fis.close();
    }
}

allows you to handle it in the same try/catch block as your other file-related exceptions. This has brevity and reduced nesting advantages over the following:

try {
    FileInputStream fis = new FileInputStream("some.file");
    try {
        // do something useful with fis
    } finally {
        fis.close();
    }
} catch (FileNotFoundException e) {
    return false;
}

And if you're not catching the exception and just letting it propagate, then your suggestion is cleaner - however given the heavy use of checked exceptions in the IO libraries (including from class constructors), catching and handling an exception is common enough, and I think it makes sense to have just one pattern for tutorial purposes instead of showing someone a slightly different pattern each time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM