ThinkInJava_12_异常

12.1 概念

12.2 基本异常

throw new NullPointerException("t = null");
  • 可以抛出任意类型的 Throwable 对象,它是异常类型的根类

12.3 捕获异常

try {
	
} catch(Type1 id1) {
	
} catch(Type2 id2) {
	
}

终止与恢复

  • 终止模型,认为错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行
  • 恢复模型,认为异常处理的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功,希望异常被处理完后能够继续执行程序

12.4 创建自定义异常

class SimpleException extends Exception {} //自定义异常
public class InheritingExceptions {
    public void f() throws SimpleException {
        System.out.println("Throw SimpleException from f()");
        throw new SimpleException(); //抛出异常
    }
    public static void main(String[] args) {
        InheritingExceptions sed = new InheritingExceptions();
        try {
            sed.f();
        } catch (SimpleException e) { //捕获异常
            System.out.println("Caught it!");
        }
    }
}
/*
Throw SimpleException from f()
Caught it!
*/
e.printStackTrace(System.out); //打印从方法调用处到异常抛出处的方法调用序列
import java.util.logging.*;
import java.io.*;
//通过异常输出日志
class LoggingException extends Exception {
    //与String相关的Logger对象,将其输出发送到System.err
    private static Logger logger =
            Logger.getLogger("LoggingException");
    public LoggingException() {
        StringWriter trace = new StringWriter();
        printStackTrace(new PrintWriter(trace));
        logger.severe(trace.toString());
    }
}
public class LoggingExceptions {
    public static void main(String[] args) {
        try {
            throw new LoggingException();
        } catch (LoggingException e) {
            System.err.println("Caught " + e);
        }
        try {
            throw new LoggingException();
        } catch (LoggingException e) {
            System.err.println("Caught " + e);
        }
    }
}
/*
九月 21, 2020 10:01:13 下午 learn.LoggingException <init>
严重: learn.LoggingException
	at learn.LoggingExceptions.main(LoggingExceptions.java:16)

Caught learn.LoggingException
九月 21, 2020 10:01:13 下午 learn.LoggingException <init>
严重: learn.LoggingException
	at learn.LoggingExceptions.main(LoggingExceptions.java:21)

Caught learn.LoggingException
 */
  • 必须在异常处理程序中生成日志信息

12.5 异常说明

  • 要告知客户端程序员某个方法可能抛出异常的类型,它属于方法声明的一部分
void f() throws TooBig,TooSmall,DivZero {}
  • 说明中抛出的异常必须在方法中写出异常的处理,即try {} catch {}结构

12.6 捕获所有异常

catch(Exception e) {}
//可以调用它从基类 Throwable 中继承的方法
String getMessage();
String getLocalizedMessage()
void printStackTrace()
void printStackTrace(PrintStream)
void printStackTrace(java.io.PrintWriter)
public class ExceptionMethods {
    public static void main(String[] args) {
        try {
            throw new Exception("My Exception");
        } catch(Exception e) {
            System.out.println("Caught Exception");
            System.out.println("getMessage():" + e.getMessage());
            System.out.println("getLocalizedMessage():" + e.getLocalizedMessage());
            System.out.println("toString():" + e);
            System.out.println("printStackTrace():");
            e.printStackTrace(System.out);
        }
    }
}
/*
Caught Exception
getMessage():My Exception
getLocalizedMessage():My Exception
toString():java.lang.Exception: My Exception
printStackTrace():
java.lang.Exception: My Exception
	at learn.ExceptionMethods.main(ExceptionMethods.java:6)
*/
public class WhoCalled {
    static void f() {
        try {
            throw new Exception();
        } catch (Exception e) {
            //e.getStackTrace() 获取由栈轨迹元素构成的数组
            for(StackTraceElement ste : e.getStackTrace())
                System.out.println(ste.getMethodName());
        }
    }
    static void g() { f(); }
    static void h() { g(); }
    public static void main(String[] args) {
        f();
        System.out.println("--------------------------------");
        g();
        System.out.println("--------------------------------");
        h();
        System.out.println("--------------------------------");
    }
}
/*
f
main
--------------------------------
f
g
main
--------------------------------
f
g
h
main
--------------------------------
 */
