Java解惑中的有关总结二
Java解惑中的相关总结二
异常 1、finally与中断 Java代码 //该方法返回false static boolean f() { try { return true; } finally { return false; } } 不要用return、break、continue或throw来退出finally语句块,并且千万不要允许受检查的异常传播到finally语句 块之外。也就是说不要在finally块内终止程序,而是执行完finally块后,要将控制权移交给try块,由try最终决定 怎样结束方法的调用。 对于任何在finally语句块中可能抛出的受检查异常都要进行处理,而不是任其传播,下面流拷贝程序在关闭流时没有 防止异常的传播,这会有问题: Java代码 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)之前被执行的: Java代码 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"); } } 2、catch捕获异常规则 捕获RuntimeException、Exception或Throwable的catch语句是合法,不管try块里是否抛出了这三个异常。但如果try 块没有抛出或不可能抛出检测性异常,则catch不能捕获这些异常,如IOException异常: Java代码 public class Test { public static void main(String[] args) { try{ //... }catch (Exception e) { }catch (Throwable e) { } /* !! 编译出错 try{ //... }catch (IOException e) { } */ } } 3、重写时方法异常范围 重写或实现时不能扩大异常的范围,如果是多继承,则异常取所有父类方法异常的交集或不抛出异常: Java代码 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 { } } 4、静态与非静态final常量不能在catch块中初始化 静态与非静态块中如果抛出了异常,则一定要使用try-catch块来捕获。 Java代码 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进行赋值是很困难的问题。语言规范在这一点上采用了保守的方式。 5、System.exit()与finally Java代码 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之外的资源很有帮助。使用挂钩程 序修改上面程序: Java代码 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类的相关方法来实现的。 6、递归构造 Java代码 public class S { private S instance = new S(); public S() {} } 如果在程序外面构造该类的实例,则会抛出java.lang.StackOverflowError错误。其原因是实例变量的初始化操作将 先于构造器的程序体而运行。 7、构造器中的异常 如果父类构造器抛出了检测异常,则子类也只能抛出,而不能采用try-catch来捕获: Java代码 public class P { public P() throws Exception {} } class S extends P { public S() throws Exception { try { // 不能在try块中明确调用父类构造器,因为构造的 // 明确调用只能放在第一行 // !! super(); //try-catch不能捕获到父类构造器所抛出的异常,子类只能抛出 } catch (Exception e) { } } } 如果初使化实例属性时抛出了异常,则构造器只能抛出异常,在构造器中捕获不起作用: Java代码 public class A { private String str = String.class.newInstance(); public A() throws InstantiationException, IllegalAccessException {} public A(int i) throws Exception { try {//即使这里捕获了,方法签名还是得要抛出 } catch (Exception e) { } } /* * !!编译不能通过,因为str2为静态的,他不能通过构造器来捕获,所以只 * 能使用静态方法来捕获。即初始化静态成员时不能抛出捕获性异常。 */ //!!private static String str2 = String.class.newInstance(); // 只能使用静态方法来捕获异常,如果是抛出的运行时异常则不需要捕获 private static String str2 = newInstance(); private static String newInstance() throws RuntimeException { try { return String.class.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } } 8、StackOverflowError Java虚拟机对栈的深度限制到了某个值,当超过这个值时,VM就抛出StackOverflowError。一般VM都将栈的深度限制 为1024,即当方法调用方法的层次超过1024时就会产生StackOverflowError。异常 9、finally与中断 Java代码 //该方法返回false static boolean f() { try { return true; } finally { return false; } } 不要用return、break、continue或throw来退出finally语句块,并且千万不要允许受检查的异常传播到finally语句 块之外。也就是说不要在finally块内终止程序,而是执行完finally块后,要将控制权移交给try块,由try最终决定 怎样结束方法的调用。 对于任何在finally语句块中可能抛出的受检查异常都要进行处理,而不是任其传播,下面流拷贝程序在关闭流时没有 防止异常的传播,这会有问题: Java代码 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)之前被执行的: Java代码 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"); } } 10、catch捕获异常规则 捕获RuntimeException、Exception或Throwable的catch语句是合法,不管try块里是否抛出了这三个异常。但如果try 块没有抛出或不可能抛出检测性异常,则catch不能捕获这些异常,如IOException异常: Java代码 public class Test { public static void main(String[] args) { try{ //... }catch (Exception e) { }catch (Throwable e) { } /* !! 编译出错 try{ //... }catch (IOException e) { } */ } } 11、重写时方法异常范围 重写或实现时不能扩大异常的范围,如果是多继承,则异常取所有父类方法异常的交集或不抛出异常: Java代码 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 { } } 12、静态与非静态final常量不能在catch块中初始化 静态与非静态块中如果抛出了异常,则一定要使用try-catch块来捕获。 Java代码 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进行赋值是很困难的问题。语言规范在这一点上采用了保守的方式。 13、System.exit()与finally Java代码 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之外的资源很有帮助。使用挂钩程 序修改上面程序: Java代码 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类的相关方法来实现的。 14、递归构造 Java代码 public class S { private S instance = new S(); public S() {} } 如果在程序外面构造该类的实例,则会抛出java.lang.StackOverflowError错误。其原因是实例变量的初始化操作将 先于构造器的程序体而运行。 15、构造器中的异常 如果父类构造器抛出了检测异常,则子类也只能抛出,而不能采用try-catch来捕获: Java代码 public class P { public P() throws Exception {} } class S extends P { public S() throws Exception { try { // 不能在try块中明确调用父类构造器,因为构造的 // 明确调用只能放在第一行 // !! super(); //try-catch不能捕获到父类构造器所抛出的异常,子类只能抛出 } catch (Exception e) { } } } 如果初使化实例属性时抛出了异常,则构造器只能抛出异常,在构造器中捕获不起作用: Java代码 public class A { private String str = String.class.newInstance(); public A() throws InstantiationException, IllegalAccessException {} public A(int i) throws Exception { try {//即使这里捕获了,方法签名还是得要抛出 } catch (Exception e) { } } /* * !!编译不能通过,因为str2为静态的,他不能通过构造器来捕获,所以只 * 能使用静态方法来捕获。即初始化静态成员时不能抛出捕获性异常。 */ //!!private static String str2 = String.class.newInstance(); // 只能使用静态方法来捕获异常,如果是抛出的运行时异常则不需要捕获 private static String str2 = newInstance(); private static String newInstance() throws RuntimeException { try { return String.class.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } } 33. StackOverflowError Java虚拟机对栈的深度限制到了某个值,当超过这个值时,VM就抛出StackOverflowError。一般VM都将栈的深度限制 为1024,即当方法调用方法的层次超过1024时就会产生StackOverflowError。