第十二章 通过异常处理错误 基本使用

转载 2016年09月06日 20:40:25

1.创建自定义异常

  • 定义自定义异常,必须从已有的异常类继承,最好选择意思相近的异常类继承。
  • System.err 将错误发送给标准错误流,而System.out会重定向。
    class MyException extends Exception{
    	public MyException(){}
    	public MyException(String msg){super(msg);}
    }
    public class FullConstructors {
    	public static void f() throws MyException{
    		System.out.println("Throwing MyException from f()");
    		throw new MyException();//抛出这个异常
    	}
    	public static void g() throws MyException{
    		System.out.println("Throwing MyException from g()");
    		throw new MyException();
    	}
    	public static void main(String[] args) {
    		try {
    			f();
    		} catch (MyException e) {
    			e.printStackTrace();
    		}
    		try {
    			g();
    		} catch (MyException e) {
    			e.printStackTrace(System.out);
    		}
    	}
    	
    }
    /**
     * Throwing MyException from f()
    com.yue.yich.MyException
    	at com.yue.yich.FullConstructors.f(FullConstructors.java:10)
    	at com.yue.yich.FullConstructors.main(FullConstructors.java:18)
    Throwing MyException from g()
    com.yue.yich.MyException
    	at com.yue.yich.FullConstructors.g(FullConstructors.java:14)
    	at com.yue.yich.FullConstructors.main(FullConstructors.java:23)
     * 
     */
    e.printStackTrace(System.out);方法,它将打印“从方法调用处知道异常抛出处”的方法调用序列。这里信息将被发送到System.out,并自动地被捕获和显示在输出中

    如果是e.printStackTrace(); 则将信息输出到标准错误流。

2.异常与记录日志

  • java.util.logging工具将输出记录到日志中。
    class LoggingException extends Exception{
    	private static Logger logger = 
    			Logger.getLogger("LoggingException");
    	public LoggingException() {
    		StringWriter trace = new StringWriter();
    		printStackTrace(new PrintWriter(trace));//将异常信息发送到new PrintWriter(trace)中去
    		logger.severe(trace.toString());<span style="color:#FF0000;">//向logger写入异常,logger对象会将这个写入的参数发送到System.err(标准错误流)</span>,system.err在控制台打印错误
    	}
    }
    public class LoggingExceptions {
    	public static void main(String[] args) {
    		try {
    			throw new LoggingException();
    		} catch (LoggingException e) {
    			System.err.println("Caught " + e);
    		}
    	}
    }
    /**
     * 八月 30, 2016 8:38:06 下午 com.yue.yich.LoggingException <init>//logger.severe("")打印的东西
    严重: com.yue.yich.LoggingException    //trace.toString()打印出来的东西
    	at com.yue.yich.LoggingExceptions.main(LoggingExceptions.java:19)
    
    Caught com.yue.yich.LoggingException
     */
  • 捕获和记录他人编写的异常,因此我们必须在异常处理程序中(catch)生成日志消息。
    public class LoggingException2 {
    	private static Logger logger =
    			Logger.getLogger("LoggingException2");
    	static void logException(Exception e){//生成日志的消息的方法
    		StringWriter trace = new StringWriter();
    		e.printStackTrace(new PrintWriter(trace));
    		logger.severe(trace.toString());//logger将trace.toString()发送到System.err.
    	}
    	public static void main(String[] args) {
    		try {//捕获抛出的异常
    			throw new NullPointerException();//抛出异常
    		} catch (NullPointerException e) {//寻找到相应的catch进行处理
    			logException(e);//捕获到异常并生成日志消息
    		}
    	}
    }
    /**
     * 八月 30, 2016 9:24:48 下午 com.yue.yich.LoggingException2 logException
    严重: java.lang.NullPointerException
    	at com.yue.yich.LoggingException2.main(LoggingException2.java:18)
     */
  • 对于异常类,getMessage()方法类似于toString()
  • 异常类也是对象的一种。

3.异常说明,声明异常

  • 	//异常说明 异常类型跟在throw后面 明确告诉客户端程序员此方法会抛出什么样的异常
    	public void f() throws NullPointerException,IllegalAccessException{}
         //表示不会抛出任何异常
         void f(){}
    
  • 可能被忽略需要注意的地方:代码必须与异常说明保持一致,如果抛出了异常而没有异常说明,编译器会提醒(System.err)要么处理这个异常,要么给方法加上异常说明。
  • 声明异常而不抛出(被检查异常):定义抽象类和接口时预先声明可能要抛出的异常,那么其实现类将强制抛出这些预先声明的异常。
    编译时被强制检查的异常被称为编译时异常。

