@@ -983,10 +983,315 @@ at NeverCaught.main(NeverCaught.java:13)
983983
984984< ! -- Performing Cleanup with finally -- >
985985
986- ## finally 关键字
986+ ## 使用 finally 进行清理
987987
988+ 有一些代码片段,可能会希望无论try 块中的异常是否抛出,它们都能得到执行。这通常适用于内存回收之外的情况(因为回收由垃圾回收器完成),为了达到这个效果,可以在异常处理程序后面加上finally 子句。完整的异常处理程序看起来像这样:
989+
990+ ```java
991+ try {
992+ // The guarded region: Dangerous activities
993+ // that might throw A, B, or C
994+ } catch (A a1) {
995+ // Handler for situation A
996+ } catch (B b1) {
997+ // Handler for situation B
998+ } catch (C c1) {
999+ // Handler for situation C
1000+ } finally {
1001+ // Activities that happen every time
1002+ }
1003+ ```
1004+
1005+ 为了证明finally 子句总能运行,可以试试下面这个程序:
1006+
1007+ ```java
1008+ // exceptions/FinallyWorks.java
1009+ // The finally clause is always executed
1010+ class ThreeException extends Exception {}
1011+ public class FinallyWorks {
1012+ static int count = 0 ;
1013+ public static void main (String [] args ) {
1014+ while (true ) {
1015+ try {
1016+ // Post-increment is zero first time:
1017+ if (count++ == 0 )
1018+ throw new ThreeException ();
1019+ System . out. println(" No exception" );
1020+ } catch (ThreeException e) {
1021+ System . out. println(" ThreeException" );
1022+ } finally {
1023+ System . out. println(" In finally clause" );
1024+ if (count == 2 ) break ; // out of "while"
1025+ }
1026+ }
1027+ }
1028+ }
1029+ ```
1030+
1031+ 输出为:
1032+
1033+ ```
1034+ ThreeException
1035+ In finally clause
1036+ No exception
1037+ In finally clause
1038+ ```
1039+
1040+ 可以从输出中发现,无论异常是否被抛出,finally 子句总能被执行。这个程序也给了我们一些思路,当Java 中的异常不允许我们回到异常抛出的地点时,那么该如何应对呢?如果把try 块放在循环里,就建立了一个“程序继续执行之前必须要达到”的条件。还可以加入一个static 类型的计数器或者别的装置,使循环在放弃以前能尝试一定的次数。这将使程序的健壮性更上一个台阶。
1041+
1042+ ### finally 用来做什么?
1043+
1044+ 对于没有垃圾回收和析构函数自动调用机制的语言来说,finally 非常重要。它能使程序员保证:无论try 块里发生了什么,内存总能得到释放。但Java 有垃圾回收机制,所以内存释放不再是问题。而且,Java 也没有析构函数可供调用。那么,Java 在什么情况下才能用到finally 呢?
1045+
1046+ 当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally 子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关,如下面例子所示:
1047+
1048+ ```java
1049+ // exceptions/Switch.java
1050+ public class Switch {
1051+ private boolean state = false ;
1052+ public boolean read () { return state; }
1053+ public void on () {
1054+ state = true ;
1055+ System . out. println(this );
1056+ }
1057+ public void off () {
1058+ state = false ;
1059+ System . out. println(this );
1060+ }
1061+ @Override
1062+ public String toString () {
1063+ return state ? " on" : " off" ;
1064+ }
1065+ }
1066+ // exceptions/OnOffException1.java
1067+ public class OnOffException1 extends Exception {}
1068+ // exceptions/OnOffException2.java
1069+ public class OnOffException2 extends Exception {}
1070+ // exceptions/OnOffSwitch.java
1071+ // Why use finally?
1072+ public class OnOffSwitch {
1073+ private static Switch sw = new Switch ();
1074+ public static void f ()
1075+ throws OnOffException1 , OnOffException2 {}
1076+ public static void main (String [] args ) {
1077+ try {
1078+ sw. on();
1079+ // Code that can throw exceptions...
1080+ f();
1081+ sw. off();
1082+ } catch (OnOffException1 e) {
1083+ System . out. println(" OnOffException1" );
1084+ sw. off();
1085+ } catch (OnOffException2 e) {
1086+ System . out. println(" OnOffException2" );
1087+ sw. off();
1088+ }
1089+ }
1090+ }
1091+ ```
1092+
1093+ 输出为:
1094+
1095+ ```
1096+ on
1097+ off
1098+ ```
1099+
1100+ 程序的目的是要确保main()结束的时候开关必须是关闭的,所以在每个try 块和异常处理程序的末尾都加入了对sw. offo方法的调用。但也可能有这种情况:异常被抛出,但没被处理程序捕获,这时sw. off()就得不到调用。但是有了finally ,只要把try 块中的清理代码移放在一处即可:
1101+
1102+ ```java
1103+ // exceptions/WithFinally.java
1104+ // Finally Guarantees cleanup
1105+ public class WithFinally {
1106+ static Switch sw = new Switch ();
1107+ public static void main (String [] args ) {
1108+ try {
1109+ sw. on();
1110+ // Code that can throw exceptions...
1111+ OnOffSwitch . f();
1112+ } catch (OnOffException1 e) {
1113+ System . out. println(" OnOffException1" );
1114+ } catch (OnOffException2 e) {
1115+ System . out. println(" OnOffException2" );
1116+ } finally {
1117+ sw. off();
1118+ }
1119+ }
1120+ }
1121+ ```
1122+
1123+ 输出为:
1124+
1125+ ```java
1126+ on
1127+ off
1128+ ```
1129+
1130+ 这里sw. off()被移到一处,并且保证在任何情况下都能得到执行。
1131+
1132+ 甚至在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高一层的异常处理程序之前,执行finally 子句:
1133+
1134+ ```java
1135+ // exceptions/AlwaysFinally.java
1136+ // Finally is always executed
1137+ class FourException extends Exception {}
1138+ public class AlwaysFinally {
1139+ public static void main (String [] args ) {
1140+ System . out. println(" Entering first try block" );
1141+ try {
1142+ System . out. println(" Entering second try block" );
1143+ try {
1144+ throw new FourException ();
1145+ } finally {
1146+ System . out. println(" finally in 2nd try block" );
1147+ }
1148+ } catch (FourException e) {
1149+ System . out. println(
1150+ " Caught FourException in 1st try block" );
1151+ } finally {
1152+ System . out. println(" finally in 1st try block" );
1153+ }
1154+ }
1155+ }
1156+ ```
1157+
1158+ 输出为:
1159+
1160+ ```java
1161+ Entering first try block
1162+ Entering second try block
1163+ finally in 2nd try block
1164+ Caught FourException in 1st try block
1165+ finally in 1st try block
1166+ ```
1167+
1168+ 当涉及break 和continue 语句的时候,finally 子句也会得到执行。请注意,如果把finally 子句和带标签的break 及continue 配合使用,在Java 里就没必要使用goto语句了。
1169+
1170+ ### 在 return 中使用 finally
1171+
1172+ 因为finally 子句,总是会执行的,所以在一个方法中,可以从多个点返回,并且可以保证重要的清理工作仍旧会执行:
1173+
1174+ ```java
1175+ // exceptions/MultipleReturns.java
1176+ public class MultipleReturns {
1177+ public static void f (int i ) {
1178+ System . out. println(
1179+ " Initialization that requires cleanup" );
1180+ try {
1181+ System . out. println(" Point 1" );
1182+ if (i == 1 ) return ;
1183+ System . out. println(" Point 2" );
1184+ if (i == 2 ) return ;
1185+ System . out. println(" Point 3" );
1186+ if (i == 3 ) return ;
1187+ System . out. println(" End" );
1188+ return ;
1189+ } finally {
1190+ System . out. println(" Performing cleanup" );
1191+ }
1192+ }
1193+ public static void main (String [] args ) {
1194+ for (int i = 1 ; i <= 4 ; i++ )
1195+ f(i);
1196+ }
1197+ }
1198+ ```
1199+
1200+ 输出为:
1201+
1202+ ```java
1203+ Initialization that requires cleanup
1204+ Point 1
1205+ Performing cleanup
1206+ Initialization that requires cleanup
1207+ Point 1
1208+ Point 2
1209+ Performing cleanup
1210+ Initialization that requires cleanup
1211+ Point 1
1212+ Point 2
1213+ Point 3
1214+ Performing cleanup
1215+ Initialization that requires cleanup
1216+ Point 1
1217+ Point 2
1218+ Point 3
1219+ End
1220+ Performing cleanup
1221+ ```
1222+
1223+ 从输出中可以看出,从何处返回无关紧要,finally 子句永远会执行。
1224+
1225+ ### 缺憾:异常丢失
1226+
1227+ 遗憾的是,Java 的异常实现也有瑕疵。异常作为程序出错的标志,决不应该被忽略,但它还是有可能被轻易地忽略。用某些特殊的方式使用finally 子句,就会发生这种情况:
1228+
1229+ ```java
1230+ // exceptions/LostMessage.java
1231+ // How an exception can be lost
1232+ class VeryImportantException extends Exception {
1233+ @Override
1234+ public String toString () {
1235+ return " A very important exception!" ;
1236+ }
1237+ }
1238+ class HoHumException extends Exception {
1239+ @Override
1240+ public String toString () {
1241+ return " A trivial exception" ;
1242+ }
1243+ }
1244+ public class LostMessage {
1245+ void f () throws VeryImportantException {
1246+ throw new VeryImportantException ();
1247+ }
1248+ void dispose () throws HoHumException {
1249+ throw new HoHumException ();
1250+ }
1251+ public static void main (String [] args ) {
1252+ try {
1253+ LostMessage lm = new LostMessage ();
1254+ try {
1255+ lm. f();
1256+ } finally {
1257+ lm. dispose();
1258+ }
1259+ } catch (VeryImportantException | HoHumException e) {
1260+ System . out. println(e);
1261+ }
1262+ }
1263+ }
1264+ ```
1265+
1266+ 输出为:
1267+
1268+ ```
1269+ A trivial exception
1270+ ```
1271+
1272+ 从输出中可以看到,VeryImportantException 不见了,它被finally 子句里的HoHumException 所取代。这是相当严重的缺陷,因为异常可能会以一种比前面例子所示更微妙和难以察党的方式完全丢失。相比之下,C ++ 把“前一个异常还没处理就抛出下一个异常”的情形看成是糟糕的编程错误。也许在Java 的未来版本中会修正这个问题(另一方面,要把所有抛出异常的方法,如上例中的dispose()方法,全部打包放到try - catch 子句里面)。
1273+
1274+ 一种更加简单的丢失异常的方式是从finally 子句中返回:
1275+
1276+ ```java
1277+ // exceptions/ExceptionSilencer.java
1278+ public class ExceptionSilencer {
1279+ public static void main (String [] args ) {
1280+ try {
1281+ throw new RuntimeException ();
1282+ } finally {
1283+ // Using 'return' inside the finally block
1284+ // will silence any thrown exception.
1285+ return ;
1286+ }
1287+ }
1288+ }
1289+ ```
1290+
1291+ 如果运行这个程序,就会看到即使抛出了异常,它也不会产生任何输出。
9881292
9891293< ! -- Exception Restrictions -- >
1294+
9901295## 异常限制
9911296
9921297
@@ -1021,4 +1326,7 @@ at NeverCaught.main(NeverCaught.java:13)
10211326
10221327< ! -- 分页 -- >
10231328
1024- < div style= " page-break-after: always;" >< / div>
1329+ < div style= " page-break-after: always;" >< / div>
1330+ ```
1331+
1332+ ```
0 commit comments