第八章 异常处理

本文详细介绍了Java的异常处理机制,包括异常的概述、分类(系统错误Error和异常Exception)、捕捉处理异常(try...catch和finally代码块)、在方法中抛出异常(使用throws和throw关键字)以及自定义异常。强调了异常处理的原则,如避免过度使用异常,不要忽略捕获到的异常。同时,通过实例展示了如何处理和抛出异常,包括自定义异常的创建和使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

8.1 异常概述

8.2 异常的分类

8.2.1 系统错误——Error

8.2.2 异常——Exception

8.3 捕捉处理异常

8.3.1 try…catch 代码块

8.3.2 finally 代码块

8.4 在方法中抛出异常

8.4.1 使用throws关键字抛出异常

8.4.2 使用throw关键字抛出异常

8.5 自定义异常

8.6 异常的使用原则


8.1 异常概述

        在程序中,错误可能产生于程序员没有预料到的各种情况,或者是超出了程序员可控范围的环境因素,如试图打开一个根本不存在的文件等,在 Java中,这种在程序运行时可能出现的一些错误称为异常。Java语言的异常处理机制优势之一就是可以将异常情况在方法调用中进行传递,通过传递可以将异常情况传递到合适的位置再进行处理, 这种机制类似于现实中发现了火灾,一个人是无 法扑灭大火的,那么可以将这种异常情况传递给119,119 再将这个情况传递给附近的消防队,消防队及时赶到并进行灭火。使用这种处理机制,使得Java语言的异常处理更加灵活,Java语言编写的项目更加稳定。当然,异常处理机制也存在一些弊端,例如,使用异常处理可能会降低程序的执行效率,增加语法复杂度等。

public class Baulk {//创建Baulk类
	public static void main(String[] args) {//主方法
		int result = 3/0;//定义int型变量并赋值
		System.out.println(result);//将变量输出
	}
}

        程序运行的结果报告发生了算术异常Arithn neticException(根据给出的错误提示可知发生错误是因为在算术表达式“3/0”中,0作为除数出现) 系统不再执行下去,提前结束。这种情况就是所说 
的异常。 

        有许多异常的例子,如空指针、数组溢出等。由于 Java 语言是一门面向对象的编程语言,因此异常在Java式。某一方法中发生错误时,这个方法会创建个对象,并且把它传递给正在运行的系统。这个对象就是异常对象。通过异常处理机制,可以将非正常情况下的处理代码与程序的主逻辑分离,即在编写代码主流程的同时在其他地方处理异常。 

8.2 异常的分类

        Java类库的每个包中都定义了异常类,所有这些类都是 Throwable 类的子类。Throwable 类派生了两个子类,分别是 Error 类和Exception类,其中,Error类及其子类用来描述Java运行系统中的内部错误以及资源耗尽的错误,这类错误比较严重。Exception 类称为非致命性类,可以通过捕捉处理使程序继续执行。Exception 类又可以根据错误发生的原因分为运行时异常和非运行时异常。

8.2.1 系统错误——Error

         Error 类及其子类通常用来描述 Java 运行系统中的内部错误 该类定义了常规环境下不希望由 程序捕获的异常,比如 OutOfMemoryError、ThreadDeath等,这些错误发生时,Java虚拟机(JVM)一般会选择线程终止。

public static void main(String[] args) {//主方法
		System.out.println("梦想照亮现实!!!")//此处缺少必要的分号
	}
}

         从图提示可以看到显示的异常信息为“java lang.Error”,说明这是一个系统错误,程序遇到这种错误,通常都会停止执行,而且这类错误无法使使用异常处理语句处理。

8.2.2 异常——Exception

         Exception是程序本身可以处理的异常,这种异常主要分为运行行时异常和非运行时异常,程序中应当尽可能去处理这些异常,本节将分别对这两种异常进行讲解。

1.运行时异常

        运行时异常是程序运行过程中产生的异常,它是Runtime Exception 类及其子类异常,如