4.捕获所有异常

  • 只写一个异常处理程序(catch),用来处理所有的异常。通过捕获异常类型的基类Exception,也可以捕获其他基类。
    catch (Exception e) {
    			e.printStackTrace();
    		} 
    这将捕获所有的异常。
  • 最好将异常基类放在异常处理程序列表(多个catch)的末尾,以防它抢在其他异常子类前把异常捕获了。
  • Exception是与编程相关的所有异常类的基类,它的基类是Throwable(所有异常的基类),Exception不会包含太多的信息
    1. String getMessage():用来获取详细信息。
    2. String getLocalizedMessage():用来获取本地语言表示的详细信息。
    3. String toString():返回对Throwable的简单描述,要有详细信息的话,也会把它包含在内。
    4. void priStackTrace()、void printStackTrace(PrintStream)、void printStackTrace(java.io.PrintWriter)

      这三个方法打印Throwable和Throwable的调用栈轨迹。调用栈显示“异常抛出点”的方法调用序列。第一个版本输出到标准错误流。后两个允许选择输出的流。

  • Throwable fillInStackTrace():用于在Throwable对象的内部记录栈帧的当前状态。

    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
    getLocalizedMessageMy Exception
    toString():java.lang.Exception: My Exception
    printStackTrace():
    java.lang.Exception: My Exception
    	at com.yue.yc.ExceptionMethods.main(ExceptionMethods.java:6)
     */

    每一个方法都是前面一个方法的超集。

