java异常处理

 

异常的运行过程理解:

java异常处理_第1张图片

1如果程序在运行过程中出现了异常后,那么会由JVM自动根据异常的类型实例化一个与之类型匹配的异常类对象(此处用户不用去关心new,由系统自动负责处理)
2、产生了异常对象之后会判断当前语句上是否存在有异常处理,如果没有异常处理,那么就交给JVM进行默认的异常处理(输出异常信息,而后结束结束程序的调用)
3、如果此时存在有异常操作,那么会由try语句来捕获产生的异常类实例化对象,而后先后与catch语句(自上而下)参数进行匹配,如果匹配成功,则先执行fianlly代码体,后执行catch代码体,然后继续执行finally后面的代码,此时就算是处理过参数所代表的异常了但是如果匹配不成功,则会执行fianlly语句,但是后面的程序就不会执行了。
4、所以对于所有的catch(**),相当于方法的重载,可以使用向上转型来确定catch参数是Exception类的实例化对象,省去代码量(即直接以父类的实例化对象作为参数,代表所有的子类),不然就得具体写出具体的子异常类,也就是说所有的异常可以使用一种处理方式,但是最好分开处理

使用finally回收资源:

有些时候,程序在try块里面打开了一些物理资源(例如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。(Java的垃圾回收机制不会回收任何的物理资源,垃圾回收机制只能回收堆内存中的对象所占用的内存)。如果在try或者catch块里进行回收,则可能会导致部分代码因为异常的出现,在try中某条发生异常的语句之后的代码都不会被执行(包括可能的资源回收语句),如果catch语句中进行资源回收,但是catch块完全有可能不被执行。

所以finally块可以完全用来回收资源。不管try块中的代码是否出现异常,也不管哪一个catch块被执行,只要在try或catch块中没有调用退出JVM的方法,finally块总会被执行。

package com.java.exception;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author jack.chen
* @version 创建时间:2018年7月29日 下午2:21:34
* 类说明:
*/
public class TestFinally {
	public static void main(String[] args) {
		FileInputStream fis = null;
		try 
		{
			fis = new FileInputStream("a.txt");  //从文件a.txt中读取字节到输入流,如果a.txt文件找不到,则fis值还是null
		}
		catch(IOException ioe) 
		{
			System.out.println(ioe.getMessage());
			return; //利用return语句强制main方法返回,但是先会执行finally块再返回(在方法返回之前执行finally块)
			//System.exit(1);//使用exit来退出虚拟机 ,如果注释掉上面的return语句,执行本句,即在异常处理的catch块中
			//使用System.exit(1)来退出JVM,则finally将失去执行的机会。所以只要在try或catch块中调用了退出了JVM的方法,
			//finally块就不会执行。
		}
		finally  //异常处理嵌套,catch、try也可以
		{
			if(fis!=null)  //如果a.txt文件存在 
			{
				try 
				{
					fis.close();  //Java垃圾回收机制只会回收堆内存中的对象所占用的内存
					//关闭磁盘文件,避免资源浪费在其他不必要的地方,回收在try块中打开的物理资源
				}
				catch(IOException ioe)
				{
					ioe.printStackTrace();
				}
			}
			System.out.println("程序已经执行了finally里的资源回收!");
		}
		
	}
}
//Output:
//a.txt (系统找不到指定的文件。)
//程序已经执行了finally里的资源回收!

 

try-catch-finally执行顺序

异常处理语法结构中,只有try块是必须的,catch块和finally块至少出现其一。
1当try没有捕获到异常时: try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
2当try捕获到异常时

catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM默认处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
②catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,再执行finally语句块里的语句,最后执行finally语句块后的语句

 注意:

**try ****块:**用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
**catch ****块:**用于处理try捕获到的异常。
**finally ****块:一般来说finally中的方法都是会被执行的,其中finally中很大程度上用于资源的释放。
无论是否捕获或处理异常,finally块里的语句都会被执行。但在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。
在以下4种特殊情况下,finally块不会被执行:
                    1)在finally语句块中发生了异常。
                    2)在前面的代码中用了System.exit(0)退出程序。
                    3)程序所在的线程死亡。
                    4)关闭CPU。

 

注意:

    通常情况下,不要使用return 或者throw等导致方法终止的语句,一旦在finally块中使用了return或throw语句,
将会导致try块、catch块中的return、throw语句(一一对应)失效。

package com.java.exception;
/**
* @author jack.chen
* @version 创建时间:2018年7月29日 下午3:05:56
* 类说明:
*/
public class TestFinallyFlow 
{
	public static void main(String[] args) throws Exception
	{
		boolean a = test(); 
		System.out.println(a);  //false
	}
	public static boolean test() {
		try //失效
		{
			return true;
		}
		finally 
		{
			return false;
		}
	}
}

当程序执行try块、catch块时遇到了return 语句或throw语句,这两个语句都将导致该方法立即结束。所以系统并不会立即执行这两个语句,而是去寻找该异常处理流程中是否包含了finally块。如果没有,则执行return或throw语句,终止方法的执行,如果有,则立即开始执行finally块,如果finally块中没有return或throw语句,系统就会再次跳回来执行try块、catch块里的return或throw语句。如果遇到了finally中有return或throw语句,则在finally块中终止方法,不会跳回来执行try块、catch块里的return或throw语句了。

