Java学习12:异常

时间:2022-03-11 20:19:32

概念

程序在运行时出现不正常的情况。

异常由来

问题也是现实生活中的一个具体的事物,也可以通过java类的形式进行描述,并且封装成对象,其实就是java对不正常情况进行描述后的对象体现。
对于问题的划分有两种:严重的,java通过Error类进行描述,一般不编写针对性的代码对其进行处理;不严重的,通过Exception类进行描述,可以使用针对性的处理方式进行处理。
Throwable
|–Error
|–Exception

异常处理

try
{
需要被检测的代码;
}
catch(异常类 变量)
{
处理异常的代码;(处理方式)
}
finally
{
一定会执行的语句;
}

/*
div 0!
/ by zero
java.lang.ArithmeticException: / by zero
java.lang.ArithmeticException: / by zero
at Function.div(Demo.java:178)
at Demo.main(Demo.java:187)
over
*/

class Function
{
double div(int a, int b)
{
return a / b;//(1) new AritchmeticException()
}
}
class Demo
{
public static void main(String[] args)
{
Function f = new Function();
try {
double x = f.div(1, 0);//(2) (1)传过来的new AritchmeticException()
System.out.println("x = " + x);
}
catch(Exception e)//(3) Exception e = new AritchmeticException();
{
System.out.println("div 0!");
System.out.println(e.getMessage());
System.out.println(e.toString());// 异常名称:异常信息
e.printStackTrace();// 异常名称,异常信息,异常出现的位置 其实JVM默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。
}
System.out.println("over");
}
}

对捕获到的异常对象进行常见方法操作

String getMessage() 返回此throwable的详细消息字符串
String toString() 返回此throwable的简短表现形式
void printStackTrace() 将此throwable及其追踪输出至标准错误流

throws

在函数上通过throws的关键字声明了该功能有可能会出现问题。便于提高安全性,让调用者进行处理(捕获或抛出),不处理编译失败。

class Function
{
double div(int a, int b) throws Exception
{
return a / b;
}
}
class Demo
{
public static void main(String[] args) //throws Exception
{
Function f = new Function();
try {//不捕获编译会报错(或者在主函数后面抛出异常) Error:(187, 29) java: 未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出
double x = f.div(1, 0);
System.out.println("x = " + x);
}
catch(Exception e)
{
System.out.println(e.toString());

}
System.out.println("over");
}
}

对多异常的处理

  1. 声明异常时,建议声明为更具体的异常,这样处理的可以更具体。
  2. 对方声明几个异常, 就对应有几个catch块。如果多个catch中的异常出现继承关系,父类异常catch块放在最下面。
  3. 在进行catch处理时,catch中一定要定义具体处理方式,不要简单定义一句e.printStackTrace(),也不要简单的书写一条输出语句。
class Function
{
double div(int a, int b) throws ArithmeticException, ArrayIndexOutOfBoundsException
{
int[] x = new int[a];
System.out.println(x[4]);
return a / b;
}
}
class Demo
{
public static void main(String[] args) //throws Exception 抛给虚拟机
{
Function f = new Function();
try {
double x = f.div(5, 0);
System.out.println("x = " + x);
}
catch(ArithmeticException e)//java.lang.ArithmeticException: / by zero
{
System.out.println(e.toString());
System.out.println("div 0!");
}
catch(ArrayIndexOutOfBoundsException e)//java.lang.ArrayIndexOutOfBoundsException: 4
{
System.out.println(e.toString());
System.out.println("数组越界!");
}
System.out.println("over");
}
}

自定义异常

因为项目中会出现特有的问题,这些问题并没有被java所描述并封装对象,所以这些特有的问题可以按照java的对问题封装的思想,将特有的问题进行自定义的异常封装。
需求:本程序中,对于除数为负数,也视为是错误的,是无法进行运算的,那么仅需要对此问题进行自定义的描述。

/*
FuShuException
div FuShu!
over
*/

class FuShuException extends Exception//自定义异常
{}
class Function
{
double div(int a, int b) throws ArithmeticException, FuShuException
{
if(b < 0)
throw new FuShuException();
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
try
{
double result = new Function().div(1, -1);
System.out.println("result = " + result);
}
catch(ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("div 0!");
}
catch(FuShuException e)
{
System.out.println(e.toString());
System.out.println("div FuShu!");
}
System.out.println("over");
}
}

上述程序中的打印结果中只有异常的名称,却没有异常的信息,因为自定义的异常中未定义信息。如何定义异常信息?
因为父类中已经把异常信息的操作都完成了,所以子类只要在构造时,将异常信息通过super语句传递给父类,那么就可以直接通过getMessage方法获取自定义的异常信息(toString调用getMessage方法)。下面程序中还包含了传递特有信息。

