概述
异常的概述
异常就是程序在运行时出现的不正常情况。我们不仅要问异常是怎么来的呢?问题也是现实生活中一个具体的事物,也可以通过Java的类的形式进行描述,并封装成对象。异常其实就是Java对不正常情况进行描述后的对象体现。
异常的体系
对于问题的划分有两种:一种是严重的问题,一种是非严重的问题。
- 对于严重的,Java通过Error类进行描述(通常出现重大问题,如运行的类不存在或者内存溢出等)。对于Error一般不编写针对性的代码对其进行处理。Error是由系统底层发生的,它将告诉JVM,JVM告诉使用者。Error一旦出现不做针对性的处理,直接修改代码。
- 对于非严重的,Java通过Exception类进行描述。Exception是由JVM发生的,并告诉调用者,对于Exception可以使用针对性的处理方式进行处理。
无论Error或者Exception都具有一些共性内容,比如不正常情况的信息、引发原因等。
异常的处理
Java提供了两种对异常的处理方式:
- 遇到问题不进行具体的处理,而是继续抛给调用者。其实就是在函数上通过throws关键字声明异常,告诉调用者处理。对这种处理方式的详细解释:在编写功能时,编写者会知道该功能有可能发生问题,而这个问题很容易来自于调用者传递的参数,而导致功能无法运行。这时发生的问题就应该让调用者知道,并最后让调用者有预先的处理方式,所以在定义功能时,需要在功能上对有可能发生的问题进行声明。声明问题需要使用关键字(throws):throws 异常类,声明的目的:就是让调用者可以进行处理。
针对性的处理方式——捕获!
try { // 有可能发生异常的代码 } catch(异常类 变量) { // 这是真正的捕获,处理异常的代码; } finally { // 一定会被执行的代码; }
异常的常见方法
方法声明 | 功能描述 |
---|---|
getMessage() | 获取异常信息,返回字符串 |
toString() | 获取异常类名和异常信息,返回字符串 |
printStackTrace() | 获取异常类名和异常信息,以及异常出现在程序中的位置,返回值为void。JVM默认处理收到的异常就是调用这个方法,将信息显示在屏幕上 |
printStackTrace(PrintStream s) | 通常用该方法将异常内容保存在日志文件中,以便查阅 |
class Demo {
int div(int a, int b) throws Exception { // 在功能上通过throws的关键字声明了该功能有可能会出现问题
return a/b; // new ArithmeticException();
}
}
class ExceptionDemo {
public static void main(String[] args) {
Demo d = new Demo();
try {
int x = d.div(4, 1); // new ArithmeticException();
System.out.println("x="+x);
} catch(Exception e) { // Exception e = new ArithmeticException();
System.out.println("除零啦!");
System.out.println(e.getMessage()); // / by zero
System.out.println(e.toString());// 异常名称:异常信息
e.printStackTrace(); // 异常名称,异常信息,异常出现的位置
// 其实JVM默认的异常处理机制,就是在调用printStackTrace(),打印异常在堆栈中的跟踪信息
}
System.out.println("over");
}
}
在函数上声明异常(throws)。便于提高安全性,让调用者进行处理,不处理编译失败。
自定义异常
因为项目中会出现特有的问题,而这些问题并未被Java所描述并封装成对象。所以对于这些特有的问题可以按照Java的对问题封装的思想,将特有的问题,进行自定义的异常封装。那么自定义异常如何定义异常信息呢?因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息传递给父类,通过super语句,那么就可以直接通过getMessage()获取自定义的异常信息。
下面通过一个案例的讲解,你就能知道该如何自定义异常了。
/*
需求:在本程序中,对于除数是-1,也视为是错误的,是无法进行运算的,那么就需要对这个问题进行自定义的描述
*/
class FuShuException extends Exception {
private int value;
FuShuException() {
super();
}
FuShuException(String msg, int value) {
super(msg);
this.value = value;
}
public int getValue() {
return value;
}
}
class Demo {
int div(int a, int b) throws FuShuException {
if(b < 0)
throw new FuShuException("出现除数是负数的情况----- / by fushu", b); // 手动通过throw关键字抛出一个自定义异常对象
return a/b;
}
}
class ExceptionDemo {
public static void main(String[] args) {
Demo d = new Demo();
try {
int x = d.div(4, -9);
System.out.println("x="+x);
} catch(FuShuException e) {
System.out.println(e.toString());
// System.out.println("除数出现负数了");
System.out.println("错误的负数是:"+e.getValue());
}
System.out.println("over");
}
}
由以上程序代码可知,我们需要注意两点:
- 自定义异常必须是自定义类继承Exception。继承Exception的原因:异常体系有一个特点,因为异常类和异常对象都可被抛出,它们都具备可抛性,这个可抛性是Throwable这个体系中独有的特点,只有这个体系中的类和对象才可以被throws和throw操作。
- 当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。要么在内部try catch处理,要么在函数上声明让调用者处理。一般情况下,函数内出现异常,函数上需要声明。
throw和throws的区别
位置不同
- throws使用在函数上,throws后面跟的是异常类,可以跟多个,用逗号隔开;
- throw使用在函数内,throw后跟的是异常对象。
功能不同
- throws用来声明异常,让调用者知道该功能有可能出现的问题,并由调用者可以给出预先的处理方式;
- throw抛出具体的问题对象,执行到throw,功能就已经结束了,跳转到调用者,并将具体的问题对象也抛给调用者。
异常的分类
对于异常可分为两种:
- 编译时被检测的异常;
- 编译时不被检测的异常(运行时异常,RuntimeException以及其子类)。
编译时被检测异常
只要是Exception和其子类都是,除了特殊子类RuntimeException体系。这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式,这样的问题都可以针对性的处理。
编译时不被检测异常(运行时异常)
Exception中有一个特殊的子类异常RuntimeException(运行时异常)。
- 如果在函数内容里抛出该异常,函数上可以不用声明,编译一样通过。之所以不用在函数上声明,是因为不需要让调用者处理,当该异常发生,希望程序停止。因为在运行时,出现了无法继续运算的情况,希望停止程序后,程序员对代码进行修正。
- 如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。
所以,在自定义异常时,如果该异常的发生,无法再继续进行运算,那么就可让自定义异常继承RuntimeException。例如,
class FuShuException extends RuntimeException {
FuShuException(String msg) {
super(msg);
}
}
class Demo {
int div(int a, int b) {
if(b < 0)
throw new FuShuException("出现了除数为负数啦!");
if(b == 0)
throw new ArithmeticException("被零除啦!");
return a/b;
}
}
class ExceptionDemo {
public static void main(String[] args) {
Demo d = new Demo();
int x = d.div(4, -9);
System.out.println("x="+x);
System.out.println("over");
}
}
异常处理的原则
- 函数内容中如果抛出需要检测的异常,那么函数上必须要声明。否则,必须在函数内用try/catch捕捉,否则编译失败;
- 如果调用到了声明异常的函数,要么try/catch,要么throws,否则编译失败;
- 什么时候try/catch,什么时候throws呢?
功能内部可以解决,用try/catch。解决不了或者解决了还必须告诉调用者问题,用throws告诉调用者,由调用者解决。 - 一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性处理。内部有几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。
对多异常的处理
声明异常时,建议声明更为具体的异常,这样处理的可以更具体。对方声明几个异常,就对应有几个catch块,不要定义多余的catch块。如果多个catch块中的异常出现继承关系,父类异常catch块放在最后。例如:
class Demo {
int div(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException { // 在功能上通过throws的关键字声明了该功能有可能会出现问题
int[] arr = new int[a];
System.out.println(arr[4]);
return a/b; // new ArithmeticException();
}
}
class ExceptionDemo {
public static void main(String[] args) {
Demo d = new Demo();
try {
int x = d.div(5, 0); // new ArithmeticException();
System.out.println("x="+x);
} catch(ArithmeticException e) {
System.out.println(e.toString());
System.out.println("除零啦!");
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println(e.toString());
System.out.println("角标越界啦!");
} catch(Exception e) {
System.out.println("haha:"+e.toString());
}
System.out.println("over");
}
}
建议在进行catch处理时,catch中一定要定义具体的处理方式。不要简单定义一句:e.printStackTrace();
,也不要简单的就书写一条输出语句。
finally代码块
定义一定执行的代码。通常用于关闭资源,即无论是否有异常发生,都要对资源进行释放。释放资源的动作就定义在finally代码块中。例如:
class ExceptionDemo
{
public static void main(String[] args)
{
try
{
int num = 4 / 0;
System.out.println("num = " + num);
}
catch (Exception e)
{
System.out.println(e.toString());
return;
// System.exit(0); // 退出JVM,只有这种情况,finally才不执行。
}
finally
{
System.out.println("finally");//如果前面执行了System.exit(0);,故不会执行此语句。
}
System.out.println("over");
}
}
再看以下例子,我们可以在finally代码块中关闭数据库资源,以后必然避免不了写这样的代码。
class NoValueException extends Exception {
}
public void operate() throws NoValueException
{
// 连接数据库
try
{
// 数据操作 throw new SQLException();
}
catch (SQLException e)
{
// 解决了数据库异常
throw new NoValueException();
}
finally {
// 关闭数据库
}
}
记住一点:catch是用于处理异常,如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明。
处理异常代码块的组合方式
处理异常代码块的组合方式有多种,视具体需求而定。
没有资源需要释放,仅仅是处理异常;
try { 需要被检测的代码; } catch() { 处理异常的代码; }
一个try多个catch,一般对应的是被调用的函数,抛出多个异常的情况,分别处理;
try { } catch () { } catch () { } catch () { }
注意:在多catch语法上特殊的地方,如果catch中的异常类存在子父类,父类的catch一定要放在子类的下面,否则编译失败。
不一定要处理异常,但是有资源需要释放。
try { 需要被检测的代码; } finally { 一定会指执行的代码; }
异常在子父类覆盖中的体现
- 子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类或者不抛;
- 如果父类方法抛出多个异常,那么子类在覆盖这个方法时,只能抛出父类异常的子集(不抛也行);
- 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常。如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。
异常综合案例
毕老师用电脑上课,在上课过程中有可能会发生问题,比如电脑蓝屏了、电脑冒烟了。请对问题进行描述,并封装成对象。
// 蓝屏是可以处理的,继承Exception
class LanPingException extends Exception
{
LanPingException()
{
super();
}
LanPingException(String message)
{
super(message);
}
}
// 冒烟
class MaoYanException extends Exception
{
MaoYanException()
{
super();
}
MaoYanException(String message)
{
super(message);
}
}
// 课时无法进行
class NoPlanException extends Exception
{
NoPlanException()
{
super();
}
NoPlanException(String message)
{
super(message);
}
}
class Computer
{
private int state = 2; // 0:正常状态
public void run() throws LanPingException, MaoYanException
{
if (state == 1)
throw new LanPingException("电脑蓝屏啦!");
if (state == 2)
throw new MaoYanException("电脑冒烟啦!");
System.out.println("电脑运行");
}
public void reset()
{
System.out.println("电脑重启");
state = 0;
}
}
class Teacher
{
private String name;
private Computer comp;
Teacher(String name)
{
this.name = name;
comp = new Computer();
}
// 讲课
public void prelect() throws NoPlanException
{
try
{
comp.run();
System.out.println("讲课");
}
catch (LanPingException e)
{
System.out.println(e.toString());
comp.reset();
// 继续讲课
prelect();
}
catch (MaoYanException e) // MaoYanException e = new MaoYanException("...");
{
System.out.println(e.toString());
test();
throw new NoPlanException("课时进度停止"); // 继续抛,但进行异常转换。封装本层异常,对外暴露对方能处理的异常。
}
}
public void test()
{
System.out.println("练习");
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Teacher t = new Teacher("毕老师");
try
{
t.prelect();
}
catch (NoPlanException e)
{
System.out.println("换老师");
}
}
}
最后
以上就是无语小丸子为你收集整理的第十四讲 面向对象基础——异常异常的概述异常的体系异常的处理异常的常见方法自定义异常throw和throws的区别异常的分类异常处理的原则对多异常的处理finally代码块处理异常代码块的组合方式异常在子父类覆盖中的体现异常综合案例的全部内容,希望文章能够帮你解决第十四讲 面向对象基础——异常异常的概述异常的体系异常的处理异常的常见方法自定义异常throw和throws的区别异常的分类异常处理的原则对多异常的处理finally代码块处理异常代码块的组合方式异常在子父类覆盖中的体现异常综合案例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复