5.轨迹栈

  • public class WhoCalled {
    	static void f(){
    		try {
    			throw new Exception();
    		} catch (Exception e) {
    			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();
    	}
    }
    /**
     f
    main
    -------------------
    f
    g
    main
    ---------------------
    f
    g
    h
    main
     */
  • printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法
    将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。
    元素0是栈顶的元素,并且是调用序列中的最后一个方法调用(异常对象被创建和抛出指出)。
    最后一个元素和栈底是调用序列中的第一个方法调用

6.重新抛出异常

  • catch (Exception e) {
    			System.out.println("Inside g().e.printStackTrace()");
    			e.printStackTrace(System.out);
    			throw e;//重新抛出异常
    		}

  • 重新抛出异常会把异常抛给上一级环境中的异常处理程序【即使用这个重新抛出异常方法的环境】,同一个try块的后续catch子句将被忽略。此外,异常对象的所有信息都得以保持,所以高一级环境捕获此异常的处理程序(catch)可以从这个异常对象中得到所有的信息。
  • fillInStackTrace:如果只把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息,要更新这个信息,可以调用fillInStackTrace()方法,它返回一个Throwable对象,是通过当前调用栈信息填入原来那个异常对象而建立的。

    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) {//捕获到f抛出的异常并进行处理
    			System.out.println("Inside g().e.printStackTrace()");
    			e.printStackTrace(System.out);
    			throw e;// 重新抛出f的异常
    		}
    	}
    
    	public static void h() throws Exception {
    		try {
    			f();
    		} catch (Exception e) {//捕获到f抛出的异常
    			System.out.println("Inside h().eprintStackTrace()");
    			e.printStackTrace(System.out);//打印异常信息(调用栈信息)
    			/**
    			 * //这里调用了fillInStackTrace(它返回一个Throwable对象,是通过当前调用栈信息填入原来那个异常对象而建立的。)
    			 *  更新抛出点的调用栈信息,并重新抛出异常e
    			 */
    			throw (Exception) e.fillInStackTrace();
    
    		}
    	}
    
    	public static void main(String[] args) {
    		try {
    			g();
    		} catch (Exception e) {//捕获到g抛出的异常并进行处理,其实这个异常是f抛出的异常重新抛出(看g的catch)
    			System.out.println("main: printStackTrace()");
    			e.printStackTrace(System.out);//这儿输出的是f抛出的异常
    		}
    		try {
    			h();
    		} catch (Exception e) {//捕获h抛出的异常并打印,其实还是f的异常,但因为调用了fillInStackTrace方法,所以更新了调用点信息
    			System.out.println("main: printStackTrace()");
    			e.printStackTrace(System.out);
    		}
    	}
    }
    /**
    originating the exception in f()
    Inside g().e.printStackTrace()
    java.lang.Exception: thrown from f()
    	at com.yue.yz.Rethrowing.f(Rethrowing.java:6)//f抛出异常的调用栈信息
    	at com.yue.yz.Rethrowing.g(Rethrowing.java:11)
    	at com.yue.yz.Rethrowing.main(Rethrowing.java:31)
    main: printStackTrace()
    java.lang.Exception: thrown from f()
    	at com.yue.yz.Rethrowing.f(Rethrowing.java:6)//这里返回的和上面的一样 其实是上面抛出点的调用栈信息(原来的调用栈信息)
    	at com.yue.yz.Rethrowing.g(Rethrowing.java:11)
    	at com.yue.yz.Rethrowing.main(Rethrowing.java:31)
    originating the exception in f()
    Inside h().eprintStackTrace()
    java.lang.Exception: thrown from f()
    	at com.yue.yz.Rethrowing.f(Rethrowing.java:6)//f抛出异常的调用栈信息
    	at com.yue.yz.Rethrowing.h(Rethrowing.java:21)
    	at com.yue.yz.Rethrowing.main(Rethrowing.java:37)
    main: printStackTrace()
    java.lang.Exception: thrown from f()
    	at com.yue.yz.Rethrowing.h(Rethrowing.java:25)//因为h方法调用了fillInStackTrace,更新了调用栈信息,所以下面的调用栈信息和上面不一样
    	at com.yue.yz.Rethrowing.main(Rethrowing.java:37)
     */

  • 调用fillInStackTrace()的那一行就成了异常的新发生地。
  • 有可能在捕获异常之后抛出另一种异常。这样做有关原来异常发生点的信息会丢失,剩下的是与新的抛出点有关的信息。
    class OneException extends Exception{
        public OneException(String s) {super(s);}
    }
    class TwoException extends Exception{
        public TwoException(String s) {super(s);}
    }
    public class RethrowNew {
        public static void f() throws OneException{
            System.out.println("originating the exception in f()");
            throw new OneException("thrown from f()");
        }
    
        public static void main(String[] args) {
            try {
                try {
                    f();
                } catch (OneException e) {//捕获异常之后抛出另一种异常
                    System.out.println("Caught in inner try,e.printStackTrace()");
                    e.printStackTrace(System.out);//打印异常信息
                    throw new TwoException("from inner try");//抛出另一个异常TwoException
                }
            } catch (TwoException e1) {//捕获抛出的另一个异常TwoException
                System.out.println("Caught in outer try,e.printStackTrace()");
                e1.printStackTrace(System.out);
            }
        }
    }
    /**
     originating the exception in f()
    Caught in inner try,e.printStackTrace()
    com.yue.yz.OneException: thrown from f()//第一个异常
        at com.yue.yz.RethrowNew.f(RethrowNew.java:12)
        at com.yue.yz.RethrowNew.main(RethrowNew.java:18)
    Caught in outer try,e.printStackTrace()
    com.yue.yz.TwoException: from inner try//第二个异常  原来的异常发生点信息丢失
        at com.yue.yz.RethrowNew.main(RethrowNew.java:22)
     */
    
    
    
  • 异常对象也是对象,他们用new在堆上创建对象,垃圾回收期会自动把他们清理掉。

。。。。。。。。。。。。。。。。。。。。。。。。这儿还有好多内容没总结

7.异常链

  • 捕获一个异常后抛出另一个异常,并把原始异常的信息保存下来,这被称为异常链。
  • 所有的Throwable的子类在构造器中都可以接受一个cause对象作为参数。
    cause就表示原始异常,这样通过把原始异常传递给新异常,使得即使在当前位置创建并抛出新的异常
    也能通过这个异常链(保存的cause)追踪到异常最初发生的位置。
  • Throwable的子类中,只有Error(用于Java虚拟机报告系统错误)、Exception以及RuntimeException提供了带cause参数的构造器。
  • 如果要把其他类型的异常链接起来,应该使用initCause()方法而不是构造器。

