Java的异常体系

Java异常

程序在运行过程中发生错误或异常情况是不可避免的,如果每一个运行时错误都要由程序员事先想到并手动控制和处理,其工作量是不可想象的。 Java语言中的异常处理机制就解决的上述问题,把错误与异常的管理以类的方式带到了面向对象的世界,即 Java语言定义了很多异常类,将运行错误和异常的信息和处理方法封装在了异常类中,帮助程序员检查和控制异常 。

一、异常的分类及继承结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MWN5FLuQ-1621049432850)(img/1620997340453.png)]

从图中可以看出, Java语言按照错误严重性,从throwale根类衍生出Error和Exception两大派系

1.所有的异常都是从Throwable继承而来的,是所有异常的共同祖先。

2.Throwable有两个子类,Error和Exception。其中Error是错误,对于所有的编译时期的错误以及系统错误都是通过Error抛出的。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。 错误对程序而言是致命的,将导致程序无法运行。常见的错误有内存溢出,jvm虚拟机自身的非正常运行,calss文件没有主方法。程序本身是不能处理错误的,只能依靠外界干预。Error是系统内部的错误,由jvm抛出,交给系统来处理。
 

3.Exception,是另外一个非常重要的异常子类。是程序正常运行中,可以预料的意外情况。比如数据库连接中断,空指针,数组下标越界。异常出现可以导致程序非正常终止,也可以预先检测,被捕获处理掉,使程序继续运行。
异常和错误的区别是,异常是可以被处理的,而错误是没法处理的。 


按照性质,异常又分为编译异常(可检测)和运行时异常(不可检测)。
4.Checked Exception
可检查的异常,这是编码时非常常用的,所有checked exception都是需要在代码中处理的。它们的发生是可以预测的,正常的一种情况,可以合理的处理。比如IOException,或者一些自定义的异常。除了RuntimeException及其子类以外,都是checked exception。

5.Unchecked Exception
RuntimeException及其子类都是unchecked exception。比如NPE空指针异常,除数为0的算数异常ArithmeticException等等,这种异常是运行时发生,无法预先捕捉处理的。Error也是unchecked exception,也是无法预先处理的。

我们主要处理的异常是Exception这个派系的,包括运行时异常以及IO异常。

二、Java的异常处理机制

既然程序运行过程中出现异常了会导致程序运行的中断,这当然不是我们想要的,那么我们该怎么办呢?这个问题看起来不是很聪明的样子,看标题就知道出了异常就要处理,处理完了程序就可以继续运行了。那咋个处理才是我们要讨论的事情.。

(一)、两种处理异常的机制

为了可以让我们程序员处理能够预见的异常,确保程序继续运行,Java语言给我们提供了两种异常的处理方式。

1、使用 throw ,throws关键字抛出异常

一个方法不处理这个异常,而是调用层次向上传递,谁调用这个方法,这个异常就由谁来处理。

注意:重写之后的方法不可以比重写之前的方法抛出更大的异常
1.1、throws

将throws关键声明在可能出现异常的方法声明处,就能够将将来程序运行时出现的异常,抛给”上一级“ ,提醒该方法的调用者来处理异常 。所谓上一级就是调用这个出现了异常的方法的调用者,调用者同样需要对这个抛上来的异常进行处理,它的处理方式也有两种。

注意:若是Java中的异常发生之后一直往上抛,且抛的过程中不能解决这个异常,最终抛给了main方法,main方法抛给jvm,jvm知道异常发生后就会终止程序的运行

格式:

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2 ... { }

示例:

public class Test  {
	public static void main(String[] args) {
		doSome();
	}
	public static  void doSome() throws ClassNotFoundException{
		 System.out.println("dosome...");
	 }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XELY9izA-1621049432854)(img/1620999323608.png)]

程序出错的原因是什么?

因为dosome方法在声明位置使用了throws关键字 :throws ClassNotFoundException  ,这样做的意思是dosome方法在运行过程中可能会出现ClassNotFoundException,而这个ClassNotFoundException他是编译时异常,编译时异常必须编写代码来处理,否则就会报异常。让它不报错的一个方法是在main方法也使用throws抛出一个ClassNotFoundException异常或者是ClassNotFoundException的父类异常。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1l7IczQw-1621049432855)(img/1620999638861.png)]

当然也可以使用try catch的方式去处理。

1.2、throw

throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。

使用格式:

throw new 异常类名(参数);

示例:

public class Test {
    public static void main(String[] args) {
      int a =   Test.div(4,0);
      System.out.println(a);
    }
 
   public static int div(int a,int b)
      {
            if(b==0)
              throw new ArithmeticException(" 除数不能为0异常");//抛出具体问题,编译时不检测
            return a/b;
     }
} 

区别:

