#异常的基本知识
##异常的概念
要知道,异常指的都是运行时异常!编译错误一般指语法什么的错误,在运行之前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