发布时间:2023-02-09 文章分类:编程知识 投稿人:赵颖 字号: 默认 | | 超大 打印

第7章:异常处理

1、异常概述与异常体系结构

1.1 异常引出: 在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是考代码能够避免的(如客户输入数据的格式、读取文件是否存在、网络是否始终保持通畅等)。

1.2 异常概念:

异常: 在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)

1.3 异常事件分类:

Java程序在执行过程中发生的异常事件可以分为两类:

  1. Error: Java虚拟机无法解决的严重问题。如JVM系统内部错误、资源耗尽等严重情况、StackOverflowError(栈溢出,如无限递归函数)和OOM(OutOfMemoryError,堆溢出,存储空间太大)。一般不编写针对性的代码进行处理。
  2. Exception: 其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。如空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界。

1.4 异常体系结构:

对于异常体系结构是一个继承关系
java.lang.Throwable
	|-----java.lang.Error:一般不编写针对性的代码处理
    |-----java.lang.Exception:可以进行异常的处理
        |-----编译时异常(checked,非受检异常)
        	|-----IOException
        		|-----FileNotFoundException
        	|-----ClassNotFoundException
        	|----- …………
        |-----运行时异常(unchecked,受检异常)
			|-----NullPointerException
        	|-----ArrayIndexOutOfBoundsException
        	|-----ClassCastException
        	|-----NumberFormatException
        	|-----InputMismatchException
        	|-----ArithmeticException
编译时异常:执行javac.exe命令时,出现的异常,不让过编译。
运行时异常:执行java.ext命令时,可能出现的异常。

1.5 异常处理概述:

使用过多的if-else分支去处理异常,会导致程序的代码加长、臃肿、可读性差,因此我们此阿勇异常处理机制。

Java异常处理机制: 是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅、并易于维护。

1.6 Java异常处理的方式:

  1. try-catch-finally
  2. throws + 异常类型

1.7 异常处理:抓抛模型

过程一:“抛”:程序在正常执行过程中,一旦出现一异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出给程序的调用者,一旦抛出对象以后,其后的代码就不再执行。

关于异常对象的产生:1、系统自动生成异常对象;2、手动的生成一个异常对象,并抛出(throw)

过程二:“抓”:可以理解为异常的处理方式:1、try-catch-finally 2、throws

2、常见异常

2.1 常见异常举例:

package ExceptionTest;
import java.util.Date;
import java.util.Scanner;
import org.junit.Test;
public class ExceptionTest {
	//**********以下是编译时异常*****************
	//在编译的时候就打叉了,无法进行编译,即javac,无法生成字节码文件
	//**********以下是运行时异常*****************
	//可以执行,可以生成对应的字节码文件
	//当进行运行时,即java时,会出现问题
	//NullPointerException —— 空指针
	@Test
	public void test1(){
		//例子1:
		//int[] arr = null;
		//arr[1] = 10;
		//例子2
		String str = "abc";
		str = null;
		System.out.println(str.charAt(0));
	}
	//ArrayIndexOutOfBoundsException ——数组角标越界
	@Test
	public void test2(){
		int[] arr = new int[10];
		System.out.println(arr[10]);
	}
	//ClassCastException —— 类型转换异常
	@Test
	public void test3(){
		Object obj = new Date();
		String str = (String)obj;
	}
	//NumberFormatException —— 数值格式异常
	@Test
	public void test4(){
		String str = "abc";
		int num = Integer.parseInt(str);
	}
	//InputMismatchException —— 输入类型不匹配
	@Test
	public void test5(){
		Scanner scan = new Scanner(System.in);
		int sc = scan.nextInt();	//输入“abc”
		System.out.println(sc);
	}
	//ArithmetticException —— 算术异常
	@Test
	public void test6(){
		int a = 2;
		int b = 0;
		System.out.println(a / b);
	}
}

3、异常处理机制一:try-catch-finally

3.1:try-catch-finally的使用

