[Java解惑]异常

异常... 17
26.      finally与中断... 17
27.      catch捕获异常规则... 18
28.      重写时方法异常范围... 19
29.      静态与非静态final常量不能在catch块中初始化... 19
30.      System.exit()与finally. 20
31.      递归构造... 21
32.      构造器中的异常... 21
33.      StackOverflowError 22

异常

26. finally与中断

//该方法返回false
static boolean f() {
       try {
              return true;
       } finally {
              return false;
       }
}
 
不要用return、break、continue或throw来退出finally语句块,并且千万不要允许受检查的异常传播到finally语句块之外。也就是说不要在finally块内终止程序,而是执行完finally块后,要将控制权移交给try块,由try最终决定怎样结束方法的调用。
 
对于任何在finally语句块中可能抛出的受检查异常都要进行处理,而不是任其传播,下面流拷贝程序在关闭流时没有防止异常的传播,这会有问题:
static void copy(String src, String dest) throws IOException {
       InputStream in = null;
       OutputStream out = null;
       try {
              in = new FileInputStream(src);
              out = new FileOutputStream(dest);
              byte[] buf = new byte[1024];
              int n;
              while ((n = in.read(buf)) >= 0) {
                     out.write(buf, 0, n);
              }
       } finally{
              //这里应该使用try-catch将每个close包装起来
              if(in != null){in.close();}
              if(in != null){out.close();}
       }
}
 
catch块中的return语句是不会阻止finally块执行的,那么catch块中的continue和break能否阻止?答案是不会的,与return一样,finally语句块是在循环被跳过(continue)和中断(break)之前被执行的:
int i = 0;
System.out.println("--continue--");
while (i++ <= 1) {
       try {
              System.out.println("i=" + i);
              continue;
       } catch (Exception e) {
       } finally {
              System.out.println("finally");
       }
}
System.out.println("--break--");
while (i++ <= 3) {
       try {
              System.out.println("i=" + i);
              break;
       } catch (Exception e) {
       } finally {
              System.out.println("finally");
       }
}

27. catch捕获异常规则

捕获RuntimeException、Exception或Throwable的catch语句是合法,不管try块里是否抛出了这三个异常。但如果try块没有抛出或不可能抛出检测性异常,则catch不能捕获这些异常,如IOException异常:
public class Test {
       public static void main(String[] args) {
              try{
                     //...
              }catch (Exception e) {
                    
              }catch (Throwable e) {
                    
              }
             
              /* !! 编译出错
                     try{
                            //...
                     }catch (IOException e) {
                           
                     }
               */
       }
}

28. 重写时方法异常范围

重写或实现时不能扩大异常的范围,如果是多继承,则异常取所有父类方法异常的交集或不抛出异常:
interface I1 {
       void f() throws Exception;
}
 
interface I2 {
       void f() throws IOException;
}
 
interface I3 extends I1, I2 {}
 
class Imp implements I3 {
       // 不能编译通过,多继承时只能取父类方法异常交集,这样就不会扩大异常范围
       // !! void f () throws Exception;
       // void f();// 能编译通过
       // 能编译通过,Exception与IOException的交集为IOException
       public void f() throws IOException {
       }
}

29. 静态与非静态final常量不能在catch块中初始化

静态与非静态块中如果抛出了异常,则一定要使用try-catch块来捕获。
 
public class Test {
       static final int i;
       static {
              try {
                     i = f();
              } catch (RuntimeException e) {
                     i = 1;
              }
       }
 
       static int f() {
              throw new RuntimeException();
       }
}
上面的程序编译不能通过。表面上是可以的,因为i第一次初始化时可能抛出异常,所以抛异常时可以在catch块中初始化,最终还是只初始化一次,这正是空final所要求的,但为什么编译器不知道这些呢?
 
要确定一个程序是否不止一次地对一个空final进行赋值是很困难的问题。语言规范在这一点上采用了保守的方式。

30. System.exit()与finally

try {
       System.out.println("Hello world");
       System.exit(0);
       // 或者使用Runtime退出系统
       // Runtime.getRuntime().exit(0);
} finally {
       System.out.println("Goodbyte world");
}
上面的程序会打印出"Goodbyte world"吗?不会。
 
System.exit将立即停止所有的程序线程,它并不会使finally语句块得到调用,但是它在停止VM之前会执行关闭挂钩操作(这此挂钩操作是注册到Runtime.addShutdownHook上的线程),这对于释放VM之外的资源很有帮助。使用挂钩程序修改上面程序:
System.out.println("Hello world");
Runtime.getRuntime().addShutdownHook(new Thread() {
       public void run() {
              System.out.println("Goodbyte world");
       }
});
System.exit(0);
 
对象回收时,使用VM调用对象的finalize()方法有两种:
System.runFinalization():该方法让虚拟机也只是尽最大努力去完成所有未执行的finalize()终止方法,但不一定会执行。
System.runFinalizersOnExit(true):该方法一定会回收,但不安全,已被废弃。因为它可能对正在使用的对象调用终结方法,而其他线程同时正在操作这些对象,从而导致不正确的行为或死锁。
 