Error和Exception和异常的区别与联系:

1、在编写多个catch异常中的参数问题,捕获范围要先小后大(如果先大后小,则后面的“小”则永远没机会得到执行)

2所有的异常类都是Throwable的子类,而在Throwable下有还有一个子类Error
       Error:指的是JVM错误,Java虚拟机无法解决的JVM系统内部错误、资源耗尽等严重问题,一般不编写针对性的代码进行处理。即此时的程序还没有执行,用户无法处理。如栈溢出: java.lang.StackOverflowError
        Exception:指的是程序运行中产生的异常,即其他因编程错误或者是偶然的外在因素导致的一般性问题,用户可以可以使用针对性的代码进行处理。如:空指针访问,试图读取不存在的文件,网络连接中断

3、 (异常跟踪栈信息如:at java.io.FileInputStream open...)

所有的异常对象都包含了如下的几个常见用法:
getMessage():返回改异常的详细描述字符串;
printStackTrace():返回该异常的跟踪栈信息,输出到标准错误输出流中;
printStackTrace(PrintStream s):将该异常的跟踪栈信息,输出到指定的输出流中;
getStackTrace():返回该异常的跟踪栈信息

 

throws关键字:

package com.java.exception;
/**
* @author jack.chen
* @version 创建时间:2018年7月27日 上午12:31:09
* 类说明:throws关键字,用于方法声明,指的是此方法在被调用的过程中产生的异常交由调用此方法的地方处理
* 
*/

class test{
	public static int div(int x, int y)throws Exception /*抛出异常类,异常类可以有多个,用逗号隔开*/{
		return x/y;  //使用throws声明抛出异常的方法,无须使用try...catch
	}
}
public class Test4 {
	public static void main(String[] args) {
		try {
		System.out.println(test.div(10, 2));
		}catch(Exception e) {
			e.printStackTrace();
		}	
	}
}
//主方法使用throws关键字,如果发生了异常,则异常会抛给JVM处理,采用默认的异常处理方式,输出异常,然后结束程序的执行
	//主方法不用throws,因为程序一旦出错,也希望程序能够执行完毕
	//public static void main(String[] args) throws Exception {
	//	System.out.println(test.div(10, 0));
	//}

注意:

       使用throws关键字声明抛出异常时有一个和限制:就是方法重写时的“两小”中的一条规则:子类方法中声明抛出的异常类型应该是 父类方法中声明抛出的异常类型的子类或者相等。子类方法中不允许比父类方法声明抛出更多更大的异常。

 

throw关键字: 

