一、异常
-
就是程序在运行时出现不正常的情况
-
异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述,并封装成对象
-
所以,异常其实就是java对不正常情况进行描述后的对象体现
二、好处
- 将问题进行封装
- 将正常流程代码和问题处理代码相分离,方便于阅读
三、问题有两种
1,严重的问题:java通过Error类进行描述
对于Error一般不编写针对性的代码对其进行处理
2,非严重的问题:java通过Exception类进行描述
对于Exception可以使用针对性的处理方式进行处理
3,无论Error或者Exception都具有一些共性内容。比如:不正常情况的信息,引发原因等
四、异常的体系:
Throwable
|--Error :通常出现重大问题如:运行的类不存在或者内存溢出等
|--Exception :在运行时出现的一些情况,可以通过try catch finally等处理
五、异常体系的特点
-
这个体系中的所有类和对象都具备一个独有的特点;就是可抛性
-
即可以被:throw throws 操作
六、Throwable类
-
Throwable 类是 Java 语言中所有错误或异常的超类。
-
只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。
-
类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。
public class Throwable implements Serializable
{
//成员变量
private static final long serialVersionUID = -3042686055658047285L;
private String detailMessage;
private Throwable cause = this;
//1、构造方法:构造一个将 null 作为其详细消息的新 throwable。
public Throwable() {
fillInStackTrace();
}
//2、构造方法:构造带指定详细消息的新 throwable。
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
//3、构造方法:构造一个带指定详细消息和 cause 的新 throwable。
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
//4、构造方法:构造一个带指定 cause 和 (cause==null ? null :cause.toString())(它通常包含类和 cause 的详细消息)的详细消息的新 throwable。
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
//-----------------------------------------------
//1、在异常堆栈跟踪中填充。此方法在 Throwable 对象信息中记录有关当前线程堆栈帧的当前状态
public synchronized native Throwable fillInStackTrace();
//2、返回此 throwable 的详细消息字符串。
public String getMessage() {
return detailMessage;
}
//3、返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null
public Throwable getCause() {
return (cause==this ? null : cause);
}
//4、创建此 throwable 的本地化描述。子类可以重写此方法,以便生成特定于语言环境的消息。对于不重写此方法的子类,默认实现返回与 getMessage() 相同的结果。
public String getLocalizedMessage() {
return getMessage();
}
//5、将此 throwable 及其追踪输出至标准错误流。
public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
return (message != null) ? (s + ": " + message) : s;
}
//6、将此 throwable 及其追踪输出至标准错误流
//此方法将此 Throwable 对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。
public void printStackTrace() {
printStackTrace(System.err);
}
//7、将此 throwable 及其追踪输出到指定的输出流。
public void printStackTrace(PrintStream s) {
synchronized (s) {
s.println(this);//toString
StackTraceElement[] trace = getOurStackTrace();
for (int i=0; i < trace.length; i++)
s.println("\tat " + trace[i]);
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printStackTraceAsCause(s, trace);
}
}
//8、将此 throwable 及其追踪输出到指定的 PrintWriter。
public void printStackTrace(PrintWriter s) {
synchronized (s) {
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
for (int i=0; i < trace.length; i++)
s.println("\tat " + trace[i]);
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printStackTraceAsCause(s, trace);
}
}
}
七、Exception 类
Exception 及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。
public class Exception extends Throwable
{
//字段
static final long serialVersionUID = -3387516993124229948L;
//构造方法
public Exception() {
super();
}
public Exception(String message) {
super(message);
}
public Exception(String message, Throwable cause) {
super(message, cause);
}
public Exception(Throwable cause) {
super(cause);
}
/*方法
从类 java.lang.Throwable 继承的方法
fillInStackTrace, getCause, getLocalizedMessage, getMessage,
getStackTrace, initCause, printStackTrace, printStackTrace,
printStackTrace, setStackTrace, toString
从类 java.lang.Object 继承的方法
clone, equals, finalize, getClass,
hashCode, notify, notifyAll, wait, wait, wait
*/
}
八、异常的命名
通常前面是功能后面是父类名
九、异常的处理
1,可抛可try(捕获)
2,java提供了特有的语句捕捉异常
try
{
需要被检测的代码
}
catch(异常类 变量)
{
处理异常的代码:(处理方式)
}
finally
{
一定会执行的语句
}
常见的三种格式:
try{} catch(){}
try{} catch(){} finally{}
try{} finally{}
记住一点:catch是用于处理异常.如果没有catch就代表异常没有被处理过,如果该异常是检测时异常,那么必须声明
3,在开发时,如果定义功能时,发现该功能会出现一些问题,应该将问题在定义功能时标示出来,这样调用者就可以在使用这个功能的时候,预先给出处理方式。
-
如何标示呢?通过throws关键字完成, 格式:throws 异常类名,异常类名...
-
这样标示后,调用者在使用该功能时,就必须要处理,否则编译失败
4,异常处理原则
-
功能抛出几个异常,功能调用如果进行try处理,需要与之对应的catch处理代码块,这样的处理有针对性,抛几个就处理几个。
-
特殊情况:try对应多个catch时,如果有父类的catch语句块,一定要放在下面。
-
建议在进行catch处理时,catch中一定要定义具体处理方式,不要简单定义一句e.printStackTrace()
-
也不要简单的就写一条输出语句
-
因为项目中会出现特有的问题,而这些问题并未被java所描述并封装对象。
-
所以对于这些特有的问题可以按照java的对问题封装的思想,将特有的问题,进行自定义的异常封装 。
十、自定义异常
/*
需求:在本程序中,对于除数是-1,也视为是错误的是无法进行运算的
思路:需要对这个问题进行自定义描述
*/
//自定义异常
class FuShuException extends Exception
{
FuShuException(String s)
{
super(s);
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0)
throw new FuShuException("除数出现了负数");
return a/b;
}
}
class ExceptionTest
{
public static void main(String[] args)
{
Demo d = new Demo();
try{
int x = d.div(4,-1);
}catch(FuShuException e){
System.out.println(e.toString());
}
System.out.println("over");
}
}
十一、throw和throws的区别
-
throw:用在函数内,后面跟的是异常对象
-
throws:用在函数上,后面跟的是异常类,可以跟多个,用逗号隔开
-
通常情况:函数内容如果有throw,抛出异常对象,并没有进行处理,那么函数上一定要声明,否则编译失败。
-
特殊情况:
-
Exception 中有一个特殊的子类异常 RuntimeException运行时异常,
-
如果在函数内抛出该异常,函数上可以不用声明,编译一样通过
-
如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过
-
之所以不用在函数声明,是因为不需要让调用者处理, 当该异常发生,希望程序停止,因为在运行时,出现了无法继续运算的情况,希望停止程序后, 对代码进行修正。
十二、finally代码块
-
存放的是一定要执行的代码,通常用于关闭资源
-
finally只有一种情况不会执行,当执行到System.exit(0);finally不会执行
十三、异常分两种
-
编译时被检测的异常,只要是Exception及其子类都是编译时被检测的异常。
-
编译时不被检测的异常(运行时异常,RuntimeException以及其子类)
-
示例:老师用电脑讲课
/*
需求:模拟老师用电脑讲课
思路:
1,定义老师和电脑类Teacher Computer
2,电脑可能会出现异常:蓝屏,冒烟
3,所以自定义蓝屏和冒烟异常
4,当电脑出现蓝屏异常,重启就行了
5,当电脑出现冒烟异常,讲课将被迫终止,出现新的异常:无法完成课时计划
6,对无法完成课时计划异常进行处理:放假
*/
//电脑蓝屏异常
class LanPingException extends Exception
{
LanPingException(String msg)
{
super(msg);
}
}
//电脑冒烟异常
class MaoYanException extends Exception
{
MaoYanException(String msg)
{
super(msg);
}
}
//无法完成课时计划异常
class NoPlanException extends Exception
{
NoPlanException(String msg)
{
super(msg);
}
}
//老师类
class Teacher
{
private String name;
private Computer computer;
Teacher(String name,Computer computer)
{
this.name = name;
this.computer = computer;
}
public void prelect()throws NoPlanException
{
try{
computer.run();
}catch(LanPingException e){
System.out.println(e.getMessage());
computer.reset();
}catch(MaoYanException e){
throw new NoPlanException(e.getMessage()+"无法继续课程");
}
System.out.println("开始讲课");
}
}
//电脑类
class Computer
{
private int state = 2;
public void run() throws LanPingException,MaoYanException
{
if(state == 2)
throw new LanPingException("电脑蓝屏了");
if(state == 3)
throw new MaoYanException("电脑冒烟了");
System.out.println("电脑正常运行");
}
public void reset()
{
state = 1;
System.out.println("重启电脑");
try{run();}catch(Exception e){}
}
}
//测试类
class ExceptionTest
{
public static void main(String[] args)
{
Teacher t = new Teacher("毕老师",new Computer());
try{
t.prelect();
}catch(NoPlanException e){
System.out.println(e.getMessage());
System.out.println("放假");
}
}
}
十四、异常的转换思想
当出现的异常是调用者处理不了的,就需要将此异常转换为一个调用者可以处理的异常抛出。如上述例子中讲冒烟异常转换成无法完成课时异常
十五、异常在子父类函数覆盖中的体现
-
- 子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者该异常的子类,(也可以不抛,自己处理了)
- 如果父类方法抛出多个异常,那么子类在覆盖该方法时,只能抛出父类异常的子集
- 如果父类或者接口的方法中没有异常抛出,那么子类在覆盖方法时,也不可以抛出异常
- 如果子类方法发生了异常,就必须要进行try处理,绝对不能抛
十六、示例
/*
需求:求图形的面积
分析:
1,求面积不能出现负数
2,因为出现数据异常就灭有必要再继续下面的操作,
所以直接抛出RuntimeException终止程序,让调用者判断数据
*/
//抽象类定义图形的基本功能
abstract class Shape
{
abstract double getArea();
}
//长方形
class Chang extends Shape
{
private double len,wid;
Chang(double len,double wid)
{
if(len <=0 || wid <= 0)
throw new ErrorValueException("数据错误");
this.len = len;
this.wid = wid;
}
public double getArea()
{
return len * wid;
}
}
//自定义异常,处理负数异常
class ErrorValueException extends RuntimeException
{
ErrorValueException(String msg)
{
super(msg);
}
}
//测试
class ExceptionTest
{
public static void main(String[] args)
{
Chang chang = new Chang(5,-9);
double area = chang.getArea();
System.out.println("面积是:"+area);
}
}
。使用异常处理机制,将正常流程代码和问题代码分离
。在功能内不用if...else..等来判断处理,直接抛出异常
。让调用者自己判断处理数据
。可以提高代码的阅读性
。以后处理'问题'就要这样,用异常来封装