黑马程序员-java异常处理机制

本文深入讲解Java异常处理机制,包括异常体系、处理机制、自定义异常、异常转型和异常链等内容,并提供实用的异常处理原则。

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

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


异常是程序运行过程中出现的错误。java语言的异常处理机制是java语言健壮性的一个重要体现。

一、异常体系


图1 java异常体系

下面将详细讲述各异常之间的区别与联系:

1、Throwable:

Throwable类是所有异常和错误的超类,它有两个子类Error和Exception,分别表示错误和异常。只有此类(或其子类)的对象才能通过java虚拟机或java throw语句抛出。自定义异常时,请勿直接继承本类。

2、Error

Error是Throwable的子类,表示仅靠程序本身无法恢复的严重错误,用于指示合理的应用程序不应该试图捕获的严重问题。大多数Error与代码编写者执行的操作无关,而表示代码运行时JVM出现的问题。例如,Virtual MachineError(java虚拟机运行错误)。

在执行该方法期间,无需在方法中通过throws声明可能抛出但未捕获的Error的任何子类,因为java编译器不去检查它。因此,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,或者用throws语句声明抛出它,程序仍会编译通过。

3、Exception

Exception是Throwable的子类,表示程序本身可以处理的异常。它又分为检查异常(Checked Exception)和不检查异常(Unchecked Exception)两类,而不检查异常又称为运行时异常(RuntimeException)。

1)不检查异常(Unchecked Exception):指RuntimeException类及其子类异常。RuntimeException是那些可能在java虚拟机正常运行期间抛出的异常的超类。java编译器不去检查它。因此,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,或者用throws语句声明抛出它,程序仍会编译通过。这种异常可以通过改进代码来避免。例如,NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等。

2)检查异常(Checked Exception):指除了RuntimeException类及其子类外,其他的Exception类及其子类。java编译器会检查它。因此,当程序中可能出现这类异常时,要么用try...catch语句捕获它,要么用throws语句声明抛出它,否则程序会编译不通过。例如,IOException(输入输出异常)。


异常体系的特点:异常体系中的所有类及建立的对象都具备可抛性,也就是说可以被throw和throws关键字所操作。只有异常体系具备这个特点。


二、java异常处理机制

java异常处理涉及到五个关键字,分别是:try、catch、finally、throw与throws。通过认识这五个关键字,即可掌握基本的异常处理知识。

1、异常处理的基本语法

try
{
    需要被检测的代码;
}
catch()
{
    异常处理代码;
}
finally
{
    方法返回之前,一定会执行的代码;
}

以上语句分为三个代码块:

1)try语句块:try语句块中代码受异常监控,其中代码发生异常时,会抛出异常对象。

2)catch语句块:catch语句块会捕获try语句块中产生的异常并在其代码块中做异常处理。

3)finally语句块:finally语句块紧跟着catch语句块,其中的代码总是会在方法返回之前执行,而不管try语句块中是否发生异常。目的是给程序一个补救的机会。


注意事项:

1)try、catch、finally三个语句块均不能单独使用,三者可以组成try...catch...finally、try...catch、try...finally三种结构。catch语句块可以有一个或多个,finally语句块最多一个。

2)try、catch、finally三个语句块中变量的作用域为语句块内部,分别独立而不能相互访问。如果要在三个语句块中均可以访问,则需要将变量定义到这些快的外面。

3)多个catch块时,匹配catch语句的顺序是由上至下。当实际抛出的异常对象是某个catch块的异常类型或其子类的实例,则执行该catch语句块代码,而不会再执行其他catch语句块。

4)可嵌套try...catch...finally结构。

5)在以下几种特殊情况下,finally语句块不会被执行:在前面的代码中使用了System.exit()退出程序;在finally语句块中发生了异常;程序所在的线程死亡;关闭CPU。

例1:异常处理

class Test
{
	public static void main(String[] args)
	{
		try
		{
			int x=div(4,0);                     //除数为零,抛出异常
			System.out.println("x="+x);         //此句代码未被执行
		}
		catch (Exception e)                         //Exception e=new ArithmeticException();
		{
			System.out.println("除数为零!");
			System.out.println(e.getMessage());
			System.out.println(e.toString());  //显示异常名称,异常信息
			e.printStackTrace();               //打印异常的堆栈的跟踪信息,显示异常名称,异常信息,异常出现的位置,是JVM默认的异常处理机制。
		}
	}
	public static int div(int a,int b)
	{
		return a/b;
	}
}

输出结果:

除数为零!
/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
              at Test.div(Test.java:39)
              at Test.main(Test.java:26)


2、throws和throw语句

1)throws关键字用于方法体外部的方法声明部分,用来声明方法可能会抛出某些异常。语法格式:throws 异常类型1,异常类2,...,异常类型n

2)throw关键字用于方法体内部,用来抛出一个Throwable类型的异常。语法格式:throw 异常对象


注意事项:

1)如果抛出的是不检查异常,即RuntimeException和Error或它们的子类,那么可以不必用try...catch语句捕获或用throws语句声明要抛出的异常。编译仍能通过,但在运行时会被系统抛出。

2)如果抛出的是检查异常,那么要么用try...catch语句捕获它,要么用throws语句声明抛出它,否则程序会编译不通过。

3)throw语句后不允许有其他语句紧跟,因为没有机会执行这些语句。

4)异常在子父类覆盖中的体现:

    子类在覆盖时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或该异常的子类。

   如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集。

    如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常;如果子类方法发生了异常,则必须要进行try处理,绝对不能抛。


三、自定义异常

在实际项目中会出现特有的问题,而这些问题并未被java所描述并封装成对象。所以对于这些特有的问题,可以按照java对问题封装的思想,将特有的问题,进行自定义的异常封装。

自定义类必须继承Exception类或其子类。因为根据异常体系的特点可知,只有这个体系中的类和对象才可以被throws和throw语句操作。

例2:自定义异常

class FuShuException extends Exception   //自定义异常需要继承Exception类或它的子类
{
	private int value;
	
	FuShuException()            
	{
		super();                        
	}
	FuShuException(String msg,int value) 
	{
		super(msg);                     //通过调用父类构造方法,来自定义异常信息
		this.value=value;
	}

	public int getValue()
	{
		return value;
	}
}
class Test
{
	public static void main(String[] args)
	{
		try
		{
			int x=div(4,-1);
			System.out.println("x="+x);
		}
		catch (FuShuException e)
		{
			e.printStackTrace();
			System.out.println("除数为"+e.getValue());
		}
	}
	public static int div(int a,int b)throws FuShuException  //方法中抛出了FuShuException异常对象,且未进行捕获,因此需要用throws语句声明
	{
		if(b<0)
			throw new FuShuException("除数是负数",b);        //用throw语句抛出异常对象
		return a/b;
	}
}
输出结果:

FuShuException:除数是负数
                 at Test.div(Test.java:38)
                 at Test.main(Test.java:26)
除数为-1


四、异常转型和异常链

异常转型:即捕获到异常后,将异常以新的类型的异常再抛出。这样做一般为了让异常的信息更加直观。

例3:异常转型

class Test 
{
	public static void main(String[] args) 
	{
		Teacher t=new Teacher("王老师");
		try
		{
			t.prelect();
		}
		catch (NoplanException e)
		{
			e.printStackTrace();
			System.out.println("放假!");
		}

	}
}
//电脑教学案例
class MaoyanException extends Exception
{
	MaoyanException(String msg)
	{
		super(msg);
	}
}
class NoplanException extends Exception
{
	NoplanException(String msg)
	{
		super(msg);
	}
}
class Computer
{
	private int state=2;
	public void run() throws MaoyanException
	{
		if(state==2)
			throw new MaoyanException("电脑冒烟了");
		System.out.println("电脑正常运行");

	}
	public void reset()                       //重启电脑
	{
		state=1;
		System.out.println("电脑重启");
	}
}
class Teacher
{
	private String name;
	private Computer c;
	Teacher(String name)
	{
		this.name=name;
		c=new Computer();
	}
	public void prelect() throws NoplanException
	{
		try
		{
			c.run();
		}
		catch (MaoyanException e)
		{
			e.printStackTrace();
			throw new NoplanException("课时无法继续"); //异常转换,让调用者更易理解
		}
	}
}

输出结果:


异常链:

jdk1.4以后版本中,Throwable类支持异常链机制。顾名思义,就是将异常发生的原因一个串一个的串起来,即把底层的异常信息传给上层,这样逐层抛出

例3中,我们看到了NoplanException异常的信息,但不知道引起它的异常的信息,而这点可以通过异常链解决。

例4:异常链

class Teacher
{
	private String name;
	private Computer c;
	Teacher(String name)
	{
		this.name=name;
		c=new Computer();
	}
	public void prelect() throws NoplanException
	{
		try
		{
			c.run();
		}
		catch (MaoyanException e)
		{
			e.printStackTrace();
			NoplanException np= new NoplanException("课时无法继续"); 
			np.initCause(e);          //异常链
			throw np;
		}
	}
}

输出结果:



五、java异常处理的原则和技巧

1、避免过大的try语句块,不要把不会出现异常的代码放到try语句块中,尽量保持一个try语句块对应一个或多个异常。

2、细化异常的类型,不要不管什么类型的异常都写成Exception

3、catch语句块尽量保持一个语句块捕获一类异常,不要忽略捕获的异常,捕获后要么处理,要么重新抛出新类型的异常等。

4、不要把自己能处理的异常抛给别人。

5、不要用try...catch语句参与控制程序流程,异常处理机制的根本目的在于处理程序的非正常情况。


延伸阅读:

1、如何设计异常处理的代码?

2、什么时候需要抛出异常?

3、捕获到了异常时该怎样处理?

要了解如何进行异常处理设计,请参阅这篇文章“java异常处理终结篇——如何进行java异常处理设计”



---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看: www.itheima.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值