public class Test5 {
	public static void main(String[] args) {
		try {
			throw new Exception("自己定义的异常!");//利用Exception构造方法
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

 再次编译:编译通过、运行成功

如果throw语句抛出的是Checked异常,·则该throw语句要么位于try块里,显式捕获该异常,要么放在一个带throws声明抛出的方法中,即把该异常交给该方法的调用者处理。如果是Runtime异常,则该throw语句无须位于try块里,显式捕获该异常,也无须放在一个带throws声明抛出的方法中,程序既可以使用try...catch来捕获,并处理该异常,也可以不处理该异常。

package com.java.exception;
/**
* @author jack.chen
* @version 创建时间:2018年7月29日 下午3:48:15
* 类说明:
*/
public class TestThrow {
	public static void main(String[] args) {
		try 
		{
			throwChecked(-3);//调用带throws声明的方法,必须显式捕获该异常,否则,必须在main方法中再次声明抛出
		}
		catch(Exception e) 
		{
			System.out.println(e.getMessage());
		}
		throwRuntime(3);//调用抛出Runtime异常类的方法,既可以显式捕获该异常,也可以不理会该异常
	}
	public static void throwChecked(int a)throws Exception{
		if(a>0)  
			throw new Exception("a的值大于0,不符合要求!"); //自行抛出Exception异常,该代码位于try块里,或处于带throws声明的方法中
	}
	public static void throwRuntime(int a){
		if(a>0)
			throw new RuntimeException("a的值大于0,不符合要求!");//自行抛出RuntimeException异常,既可以显式捕获该异常,也可以完全不用理会该异常,把该异常交给调用该方法的调用者处理
	}
}

下面给出一个经典的自定义异常

package com.java.exception;
/**
* @author jack.chen
* @version 创建时间:2018年7月29日 上午12:01:01
* 程序功能:使用throw关键字自定义异常
*/
class MyException extends Exception   //自定义异常类要继承自Exception或RuntimeException
{
	String message;
    public MyException(){}  //定义一个无参构造器
	public MyException(String ErrorMessagr) {
		message = ErrorMessagr; 
        //通常使用super(**);
	}
	public String getMessage() //定义一个重写getMessage()的方法
	{
		return message;
	}
}
public class Test7 
{
	public static int div(int x, int y) throws MyException //会产生异常,要抛异常
	{
		if(y<0) 
		{
			throw new MyException("除数不能为负数"); //产生异常则(向调用此方法的main函数)抛出一个new的MyException实例化对象
		}
		return x/y;
	}
	public static void main(String[] args) {
		try 
		{
			int result = div(3,-1);//主方法中调用方法,会接收一个new的MyException实例化对象,转于catch语句进行异常类的匹配
		}catch(MyException e) {
			System.out.println(e.getMessage());//输出异常信息
		}catch(ArithmeticException e) {
			System.out.println("除数1不能为0"); 
		}catch(Exception e) {
			System.out.println("程序发生了其他的异常");
		}
	}
}

 

 

 

 

RumtimeException异常类:

java异常类可以分为两大体系:

Checked异常和Runtime异常体系,而所有的RuntimeException类及其子类的实例都被称为Runtime异常,其他的都是Checked异常。

先来观察函数parseInt()方法的原型:

public Integer(String s) throws NumberFormatException
public class Test8 {
	public static void main(String[] args) {
		int x = Integer.parseInt("100");
	}
}

 但是上面的代码编译执行是可以通过的。按道理来讲应该强制性的捕获异常,但是现在并没有这种捕获异常的try...catch语句。根据异常的知识,如果一个方法抛出了异常,就应该在调用此方法的方法中定义try...catch代码语句,但是这里在调用parseInt()方法中的main函数中并没有try...catch语句。

打开API,会发现NumberFormatException异常类的继承结构如下:
java.lang.Object
   java.lang.Throwable
      java.lang.Exception
         java.lang.RuntimeException
            java.lang.IllegalArgumentException
               java.lang.NumberFormatException
所以,发现了NumberFormatException异常类是RuntimeException异常类的间接子类。
而如 ArithmeticException,NullPointerException,以及ClassCastException都是常见的RuntimeException直接子异常类。
实际上,在Java中,为了方便用户代码的编写,专门提供了RuntimeException异常类,这种异常类的最大特征在于:
如果程序在编译的时候不会强制性地要求用户处理异常,用户可以选择性地处理异常。如果发生了异常,
但是如果没有进行处理,就交给JVM进行默认处理(即输出异常,然后结束程序的执行)。
也就是说RuntimeException的子异常类,可以由用户根据自己的需要选择性地进行异常的处理。
而作为RuntimeException的父类Exception异常类,如果发生了异常就必须有try...catch语句,也就是进行异常的处理。
注意:对于RuntimeException的子类最好也使用异常处理机制。虽然RuntimeException的异常可以不使用try...catch进行处理,
但是如果一旦发生异常,则肯定会导致程序中断执行,所以,为了保证程序再出错后依然可以执行,在开发代码时最好使用try...catch的异常处理机制进行处理。

 

 

一种重要的代码模型:

class T{
	public static int div(int x, int y) {
		int result = 0;
		System.out.println("***1、除法运算开始 ***");
		result = x/y;
        System.out.println("***2、除法运算结束 ***");
		return result;
	}
}
public class Test6 {
	public static void main(String[] args) {
		try {
			System.out.println(T.div(12, 0));
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

编译运行结果是:

 java异常处理_第2张图片

可见一出现异常, 处于发生异常代码句之后的代码句

System.out.println("***2、除法运算结束 ***");
return result;

就不会执行了,如果此时我们需要让它们执行呢,那么就可以将x/y语句作为try的代码块:

package com.java.exception;
/**
* @author jack.chen
* @version 创建时间:2018年7月28日 上午12:03:01
* 类说明:throw关键字通常用在方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即停止,它后面的语句都不执行。
* 通过throw抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法声明中指明要抛出的异常;
* 如果要捕捉throw抛出的异常,则必须使用try—catch语句。
*/
class T{
	public static int div(int x, int y) throws Exception {
		int result = 0;
		System.out.println("***1、除法运算开始 ***");/*相当于把门关上*/
		try{
			result = x/y; 
			/*产生异常之后,就执行try代码块,判断catch参数是否有相关的异常类,如果有的话可以执行catch的代码体,
			但是由于后面有finally代码块,所以先执行finally代码块(不管出不出现异常都会执行finally语句),然后才执行catch()
			代码体,所以此处定义的throw e,指的是如果匹配到了异常类Exception的实例对象e,就吧把异常向上抛到"throws Exception"
			关联的(调用此div()方法的main函数中的catch语句匹配实例化异常类对象)*/
		}catch(Exception e) {
			throw e;
		} finally {
			System.out.println("***2、除法计算结束 ***"); /*finally代码块,是无论中途会不会出现异常,都会执行的,相当于把门关上*/
		}
		return result;
	}
}
public class Test6 {
	public static void main(String[] args) {
		try {
			System.out.println(T.div(12, 0));
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

编译执行:

java异常处理_第3张图片

 实际上,以上代码可以缩写,即:将div函数中的catch语句删除,即不处理出现的异常,就直接将异常抛给调用div方法的main函数进行异常处理。

你可能感兴趣的:(java学习笔记)