NullPointerException、IndexOutOfBoundsException等,这些异常 般是由程序逻辑错误引起的,程 序应该从逻辑角度尽可能避免这类异常的发生。
        Java中提供了常见的RuntimeException异常,这些异常可通道过try...catch语句捕获,如表所示。

        例如,将一个字符串转换为整型,可以通过Integer 类的parseInt()方法来实现。如果该字符串不是数字形式,parseInt(方法就会显示异常,程程序将在出现异常的位置终止,不再执行下面的语句。

public class Thundering {//创建Thundering类
	public static void main(String[] args) {//主方法
		String str = "lili";//定义字符串
		System.out.println(str +"年龄是:");//输出的提示符
		int age = Integer.parseInt("20L");//数据类型的转换
		System.out.println(age);//输出信息
	}
}

        从结果中可以看出,本实例报出的是NumberFormatExcep stion(字符串转换为数字)异常,该异常实质上是由于开发人员的逻辑错误造成的。

2.非运行时异常

        非运行时异常是RuntimeException类及其子类异常以外的异常。从程序语法角度讲,这类异常是必须进行处理的异常,如果不处理,程序就不能编译通过, 如IOException、SQLException 以及用户自定义的异常等.Java中常见的非运行时异常类如表所示。

public class FootballTeam {//创建FootballTeam类
	private int playerNum;//定义“球员数量”
	private String teamName;//定义“球队名称”
	public FootballTeam(){//构造FootballTeam()
		//寻找“教练”类
		try {//try语句中包含可能出现异常的程序代码
			Class.forName("com.mrsoft.Coach");
		} catch (ClassNotFoundException e) {//catch代码块用来获取异常信息
			e.printStackTrace();
		}//通过反射机制寻找“教练”类
	}
	public static void main(String[] args) {//主方法
		FootballTeam team = new FootballTeam();//创建对象team
		team.teamName = "com.mrsoft";//初始化teamName
		team.playerNum = 19;//初始化playerNum
		System.out.println("\n球队名称: "+ team.teamName +"\n"+"球员数量: "+team.playerNum +"名");//输出信息
	}
}

 在Eclipse中编写完上面代码后,会直接在编译器中显示错误,将光标移动到显示错误的行上。

        从这里可以看出,对于非运行时异常,必须使用trycatch代码块进行处理,或者使用throw关键字抛出。

8.3 捕捉处理异常

  其中,try代码块中是可能发生异常的Java代码;catch代码块在try代码块之后,用来激发被捕获的异常;finally代码块是异常处理结构的最后执行部分,无论程序是否发生异常,finally代码块中的代码都将执行,因此,在 finally 代码块中通常放置一些释放资源、关闭对象的代码。
通过try…catch 代码块的语法可知,捕获处理异常分为try….catch 代码块和 finally 代码块两部分

8.3.1 try…catch 代码块

public class Take {//创建Take类
	public static void main(String[] args) {//主函数
		try {//try语句中包含可能出现异常的程序代码
			String str = "lili";//定义字符串变量
			System.out.println(str + "年龄是:");//输出的信息
			int age = Integer.parseInt("20L");//数据类型的转换
			System.out.println(age);//输出信息
		}catch(Exception e) {//catch代码块用来获取异常信息
			e.printStackTrace();//输出异常性质
		}
		System.out.println("program over");//输出信息
	}
}

注意:Exception是try代码块传递给catch代码块的类型,e是对象名。 

        从代码中可以看出,程序仍然输出最后的提示信息,没有因为异常而终止。在例 8.4 中将可能出现异常的代码用trac代进行理,tr代码中语句发生异常时,程序就会跳转到catch代码块中执行,执行catch代码中的程代码后,将继续执行catch码,而不会执行try代码块中发生异常语句后面的代码。由此可知,Java的异常处理是结构化的,不会因为一个异常影响整个程序的执行。
        上面代码中,在catch代码块中使用了Exception对象的printStackTrace()方法输出了异常的栈日志,除此之外,Exception对象还提供了其他的方法用于获取异常的相关信息,其最常用的3个方法如下。

1.getMessage()方法:获取有关异常事件的信息。
2.toString()方法: 获取异常的类型与性质。
3.printStackTrace()方法:获取异常事件发生时执行堆栈的内容 

 注意:有时为了编程简单会忽略catch代码块后的代码,这样try…catch语句就成了一种摆设,一旦程序在运行过程中出现了异常,就会导致最终运行结果与期望的不一致,而错误发生的原因很难查找。因此要养成良好的编程习惯,最好在catch代码块中写入处理异常的代码。
        在例题中,虽然try代码块后面用了一个catch代码块来捕捉异常,但是遇到需要处理多种异常信息的情况时,可以在一个try代码后面多catch代码块。这里需要注意的是,如果使用多个catch 代码块,则catch 代码块中的异常类顺序是先子类后父类,因为父类的引用可以引用子类的对象。 

8.3.2 finally 代码块

        完整的异常处理语句应该包含 finally 代码块,通常情况下,无论程序中有无异常发生,finally代码块中的代码都可以正常执行。 

public class Take1 {//创建Take类
	public static void main(String[] args) {//主函数
		try {//try语句中包含可能出现异常的程序代码
			String str = "lili";//定义字符串变量
			System.out.println(str + "年龄是:");//输出的信息
			int age = Integer.parseInt("20L");//数据类型的转换
			System.out.println(age);//输出信息
		}catch(Exception e) {//catch代码块用来获取异常信息
			e.printStackTrace();//输出异常性质
	} finally {//finally代码块中的代码都能正常执行
		System.out.println("program over");//输出信息
		}
	}
}

 程序在捕捉完异常信息之后,会执行finally代码中的代码。                                                 

在以下3种特殊情况下,finally块不会被执行

1.在finally代码块中发生了异常。

2.在前面的代码中使用了System.exit()退出程序。

3.程序所在的线程死亡。

8.4 在方法中抛出异常

        如果某个方法可能会发生异常,但不想在当前方法中处理这个异常,则可以使用throws、throw关键字在方法中抛出异常,本节将对如何在方法中抛出异常进行讲解。 

8.4.1 使用throws关键字抛出异常

        throws 应用在明法时,用来指定方法可能抛出的异常,多个异常可使用逗号分隔。使用 throws 关键字抛出异常的语法格式为:

返回值类型名 方法名(参数表) throws异常类型名{
    方法体                                                                                                                                                    }
public class Shoot {//创建Shoot类
	static void pop() throws NegativeArraySizeException {//定义方法抛出NegativeArraySizeException异常
		int[] arr = new int [-3];//创建数组
	}
	public static void main(String[] args) {//主方法
		try {//try语句处理异常
			pop();//调用pop()方法
		}catch(NegativeArraySizeException e) {//catch代码块用来获取异常信息
			System.out.println("pop()方法抛出的异常");//输出异常信息
		}
	}  
}

 

注意:用throws 为方法抛出异常时,如果子类继承父类,子类重写方法抛出的异常也要和原父类方法抛出的异常同或是其异常的子类,除非 throws 异常是RuntimeException。 

8.4.2 使用throw关键字抛出异常

        throw 关键字通常用于在方法体中“制造”一个异常,程序在执行到throw 语句时立即终止,它后面的语句都不执行。使用 throw 关键字抛出异常的语法格式为:
throw new  异常类型名(异常信息)
        throw 通常用于在程序出现某种逻辑错误时,由开发者主动抛出某种特定类型的异常,下面通过一个实例介绍throw的用法。

public class ThrowTest {//创建ThrowTest类
	public static void main(String[] args) {//主方法
		int num1 = 25;//num1的值为25
		int num2 = 0;//num2的值为0
		int result;//结果
		if (num2 == 0)//判断num2是否等于0,如果等于0,抛出异常
		{
			throw new ArithmeticException("这都不会,小学生都知道:出书不能是0!!!");
		}//抛出ArithmeticException异常
		result = num1 / num2;//计算int1除以int2的值
		System.out.println("两个数的商为:"+ result);//输出结果
	}
}

说明:通常用来抛出用户自定义异常,通过 throw 关键字抛出异常后,如果想在上一级代码中捕获并处理异常,最好在抛出异常的方法声明中使用 throws 关键字指明要抛出的异常;如果要捕捉 throw 抛出的异常,则需要使用try...catch代码块。
throws 关键字和 throw 关键字的区别如下。
(1)throws 用在方法声明后面,表示抛出异常,由方法的调用者处理,而 throw 用在方法体内,用来制造一个异常,由方法体内的语句处理。
(2)throws 是声明这个方法会抛出这种类型的异常,以便使它的调用者知道要捕获这个异常,而 throw 是直接抛出一个异常实例。
(3)throws表示出现异常的一种可能性,并不一定会发生这些异常,如果使用throw,就一定会产生某种异常。

8.5 自定义异常

 使用Java内置的异常类可以描述在编程时出现的大部分异常情况,但是有些情况是通过内置异常类无法识别的,例如,下 面的一段代码:

int age=-50;
System. out.println("王师傅今年"+age+" 岁了! ");

 上面代码运行时没有任何问题,但是大家想一想:人的年龄可能是负数吗?这类问题编译器是无法识别的,但很明显不符合常理,那么,对于这类问题即可通过自定义异常对它们进行处理。Java中可以通过继承Exception类自定义异常类。

在程序中使用自定义异常类,大体可分为以下几个步骤                                                                 (1)创建自定义异常类。
(2)在方法中通过throw关键字抛出异常对象。
(3)如果在当前抛出异常的方法中处理异常,可以使用try…catch代码块捕获并处理,否则,在方法的声明处通过throws关键字指明要抛给方法调用者的异常,继续进行下一步操作。
(4)在出现异常方法的调用者中捕获并处理异常。有了自定义异常,再来解决年龄为负数的异常问题。 

public class MyException extends Exception{//创建自定义异常,继承Exception类
	public MyException (String ErrorMessage) {//构造方法
		super (ErrorMessage);//父类构造方法
	}
}
public class Tran {//创建类
	static void avg(int age) throws  MyException{//抛出MyException异常错误
		  if(age<0) {      //判断方法中参数是否满足指定条件
		   throw new MyException("年龄不可以使用负数");//错误信息
		  }else {                  //反之
		   System.out.println("王师傅今年"+ age +"岁了!");//输出王师傅今年多少岁了
		  }
	}
		   public static void main(String[] args) { // 主方法
		    try {     //try代码块处理可能出现异常的代码
		     avg(-50);              //负数
		    }catch(MyException e) {   //捕捉错误信息
		     e.printStackTrace();   //输出异常性质
		    }
	 }
}

自定义异常主要用在以下场合。
(1)使异常信息更加具体,比如跟别人合作开发时,程序出现了空指针异常,但别人可能不清楚这个空指针是如何产生的,这时即可自定义一个显示具体信息的异常,比如自定义一个用户信息为空时抛出的异常:NullOfUserlnfoException,当这个异常发生就代表用户填写的信息不完整。
(2)程序中有些错误是符合Java语法的,但不符合业务逻辑或者实际情况,比如程序中出现了一个人的年龄是负数、人员个数为小数等。
(3)在分层的软件架构中,通常在表现层统一对系统其他层次的异常进行捕获处理。 

8.6 异常的使用原则

        Java异常处理不应该用来控制程序的正常流其主要作用是捕获程序在运行时发生的异常并进行相应的处理。编写代码处理某个方法可能出现异常时,可遵循以下原则。
(1)不要过度使用异常,虽然通过异常可以增强程序的健壮性,但如果使用过多不必要的异常处理,可能会影响程序的执行效率。
(2)不要使用过于庞大的 try...catch块。在一个try块中放置大量的代码。这种写法看上去“很简单”但是由于try块中的代码过于庞大,业务过于复杂,会造成try块中出现异常的可能性大大增加,从而导致分析异常原因的难度也大大增加。
(3)避免使用catch(Exception)。因为如果所有异常都采用相同的处理方式,将导致无法对同异常分情况处理;另外,这种捕获方式可能将程序中的全部错误,异常捕获到,这时如果出现些“关键”异常,可能会被“悄悄地”忽略掉。
(4)不要忽略捕捉到的异常,遇到异常一定要及时处理。
(5)如果父类抛出多个异常,则覆盖方法必须抛出相同的异常或其异常的子类,不能抛出来异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值