catch(Exception e) {
	throw e;
}
//重新抛出异常,把异常信息抛给上一级环境中的异常处理程序
package learn;

public class Rethrowing {
    public static void f() throws Exception {
        System.out.println("originating the exception in f()");
        throw new Exception("thrown from f()");
    }
    public static void g() throws Exception {
        try {
            f();
        } catch(Exception e) {
            System.out.println("Inside g(),e.printStackTrace");
            e.printStackTrace();
            throw (Exception)e.fillInStackTrace();
        }
    }
    public static void h() throws Exception {
        try {
            f();
        } catch(Exception e) {
            System.out.println("Inside h(),e.printStackTrace");
            e.printStackTrace();
            throw (Exception)e.fillInStackTrace();
        }
    }
    public static void main(String[] args) {
        try {
            g();
        } catch(Exception e)  {
            System.out.println("main: printStackTrace()");
            e.printStackTrace();
        }
        try {
            h();
        } catch(Exception e)  {
            System.out.println("main: printStackTrace()");
            e.printStackTrace();
        }
    }
}
/*
originating the exception in f()
Inside g(),e.printStackTrace
main: printStackTrace()
originating the exception in f()
Inside h(),e.printStackTrace
main: printStackTrace()
java.lang.Exception: thrown from f()
	at learn.Rethrowing.f(Rethrowing.java:6)
	at learn.Rethrowing.g(Rethrowing.java:10)
	at learn.Rethrowing.main(Rethrowing.java:28)
java.lang.Exception: thrown from f()
	at learn.Rethrowing.g(Rethrowing.java:14)
	at learn.Rethrowing.main(Rethrowing.java:28)
java.lang.Exception: thrown from f()
	at learn.Rethrowing.f(Rethrowing.java:6)
	at learn.Rethrowing.h(Rethrowing.java:19)
	at learn.Rethrowing.main(Rethrowing.java:34)
java.lang.Exception: thrown from f()
	at learn.Rethrowing.h(Rethrowing.java:23)
	at learn.Rethrowing.main(Rethrowing.java:34)
 */
  • 只是抛出当前异常,printStackTrace将显示原来异常抛出点的调用栈信息,可以调用fillStackTrace方法更新异常信息为重新抛出点的信息
  • 调用fillStackTrace的那一行就变成新的异常发生地了
  • 异常链,在捕获一个异常后抛出另一个异常
Error(cause)
Exception(cause)
RuntimeException(cause)
initCause()
public Object setField(String id,Object value) throws MyException {
	if(value == null) {
		MyException me = 
		new MyException();
		me.initCause(new YourException());
		//通过initCause()把YourException异常对象插入
		throw me;
	}
}
catch(NoSuchFieldException e) {
	throw new RuntimeException(e);
	//使用RuntimeException()构造器把NoSuchFieldException异常转换成RuntimeException异常
}

12.7 Java标准异常

  • RuntimeException 运行时异常不必手动抛出
  • RuntimeException 没有被捕获而直接直达 main ,程序退出前会自动调用 printStackTrace()

12.8 使用 finally 进行清理

try {
} catch() {
} catch() {
} finally {
//这里的代码一定会执行,无论异常是否抛出
}
  • 当要把除内存之外的资源恢复到初始状态的时候,就要用finally,如已打开的文件或绘制的图形
  • 即使在 try { return; } 中有return 语句,也能执行 finally 的清理工作
  • 在 finally 中抛出异常,可能会覆盖 try 中抛出的异常,从而无法在 catch 中捕获异常,这是 Java 的语言缺陷

12.9 异常的限制

class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}
abstract class Inning {
    public Inning() throws BaseballException {}
    public void event() throws BaseballException {
        //没有真的抛出异常
    }
    public abstract void atBat() throws Strike,Foul;
    public void walk() {} //不抛出受检查的异常
}
class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}

interface Storm {
    public void event() throws RainedOut;
    public void rainHard() throws RainedOut;
}

