1、概述

异常是对问题的描述,并封装为类。异常处理是一种程序容错机制,程序在运行过程中,遇到用户或环境中的错误时,要处理这些错误,继续执行。如果不能恢复,至少也给用户一个明确的信息,同时在程序结束前,做好善后工作。

异常将问题进行分组和封装;将常规代码和问题处理代码相分离,方便于阅读;但异常处理也需要更多的时间和资源。一般来说,一个项目中多个类都会发生的共同异常考虑作为一种异常类,多个方法的简单错误最好进行局部处理。try-catch块也最好用来处理不可预料的错误。

Java的异常处理通常有三个操作:

(1)抛出异常:检测到错误的程序可以创建一个合适的异常对象,并抛出。如果是预定义的异常错误,Java虚拟机可以自动创建对象并自动抛出,也可以手动抛出异常对象throw new IlleaglArgumentException(“Wrong Argument”);//无参或带String参数。也可能是调用一个可能会抛出异常的方法而抛出。

(2)声明异常:如果某方法内抛出异常又不在当前方法内处理异常,可以使用throws语句将异常抛出到调用方法中,如果所有方法都选择了抛出此异常,最后Java虚拟机将捕获它,输出相关的错误信息,并终止程序运行。声明异常时,建议声明为更具体的异常,以便处理得更具体。throws使用在方法上,)和{之间,后面跟的异常类可以有多个,用,隔开。

(3)捕获异常:当被调用的方法声明了异常,可以在该方法中继续抛出,也可以在try-catch块中捕获处理它。try中是可能产生异常的代码块,当没有产生异常时,不会执行catch块;try中代码出现异常时,跳过try块中剩余语句,执行catch中的处理方法。

try中的方法声明几个异常,就对应几个catch块,如果多个catch块中异常有继承关系,父类放在最下面。进行catch处理时,要定义具体的处理方式,不要简单写一条输出语句,通常把发生的问题用硬盘文件(异常日志文件)记录下来,维护人员看。可以使用同样的代码处理多个异常,catch (Exception1|Exception2|… e){},如果要区分这些异常要使用if和instanceof。

处理流程:当try中语句出现问题时,根据描述该问题的类创建对象new AritchmeticException(),抛出被try检测到,正常的执行流程被中断。try把该对象抛给catch,catch中声明的引用变量接收该对象,catch块中的代码被执行以处理异常。执行catch块之后的语句。没有try时,虚拟机调用默认异常处理机制,导致程序停止。

class Div{
	//在方法上通过throws关键词声明该方法可能会出现问题,
	//调用该方法要进行异常处理或声明抛出,否则编译失败
	int div(int a,int b) throws ArithmeticException{
		return a/b;
	}
}

public class ExceptionDemo {

	public static void main(String[] args) {
		Div d=new Div();
		
		try{
			int x=d.div(2,0);//如果变量定义在该代码块,则为局部变量
			System.out.println(x);
		}
		catch(ArithmeticException e){//异常类 变量
			System.out.println("除数为0");
			System.out.println(e.getMessage());//异常信息
			System.out.println(e.toString());//异常名称:异常信息
			e.printStackTrace();//异常名称:异常信息 异常出现位置
			//JVM默认的异常处理机制,就在调用该方法,打印异常堆栈的跟踪信息
		}
		System.out.println("over");
	}
}

finally代码块,定义一定会执行的代码,常用于关闭资源。finally只有一种情况不会执行,即之前执行到System.exit(0);JVM结束。return语句之后还会执行。

public void method() throws NoException{//数据库操作	
	try{
		//连接数据库;
		//数据操作; //throw new SQLException();
	}catch(SQLException e){
		//对数据库进行异常处理,不抛,数据库的问题内部解决
		throw new NoException(); //告知调用者结果
		//非本功能异常,将该异常产生的问题提供出去,让调用者解决
	}finally{
		//关闭数据库; //无论数据操作是否成功,一定会关闭资源
	}
}

模块化开发:一个负责数据的部分,一个负责数据库部分,谁出现问题谁处理,不需要对外暴露细节,只告诉对方结果,暴露对方能处理的问题(重新抛出异常)。

组合try-catch、try-catch-finally、try-finally(一个方法中出现了异常,但并不处理,此时需要关资源)。

2、异常类型

异常是对象,采用类来定义,异常的根类是java.lang.Throwable。根据错误的严重程度不同,分为系统错误(Error类)和异常(Exception类),二者是Throwable类的两个已知直接子类。错误是致命性的,程序无法处理,只能通知用户以及终止程序;异常,可编写程序进行捕获和处理。

运行时异常(RuntimeException类)是Exception的一个特殊的子类异常,描述程序设计错误。RuntimeException类的最常见子类有ArithmeticException(除0)、NullPointerException(通过null访问对象)、ArrayIndexOutOfBoundsException(数组下标越界)等。

异常通常被分为两种,检查型异常和非检查型异常。Exception及其子类是编译时被检测的,当函数内部出现throw抛出异常对象(手动或自动),必须在方法上声明抛出异常(捕获到的异常本功能处理不了)或在内部try-catch处理,处理完继续运行。Error和RuntimeException及其子类是编译时不被检测的运行时异常,通常由Java虚拟机抛出。如果在方法内抛出该异常,方法上可以不声明,即使声明,调用者也不用处理。当该异常发生,程序会停止,需要修正代码(逻辑错误),如果处理,问题就被隐藏。

不要求处理免检异常的原因:引发RuntimeException的操作在Java应用程序中频繁出现,若经常检查,整个程序中会有大量的try-catch块;它们表示的问题不一定作为异常处理,如name.equals(“yaocong”);如果name指向null会发生空指针异常,除了抛出还有更好的解决办法,”yaocong”.equals(name);//if(name!=null&& name.equals(“yaocong”))。

3、创建自定义异常类

项目中会出现特有问题,这些问题并未被java所描述并封装进对象。进行自定义的异常封装。自定义异常类必须派生自Throwable及其子类Exception、RuntimeException等,可抛性是Throwable体系独有特点,只有这个体系中的类和对象才可以被throw和throws。自定义异常时,如果该异常的发生使运算无法继续进行,就让自定义异常继承RuntimeException。

定义异常信息时通过访问父类的构造方法super(String msg),通过getMessage方法获取自定义的异常信息msg。因为父类Throwable中有带参数msg的构造函数,msg为私有变量,getMessage返回msg。

class NegativeException extends Exception{//1
	private int value;
	
	public NegativeException(String msg){
		super(msg);//5
	}
	/*
	private String msg;
	public NegativeException(String msg){
		this.msg=msg;
	}
	@Override
	public String getMessage(){
		return msg;
	}
	*/
	public NegativeException(String msg,int value){//6
		super(msg);
		this.value=value;
	}
	
	public int getValue(){//特有方法
		return value;
	}
}

class Div{
	int div(int a,int b) throws NegativeException{//3
		if(b<0)
			throw new NegativeException("/ by negative number",b);//2
		
		return a/b;
	}
}

public class ExceptionDemo {

	public static void main(String[] args) {
		Div d=new Div();
		
		try{//4
			int x=d.div(2,-1);
			System.out.println(x);
		}
		catch(NegativeException e){
			System.out.println(e.toString());
			System.out.println("Negetive number:"+e.getValue());
		}
	}
}

4、异常与覆盖

子类在覆盖父类中的方法时,只能抛出该方法异常的子集。如果子类方法有新异常,必须在内部进行异常处理。因为一个通用的父类可以派生出各种异常类,如果一个catch块可以捕获父类的异常对象,就能捕获该父类所有子类的异常对象(多态),所以子类可以抛出父类方法的子集,都可以被父类异常变量捕获,父类catch块也要写在子类之后。

class AException extends Exception{
}
class BException extends AException{
}
class CException extends Exception{
}

class Fu{
	void show() throws AException{
		
	}
}

class Test{//不能处理后期产生的新异常
	void function(Fu f){
		try{
			f.show();
		}catch(AException e){
			
		}
	}
}

class Zi extends Fu{
	void show() throws CException{
		//子类有新异常只能在内部处理,不能throws
	}
}

class Demo{
	public static void main(String[] args) {
		Test t=new Test();
		t.function(new Zi());
	}
}