《Java 解惑》 第五章 异常之谜
2015-02-23 02:17
609 查看
简述:
《Java 解惑》 第五章 异常之谜 - 笔记
内容:
结果输出了false ,
原因:
无论try语句块是正常结束的,还是意外结束的,在一个try-finally语句中,finally语句块总是在控制权离开try语句块时执行
警告:不要使用return、break、continue或throw来退出finally语句块,并且不要让受检查的异常传播到finally语句之外
![](https://oscdn.geek-share.com/Uploads/Images/Content/202012/01/c6fedaa8094e926c33d90969961b36e2)
说明: try语句中没有声明会抛出任何受检查异常,而在catch字句中要捕获一个类型为E的受检查异常
这就是一个编译期错误
2)
说明:捕获Exception或Throwable的catch子句是合法的,不管其相对应的try字句的内容为何
3)
Type1.java
Type2.java
Type3.java
Arcane3.java
说明:一个方法可以抛出的受检查异常集合是它所适用的所有类型声明要抛出的受检查异常集合的交集。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202012/01/62e6a7a523c741bcc3d108ded598cca9)
解释:
Java编译器规定,final字段只有在他的确未赋过值的地方才可以被赋值,而本例中try方法中可能会对USER_ID作赋值
编译器谨慎起见组织了catch中的赋值
解决方式,重构静态语句块中代码为一个辅助方法
解释: 当调用System.exit时, 虚拟机(VM)在关闭前要执行两项清理工作。首先它执行所有的关闭挂钩操作,这些挂钩已经注册到Runtime.addShutdownHook上。这对释放VM之外的资源很有帮助。务必要为那些必须在VM退出之前发生的行为关闭挂钩。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202012/01/c2abfddeb0f0520a9c5cda2fc9e52b7f)
输出:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202012/01/a7c27af4349140ec5c64cad9b681114a)
说明:
本程序包含了一个无线递归。当你调用一个构造器时,实例变量的初始化操作将先于构造器的程序体而运行。
实例变量的初始化操作将先于构造器的程序体而运行,本例中,internalInstance变量的初始化操作递归调用
了构造器,而该构造器通过再次调用Reluctant构造器而初始化该变量自己的internalInstance字段,而无限递归下去
说明:
finally中的close方法也可能跑出IOException异常,如果这个正好发生在in.close被调用的时候,那么就会阻止out.close被调用
修改方式, 自定义finally中关闭的方法,从5.0开始,stream中都实现了closeable接口
说明:
不要使用异常控制循环,应该只为异常条件而使用异常
修改为:
运行结束后,控制台没有输出什么东西,看来是被异常终止了
下面用Class.newInstance方法
该方法将传播从空的构造器所抛出的任何异常,包括受检查的异常,使用这个方法,可以有效地绕开在其他情况下都会执行的编译器异常检查
备注:finally语句块中赋值为空,是防止内存泄露
解释:Class.newInstance的文档描述,Constructor。newInstance方法通过将构造器抛出的任何异常都包装在一个(受检查的)InvocationTargetException异常中而避免了这个问题
Strange2.java
Missing.java
测试:
运行Strange1和Strange2之前删除Missing.class 文件,就会发现这两个程序的行为有所不同。
其中一个跑出了一个未被捕获的NoClassDefFoundError异常,而另一个却打印了Got it!
Strange1 运行结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202012/01/f90e68d09c2b5030f1fc6e2adb164733)
Strange2 运行结果:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202012/01/cdb2a8b888d6013ea5ae97f8c63cd3e2)
编写一个能够探测类丢失的程序,用反射来实现
《Java 解惑》 第五章 异常之谜 - 笔记
内容:
谜题36: try中的return不会影响finally中方法执行
package 异常之谜.优柔寡断; public class Indecisive { public static void main(String[] args){ System.out.println(decision()); } static boolean decision() { try{ return true; } finally { return false; } } }
结果输出了false ,
原因:
无论try语句块是正常结束的,还是意外结束的,在一个try-finally语句中,finally语句块总是在控制权离开try语句块时执行
警告:不要使用return、break、continue或throw来退出finally语句块,并且不要让受检查的异常传播到finally语句之外
谜题37:受检查的异常不能随意捕获
1)说明: try语句中没有声明会抛出任何受检查异常,而在catch字句中要捕获一个类型为E的受检查异常
这就是一个编译期错误
2)
package 异常之谜.极端不可思议; public class Arcane2 { public static void main(String[] args) { try { // do nothing } catch (Exception e) { System.out.println("exception!!"); } } }
说明:捕获Exception或Throwable的catch子句是合法的,不管其相对应的try字句的内容为何
3)
Type1.java
public interface Type1 { void f() throws CloneNotSupportedException; }
Type2.java
public interface Type2 { void f() throws InterruptedException; }
Type3.java
public interface Type3 extends Type1, Type2 { }
Arcane3.java
public class Arcane3 implements Type3 { public void f() { System.out.println("Hello World "); } public static void main(String[] args) { Type3 t3 = new Arcane3(); t3.f(); } }
说明:一个方法可以抛出的受检查异常集合是它所适用的所有类型声明要抛出的受检查异常集合的交集。
谜题38:final 字段只有在的确未赋过值的地方才能被赋值
解释:
Java编译器规定,final字段只有在他的确未赋过值的地方才可以被赋值,而本例中try方法中可能会对USER_ID作赋值
编译器谨慎起见组织了catch中的赋值
解决方式,重构静态语句块中代码为一个辅助方法
package 异常之谜.不受欢迎的宾客; public class UnwelcomeGuest { public static final long GUEST_USER_ID = -1; private static final long USER_ID = getUserIdOrGuest(); private static long getUserIdOrGuest(){ try { return getUserIdFromEnvironment(); } catch (IdUnavailableException e) { System.out.println("Logging in as guest"); return GUEST_USER_ID; } } private static long getUserIdFromEnvironment() throws IdUnavailableException { throw new IdUnavailableException(); } public static void main(String[] args){ System.out.println("User ID: " + USER_ID); } } class IdUnavailableException extends Exception { IdUnavailableException(){} }
谜题39:System.exit 的中断
package 异常之谜.您好_再见; public class HelloGoodbye { public static void main(String[] args) { try { System.out.println("Hello World"); System.exit(0); } finally { System.out.println("Goodbye World!"); } } }
解释: 当调用System.exit时, 虚拟机(VM)在关闭前要执行两项清理工作。首先它执行所有的关闭挂钩操作,这些挂钩已经注册到Runtime.addShutdownHook上。这对释放VM之外的资源很有帮助。务必要为那些必须在VM退出之前发生的行为关闭挂钩。
package 异常之谜.您好_再见; public class HelloGoodbye { public static void main(String[] args) { System.out.println("Hello World!"); Runtime.getRuntime().addShutdownHook( new Thread() { @Override public void run() { System.out.println("Goodbye World!"); } }); System.exit(0); //停止所有程序县城,在停止VM之前会执行关闭挂钩操作 } }输出:
谜题40:构造器抛出的异常
package 异常之谜.不情愿的构造器; public class Reluctant { private Reluctant internalInstance = new Reluctant(); public Reluctant() throws Exception { throw new Exception("I'm not coming out"); } public static void main(String[] args){ try { Reluctant b = new Reluctant(); System.out.println("Surprise!"); } catch (Exception ex){ System.out.println("I told you so"); } } }
输出:
说明:
本程序包含了一个无线递归。当你调用一个构造器时,实例变量的初始化操作将先于构造器的程序体而运行。
实例变量的初始化操作将先于构造器的程序体而运行,本例中,internalInstance变量的初始化操作递归调用
了构造器,而该构造器通过再次调用Reluctant构造器而初始化该变量自己的internalInstance字段,而无限递归下去
谜题41:finally中关闭流也要考虑异常
package 异常之谜.字段和流; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class Test { 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[2014]; int n; while ((n = in.read(buf)) >= 0){ out.write(buf, 0, n); } } finally { if (in != null) in.close(); if(out != null) out.close(); } } public static void main(String[] args) throws IOException { String src = "in.txt"; String dest = "dest.txt"; copy(src, dest); } }
说明:
finally中的close方法也可能跑出IOException异常,如果这个正好发生在in.close被调用的时候,那么就会阻止out.close被调用
修改方式, 自定义finally中关闭的方法,从5.0开始,stream中都实现了closeable接口
package 异常之谜.字段和流; import java.io.Closeable; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class Test { 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[2014]; int n; while ((n = in.read(buf)) >= 0){ out.write(buf, 0, n); } } finally { closeIgnoringException(in); closeIgnoringException(out); } } public static void main(String[] args) throws IOException { String src = "in.txt"; String dest = "dest.txt"; copy(src, dest); } private static void closeIgnoringException(Closeable c){ if(c != null){ try { c.close(); } catch(IOException ex){ // do if it fails } } } }
谜题42:不要用异常终止循环
package 异常之谜.异常为循环而抛; public class Loop { private static boolean thirdElementIsThree(int[] a) { return a.length>=3 && a[2]==3; } public static void main(String[] args) { int[][] tests = { {6,5,4,3,2,1}, {1,2}, {1,2,3}, {1,2,3,4}, {1} }; int successCount = 0; try { int i = 0; while(true){ if(thirdElementIsThree(tests[i++])) successCount ++; } } catch(ArrayIndexOutOfBoundsException e) { // No more tests to process } System.out.println(successCount); } }
说明:
不要使用异常控制循环,应该只为异常条件而使用异常
修改为:
package 异常之谜.异常为循环而抛; public class Loop { private static boolean thirdElementIsThree(int[] a) { return a.length>=3 && a[2]==3; } public static void main(String[] args) { int[][] tests = { {6,5,4,3,2,1}, {1,2}, {1,2,3}, {1,2,3,4}, {1} }; int successCount = 0; for (int[] test : tests) { if(thirdElementIsThree(test)) successCount++; } System.out.println(successCount); } }
谜题43:实现throw语句要做的事情,但是它绕过了编译器所有异常检查操作
package 异常之谜.异常地危险; public class Test { public static void sneakyThrow(Throwable t){ Thread.currentThread().stop(); } public static void main(String[] args) { sneakyThrow(new Exception("超级异常")); System.out.println("execute after throw!"); } }
运行结束后,控制台没有输出什么东西,看来是被异常终止了
下面用Class.newInstance方法
该方法将传播从空的构造器所抛出的任何异常,包括受检查的异常,使用这个方法,可以有效地绕开在其他情况下都会执行的编译器异常检查
package 异常之谜.异常地危险; public class Thrower { private static Throwable t; private Thrower() throws Throwable { throw t; } public static synchronized void sneakyThrow(Throwable t){ Thrower.t = t; try { Thrower.class.newInstance(); } catch (InstantiationException e) { throw new IllegalArgumentException(); } catch (IllegalAccessException e) { throw new IllegalArgumentException(); } finally { Thrower.t = null; // Avoid Memory Leak } } }
备注:finally语句块中赋值为空,是防止内存泄露
解释:Class.newInstance的文档描述,Constructor。newInstance方法通过将构造器抛出的任何异常都包装在一个(受检查的)InvocationTargetException异常中而避免了这个问题
谜题44:删除类
Strange1.javapackage 异常之谜.删除类; public class Strange1 { public static void main(String[] args) { try { Missing m = new Missing(); } catch (java.lang.NoClassDefFoundError ex) { System.out.println("Got it!"); } } }
Strange2.java
package 异常之谜.删除类; public class Strange2 { public static void main(String[] args) { Missing m; try { m = new Missing(); } catch (java.lang.NoClassDefFoundError ex) { System.out.println("Got it!"); } } }
Missing.java
package 异常之谜.删除类; public class Missing { Missing() {} }
测试:
运行Strange1和Strange2之前删除Missing.class 文件,就会发现这两个程序的行为有所不同。
其中一个跑出了一个未被捕获的NoClassDefFoundError异常,而另一个却打印了Got it!
Strange1 运行结果:
Strange2 运行结果:
编写一个能够探测类丢失的程序,用反射来实现
package 异常之谜.删除类; public class Strange { public static void main(String[] args) throws Exception { try { Object m = Class.forName("Missing").newInstance(); } catch (ClassNotFoundException ex ) { System.out.println("Missing.class lost !"); } } }
谜题45:无限函数递归
package 异常之谜.令人疲惫不堪的测验; public class Workout { public static void main(String[] args) { workHard(); System.out.println("It's nap time."); } private static void workHard() { try { workHard(); } finally { workHard(); } } }
相关文章推荐
- 2011年12月12号,学习完成了java解惑的第五章。
- java 异常 Exception 相关 try catch finally 解惑
- Java解惑4-42异常为循环所抛
- JAVAOOP-第五章(异常)学习笔记
- javaoop——异常笔记(第五章)
- 【java解惑】关于异常捕获的三条要求
- Java异常处理机制难点解惑-用代码说话
- 【java解惑】递归异常与有限循环
- Java解惑四:异常之谜
- Java解惑4——异常谜题(易混淆10处)
- 【java解惑】关于异常捕获的三条要求
- 回忆java来时路-第五章 正常地处理异常
- S2 JAVA_第五章 异常
- [Java解惑]异常
- Java解惑学习有感(四)---异常之谜
- Java解惑4-异常谜题
- 【java解惑】递归异常与有限循环
- Java异常处理机制难点解惑-用代码说话
- Java异常处理机制难点解惑-用代码说话
- Java异常处理之陋习