public class ExceptionLian {
	public void f() throws Exception{
		System.out.println("f方法");
		throw new Exception("自定义异常");
	}
	public void g() throws Exception{
		try {
			f();
		} catch (Exception e) {
			e.printStackTrace();
			//抛出一个新的异常,并将原来的异常作(f抛出的异常)为cause放入新的异常中,形成异常链
			throw new Exception("g方法抛出新异常1",e);
		}
	}
	public void h() throws IOException{
		try {
			g();
		} catch (Exception e) {
			e.printStackTrace();
			IOException io = new IOException("h方法抛出新异常2");
			//因为io异常没有cause的构造器,所以要这样链接原来的异常
			io.initCause(e);
			throw io;
		}
	}
	public static void main(String[] args) {
		try {
			new ExceptionLian().h();//调用h方法
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
/**
 f方法
java.lang.Exception: 自定义异常
	at com.yue.yz.ExceptionLian.f(ExceptionLian.java:8)
	at com.yue.yz.ExceptionLian.g(ExceptionLian.java:12)
	at com.yue.yz.ExceptionLian.h(ExceptionLian.java:21)
	at com.yue.yz.ExceptionLian.main(ExceptionLian.java:32)
java.lang.Exception: g方法抛出新异常1
	at com.yue.yz.ExceptionLian.g(ExceptionLian.java:16)
	at com.yue.yz.ExceptionLian.h(ExceptionLian.java:21)
	at com.yue.yz.ExceptionLian.main(ExceptionLian.java:32)
Caused by: java.lang.Exception: 自定义异常   //可以看到这里的caused 原来的异常
	at com.yue.yz.ExceptionLian.f(ExceptionLian.java:8)
	at com.yue.yz.ExceptionLian.g(ExceptionLian.java:12)
	... 2 more
java.io.IOException: h方法抛出新异常2
	at com.yue.yz.ExceptionLian.h(ExceptionLian.java:24)
	at com.yue.yz.ExceptionLian.main(ExceptionLian.java:32)
Caused by: java.lang.Exception: g方法抛出新异常1
	at com.yue.yz.ExceptionLian.g(ExceptionLian.java:16)
	at com.yue.yz.ExceptionLian.h(ExceptionLian.java:21)
	... 1 more
Caused by: java.lang.Exception: 自定义异常  
	at com.yue.yz.ExceptionLian.f(ExceptionLian.java:8)
	at com.yue.yz.ExceptionLian.g(ExceptionLian.java:12)
	... 2 more
 */

  • 从输出可以看出打印的异常中有caused by......说明新抛出的异常包含原来的异常,并且可以打印出来。

8.Java标准异常

  • if (t == null) 
    			throw new NullPointerException();
    如果对null引用调用,就会抛出NullPointerException异常,它属于Java运行时异常,
    不必像上面一样显式的检查引用是否为null,再抛出异常,Java会将其自动抛出。
  • 不受检查异常:属于 运行时的异常有很多,他们会被java虚拟机自动抛出。这些异常都继承于RuntimeException.构成了一组具有相同特征和行为的异常类型,
    这种异常属于错误,将会被java虚拟机自动捕获。尽管不需要在代码中捕获,但可以抛出运行时异常。
  • 如果不捕获这些异常,会???
    public class NeverCaught {
    	static void f(){
    		//抛出运行时异常
    		throw new RuntimeException("from f()");
    	}
    	static void g(){
    		f();
    	}
    	public static void main(String[] args) {
    		g();
    	}
    }
    /**
     Exception in thread "main" java.lang.RuntimeException: from f()
    	at com.yue.yc.NeverCaught.f(NeverCaught.java:6)
    	at com.yue.yc.NeverCaught.g(NeverCaught.java:9)
    	at com.yue.yc.NeverCaught.main(NeverCaught.java:12) 
     */
    对于这种异常,编译器不需要异常说明,其输出被报告给System.err
    如果RuntimeException没有被捕获而直达main(),那么程序退出前会调用异常的printStackTrace()方法
    注意:
    1. 只能在代码中忽略RuntimeException类型的异常,因为它代表的是编程错误
      无法预料的错误(如控制范围之外传来的空引用),程序员应该在代码中检查的错误。
  • 不应把java的异常处理机制当成是单一用途的工具。它被设计用来处理烦人的运行时错误,这些错误往往是由代码控制能力之外的因素导致的;然而,它对于某些编辑器无法检测到的编程错误,也是非常重要的。

9.finally子句[无论怎样都会得到执行]

  • finally保证无论异常是否被抛出,finally子句都可以得到执行。finally跟在异常处理程序的后面(catch);
  • finally用来做什么:当需要把除内存之外的资源恢复到原来的状态时。
  • finally子句在任何情况下都能够得到执行,机制异常没有被当前异常处理程序捕获的情况下,异常处理机制也会在跳到更高议程的异常处理程序之前,执行finally
  • 当设计break和continue语句的时候,即使有return时,finally字句也会得到执行。

10.异常丢失

  • class VeryImportantException extends Exception{
    	public String toString(){
    		return "A very important exception";
    	}
    }
    class HoHumException extends Exception{
    	public String toString(){
    		return "A trivial exception";
    	}
    }
    public class LostMessage {
    	void f() throws VeryImportantException{
    		throw new VeryImportantException();
    	}
    	void dispose() throws HoHumException{
    		throw new HoHumException();
    	}
    	public static void main(String[] args) {
    		try {
    			LostMessage lm = new LostMessage();
    			try {
    				lm.f();
    			} finally {
    				lm.dispose();
    			}
    		} catch (Exception e) {
    			System.out.println(e);
    		}
    	}
    }
    /**
     A trivial exception 
     */
    
    从输出可以看到 VeryImportantException异常丢失啦,它被finally字句里的HoHumException
    所取代。这是相当严重的缺陷,因为异常可能会比前面例子所示更微妙和更难以察觉的方法完全丢失。
  • 	public static void main(String[] args) {
    		try {
    			throw new RuntimeException();
    		}finally {
    			return;
    		}
    	}
    这样不会有任何输出

11.异常的限制

  • 当重写父类中的方法时,只能抛出在父类方法的异常说明(跟在throw后)里列出的那些异常。

    这样当父类使用的代码派生到其子类的时候一样可以工作,异常也不例外。
  • 但异常限制对构造器没有限制,子类的构造器并不受限于父类的构造器。

  • 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{
    
    	public StormyInning() throws RainedOut, BaseballException{}
    	public StormyInning(String s) throws Foul, BaseballException{}
    //	public void enent() throws RainedOut{}
    	@Override
    	public void rainHard() throws RainedOut {}
    	@Override
    	public void event() {
    	}
    	
    	@Override
    	public void atBat() throws Strike, Foul{}
    	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("Generic 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("Rained out");
    		} catch (BaseballException e) {
    			System.out.println("Generic baseball exception");
    		}
    	}
    }	
    	



12.构造器中的变化

  • 如果异常发生了,所有的东西都会被清理吗?
    当在构造器中打开一个文件或其他操作,必须在其执行完毕调用特殊的清理方法后才能清理,但如果在执行过程中抛出异常,清理工作就无法完成。
    如果使用finally,当对象还没有完全初始化,构造器就半途而废,而没有初始化的又在清理之内。就会出现问题。
  • public class InputFile {
    	private BufferedReader in;
    	public InputFile(String name) throws Exception{
    		try {
    			/**
    			 * 接受文件名name,并以此建立FileReader对象,如果构造失败,则抛出FileNotFoundException异常,
    			 * 对于这个异常,不需要关闭文件,因为文件还没有打开,其他的catch字句必须关闭,因为文件已经打开。
    			 */
    			in = new BufferedReader(new FileReader(name));
    		} catch (FileNotFoundException e) {
    			System.out.println("Could not open" + name);
    			throw e;//重新抛出异常
    		}catch(Exception e){//文件打开后其他的异常捕获
    			try {
    				in.close();//关闭文件,也可能发生异常 ,所以要重新捕获
    			} catch (IOException e1) {//处理关闭异常
    				System.out.println("in.close() unsuccessful");
    			}
    			throw e;//异常被重新抛出文件打开后其他的异常捕获
    		}finally{//里面啥也没有 ,如果都不抛出异常,说明对象创建成功,而且不成功也关闭了文件
    			//不能再这里调用close方法,因为文件有可能没被打开就抛出了FileNotFoundException的异常
    		}
    	}
    	public String getLine(){//返回文件下一行的字符串
    		String s;
    		try {
    			s = in.readLine();
    		} catch (IOException e) {
    			throw new RuntimeException("readLine() failed");//这里抛出了一个运行时异常。
    		}
    		return s;
    	}
    	public void dispose(){//关闭
    		try {
    			in.close();
    		} catch (IOException e) {
    			throw new RuntimeException();
    		}
    	}
    }
    
  • 对于构造阶段可能会抛出异常,并且要求清理的类,使用try字句
    public class Cleanup {
    	public static void main(String[] args) {
    		try {
    			InputFile in = new InputFile("Cleanup.java");//打开一个文件,错误抛出异常
    			try {//文件打开成功
    				String s;
    				int i = 1;
    				while((s = in.getLine()) != null)//读取操作,可能抛出异常
    					;
    			} catch (Exception e) {
    				System.out.println("Caught Exception in main");
    				e.printStackTrace(System.out);//打印异常
    			}finally {//InputFile初始化成功,说明打开了文件
    				in.dispose();//关闭
    			}
    		} catch (Exception e) {//InputFile初始化失败
    			System.out.println("InputFile construction failed");
    		}
    	}
    }
    
  • 这种通用的清理惯用手法在构造器不抛出任何异常时也应该运用,其基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句块
    class NeedsCleanup {
    	private static long counter = 1;
    	private final long id = counter ++;
    	public void dispose(){
    		System.out.println("NeedsCleanup " + id + " disposed");
    	}
    }
    
    class ConstructionException extends Exception{}
    class NeedsCleanup2 extends NeedsCleanup{
    	public NeedsCleanup2() throws ConstructionException{}
    }
    
    public class CleanupIdiom{
    	public static void main(String[] args) {
    		NeedsCleanup nc1 = new NeedsCleanup();
    		try{
    			
    		}finally {
    			nc1.dispose();
    		}
    		NeedsCleanup nc2 = new NeedsCleanup();
    		NeedsCleanup nc3 = new NeedsCleanup();
    		try {
    			
    		} catch (Exception e) {
    			nc3.dispose();
    			nc2.dispose();
    		}
    		try {
    			NeedsCleanup2 nc4 = new NeedsCleanup2();
    			
    			try {
    				NeedsCleanup2 nc5 = new NeedsCleanup2();
    				try {
    					
    				} finally {
    					nc5.dispose();
    				}
    			} catch (ConstructionException e) {
    				System.out.println(e);
    			}finally {
    				nc4.dispose();
    			}
    		} catch (ConstructionException e) {
    			System.out.println(e);
    		}
    	}
    }
    /**
    NeedsCleanup 1 disposed
    NeedsCleanup 5 disposed
    NeedsCleanup 4 disposed
     */
    




13.异常匹配

  • 抛出异常的时候,异常处理系统会按照代码的书写的顺序找出“最近”的处理程序。找到匹配的处理程序之后,就不在继续查找。
  • 查找时并不要求抛出的异常同处理程序锁声明的异常完全匹配。子类对象也可以匹配器父类的处理程序。
    class Annoyance extends Exception{}
    class Sneeze extends Annoyance{}
    public class Muman {
    	public static void main(String[] args) {
    		try {
    			throw new Sneeze();
    		} catch (Sneeze e) {
    			System.out.println("Caught Sneeze");
    		}catch (Annoyance a) {
    			System.out.println("Caught Annoyance");
    		}
    		try {
    			throw new Sneeze();
    		} catch (Annoyance e) {//父类异常
    			System.out.println("Caught Annoyance");
    		}
    	}
    }
    /**
    Caught Sneeze
    Caught Annoyance
     */
    
    父类异常会捕获它所有派生子类的异常。所以尽量把父类异常的catch放在最后。

14.其他可选方式

  1. 开发异常的初衷是方便程序员处理错误。
  2. 异常处理系统就像一个活门,是你能放弃程序的正常执行序列。当“异常情形”发生的时候,正常的执行已变得不可能或者不需要了,这是就要用到这个活门
    异常代表了当前方法不能继续执行的情形。
  3. 异常处理的一个重要原则就是“只有在你知道如何处理的情况下才捕获异常”。
  4. 异常处理的目标是将异常发生的地点和异常处理的地方分离,这样主干代码就不会与错误处理逻辑混在一起,允许一个异常处理程序处理多个错误点,
    异常处理使得错误处理代码趋于少量。
  5. 被检查异常使这个问题变得有些复杂,因为它们强制你在可能还没准备好处理错误的时候被迫追加上catch子句,这就导致了吞食有害原则。
    编译器强迫你立刻写代码来处理异常,所以这种看起来最简单的方法,却可能是最糟糕的做法。
  6. java “强静态类型语言(也就是编译时就做类型检查的语言)”。
  7. 异常说明可能有两种意思:一个是“我的代码会产生这种异常,这由你来处理”,另一个是“我的代码忽略了这些异常,这由你来处理”。
  8. 我们通常忽略异常说明所表达的完整含义。
  9. 把异常传到控制台
    public class MainException {
    	public static void main(String[] args) throws Exception{//这样 main方法里的异常就会输出到控制台。
    		FileInputStream file = 
    				new FileInputStream("MainException.java");
    		file.close();
    	}
    }
  10. 被检查异常转换成不检查异常:直接把“被检查异常”包装进RuntimeException里面
    		try {
    			
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    class WrapCheckedException {
    	void throwRuntimeException(int type){
    		try {
    			switch (type) {
    			case 0: throw new FileNotFoundException();				
    			case 1: throw new IOException();				
    			case 2: throw new RuntimeException("Where am I?");
    			default: return;
    			}
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    	}
    }
    class SomeOtherException extends Exception{}
    public class TurnOffChecking {
    	public static void main(String[] args) {
    		WrapCheckedException wce = new WrapCheckedException();
    		wce.throwRuntimeException(3);
    		for(int i = 0; i < 4; i++)
    			try{
    				if (i < 3)
    					wce.throwRuntimeException(i);
    				else 
    					throw new SomeOtherException();
    			}catch (SomeOtherException e) {
    				System.out.println("SomeOtherException: " + e);
    			}catch (RuntimeException re) {
    				try{
    					throw re.getCause();
    				}catch(FileNotFoundException e){
    					System.out.println("FileNotFoundException: " + e);
    				}catch(IOException e){
    					System.out.println("IIOException: " + e);
    				}catch(Throwable e){
    					System.out.println("Throwable: " + e);
    				}
    			}
    	}
    }
    /**
    FileNotFoundException: java.io.FileNotFoundException
    IIOException: java.io.IOException
    Throwable: java.lang.RuntimeException: Where am I?
    SomeOtherException: com.yue.yich.SomeOtherException
     */
    

15.异常使用指南

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

总结

  • 异常是java程序设计不可分割的一部分,如果不了解如何使用它们,那你只能完成有限的工作。











































相关文章推荐

java编程思想读书笔记 第十二章 通过异常处理错误(上)

java异常处理的目的在于通过使用少于目前数量的代码来简化大型、可靠的程序的生成,并且通过这种方式可以使你更加自信。本章将介绍如何编写正确的异常处理程序,并将展示当方法出问题的时候,如何产生自定义异常...

java编程思想读书笔记 第十二章 通过异常处理错误(下)

1.异常的限制 当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工资,异常也不例外。 下面的例子是在编译时施加在异常上面...

java编程思想读书笔记----第十二章 通过异常处理错误

发现异常的理想时期是在编译期,也就是在试图运行代码之前。然而编译期不能找到所有的错误,余下的问题必须在运行期间解决,也就是异常处理。

java编程思想读书笔记 第十二章 通过异常处理错误(中)

1.捕获所有异常 可以只写一个异常处理程序捕获所有类型的异常。通过捕获异常类型的基类Exception(事实上还有其他的基类,但Exception是同编程活动相关的基类),就可以做到这一点:catc...

Java编程思想笔记——第十二章 通过异常处理错误

Java编程思想笔记——第十二章 通过异常处理错误

Java编程思想第四版第十二章学习——通过异常处理错误(1)

使用异常带来的好处: 它降低了错误处理代码的复杂度。使用异常后,不需要检查特定的错误并在程序中的许多地方去处理它。因为异常机制将保证能够捕获这个错误且只需在一个地方处理错误,即异常处理程序中。 ...

Java编程思想第四版读书笔记——第十二章 通过异常处理错误

第十二章 通过异常处理错误

第十二章 通过异常处理错误

 Java的基本理念是“结构不佳的代码不能运行” 发现错误的理想时机是在编译阶段,然而,编译期间并不能找出所有的错误,余下的问题必须在运行期间解决。 1.概念 错误处理的解决方法是:用强制...

Java编程思想第四版第十二章学习——通过异常处理错误(2)

1.Java异常标准Throwable类用来表示任何可以作为异常被抛出的类。Throwable对象可以分为两类:Error用来表示编译时和系统错误(一般无需关心),Exception是可以被抛出的基本...

JAVA编程思想学习总结:第十二章通过异常处理错误

(1)认识异常 (2)捕获异常 (3)使用finally (4)异常的限制 (5)异常匹配 (6)其它可选方式 (7)异常使用指南
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)