#异常的基本知识
##异常的概念
要知道,异常指的都是运行时异常!编译错误一般指语法什么的错误,在运行之前IDE就会有提示。
上图中的代码就是数组下标越界
##异常的分类
error是系统异常,例如当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。 程序处理不了。exception是可以处理的,runtime exception是一种经常出的错误,可以catch也可以不catch。而其它exception,=是必须得逮的exception,方法自己就自带的exception
Java中的异常分为两大类:
1.Checked Exception(非Runtime Exception)
2.Unchecked Exception(Runtime Exception)
运行时异常
RuntimeException类是Exception类的子类,它叫做运行时异常,Java中的所有运行时异常都会直接或者间接地继承自RuntimeException类。
非运行时异常
Java中凡是继承自Exception,而不继承自RuntimeException类的异常都是非运行时异常。对于非运行时异常(checked exception),必须要对其进行处理,否则无法通过编译。
处理方式有两种:
1.使用try…catch…finally进行捕获;
一个try后面可以跟多个catch,但不管多少个,最多只会有一个catch块被执行。
2.在产生异常的方法声明后面写上throws 某一个Exception类型,如throws Exception,将异常抛出到外面一层去。
对于运行时异常(runtime exception),可以对其进行处理,也可以不处理。推荐不对运行时异常进行处理。
#处理异常
知道了上边异常的组成部分,我们针对其中需要处理的(非运行时异常)的两种处理方式。
##Try…catch…finally
###基本流程
在发现语句1出错,后边的语句不会执行,也就是说语句2不会被执行了,而是执行finally之后的 try catch之外的其它语句
public class Fileexception {
public static void main(String[] args) {
FileInputStream in = null; //一定要定义在try外边,记住作用域是在大括号之间
try {
in = new FileInputStream("myfile.txt");
int b;
b = in.read();
while (b != -1) {
System.out.print((char) b);
b = in.read();
}
} catch (FileNotFoundException e) { //一定要先写FileNotFoundException,因为FileNotFoundException是IOException的子类,因为写catch是先小后大
e.printStackTrace();
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
###使用多重catch
很多情况下,由单个的代码段可能引起多个异常。处理这种情况,我们需要定义两个或者更多的catch子句,每个子句捕获一种类型的异常,当异常被引发时,每个catch子句被依次检查,第一个匹配异常类型的子句执行,当一个catch子句执行以后,其他的子句将被旁路。
顺序问题:先小后大,即先子类后父类
Java通过异常类描述异常类型。对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。例如:RuntimeException异常类包括运行时各种常见的异常,ArithmeticException类和ArrayIndexOutOfBoundsException类都是它的子类。因此,RuntimeException异常类的catch子句应该放在最后面,否则可能会屏蔽其后的特定异常处理或引起编译错误。
###try ,catch,finally组合情况
亲测后有以下几条规则
1,try+catch后有没有finally无所谓
try+catch+finally可以使用
try+catch可以使用
2,try必不可少
try+finally,try+catch都可以
3,三个变量不能单独使用任何一个,即使是try如果不加catch也必须有finally(声明了异常就一定要处理,不管是在catch还是finally中)
总结就是必须有try和(catch,finally,catch+finally)三个组合中的一种
###return的执行时机
通用场景
1、不管有没有出现异常,finally块中代码都会执行;
finally里没有return的场景,但try或catch里有
2、当try和catch中有return时,finally里没有return,finally仍然会执行,因此在return返回时不是直接返回变量的值,而是复制一份,然后返回,因此,对于基本类型的,finally的改变没有影响,对于引用类型的就有影响了
package test;
/
* @author 田茂林
* @data 2017年9月6日 下午9:46:42
*/
public class TestFinally{
public static int testFinally1(){ //基本类型的
int result =1;
try {
result = 2;
return result;
} catch (Exception e) {
return 0;
}finally{
result =3;
System.out.println("execult Finally1");
}
}
public static StringBuffer testFinally2(){ //引用类型的
StringBuffer s = new StringBuffer("Hello");
try {
return s;
} catch (Exception e) {
return null;
}finally{
s.append("world");
System.out.println("execult Finally2");
}
}
public static void main(String[] args) {
int result1 = testFinally1();
System.out.println(result1);
StringBuffer result2 = testFinally2();
System.out.println(result2);
}
}
输出结果
execult Finally1
2
execult Finally2
Helloworld
finally里有return的情况
3、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。返回值是finally里的return返回的值
注意,如果是return i++返回i的值,return ++i返回的是++i的值,也就是永远返回当前值
package test;
/**
* @author 田茂林
* @data 2017年9月6日 下午9:46:42
*/
public class TestFinally {
@SuppressWarnings("finally")
public static int testFinally() { // 基本类型的
int i = 1;
try {
++i;
return i;
} catch (Exception e) {
} finally {
return i++; // 2
// return ++i; //3
}
}
public static void main(String[] args) {
int result1 = testFinally();
System.out.println(result1);
}
}
总结:
对于finally块中没有return语句的情况,方法在返回之前会先将返回值保存在局部变量表中的某个slot中,然后执行finally块中的语句,之后再将保存在局部变量表中某个slot中的数据放入操作数栈的栈顶并进行返回,因此对于基本数据类型而言,若在finally块中改变其值,并不会影响最后return的值。
而对于finally块中包含了return语句的情况,则在try块中的return执行之前,会先goto到finally块中,而在goto之前并不会对try块中要返回的值进行保护,而是直接去执行finally块中的语句,并最终执行finally块中的return语句,而忽略try块中的return语句,因此最终返回的值是在finally块中改变之后的值。
###catch块和finally块里可以抛出异常么
可以但是需要加try,catch环绕,或者直接从方法上声明抛出
##throws和throw
public class ManangeException {
public static void main(String[] args) { //直到抛到main方法里,不要再main方法里写throws,main方法会打印堆栈信息,但最好在main方法里处理异常
ManangeException m=new ManangeException();
try {
m.f2();
} catch (IOException e) {
System.out.println("没有找到该文件");
}
}
public void f() throws FileNotFoundException,IOException{ //不处理只抛出
FileInputStream in = null;
in = new FileInputStream("myfile.txt");
int b;
b = in.read();
while (b != -1) {
System.out.print((char) b);
b = in.read();
}
}
public void f2() throws IOException{ //一级一级往外抛
f();
}
============================================================
public void m(int i) throws ArithmeticException{
if(i==0) throw new ArithmeticException("被除数为0"); //手动抛出异常
}
}
main调用了f2()------f2()调用了f()------f()抛出了异常
##自定义异常
class MyException extends Exception {
private int id;
public MyException(String message, int id) {
super(message);
this.id = id;
}
public int getId() {
return id;
}
}
public class TestMyException {
public void regist(int num) throws MyException{
if(num<0) throw new MyException("不合法的人数", 1); //这要是异常抛出,则不会打印下边的那句话
System.out.println(num);
}
public void m(int i){
try {
regist(i); //方法体里边不能再写方法,但是可以直接调用就好
} catch (MyException e) {
System.out.println("出现故障");
}
}
public static void main(String[] args) {
TestMyException t=new TestMyException();
t.m(1);
}
}
#常见异常类
图片来自http://www.cnblogs.com/Qian123/p/5715402.html#_label2