为了加快垃圾回收,使用System.gc(),但不一定马上执行加收动作,由虚拟机决定,实质上是调用Runtime.getRuntime().gc()。
 
System的很多方法都是调用Runtime类的相关方法来实现的。

31. 递归构造

public class S  {
       private S instance = new S();
       public S() {}
}
 
如果在程序外面构造该类的实例,则会抛出java.lang.StackOverflowError错误。其原因是实例变量的初始化操作将先于构造器的程序体而运行。

32. 构造器中的异常

如果父类构造器抛出了检测异常,则子类也只能抛出,而不能采用try-catch来捕获:
public class P {
       public P() throws Exception {}
}
 
class S extends P {
       public S() throws Exception {
              try {
                     // 不能在try块中明确调用父类构造器,因为构造的
                     // 明确调用只能放在第一行
                     // !! super();
              //try-catch不能捕获到父类构造器所抛出的异常,子类只能抛出
              } catch (Exception e) {
              }
       }
}
 
 
如果初使化实例属性时抛出了异常,则构造器只能抛出异常,在构造器中捕获不起作用:
public class A  {
       private String str = String.class.newInstance();
       public A()throws InstantiationException, IllegalAccessException {}
       public A(int i) throws Exception{
              try {
                    
              } catch (Exception e) {
                    
              }
       }
}

33. StackOverflowError

Java虚拟机对栈的深度限制到了某个值,当超过这个值时,VM就抛出StackOverflowError。一般VM都将栈的深度限制为1024,即当方法调用方法的层次超过1024时就会产生StackOverflowError。

时间: 2024-10-24 08:43:46

[Java解惑]异常的相关文章

Java解惑四:异常之谜

谜题36 finally语句中的return语句会覆盖掉try语句中的. 谜题37 该部分还需要进一步理解 一个方法可以抛出的被检查异常集合是它所适用的所有类型声明要抛出的被检查集合的交集. Java解惑四:异常之谜,布布扣,bubuko.com

【java解惑】关于异常捕获的三条要求

有如下所示代码: public class Example037 { static void output1() { try { System.out.println("如果一个 catch 子句要捕获一个类型为 E 的被检查异常," + "而其相对应的 try 子句不能抛出 E 的某种子类型的异常,那么这就是一个编译期错误"); } catch (IOException e) { System.out.println(""); } } stat

《Java解惑》读书笔记

 摘选自<Java解惑>一书,之前整理了部分,一直没看完,最近为了督促自己每天读点这本书,决定一天至少更新一个谜题的内容,欢迎讨论. 欢迎关注技术博客http://blog.sina.com.cn/u/1822488043 Java解惑读书笔记 谜题1:奇数性 取余操作的定义: ( a / b ) * b + ( a % b ) = a 其中(a/b)是java运算的结果,也就是a/b是一个整数,比如3/2=1. 所以当取余操作返回一个非零结果的时候,它与左操作数具有相同符号. 请测试你的

Java解惑八:更多库之谜

本文是根据JAVA解惑这本书,做的笔记. 电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题76 将线程的启动方法start(),写成了run(); PS:管程(monitor)锁有待进一步理解. 谜题77 线程中锁的问题. 理解不深刻. 谜题78 反射会造成访问其他包中的非公共类型的成员,引起运行期异常. 谜题79 遮蔽:Thread.sleep()方法遮蔽了自定的方法. 谜题80 反射:如何实例化非静态内部类以及静态内部类.

Java解惑八:很多其它库之谜

本文是依据JAVA解惑这本书,做的笔记. 电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题76 将线程的启动方法start(),写成了run(); PS:管程(monitor)锁有待进一步理解. 谜题77 线程中锁的问题. 理解不深刻. 谜题78 反射会造成訪问其它包中的非公共类型的成员.引起执行期异常. 谜题79 遮蔽:Thread.sleep()方法遮蔽了自定的方法. 谜题80 反射:怎样实例化非静态内部类以及静态内部类.

【java解惑】&和&&、|和||使用

如下所示代码: public class Example042 { public static void main(String[] args) { int[][] tests = { { 6, 5, 4, 3, 2, 1 }, { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } }; System.out.println("func1 out : " + func1(tests)); } private static int func1(int[

【java解惑】try-finally语句执行问题

如下所示代码: public class Example039 { public static void main(String[] args) { Example039 example039 = new Example039(); System.out.println(example039.output1()); example039.output2(); } boolean output1() { try { // ... return true; } finally { return fa

【java解惑】java构造器的那些事儿

如下所示代码: public class Example040 { private Example040 e40 = new Example040(); public Example040() throws Exception { throw new Exception("这里是exception,不是error"); } public void output() { System.out.println("a new class"); } public stati

【java解惑】输入输出流使用后及时关闭问题

如下所示代码: public class Example041 { public static void main(String[] args) throws IOException { Example041 e41 = new Example041(); e41.copy("d:\\微信名ape_it.txt", "d:\\微信名爱题猿.txt"); } private void copy(String src, String dest) throws IOExc