/*
FuShuException: / by fushu
div FuShu!
illegal negative value is : -1
over
*/

class FuShuException extends Exception//自定义异常
{
private int value;
FuShuException(String msg, int value)
{
super(msg);
this.value = value;
}
int getValue()
{
return value;
}
// private String msg;
// FuShuException(String msg)
// {
// this.msg = msg;
// }
// public String getMessage()
// {
// return msg;
// }
}
class Function
{
double div(int a, int b) throws ArithmeticException, FuShuException
{
if(b < 0)
throw new FuShuException("/ by fushu", b);
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
try
{
double result = new Function().div(1, -1);
System.out.println("result = " + result);
}
catch(ArithmeticException e)
{
System.out.println(e.toString());
System.out.println("div 0!");
}
catch(FuShuException e)
{
System.out.println(e.toString());
System.out.println("div FuShu!");
System.out.println("illegal negative value is : " + e.getValue());
}
System.out.println("over");
}
}

注意:自定义类必须是继承Exception,继承Exception的原因:
异常体系有一个特点,因为异常类和异常对象都被抛出,他们都具有可抛性,这个可抛性Throwable这个体系中独有特点,只有这个体系中的类和对象才可以被throws和throw操作。

throws和throw的区别

throws使用在函数上,throw使用在函数内。
throws后面跟的异常类,可以跟多个,用逗号隔开。throw后面跟的是异常对象。

RuntimeException

Exception中有一个特殊的子类异常RuntimeException(运行时异常)。
如果在函数内中抛出该异常,函数上可以不用声明,编译一样通过(其他异常编译不通过);
class Function
{
double div(int a, int b)
{
if(b == 0)
//throw new Exception("div zero!"); 就会报错"未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出"
throw new ArithmeticException("div zero!");
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
Function f = new Function();
f.div(1, 0);
System.out.println("over!");
}
}

: 如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过。

class Function
{
double div(int a, int b) throws ArithmeticException//如果是throws Exception,则会报错“未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出”
{
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
Function f = new Function();
f.div(1, 0);
System.out.println("over!");
}
}

之所以不用在函数声明,是因为不需要让调用者处理,当该异常发生,希望程序停止(我的理解就是这个异常太严重,已经无法处理),因为在运行时出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

所以,自定义异常时,如果该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException。

//Exception in thread "main" FuShuException: div Fushu!
class FuShuException extends RuntimeException
{
FuShuException(String msg)
{
super(msg);
}
}
class Function
{
double div(int a, int b)//throws FuShuException
{
if(b < 0)
//FuShuException继承了RuntimeException,所以不用声明异常,主函数不需要处理
throw new FuShuException("div Fushu!");
return a / b;
}
}
class Demo
{
public static void main(String[] args)
{
new Function().div(1, -2);
}
}

异常分为两种

  1. 编译时被检测的异常;
  2. 编译时不被检测的异常(运行时异常,RuntimeException及其子类)

练习

class BlueScreenException extends Exception
{
BlueScreenException(String msg)
{
super(msg);
}
}
class SmokeException extends Exception
{
SmokeException(String msg)
{
super(msg);
}
}
class NoPlanException extends Exception
{
NoPlanException(String msg)
{
super(msg);
}
}
class Computer
{
private int status = 2;
public void run() throws BlueScreenException, SmokeException
{
if(status == 2)
throw new BlueScreenException("电脑蓝屏!");
else if(status == 3)
throw new SmokeException("电脑冒烟!");
System.out.println("电脑运行!");
}
public void reset()
{
System.out.println("电脑重启!");
}
}
class Teacher
{
public void test()
{
System.out.println("练习!");
}
public void prelect() throws NoPlanException
{
Computer comp = new Computer();
try {
comp.run();
}
catch(BlueScreenException e)
{
System.out.println(e.toString());
comp.reset();
}
catch(SmokeException e)
{
test();
throw new NoPlanException("课程无法继续," + e.getMessage());
}
System.out.println("讲课!");
}
}
class Demo
{
public static void main(String[] args)
{
Teacher t = new Teacher();
try {
t.prelect();
}
catch (NoPlanException e)
{
System.out.println(e.toString());
System.out.println("更换电脑");
}
}
}

finally代码块

定义一定执行的代码,通常用于关闭资源。(在数据库操作中将关闭数据库放在这里面)

三种格式

    //1
