异常(Exception)
2022-02-21 本文已影响0人
马佳乐
在程序设计和运行的过程中,发生错误是不可避免的。
常见异常分类:
语法错(编译错)——syntax error
- 违反语法规范的错误,通常在编译时发现。Java编译器能够发现所有语法错,给出错误的位置和性质。
语义错(运行错)——semantic error(run-time error)
- 如果程序在语法上正确,但在语义上存在错误。不能被编译系统发现,只能到程序运行时才能被系统发现,含有语义错的程序能够通过编译。
- Java解释器在运行时能够发现语义错,一旦发现了语义错,Java将停止程序运行,并给出错误的位置和性质。
逻辑错——logic error
- 如果程序通过编译,可运行,但运行结果与期望值不符。
- 由于系统无法找到逻辑错,所以逻辑错最难确定和排除。程序员必须凭借自身的程序设计经验,找到错误原因及出错位置,从而改正错误。
- 设计良好的程序应该在异常发生时提供处理这些错误的方法,使得程序不会因为异常的方式而阻断或产生不可预见的结果。
- 为了保证程序有效地执行,需要对发生的异常进行相应的处理。Java提供异常处理机制,使程序能够捕获并处理运行错,从而保证Java程序运行的可靠性和容错性。
- Java中将异常封装到一个类Exception中,出现错误时,就会抛出异常。
- Java程序的执行过程中如果出现异常,则生成一个异常类对象,该异常对象封装了异常事件的信息并将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
- 当Java运行时系统接收到异常对象时,会寻找能够处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
- Java运行时系统默认的处理方式是直接把异常对象里包装的信息打印到命令行上或控制台上。
异常类
Throwable 类是 Java 语言中所有错误或异常的超类。
声明:public class Throwable extends Object implements Serializable
(1)Error类
- 错误(error):指程序运行时遇到的硬件或操作系统的错误。错误对于程序而言是致命的,将导致程序无法运行,而且程序本身不能处理错误,只能依靠外界干预,否则会一致处于非正常状态。
- 声明格式:
public class Error extends Throwable
- Error类对象由Java虚拟机生成并抛出给系统,有内存溢出错误、栈溢出错误、动态链接错误等。
(2)Exception类(异常类)
- 声明格式:
public class Exception extends Throwable
- Exception类对象是Java程序捕获和处理的对象,每一种异常对应于Exception类的一个子类,异常对象中包含错误的位置和特征信息。
(3)RuntimeException类(运行异常类)
- 声明格式:
public class RuntimeException extends Exception
- 运行异常:指由程序本身错误引发的异常,这类异常程序设计时大多可以避免。
常见运行异常:
①算数异常ArithmeticException:
当进行整数除法或取余运算时,如果除数为0,则会产生。
②空对象异常NullPointerException:
当一个数组变量赋值为空引用(null)时,如果对该数组中的运算进行操作;
例如:
int a[]=null;
a[0]=1;l/对空数组中的元素进行操作,产生空对象异常
当一个对象赋值为null时,如果通过该空对象调用方法。
例如:
String str=null;
System.out.printIn(str.length0);//调用空对象的方法,产生空对象异常
③类型强制转换异常ClassCastException:
当进行类型强制转换时,如果遇到不能进行的转换操作;
例如:
Object obj=new Object();
String str=(String)obj;//obj既不是String的实例,也不是其子类的实例,故不能转换,产生异常
④数组下标越界异常ArrayIndexOutOfBoundsException:
当访问数组元素时,如果下标越界,则产生。
例如:
int a[]=new int[1];
a[1]=1;//产生数组下标越界异常
⑤数值格式异常NumberFormatException:
当将字符串转换成整数时,如果给定字符串不符合整数格式。
例如:
int i=Integer.parseInt(123"');//正确,不产生异常
int j=Integer.parseInt(“abc”);//产生数值格式异常
异常处理语句
异常处理语句有五个关键字try、catch、finally、throw和throws。
异常处理的基本结构
try
{
语句块;//try子句指明可能产生异常的代码段
}
catch(异常类型1 异常对象1)
{
异常处理语句块1;//捕获到异常并进行处理的代码
}
catch(异常类型2 异常对象2)
{
异常处理语句块2;
}
...
finally
{
异常处理结束前的语句块;//最后必须执行的代码,无论是否捕获到异常
}
异常处理语句的执行过程
①正常情况下(即没有产生异常时)
首先执行try中的语句序列,若没有产生异常则跳过catch子句,再执行finally子句中的语句序列,然后执行继续后面的语句。
②捕获异常并处理
- 执行try子句出现运行错时,Java抛出一个异常对象,由catch子句捕获并处理该异常对象,try中发生异常后,异常后面的代码不会执行。
- 在try内定义的变量是局部变量,在catch或者finally块内都是无法访问到的,并且在整个异常处理语句之外也是不可见的。
- catch子句只有一个参数。参数是一个异常对象,其类型必须是Throwable类的子类,指定当前catch子句所能处理的异常类型。catch子句可以有多个,分别处理不同类的异常。
- 若两个catch程序块(均和某个try程序块有关)都用于捕捉同一类型异常,那么将产生语法错误。
- 若catch子句中声明的异常类是该异常对象所属的类或其祖类,则该catch子句捕获到了该异常对象,再执行该catch子句。
- catch子句捕获异常的匹配规则:
抛出异常对象与catch子句参数类型相同
抛出异常对象为catch子句参数类的子类
按照先后次序捕获抛出异常对象,只捕获一次 - Java按照多个catch子句的书写次序从上向下一次查找每个catch子句中的异常类,一旦异常对象被某个catch子句捕获并处理后,Java将自动清除该异常对象,其它catch子句或外层的try将不能再捕获该异常对象
- 多个catch子句需要按异常类从子类到父类的次序依次排列。
- 通常最后一个catch子句的异常类参数声明为Exception,这样能够保证捕获和处理所有异常对象。
getMessage():获取错误的性质
toString():给出异常的类型与性质
printStackTrace():指出异常的类型、性质、栈层次以及在程序中的位置。
③执行finally子句
- 该子句中的语句序列是最后必须执行的代码,无论是否产生异常。
- 当try子句中的某条语句产生一个异常时,该语句之后的语句序列都将不会被执行,如果有些语句肯定需要执行,不管是否产生了异常,则这些语句需要写在finally中。
- 通常在finally块中可以进行资源的清除工作,如关闭打开的文件等。
包含异常处理的程序:
public class Test1 {
public static void main(String[] args) {
double num=1;
try{
num=1/0;
}catch(ArithmeticException e){
e.printStackTrace();
}finally{
System.out.println("继续运行其他代码");
}
}
}
包含多个catch块的异常处理程序:
public class Test1 {
public static void main(String[] args) {
int a[]={0,0};
int num=1,result=0;
String str=null;
try{
result=num/0;
System.out.println(num/a[2]);
str.charAt(3);
}catch(ArithmeticException e){
System.out.println("Error1=="+e);;
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error2=="+e);
}catch(Exception e){
System.out.println("Error3=="+e);
}finally{
System.out.println("继续执行程序");
}
}
}
抛出异常的方式
若某个方法可能会发生异常,但不想在当前方法中处理这个异常,则可以使用throws、throw关键字在该方法中抛出异常。
(1) 使用throws关键字抛出异常
- throws关键字通常被应用在声明方法时,用来指定方法可能抛出的异常
- throws抛出异常的格式为:
类型 方法名(形参表) throws 异常列表
{
代码
}
异常列表可用多个异常可使用逗号分隔
- 调用声明抛出异常的方法,必须使用try语句捕获并处理指定异常类,否则编译不通过。
throws抛出异常程序:
public class Pao {
public static void main(String[] args) {
try{
p(); //方法的直接调用者俘获处理异常
}
catch(ArithmeticException e) {
System.out.println("Error: Divider is zero! \n");
}
}
private static void p() throws ArithmeticException {
//间接抛出异常,自己并未处理,让方法的直接调用者来处理
int i;
i=4/0; //此句可能引发异常,可是自己并未处理
}
}
如果是Error、RuntimeException或它们的子类,可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
如:
public class Pao {
public static void main(String[] args) {
A a=new A();
try{
a.fun(); //方法的直接调用者俘获处理异常
}
catch(ArithmeticException e) {
System.out.println("Error: Divider is zero! \n");
}
}
}
class A extends RuntimeException{
void fun(){ // 运行时产生的异常被系统抛出
int i;
i=4/0;
}
}
(2)使用throw关键字抛出异常
- 手动抛出异常:throw关键字通常用在方法体中,功能是抛出一个异常对象。程序在执行到throw语句时流程就转到相匹配的异常处理语句,它后面的语句都不执行,所以throw语句后面不能有其他语句存在,否则编译出错。
- throw抛出异常的格式为:
throw ThrowableObject;
- 通过throw抛出异常后,如果想在上一级代码中捕获并处理异常,则需要在抛出异常的方法中使用throws关键字在方法的声明中指明要抛出的异常;如果要捕捉throw抛出的异常,则必须使用try—catch语句块。
- throw通常用来抛出用户自定异常。
throw抛出异常程序:
public static void main(String[] args) {
try{
f1(0);
}catch(ArithmeticException e){
System.out.println(e.getMessage());
}
}
static void f1(int i) throws ArithmeticException{
if(i==0){
throw new ArithmeticException("被除数为0");
}
}
自定义异常
- 通过继承java.lang.Exception类或其子类声明自定义异常类。
- 在方法适当的位置生成自定义异常类对象,并且只能用throw语句抛出该异常对象。
- 在方法的声明部分用throws语句声明该方法可能抛出的异常。
- 自定义异常对象要用try~catch捕获。
自定义异常程序:
public class Self {
public static void main(String[] args) {
try{
regist(-1);
}catch(MyException e){
System.out.println("登记失败");
System.out.println("原因为:"+e.getMessage());
}
System.out.println("操作结束");
}
static void regist(int num) throws MyException{
if(num<0){
throw new MyException("人数为负值");
}
System.out.println("登记人数:"+num);
}
}
class MyException extends Exception{
public MyException(String message) {
super(message);
}
}