try{
	//可能出现异常的代码(可能出现,不一定是一定会出现的)   
}catch(异常类型1 变量名1){
 	//处理异常方式1   
}catch(异常类型2 变量名2){
 	//处理异常方式2  
}catch(异常类型3 变量名3){
 	//处理异常方式3   
}
……
finally{
    //一定会执行的代码(即不管有无异常出现都会执行)
}
/*
try-catch的说明:
1、使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
2、一旦try中的异常类型匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有写finally的情况),继续执行其后的代码。
3、catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面(因为不然父类先执行,子类后执行!应该从小到大去判断),否则报错。
4、常用的异常处理对象的方式:a、String getMessage() : 打印异常信息 ; b、printStackTrace()  : 打印整个堆栈信息
5、在try结构中声明的变量,在出了try结构以后,就不能够再被调用(类似局部变量)。
finally的使用:
1、finally 是可选的
2、finally中声明的是一定会被执行的代码。即使catch中又出现了异常,try中有return语句,catch中有return语句等情况(finally先于try和catch中的return语句)。
3、像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
try-catch-finally的使用:
1、try-catch-finally可以相互嵌套
体会1:当我们使用try-catch处理编译时异常,使得我们程序在编译时不报错,但在运行时,仍可能报错,相当于,我们使用哦try-catch-finally讲一个编译时出现的异常,延迟到运行时出现。
体会2:由于开发中,运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally。然后针对于编译时异常,我们说一定要考虑异常的处理。
*/

4、异常处理机制二:throws

4.1 throws说明:

package ExceptionTest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
 * 异常处理方式二:throws + 异常类型
 * 
 * 1."throws + 异常类型" 写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
 * 一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,
 * 就会被抛出。异常代码后续的代码就不再执行。
 * 
 * 2.体会:try-catch-finally:真正的将异常给处掉了
 * 		throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。
 * 
 * 3.
 */
public class throwsTest {
	public static void main(String[] args){
		try{
			method2();
		}catch(IOException e){
			e.getStackTrace();
		}
	}
	public static void method2() throws IOException{
		method1();
	}
	public static void method1() throws FileNotFoundException,IOException{
		File file = new File("hello.txt");
		FileInputStream fis = new FileInputStream(file);
		int data = fis.read();
		while(data != -1){
			System.out.println((char)data);
			data = fis.read();
		}
		fis.close();
	}
}

4.2 方法重写:

package ExceptionTest;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
 * 方法重写:
 * 规则一:子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(对于多态会出问题)。
 * 
 */
public class OverrideTest {
	public static void main(String[] args) {
		OverrideTest o = new OverrideTest();
		o.display(new SubClass());
	}
	public void display(SuperClass s){
		try{
			s.method();
		}catch(IOException e){
			e.printStackTrace();
		}
	}
}
class SuperClass{
	public void method() throws IOException{
	}
}
class SubClass extends SuperClass{
	public void method()throws FileNotFoundException{
	}
}

5、手动抛出异常:throw

package ExceptionTest;
import javax.management.RuntimeErrorException;
public class StudentTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.regist(-101);
			System.out.println(s);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}
}
class Student{
	private int id;
	public void regist(int id) throws Exception{
		if(id > 0)this.id = id;
		else {
			//System.out.println("您输入数据有误!");
			//手动抛出一个异常对象
			//运行时错误,不能处理
			//throw new RuntimeException("您输入的数据非法");
			//编译时异常,需要处理
			throw new Exception("您输入的数据非法");
		}
	}
	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
}

6、用户自定义异常类

package ExceptionTest;
/*
 * 如何自定义异常类:
 * 1.继承于现有的异常结构:RuntimeException(运行时异常,不用显示处理)、Exception(需要显示处理)
 * 2.提供一个serialVersionUID(全局常量),序列号去表示该类
 * 3.提供几个重载的构造器
 * 
 */
public class MyException extends RuntimeException{
	static final long serialVersionUID = -703333333333333L;
	public MyException(){
	}
	public MyException(String msg){
		super(msg);
	}
}