赞
踩
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。其实在Java中,异常是Java提供的一种识别及响应错误的一致性机制。从而可以达到程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。
JAVA语言中的异常也是通过一个对象来表示的,程序运行时抛出的异常,实际上就是一个异常对象。该对象中不仅封装了错误信息,而且提供了一些处理方法,比如getMessage()方法获取异常信息,printStackTrace()方法输出对异常的详细描述信息等。
在java语言中已经提供了一些异常用来描述经常发生的错误。对于这些异常,有的需要程序进行获取处理或声明抛出,称为“受检查异常”;有的时候java虚拟机自动进行捕获处理,称为“运行时异常”或“不受检查异常”
下面是常见异常类列表:
异常类名称 | 异常类含义 |
ArithmeticException | 算数异常 |
ArrayIndexOutBoundsException | 数组下标异常 |
ArrayStoreException | 将与数组类型不兼容的值赋值给数组元素时抛出的异常 |
ClassNotFoundException | 未找到相应类异常 |
EOFException | 文件已结束异常 |
FileNotFoundException | 文件未找到异常 |
IllegalAcessException | 访问某类被拒绝时抛出的异常 |
InstantiationException | 试图通过newInstance()方法创建一个抽象类或抽象接口的实力时出的异常 |
IOException | 输入输出异常 |
NegativeArraySizeException | 建立元素个数为负数的数组异常 |
NullPointerException | 空指针异常 |
NumberPormatExpection | 字符串转换为数字异常 |
NoSuchFiledException | 字段未找到异常 |
NoSuchMethodException | 方法未找到异常 |
SecurityExpection | 小应用程序执行浏览器打开安全设置禁止的动作抛出异常 |
SQLExpection | 操作数据库异常 |
StringIndexOutBoundsException | 字符串索引超出范围异常 |
- 所有的异常类是从 java.lang.Exception 类继承的子类。
- Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
- Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
- Error 用来指示运行时环境发生的错误。
- 例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
- 异常类有两个主要的子类:IOException 类和 RuntimeException 类。
Error类以及子类通常用来描述JAVA程序运行系统中的内部错误以及资源耗尽的错误。Error类表示的异常时比较严重的,仅靠修改程序是不能恢复执行的,被称为致命异常类。
Exception类称为非致命异常类,它代表了另一种异常。发生该类异常的程序,通过捕获处理后可以正常运行,保持程序的可读性以及可靠性。
RuntimeException异常
RuntimeException是运行时的异常,也称为不检查异常,是程序员在编写程序中发生的错误导致的,修改错误后,程序就可以进行
- 1、检查性异常: 不处理编译不能通过
- 2、非检查性异常:不处理编译可以通过,如果有抛出直接抛到控制台
- 3、运行时异常: 就是非检查性异常
- 4、非运行时异常: 就是检查性异常
异常产生后,若不进行任何的处理,则程序就会被终止,为了保证程序的有效进行,就要对产生的异常进行处理。在java中,若某个方法抛出异常,即可以在当前方法中进行捕获
异常发生的原因有很多,通常包含以下几大类:
- 用户输入了非法数据。
- 要打开的文件不存在。
- 网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
要理解 Java 异常处理是如何工作的,你需要掌握以下三种类型的异常:
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这些异常在编译时强制要求程序员处理。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
这类异常通常使用 try-catch 块来捕获并处理异常,或者在方法声明中使用 throws 子句声明方法可能抛出的异常。
非检查性异:常是那些不需要在编译时被捕获或声明的异常。它们通常是程序错误,如逻辑错误、算法错误、类型转换错误等。这类异常不强制程序员处理,因为它们往往是不可预测的,或者是在程序员控制之外的。
一般格式为:
- try{
- 可能产生异常的代码
- }catch(异常类1 异常对象){
- 异常处理代码
- }catch(异常类2 异常对象){
- 异常处理代码
- }
- .....其他的catch语句块
try语句块中的代码可能同时存在多种异常,那么到底捕获的是哪一种类型的异常,是由catch语句中的“异常类”参数来决定的。catch语句类似于方法的声明,包括一个异常类型和该类的一个对象,异常类必须是Throwable类的子类,用来指定catch语句要捕获的异常。异常类对象可以在catch语句块中被调用,如调用对象getMessage();
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数的方法是一样。
将一个字符串转化为整数型,可以通过Integer类的parseInt()方法来实现。当该方法的字符串参数包括非数字字符时,parseInt()方法会抛出异常。Integer类的parseInt()方法的声明如下:
public static int parseInt(Stirng s) throws NumberFormatException{...}
代码中通过throws抛出了NumberFormatException异常,所以,在应用中parseInt()方法时可以通过try-catch语句来捕获异常,从而进行相应的异常处理。
例如,将字符串“24L”转换成integer类型,并获取转换中产生的数字格式异常,可以使用如下代码:
- try{
- int age =Integer.parseInt("24L");
- System.out.println("打印1");
- }catch(NumberFormatException e){
- System.out.println("年龄请输入整数!");
- System.out.printLn("错误:+e.getMessage()");
- }
- System.out.printLn("打印2");
因为程序执行到“Integer.parseInt("24L")”时抛出异常,直接被catch语句捕获,程序流程跳转到catch语句块内继续执行,所以“ 打印1”不会执行;异常处理结束之后,会继续执行try-catch语句后面的代码。
在使用多个catch语句捕获try语句块中的代码抛出的异常时,需要注意catch语句的顺序。若多个catch语句所要捕获的异常类之间具有继承关系,则用来捕获子类的catch语句要放在捕获父类的catch语句的前面,否则,异常抛出后,先由捕获父类异常的catch语句捕获,而捕获子类的catch语句将成为执行不到的代码,则编译会出错。
- private static void testException2() {
- try {
- //1、对可能产生异常的代码进行检视
- //2、如果try代码块的某条语句产生了异常, 就立即跳转到catch子句执行, try代码块后面的代码不再执行
- //3、try代码块可能会有多个受检异常需要预处理, 可以通过多个catch子句分别捕获
- } catch (异常类型1 e1) {
- //捕获异常类型1的异常, 进行处理
- //在开发阶段, 一般的处理方式要么获得异常信息, 要么打印异常栈跟踪信息(e1.printStackTrace())
- //在部署后, 如果有异常, 一般把异常信息打印到日志文件中, 如:logger.error(e1.getMessage());
- } catch (异常类型2 e1) {
- //捕获异常类型2的异常, 进行处理
- //如果捕获的异常类型有继承关系, 应该先捕获子异常再捕获父异常
- //如果没有继承关系, catch子句没有先后顺序
- } finally {
- //不管是否产生了异常, finally子句总是会执行
- //一般情况下, 会在finally子句中释放系统资源
- }
- }
finally
块是异常处理机制的一部分,它用于包含那些无论是否抛出异常都应当被执行的代码。通常,finally
块用于释放资源、关闭文件句柄、网络连接或数据库连接等,确保资源的正确释放,避免资源泄漏。
finally
块紧跟在try
块和catch
块之后。无论try
块中的代码是否抛出异常,也不论哪个catch
块被执行,finally
块中的代码总是会执行,除非在try
或catch
块中执行了System.exit()
,或者遇到了严重的运行时错误导致Java虚拟机终止。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw 语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
- public class FinallyExample {
- public static void main(String[] args) {
- try {
- // 尝试打开一个文件
- // 可能会抛出FileNotFoundException
- FileInputStream fis = new FileInputStream("file.txt");
- // 读取文件内容...
- } catch (FileNotFoundException e) {
- // 文件未找到异常处理
- System.out.println("文件未找到: " + e.getMessage());
- } finally {
- // 关闭文件流,无论是否抛出异常
- // fis.close();
- System.out.println("执行finally块,确保资源被释放");
- }
- }
- }
在上面的例子中,无论文件是否找到,finally
块中的代码都会执行,确保了资源的正确释放。注意,如果try
块中成功打开了文件,那么在finally
块中应该关闭文件流。但是,在这个例子中,由于fis
可能没有正确初始化(如果文件不存在),直接在finally
块中调用fis.close()
会导致NullPointerException
。在实际代码中,应该在try
块中初始化fis
,并在finally
块中使用一个空的if
语句来检查fis
是否为null
。
若某个方法可能会发生异常,但是不想在当前方法中处理这个异常,那么可以将该异常抛出,然后在调用该方法的代码中捕获该异常并进行处理。
- public class test3 {
- public static void main(String[] args) {
- try{
- dofile("文件位置");
- } catch (IOException e) {
- System.out.println("调用doilfe()出错!");
- System.out.println("错误:"+e.getMessage());
- }
- }
- public static void dofile(String name) throws IOException{
- File file =new File(name); //创建文件
- FileWriter fileOut=new FileWriter(file);
- fileOut.write("Helloworld");//向文件中写入数据
- fileOut.close();;//关闭输出流
- fileOut.write("爱护地球");//运行出错,抛出异常
- }
- }
- 调用doilfe()出错!
- 错误:Stream closed
程序运行结果如图所示,对于一个产生异常的方法,如果不使用try-catch语句捕获并处理异常,那么必须使用throws关键字指出该方法可能会抛出的异常。但如果异常类型是Error,RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常。例如,numberFormatException或ArithmeticException异常,JAVA虚拟机会捕获此类异常。
将异常通过throws关键字抛给上一级后,如果仍然不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的代码。
注意事项:
throws
关键字只用于声明方法可能会抛出的异常,它不处理异常。- 非检查型异常(Unchecked Exceptions),如运行时异常(RuntimeExceptions),不需要在方法签名中声明,因为它们是可选的,调用者可以选择性地处理它们。
- 过多地使用
throws
关键字可能会导致调用栈上方的代码变得复杂,因为每个调用者都需要处理或声明异常。因此,最好是在适当的地方捕获并处理异常,而不是简单地将其传递给调用者。
使用throw关键字也可以抛出异常。throw用于方法体,并且抛出一个异常类对象;throws用在方法声明中,来指明方法可能抛出的多个异常。
通过throw抛出异常后,如果想由上一级代码来捕获并处理异常,则同样需要使用throws关键字在方法声明要抛出的异常;如果想在当前的方法中捕获并处理throw抛出的异常,必须使用try-catch语句。上述两种情况,若throw抛出的异常时Error,RuntimeException或它们的子类,则无需使用throws或者try-catch语句。
当输入的年龄为负数时,JAVA虚拟机不会认为这是一个错误,单实际上年龄是不能为负数的,可通过异常的方式来处理这种情况。
-
- public class test3 {
- public static int check(String strage) throws Exception{
- int age =Integer.parseInt(strage); //将字符串转换为int类型
- if (age<0){
- throw new Exception("年龄不能为负数");
-
- }
- return age;
- }
-
- public static void main(String[] args) {
- try {
- int myage =check("-101");
- System.out.println(myage);
- }catch (Exception e){
- System.out.println("数据逻辑错误");
- System.out.println("原因:"+e.getMessage());
- }
- }
- }
- 数据逻辑错误
- 原因:年龄不能为负数
在check()方法中奖异常抛给了调用者(main()方法)进行处理,check()方法可能会抛出以下两种异常。
- 数字格式的字符串转换为int类型时抛出的NumberFormatExpection异常
- 当年龄小于0时抛出的Expection异常
如何取得异常对象的具体信息,常用的方法主要有两种:
获取异常描述信息
使用异常对象的getMessage()方法,通常用于打印日志时取得异常的堆栈信息
使用异常对象的printStackTrace()方法,比较适合于程序调试阶段
- 不能单独使用try,catch,finally语句块 ,否则编译出错
- try语句块当与catch语句块一起使用时,可存在多个catch语句块,而只能存在一个finally语句块。当catch语句块与finally语句块同时存在时,finally语句块必须放在catch语句块之后。
- try语句块只有finally语句块使用,可以使程序在发生异常后抛出异常,并继续执行方法中的其他代码。
- try语句只能和catch语句块使用,可以使用多个catch语句块来捕获try语句块中可能发生地点多种异常情况。异常发生后,java虚拟机会由上到下检测当前catch语句块所捕获的异常是否与try语句块中发生异常匹配,若匹配,则不再进行其他的catch语句块。如果多个catch语句块捕获的是同种类型的异常,则捕获子类的catch语句块要放在捕获父类异常的catch的前面
在try语句块中声明的变量是局部变量,只在当前try语句块中有效,在其后面的catch,finally语句块或其他位置都不能访问该变量。但在try,catch或者finally语句块之外声明的变量,可以在try,catch,finally语句块中访问。
- public static void main(String[] args) {
- int age1 ;
- try{
- age1 =Integer.valueOf("20L");//抛出NumberFormatException异常
- int age2 =Integer.valueOf("24L");//抛出NumberFormatException异常
- }catch (ArithmeticException e){
- int age1=-1;//编译成功
- int age2=0;//编译出错,无法解析age2
- }finally {
- System.out.println(age1);//编译成功
- System.out.println(age2);//编译出错,无法解析age2
- }
- }
通常使用java语言内置的异常类就可以描述在编写程序时出现的大部分情况,但是有些时候,程序员需要根据程序设计的需要来创建自己的异常类,用以描述java语言内置异常类所不能描述的一些特殊情况。下面就来介绍如何创建和如何自定义异常。
自定义异常类必须继承自Throwable类,才能被视为异常类,通常是继承Throwable的子类Exception或者Exception类的子孙类。
下面是一个实例来演示如何创建一个自定义异常类
1、创建一个MYException异常类,它必须继承Exception类。
- public class MYException extends Exception{
- private String content;
-
- public MYException(String content) {//构造方法
- this.content = content;
- }
-
- public String getContent() {//获取描述信息
- return content;
- }
- }
2、创建Example,在其中创建一个带有String类型参数的方法check(),该方法用来检查参数是否包含英文字母以外的字符。若包含,则通过throw关键字抛出一个MYException异常对象给check()方法的调用者main()方法。
- public class Example {
- public static void check(String str) throws MYException{
- char a[]=str.toCharArray();//将字符串转换为字符数组
- int i =a.length;//将字符串转换为字符数组类型
- for(int k =0;k<i-1;k++){
- if (!((a[k]>=65&&a[k]<=90)||a[k]>=97&&a[k]<=122)){
- //抛出MYException异常类对象
- throw new MYException("字符串\""+str+"\"中含有非法字符!");
- }
- }
-
- }
-
- public static void main(String[] args) {
- String str1 ="HelloWorld";
- String str2 ="Hell!MR";
- try {
- check(str1);
- check(str2);
- }catch (MYException e){
- System.out.println(e.getContent());
- }
- }
- }
下面是运行结果:
字符串"Hell!MR"中含有非法字符!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。