preface
The try-with-resouce script is a syntactic sugar introduced in JDK1.7 to make it easy to close IO data streams.
A common Java way of writing closed IO data streams
Before JDK 1.7, let’s take a look at the common way to write it
String readTextFromFile(String path) { BufferedReader br = null; StringBuilder sb = new StringBuilder(); try { br = new BufferedReader(new InputStreamReader(new FileInputStream(path))); String line = null; while ((line = br.readLine()) ! = null) { sb.append(line).append("\n"); } br.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (br ! = null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); }Copy the code
Look at the readTextFromFile method, which reads the contents of the data from a file with a BufferedReader and returns a string. This is a very traditional and standard way of writing IO read and close, using the finally mechanism, because finally must be executed, So the resource is guaranteed to be released, and since close throws an exception, adding a try-catch to the finally can be cumbersome.
The try – with – resources capitalization
Starting with JDK 1.7, we have a convenient way to shut down IO streams
String readTextFromFile(String path){ StringBuilder sb=new StringBuilder(); try(BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(path)))){ String line=null; while((line=br.readLine())! =null){ sb.append(line).append("\n"); } return sb.toString(); } catch(IOException e){ e.printStackTrace(); } return sb.toString(); }Copy the code
As you can see, without the finally, without the close call, it feels like half the code is shortened, and all that’s required is that you put it in the try when you instantiate the BufferedReader
try(BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(path)))){
}
The principle of
Let’s see what the bytecode compiled using the try-with-resources method looks like when decompiled
String readTextFromFile(String path) { StringBuilder sb = new StringBuilder(); try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(path))); String var5; try { String line = null; while(true) { if ((line = br.readLine()) == null) { var5 = sb.toString(); break; } sb.append(line).append("\n"); } } catch (Throwable var7) { try { br.close(); } catch (Throwable var6) { var7.addSuppressed(var6); } throw var7; } br.close(); return var5; } catch (IOException var8) { var8.printStackTrace(); return sb.toString(); }}Copy the code
As you can see, this is pretty much the same as our standard Java I/O stream closure, except that instead of using the finally mechanism, we call the close method in various catch methods. As you can see, try-with-resouce is essentially a syntactic sugar. The close method call is automatically added by the compiler when compiling to bytecode, which makes your logic more focused and your code cleaner. So since it’s grammatical sugar, can we take advantage of this grammatical feature ourselves? Looking at the source code for Reader,
And you can see that the Reader implements the Closeable interface, which has a close method, which is the BufferedReader’s close method that we called, and look at the Closeable interface
Can you see that the Closeable interface inherits from the AutoCloseble interface, automatically closing objects? Does it smell like that? When you enter the interface, you can see the comments for the class
/ * *
An object that may hold resources (such as file or socket handles)
until it is closed. The {@link #close()} method of an {@code AutoCloseable}
object is called automatically when exiting a {@code
try}-with-resources block for which the object has been declared in
the resource specification header. This construction ensures prompt
release, avoiding resource exhaustion exceptions and errors that
may otherwise occur.
@apiNote
It is possible, and in fact common, for a base class to
implement AutoCloseable even though not all of its subclasses or
instances will hold releasable resources. For code that must operate
in complete generality, or when it is known that the {@code AutoCloseable}
instance requires resource release, it is recommended to use {@code
try}-with-resources constructions. However, when using facilities such as
{@link java.util.stream.Stream} that support both I/O-based and
non-I/O-based forms, {@code try}-with-resources blocks are in
general unnecessary when using non-I/O-based forms.
@author Josh Bloch
@ since 1.7
* /
As you can see, the comment makes it clear that if this interface is implemented, then the try-with-resource syntax can be used to automatically close it. The Closeable interface inherits from Cloaseable and declares the close method in Cloaseable, but it throws an IOException that is different from the Exception that AutoCloseable’s close method throws. At the beginning, I did not understand the meaning of this writing method, but now it reduces the exception range of all I/O operations and only focuses on IOException, so as to avoid the ambiguous exception direction caused by the large exception range.
By implementing the AutoCloseable interface, we can enjoy the syntactic convenience of try-with-resources. Let’s define an AutoCloseClass class to verify this
public class Test { public static void main(String[] args) { try(AutoCloseClass d=new AutoCloseClass()){ System.out.println("hello world"); } catch(Exception e){ e.printStackTrace(); } } public static class AutoCloseClass implements AutoCloseable{ @Override public void close() throws IOException { System.out.println("close honey"); }}}Copy the code
You can see that you print Hello World, then close Honey
Kotlin writing
Now that Kotlin is being developed as a first language for Android, is there a similar syntax in Kotlin? There is, in fact, an inline extension method, use, that does the same thing.
public inline fun <T : Closeable? , R> T.use(block: (T) -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } var exception: Throwable? = null try { return block(this) } catch (e: Throwable) { exception = e throw e } finally { when { apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception) this == null -> {} exception == null -> close() else -> try { close() } catch (closeException: Throwable) { // cause.addSuppressed(closeException) // ignored here } } } }Copy the code
As you can see, use is an inline generic extension function, with the receiver passing ina function type for an interface type T that implements Closeable, receiving the generic parameter T, and finally calling the close method.
conclusion
Try-with-resources is a useful syntax sugar and is recommended in Effective Java for closing IO streams.
Follow my official account, “Old arsenic on a skateboard.”