==Suppression Behavior== If two exceptions are thrown (e.g., by an operation and closing a resource), one of them is re-thrown, and the other is [[java.lang.Throwable#addSuppressed added to it as a suppressed exception]]. If the two exceptions are of different 'severities' (see below), the one of a higher severity is re-thrown, and the one of a lower severity is added to it as a suppressed exception. If the two exceptions are of the same severity, the one thrown first is re-thrown, and the one thrown second is added to it as a suppressed exception. If an exception is a [[scala.util.control.ControlThrowable `ControlThrowable`]], or if it does not support suppression (see [[java.lang.Throwable `Throwable`'s constructor with an `enableSuppression` parameter]]), an exception that would have been suppressed is instead discarded. Exceptions are ranked from highest to lowest severity as follows: -`java.lang.VirtualMachineError` -`java.lang.LinkageError` -`java.lang.InterruptedException` and `java.lang.ThreadDeath` - [[scala.util.control.NonFatal fatal exceptions]], excluding `scala.util.control.ControlThrowable` -`scala.util.control.ControlThrowable` - all other exceptions When more than two exceptions are thrown, the first two are combined and re-thrown as described above, and each successive exception thrown is combined as it is thrown.
如果 readLine 和 close 同时抛出异常,那么 finally 块里面的异常将会被抛出,其他异常将会被抑制
1 2 3 4 5 6 7 8 9 10 11 12 13
/* However, in this example, if the methods readLine and close both throw exceptions, then the method readFirstLineFromFileWithFinallyBlock throws the exception thrown from the finally block; the exception thrown from the try block is suppressed. */ static String readFirstLineFromFileWithFinallyBlock(String path)throws IOException { BufferedReaderbr=newBufferedReader(newFileReader(path)); try { return br.readLine(); } finally { if (br != null) br.close(); } }
/* In contrast, in the example readFirstLineFromFile, if exceptions are thrown from both the try block and the try-with-resources statement, then the method readFirstLineFromFile throws the exception thrown from the try block; the exception thrown from the try-with-resources block is suppressed. In Java SE 7 and later, you can retrieve suppressed exceptions; see the section Suppressed Exceptions for more information. */ static String readFirstLineFromFile(String path)throws IOException { try (BufferedReaderbr=newBufferedReader(newFileReader(path))) { return br.readLine(); } } // decompile static String readFirstLineFromFile(String path)throws IOException { BufferedReaderbr=newBufferedReader(newFileReader(path)); Throwablevar2=null; String var3; try { var3 = br.readLine(); } catch (Throwable var12) { var2 = var12; throw var12; } finally { if (br != null) { if (var2 != null) { try { br.close(); } catch (Throwable var11) { var2.addSuppressed(var11); } } else { br.close(); } } } return var3; }
/** * An exception can be thrown from the block of code associated with the try-with-resources statement. In the * example writeToFileZipFileContents, an exception can be thrown from the try block, and up to two exceptions * can be thrown from the try-with-resources statement when it tries to close the ZipFile and BufferedWriter objects. * <br/> * If an exception is thrown from the try block and one or more exceptions are thrown from the try-with-resources * statement, then those exceptions thrown from the try-with-resources statement are suppressed, and the exception * thrown by the block is the one that is thrown by the writeToFileZipFileContents method. You can retrieve these * suppressed exceptions by calling the Throwable.getSuppressed method from the exception thrown by the try block. */ publicstaticvoidwriteToFileZipFileContents(String zipFileName, String outputFileName)throws java.io.IOException { java.nio.charset.Charsetcharset= java.nio.charset.StandardCharsets.US_ASCII; java.nio.file.PathoutputFilePath= java.nio.file.Paths.get(outputFileName); // Open zip file and create output file with try-with-resources statement try (java.util.zip.ZipFilezf=newjava.util.zip.ZipFile(zipFileName); java.io.BufferedWriterwriter= java.nio.file.Files.newBufferedWriter(outputFilePath, charset)) { // Enumerate each entry for (java.util.Enumerationentries= zf.entries(); entries.hasMoreElements(); ) { // Get the entry name and write it to the output file StringnewLine= System.getProperty("line.separator"); StringzipEntryName= ((java.util.zip.ZipEntry) entries.nextElement()).getName() + newLine; writer.write(zipEntryName, 0, zipEntryName.length()); } } } // decompile publicstaticvoidwriteToFileZipFileContents(String zipFileName, String outputFileName)throws IOException { Charsetcharset= StandardCharsets.US_ASCII; PathoutputFilePath= Paths.get(outputFileName); ZipFilezf=newZipFile(zipFileName); Throwablevar5=null; try { BufferedWriterwriter= Files.newBufferedWriter(outputFilePath, charset); Throwablevar7=null; try { Enumerationentries= zf.entries(); while(entries.hasMoreElements()) { StringnewLine= System.getProperty("line.separator"); StringzipEntryName= ((ZipEntry)entries.nextElement()).getName() + newLine; writer.write(zipEntryName, 0, zipEntryName.length()); } } catch (Throwable var32) { var7 = var32; throw var32; } finally { if (writer != null) { if (var7 != null) { try { writer.close(); } catch (Throwable var31) { var7.addSuppressed(var31); } } else { writer.close(); } } } } catch (Throwable var34) { var5 = var34; throw var34; } finally { if (zf != null) { if (var5 != null) { try { zf.close(); } catch (Throwable var30) { var5.addSuppressed(var30); } } else { zf.close(); } } } }
/* output java.lang.Exception: Top Level Exception at ExceptionT.main(ExceptionT.java:7) java.lang.Exception: Top Level Exception at ExceptionT.main(ExceptionT.java:7) Suppressed: java.lang.Exception: suppress at ExceptionT.main(ExceptionT.java:12) java.lang.Exception: Top Level Exception Top Level Exception ???? java.lang.Exception: Top Level Exception Top Level Exception java.lang.Exception: Top Level Exception at ExceptionT.main(ExceptionT.java:7) Suppressed: java.lang.Exception: suppress at ExceptionT.main(ExceptionT.java:12) */
/** * 对多个资源进行管理 * @date 2022/02/09 * @note [[Using.Manager.apply]] is not same as Scala 2.13 */ objectUsing{
/** A resource manager. * * Resources can be registered with the manager by calling [[acquire `acquire`]]; * such resources will be released in reverse order of their acquisition * when the manager is closed, regardless of any exceptions thrown * during use. * * $suppressionBehavior * * @note It is recommended for API designers to require an implicit `Manager` * for the creation of custom resources, and to call `acquire` during those * resources' construction. Doing so guarantees that the resource ''must'' be * automatically managed, and makes it impossible to forget to do so. * * * Example: * {{{ * class SafeFileReader(file: File)(implicit manager: Using.Manager) * extends BufferedReader(new FileReader(file)) { * * def this(fileName: String)(implicit manager: Using.Manager) = this(new File(fileName)) * * manager.acquire(this) * } * }}} */ finalclassManagerprivate{
importManager._
privatevar closed = false private[this] var resources: List[Resource[_]] = Nil
/** Registers the specified resource with this manager, so that * the resource is released when the manager is closed, and then * returns the (unmodified) resource. */ defapply[R: Releasable](resource: R): R = { acquire(resource) resource }
/** Registers the specified resource with this manager, so that * the resource is released when the manager is closed. */ defacquire[R: Releasable](resource: R): Unit = { if (resource == null) thrownewNullPointerException("null resource") if (closed) thrownewIllegalStateException("Manager has already been closed") resources = newResource(resource) :: resources }
privatedefmanage[A](op: Manager => A): A = { var toThrow: Throwable = null try { op(this) } catch { case t: Throwable => toThrow = t null.asInstanceOf[A] // compiler doesn't know `finally` will throw } finally { closed = true var rs: List[Resource[_]] = resources resources = null// allow GC, in case something is holding a reference to `this` while (rs.nonEmpty) { val resource: Resource[_] = rs.head rs = rs.tail try resource.release() catch { case t: Throwable => if (toThrow == null) toThrow = t else toThrow = preferentiallySuppress(toThrow, t) } } if (toThrow != null) throw toThrow } } }
objectManager{ /** Performs an operation using a `Manager`, then closes the `Manager`, * releasing its resources (in reverse order of acquisition). * * Example: * {{{ * val lines = Using.Manager { use => * use(new BufferedReader(new FileReader("file.txt"))).lines() * } * }}} * * If using resources which require an implicit `Manager` as a parameter, * this method should be invoked with an `implicit` modifier before the function * parameter: * * Example: * {{{ * val lines = Using.Manager { implicit use => * new SafeFileReader("file.txt").lines() * } * }}} * * See the main doc for [[Using `Using`]] for full details of suppression behavior. * * @param op the operation to perform using the manager * @tparam A the return type of the operation * * @return a [[Try]] containing an exception if one or more were thrown, * or the result of the operation if no exceptions were thrown */ defapply[A](op: Manager => A): A = try { (newManager).manage(op) } catch { case e: Throwable => e.printStackTrace() null.asInstanceOf[A] }
privatefinalclassResource[R](resource: R)(implicit releasable: Releasable[R]) { defrelease(): Unit = releasable.release(resource) }
privatedefpreferentiallySuppress(primary: Throwable, secondary: Throwable): Throwable = { defscore(t: Throwable): Int = t match { case _: VirtualMachineError => 4 case _: LinkageError => 3 case _: InterruptedException | _: ThreadDeath => 2 case _: ControlThrowable => 0 case e if !NonFatal(e) => 1// in case this method gets out of sync with NonFatal case _ => -1 }
@inlinedefsuppress(t: Throwable, suppressed: Throwable): Throwable = { t.addSuppressed(suppressed); t }
/** A type class describing how to release a particular type of resource. * * A resource is anything which needs to be released, closed, or otherwise cleaned up * in some way after it is finished being used, and for which waiting for the object's * garbage collection to be cleaned up would be unacceptable. For example, an instance of * [[java.io.OutputStream]] would be considered a resource, because it is important to close * the stream after it is finished being used. * * An instance of `Releasable` is needed in order to automatically manage a resource * with [[Using `Using`]]. An implicit instance is provided for all types extending * [[java.lang.AutoCloseable]]. * * @tparam R the type of the resource */ traitReleasable[-R] { /** Releases the specified resource. */ defrelease(resource: R): Unit }
objectReleasable{ /** An implicit `Releasable` for [[java.lang.AutoCloseable `AutoCloseable`s]]. */ implicitobjectAutoCloseableIsReleasableextendsReleasable[AutoCloseable] { defrelease(resource: AutoCloseable): Unit = resource.close() } }
defmain(args: Array[String]): Unit = { val lines: List[String] = Using.Manager.apply { use => val r1 = use(newBufferedReader(newFileReader("/tmp/file1.txt"))) val r2 = use(newBufferedReader(newFileReader("/tmp/file2.txt"))) val r3 = use(newBufferedReader(newFileReader("/tmp/file3.txt"))) val r4 = use(newBufferedReader(newFileReader("/tmp/file4.txt")))
// use your resources here deflines(reader: BufferedReader): Iterator[String] = Iterator.continually(reader.readLine()).takeWhile(_ != null)