1、写法上 : throw 在方法体内使用,throws 函数名后或者参数列表后方法体前
2、意义 : throw 强调动作,而throws 表示一种倾向、可能但不一定实际发生
3、throws 后面跟的是异常类,可以一个,可以多个,多个用逗号隔开。throw 后跟的是异常对象,或者异常对象的引用。
 
2、使用try catch 方式捕捉异常

关键词try后的一对大括号将一块可能发生异常的代码包起来,称为监控区域。Java方法在运行过程中出现异常,则创建异常对象。将异常抛出监控区域之 外,由Java运行时系统试图寻找匹配的catch子句以捕获异常。若有匹配的catch子句,则运行其异常处理代码,try-catch语句结束。

匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹配。

使用格式:

try {
   ...  //监视代码执行过程,一旦返现异常则直接跳转至catch,
        // 如果没有异常则直接跳转至finally
} catch (SomeException e) {
    ... //可选执行的代码块,如果没有任何异常发生则不会执行;
        //如果发现异常则进行处理或向上抛出。
} finally {
    ... //必选执行的代码块,不管是否有异常发生,
        // 即使发生内存溢出异常也会执行,通常用于处理善后清理工作。
}

示例:

public static void main(String[] args) throws ClassNotFoundException {
		try {
			new FileInputStream("C:file//c.txt");
			System.out.println("出现异常后,try代码块中出现异常语句后面的代码不会执行!");
		} catch (FileNotFoundException e) {
			System.out.println("文件找不到异常!");
		}
		System.out.println("try catch 捕捉了异常 , 这里的代码继续执行");
	}
2.1、try catch 中finally子句的使用
1. 在finally子句中的代码是最后执行且一定会执行的,即使在try中出现了异常;finally子句必须跟try一起出现不能够单独出现。

示例:

public static void main(String[] args) {
		FileInputStream inputStream = null;
		String str = null;
		try {
			inputStream = new FileInputStream("C:newFile//text.txt");
			str.toString();// 这里一定会出现异常
			// inputStream.close(); 若是在这里关闭流的话,一旦出现异常,就关不了了
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();// 写在finally子句中即使出现了异常也会被执行
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}

关于finally:

finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
    1)在finally语句块中发生了异常。
    2)在前面的代码中用了System.exit()退出程序。
    3)程序所在的线程死亡。
    4)关闭CPU。
 

try、catch、finally语句块的执行顺序:

1)当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;

2)当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;

3)当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
 
2.2、有关finally的常见面试题
1、先不要看答案,你以为输出结果是什么?
public static void main(String[] args) {
		System.out.println(m());
	}
	public static int m() {
		int i = 10;
		try {
			return i;
		} finally {
			i++;
		}
	}

是11,对吗?

我告诉你,答案是10;意外吧!不是说finally子句会在return前执行吗?怎么会这样?

其实,在Java中有两条更古不变的语法规则:
   1.方法体中的代码必须遵循自上而下的顺序依次执行
   2.return语句一旦执行,整个方法必须结束
在这个例子中要不违背这两条规则,才行。所以return出现在int i=10; 的下面,所以它的返回值必须是10;同时finally子句还必须要执行且return语句在方法的最后执行,所以方法体的语句上到下依次运行到return  i=10 ;的时候还不能被执行但要保持return i=10;这个状态,等到finally子句执行完了才执行return i=10;虽然你finally中i++令i=11;但我return的状态就是i=10;所以最终返回的就是10;   

可以看看finally中的 i 是多少

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3cxqSqJP-1621049432858)(img/1621048206061.png)]

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r4okqE7c-1621049432861)(img/1621048218786.png)]

1.2 final finally finalize 有什么区别
final 关键字
      final关键字修饰的类无法被继承
      final关键字修饰的方法无法被重写
      final关键字修饰的变量无法重新赋值
 
finally 关键字
      和try一起使用,且其子句块中的代码是必须执行的

finalize 标识符
       是Object类中的一个方法名,是由垃圾回收器GC负责调用的
3、异常对象的常用方法
3.1、 getMessage(); 获取异常的简单描述信息
3.2、printStackTrace();// 打印异常追踪的堆栈信息
public static void main(String[] args) throws ClassNotFoundException {
		FileNotFoundException e = new FileNotFoundException("文件找不到异常!!!!");
		String message = e.getMessage();
		System.out.println(message);
		System.out.println("............................................");
		e.printStackTrace();

	}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWt4FpM7-1621049432863)(img/1621045641987.png)]

(二)、如何自定义异常类

方法:
   定义一个普通类去继承一个异常类,然后添加上一个无参构造器以及一个带有String类型参数的有参构造器,显示调用父类的有参构造器

示例:

public class MyException extends Exception {
	public MyException() {}
	public MyException(String message) {
		super(message);
	}
}

测试:

public static void main(String [] args ) {
		MyException e = new MyException("我的异常!");
		e.printStackTrace();
		System.out.println("异常信息:"+e.getMessage());
	}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值