java异常对象都派生于Throwable。Throwable分为两个分支:Error和Exception。Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。Error是指正常情况下,不大可能出现的情况,一般与JVM有关,如系统奔溃,虚拟机错误,动态链接失败。既然是非正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类。
java语言规范将派生于Error或RunTimeException的所有异常成为未检测(unchecked)异常。RunTimeException运行时异常通常在我们控制下,如ArrayIndexOutOfBoundException,修正程序。
所有其他异常为已检查(checked)异常。
可检查异常在源码里必须显示的进行捕获处理或者进行thorws声明抛出让其他捕获器处理。当有异常(未检测/已检查)发生,没有进行捕获时候,当前执行线程会结束。先catch小异常(子异常)再catch大异常(父异常)。只能catch可能抛出异常的语句。
父类方法申明异常A。子类方法可以申明抛出A或者A的子类异常,或者不抛异常。但是不能抛出B异常。父类方法不抛异常,子类方法不能抛异常。
用thorws声明抛异常,thorw抛异常动作。
String readData(Scanner in) thorws EOFException { ... while(...){ if(in.hasNext()){ if(n<len) thorw new EOFException(); } ... } retrun s; }
案例1 catch中异常或者有没有捕获的异常
inputStream in=new FileInputStream(...); try { //1 code that might throw exception //2 } catch(IOException e) { //3 show error message //4 } finally { //5 in.close(); } //6
1。正常流程抛出异常,并捕获了。捕获的代码处没有抛出其他异常,则执行顺序1,3,4,5,6。
2。如果catch子句抛出一个异常,异常将会抛回给调用者,执行1,3,5代码6不行。
3。代码抛出异常,但是不是catch捕获的异常,则只执行1,5。
案例2 System.exit(0); 在finally前
public class ExitFinally { public static void main(String[] args) throws IOException { FileOutputStream fos = null; try { fos = new FileOutputStream("a.bin"); System.out.println("程序打开物理资源"); System.exit(0); } finally { //使用finally关闭资源 if (fos != null) { try { fos.close(); } catch (Exception ex) { ex.printStackTrace(); } } System.out.println("程序关闭物理资源"); } } }
System.exit(0);停止当前线程和所有其他线程。所有finally不会执行。
案例3 return
public class FinallyFlowTest { public static void main(String[] args) { int a = test(); System.out.println(a); } public static int test() { int count = 5; try { //因为在finally块里面包含了return云居 //则下面的count++执行,先使用标记return 5;此时count为6 return count++;//code1 } finally { System.out.println("finally块被执行了");
// i++ 和 ++i 在理论上的区别是:
// i++:是先把i拿出来使用,然后再+1;
// ++i :是先把i+1,然后再拿出来使用;
return count++;//标记返回6,此时count为7
}
}
}
解析:返回6。code1处无论是count++或者++count。都会执行此时count为6,count++第一个renturn是5,++count的时候第一个return是6.
第二个return count++;先使用再++。即返回值是当前的count。所以为6若改为return ++count;则返回为7
public class FinallyFlowTest { public static void main(String[] args) { int a = test(); System.out.println(a); } public static int test() { int count = 5; try { //因为在finally块里面包含了return云居 //则下面的count++先使用标记下返回5再++此时就为6 return count++; } finally { System.out.println("finally块被执行了"); System.out.println("count:"+count);//打印6 count++;//执行7 System.out.println("count:"+count);//打印7 } } }
结果:finally块被执行了
count:6
count:7
5
案例4 retrun 抛出异常不终止
public class FinallyFlowTest2 { public static void main(String[] args) { int a = test(); System.out.println(a); } public static int test() { int count = 5; try { //因为finally块中包含了return语句, //则下面的return语句不会立即返回 throw new RuntimeException("测试异常"); } finally { System.out.println("finally块被执行"); return count; } } }
没有终止线程返回5。在try块和catch块里面遇到throw语句时候,thorw不是立即结束还是找finally块,只有finlly块执行完了,再跳回抛异常。如果finally块使用return结束了方法。系统不会跳回到try块和catch块去抛异常。
案例5 带资源的try
资源:一般只物理资源比如:数据库连接,网络连接,硬盘文件,打开这些资源后必须显示关闭,不然会引起资源泄露。
在原来关闭资源的时候,用 try-catch-finally 时如果try中的代码跑出了一个非 IOException,在执行finally调用close方法时close方法本身也会有可能抛出 IOException 异常。这种情况下,原始的异常将丢失,转而抛出close方法的异常。
在jkd 1.7之前的处理方法较为繁琐,如下:
public class Advice { public static void main(String[] args) { InputStream in = null; Exception ex = null; try{ try{ //code…..; } catch (Exception e) { ex = e; throw e; } } finally { try { in.close(); } catch (Exception e) { if(ex == null) throw e; } } } }
在jdk 1.7之后出现了带资源的try语句,它允许在try关键字后紧跟一对圆括号,圆括号可以声明、初始化一个或多个资源(此处的资源是指那些必须在程序结束时显式关闭的资源,比如数据库连接,网络连接等),try-with-resources 是一个定义了一个或多个资源的try 声明,try语句在该语句结束时自动关闭这些资源。try-with-resources确保每一个资源在处理完成后都会被关闭。这些资源必须实现AutoCloseable或者Closeable接口,实现这两个接口就必须实现close() 方法。
public class Xin { public static void main(String[] args) throws Exception { try(Scanner in = new Scanner(new FileInputStream(“d:\\haha.txt”)); PrintWriter out = new PrintWriter(“d:\\hehe.txt”)) { while(in.hasNext()) { out.println(in.next().toUpperCase()); } } } }
思考题:一个main程序启动一个java虚拟机进程。一个tomcat对应一个java虚拟机进程。一个tomcat部署多个spring的war包,若一个war里面controller有异常没有捕获,tomcat不挂掉,spring对这个异常进行拦截处理。项目1跑起来了,项目2的某个@bean的时候抛出异常。则tomcat停止。