try
{
}
catch()
{

//2
try
{
}
catch()
{
}
finally
{
}

//3
try
{
}
finally
{
}

注意: catch是用于处理异常,如果没有catch就代表没有被处理过,如果该异常是检测时异常,那么必须声明,见下面程序。

class Demo
{
public void method()
{
//1.编译不能通过 解决方法:(1)声明异常throws Exception;(2)捕获异常
throw new Exception();

//2.编译能通过,捕获异常
try
{
throw new Exception();
}
catch (Exception e)
{

}

//3.编译不能通过,异常未被声明或捕获
try
{
throw new Exception();
}
catch (Exception e)
{
throw e;
}

//4.编译能通过,捕获异常
try
{
throw new Exception();
}
catch (Exception e)
{
try
{
throw e;
}
catch(Exception e2)
{

}
}

//5.编译不能通过,异常未被声明或捕获
try
{
throw new Exception();
}
finally
{
//关闭资源
}
}
}

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

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

异常例子

class InvalidValueException extends RuntimeException
{
InvalidValueException(String msg)
{
super(msg);
}
}
interface Shape//接口类
{
public abstract double getArea();
}
class Rec implements Shape//长方形
{
private double len, width;
Rec(double len, double width)
{
this.len = len;
this.width = width;
}
public double getArea()
{
if(len <= 0 || width <= 0)
throw new InvalidValueException("输入非法值!");
return len * width;
}
}
class Circle implements Shape
{
double r;
private static final double PI = 3.14;//因为PI是共享数据,所以加static
Circle(double r)
{
this.r = r;
}
public double getArea()
{
if(r <= 0)
throw new InvalidValueException("出现非法值");
return r * r * PI;
}
}
class Demo
{
public static void main(String[] args)
{
Rec rec = new Rec(10, 2);
System.out.println(rec.getArea());
Circle circle = new Circle(-11);
System.out.println(circle.getArea());
}
}

总结

异常是对问题的描述,将问题进行对象的封装。

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

throw和throws的用法:
throw定义在函数内,用于抛出异常对象;
throws定义在函数上,用于抛出异常类,可以抛出多个,用逗号隔开。

当函数内容有throw抛出异常对象,并未进行try处理,必须要在函数上声明,否则编译失败。
注意:RuntimeException除外,也就是说,函数内如果抛出RuntimeException异常,函数上可以不用声明。如果函数声明了异常,调用者需要进行处理,处理方式可以是throws/try。

异常有两种:
编译时被检测异常,该异常如果没有处理,没有throws也没有try,编译失败,该异常被标识,代表可以被处理;
运行时异常(编译时不检测),在编译时不需要处理,编译器不检查,该异常发生时,建议不处理,让程序停止,需要对代码进行修正。

异常处理语句
try
{
需要被检测的代码;
}
catch()
{
处理异常的代码;
}
finally
{
一定会执行的代码;
}
注意:
(1)finally中定义的通常是关闭资源代码,因为资源必须释放;
(2)finally只有一种情况不会执行,当catch中写到System.exit(0)时系统退出,jvm结束,此时finally里面的语句不执行。

自定义类异常

按照java的面向对象思想, 将程序中出现的特有问题进行封装,定义类继承Exception或者RuntimeException。
(1)为了让自定义类具备可抛性;
(2)让该类具备操作异常的共性方法。

当要定义自定义异常的信息时,可以使用父类已经定义好的功能,异常信息传递给父类的构造函数。

class MyException extends Exception
{
MyException(String msg)
{
super(msg);
}
}

异常的好处
(1)将问题进行封装;
(2)将正常流程代码和问题处理代码相分离,方便于阅读。

异常的处理原则
(1)处理方式:try或者throws;
(2)调用到抛出异常的功能时,抛出几个,就处理几个,一个try对应多个catch;
(3)多个catch,父类的catch放到最下面;
(4)catch内,需要定义针对性的处理方式,不要简单地定义printStackTrace输出语句,也不要不写;
(5)当捕获到的异常,本功能处理不了时,可以继续在catch中抛出;

try
{
cicle.getArea();
}
catch(InvalidValueException e)
{
throw e;
}

(6)如果该异常处理不了,但并不属于该功能出现的异常, 可以将异常转换后,再抛出和功能相关的异常;或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,让调用者知道并处理,也可以将捕获异常处理后转换为新的异常;

//1
try
{
cicle.getArea();
}
catch(InvalidValueException e)
{
throw new BException();
}
//2
try
{
cicle.getArea();
}
catch(InvalidValueException e)
{
//对e进行处理,之后抛出新的异常
throw new BException();
}

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