public class StormyInning extends Inning implements Storm {
    //构造器可以增加新的异常,但必须处理基类异常 BaseballException
    public StormyInning()
        throws RainedOut,BaseballException {}
    public StormyInning(String s)
        throws Foul,BaseballException {}
    //普通方法必须按照基类抛出异常,不能添加
    //void walk() throws  PopFoul {}
    //接口不能向基类中已存在的方法中添加异常
    //public void event() throws RainedOut {}
    //即使基类中有异常,你也可以不抛出异常
    public void event() {}
    //覆盖的方法可以抛出继承的异常
    public void atBat() throws PopFoul {}
    public static void main(String[] args) {
        try {
            //导出类
            StormyInning si = new StormyInning();
            si.atBat();
        } catch(PopFoul e) {
            System.out.println("Pop Foul");
        } catch(RainedOut e) {
            System.out.println("Rained Out");
        } catch(BaseballException e) {
            System.out.println("Baseball Exception");
        }
        try {
            //基类
            Inning i = new StormyInning();
            i.atBat();
        } catch(Strike e) {
            System.out.println("Strike");
        } catch(Foul e) {
            System.out.println("Foul");
        } catch(RainedOut e) {
            System.out.println("RainedOut");
        } catch(BaseballException e) {
            System.out.println("Baseball Exception");
        }
    }
}
  • 声明抛出的异常实际可以不抛出
  • 接口中有和基类中相同的方法,则在导出类中该方法不能抛出新的异常
  • 如果接口中抛出的异常不是来自基类,则没有限制添加新的异常
  • 异常限制对构造器不起作用,派生类构造器的异常必须包含基类构造器的异常说明,派生类构造器不能捕获基类抛出的异常
  • 派生类普通方法抛出了异常,基类中没有抛出,就会发生编译错误
  • 覆盖后的方法可以不抛出任何异常
  • main 中,如果处理派生类对象,则只要处理这个类声明的异常,如果声明为基类,则要求处理捕获基类的异常

12.10 构造器

import java.io.*;

public class InputFile {
    private BufferedReader in;
    public InputFile(String fname) {
        try {
            in = new BufferedReader(new FileReader(fname));
        } //这里是打开文件错误,不需要关闭文件
            catch (FileNotFoundException e) {
            //throw e;
        } //其他类型的错误则必须关闭文件
            catch (Exception e) {
            try {
                in.close();
            } catch (IOException e) {

            }
            throw e;
        }
    }
    public String getLine() {
        String s;
        try {
            s = in.readLine();
        } catch (IOException e) {
            //将读行错误异常转化为一个RuntimeException,表示一个编程错误
            throw new RuntimeException("readLine() failed");
        }
        return s;
    }
    public void dispose() {
        try {
            in.close();
        } catch(IOException e) {
            throw new RuntimeException("in.close() failed");
        }
    }
}

12.11 异常匹配

  • 抛出异常,会匹配最近的处理程序
  • 派生类异常也可以匹配基类的处理程序
class BException extends Exception {}
class DException extends BException {}
public class Human {
    public static void main(String[] args) {
        try {
            throw new DException();
        } catch(DException e) {
            System.out.println("DException");
        } catch(Exception e) {
            System.out.println("Exception");
        }

        try {
            throw new DException();
        } catch(BException e) {
            System.out.println("BException");
        }
    }
}
/*
DException
BException
 */

12.12 其他可选方式

  • 只有在你知道如何处理的时候才抛出异常
    (1)受检查的异常:这种在编译时被强制检查的异常称为"受检查的异常"。即在方法的声明中声明的异常。
    (2)不受检查的异常:在方法的声明中没有声明,但在方法的运行过程中发生的各种异常被称为"不被检查的异常"。这种异常是错误,会被自动捕获。
    为了保证程序健壮性
  • 必须要有一致的,使用异常来报告错误的模型
  • 必须要有类型检查,不管是在编译期间还是运行期间
public static void main(String[] args) throws Exception {}
//在main中添加Exception异常说明,通过把它传递到控制台,就不必在main中写try-catch语句了

12.13 异常使用指南

1)在恰当的级别处理
2)解决问题并且重新调用产生异常的方法
3)进行少量的修补,然后绕过异常发生的地方继续执行
4)用别的数据进行计算,以替代方法预计会返回的值
5)把当前运行环境下能做的事情做完,然后把相同的异常抛到更高层
6)把当前运行环境下能做的事情做完,然后把相同的异常抛到更高层
7)终止程序
8)进行简化
9)让类库和程序更安全

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值