异常是指程序运行可能出现的不能正常继续的情况,也可以理解为程序出现了不在预期范围内的一些情况,都可以称之为异常。
所有的异常类是从java.lang.Exception类继承的子类。Exception类是Throwable类的子类。除了Exception类外,Throwable还有一个子类Error 。Java程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。Error用来指示运行时环境发生的错误。例如,JVM内存溢出。一般地,程序不会从错误中恢复。层次关系图如下
Throwable
Error Exception
other Exception RuntimeException
所以从上图可以看出,异常分为两类:
/** * Created by lili on 15/11/25. */ public class ExceptionTest { public static void main(String[] args) { int a = 10; int b = 0; System.out.println(a/b); System.out.println("---------"); } }
上述代码就是代码不严谨所致,分母不能为0,所以程序运行至a/b会抛出异常Exception in thread "main" java.lang.ArithmeticException: / by zero
java.lang.Object
继承者 java.lang.Throwable
继承者 java.lang.Exception
继承者 java.lang.RuntimeException
继承者 java.lang.ArithmeticException
查看该异常,发现是运行期异常,所以不能捕获,需要给出处理,例如加一个if判断。
/** * Created by lili on 15/11/25. */ public class ExceptionTest { public static void main(String[] args) { String dateStr = "2015-11-22"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date d = sdf.parse(dateStr); } }
上述代码无法通过编译,因为sdf.parse(dateStr)可能出现问题(尽管没问题),所以要做异常处理。
public class ExceptionTest { public static void main(String[] args) { String dateStr = "2015-11-22"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { Date d = sdf.parse(dateStr); } catch (ParseException e) { e.printStackTrace(); } finally { System.out.println("异常处理后"); } System.out.println("结束在这里"); } }
try { Date d = sdf.parse(dateStr); } catch (ParseException e) { e.printStackTrace(); System.out.println("异常处理1"); } catch(Exception e){ e.printStackTrace(); System.out.println("异常处理2"); }finally { System.out.println("异常处理后"); }
也就是说,如果抛出的是异常A,我们处理异常A的父亲异常是可以的。但是,一般建议catch准确的异常。上述多层异常处理的前后关系必须是父亲异常必须在后,即异常范围越大越靠后放,即最后才可能去比较,这也符合精确处理的原则,因为如果将Exception写在最前面,后面的处理都不会执行。最后不管是否有异常需要处理,finally都会执行。
JDK7提供了一种新的异常捕获方式,该方法用户捕获同等级的异常,并且这些异常的处理方法可以有相同的处理方式。
try{ }catch (ArithmeticException | ArrayIndexOutOfBoundsException e){ System.out.println("同类异常处理"); }finally { }
try catch执行的逻辑:
在try里面发现问题后,jvm会帮我们生成一个异常对象,然后把这个对象抛出,和catch里面的类进行匹配。
一旦有匹配的,就执行catch里面的处理,然后结束了try...catch 继续执行后面的语句。
public class ExceptionTest { public static void main(String[] args) { String dateStr = "2015-11-22"; try { dateFormat(dateStr); } catch (ParseException e) { e.printStackTrace(); }
System.out.println("这行代码可以执行");
} public static void dateFormat(String str) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(str);
}
}
上述代码展示了如何使用throws,但是调用该方法的地方还是要面临该异常。面临的时候可以选择处理或者继续抛出,但是在main方法里最好不要再抛出,不然就抛给JVM。抛给JVM的话,一旦出现问题,那后面的代码就无法继续执行了。
public class ExceptionTest { public static void main(String[] args) throws ParseException { String dateStr = "2015-11-22"; dateFormat(dateStr); System.out.println("这行代码执行不了"); } public static void dateFormat(String str) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse(str); } }
public class ExceptionTest { public static void main(String[] args) { int a = 10; int b = 0; division(a,b); } public static void division(int a,int b){ if(b==0){ throw new ArithmeticException("被除数为0"); } System.out.println(a/b); System.out.println("---------"); } }
上面之所以不会提示处理division,是因为ArithmeticException是运行时异常的子类,所以这是程序有问题,要改善程序。
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
2. throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常
finally一般用在try catch中用来做最后的一些收尾工作,如释放资源,在IO操作和数据库操作中用的比较多。但是如果在catch中有return语句,finally是否会执行呢?
public class ExceptionTest { public static void main(String[] args) { int a = 10; System.out.println("return a:"+ test(a)); } public static int test(int a) { try{ a = 10; int b = a / 0; } catch (Exception e){ a = 20; return a; } finally { a = 30; System.out.println("finally a:" + a); } a = 40; return a; } }
上面这段代码返回的是
finally a:30
return a:20
这说明,就算有返回语句,finally还是执行了,但是此时返回的a应该是30才对啊,观察debug发现,是执行到return把return语句的a替换为20后发现还有finally才去执行finally的,此时返回值已经确定。所以,finally中的语句执行在return中间;但是如果将代码改为下面这样,则返回的a也为30;
public class ExceptionTest { public static void main(String[] args) { Student s = new Student(0); System.out.println("return a:" + test(s).age); } public static Student test(Student s) { try { s.age = 10; int b = s.age / 0; } catch (Exception e) { s.age = 20; return s; } finally { s.age = 30; System.out.println("finally a:" + s.age); } s.age = 40; return s; } }
上面就提到了,异常分为两类,一类是运行期异常,一类是非运行期异常,所以自定义异常类一般是继承自这两类异常。以Exception为例进行分析。
Exception类中出了5个构造方法,啥方法都没有。
public Exception(){} public Exception(String message) { super(message); } public Exception(String message, Throwable cause) { super(message, cause); } public Exception(Throwable cause) { super(cause); } protected Exception(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); }
这里就要去了解出现异常是打印的信息从何而来?前面讲到throw的时候就可以看出,创建一个Exception对象并throw。try catch中是自动创建了一个异常对象去对比,类型一致(或者可以向上转型)则执行catch。所以打印信息应该是new对象的时候传入的,也有默认的打印(空构造时)。
所以我们自定义异常时,出了要继承两类异常外,还需要至少有两个构造的方法,一个空构造,一个非空构造,这样可以自定义异常输出。
下面是一个自定义的异常类和测试用例:
public class ExceptionTest { public static void main(String[] args){ int[] ageArr = new int[]{1,2,3,-1,130}; for(int a : ageArr){ try { ageCheck(a); } catch (AgeException e) { e.printStackTrace(); } } } public static void ageCheck(int age) throws AgeException { if(age > 120){ throw new AgeException("年龄超过120岁."); } if(age < 0){ throw new AgeException("年龄小于0岁."); } } } class AgeException extends Exception { public AgeException() { } public AgeException(String message) { super(message); } }
1. 子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
2. 如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
3. 如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
原文:http://www.cnblogs.com/gslyyq/p/4993210.html