• 程序运行期间,出现异常(程序的错误、外部环境的影响等)的情况,应该:
  1. 向用户通告错误
  2. 保存所有的工作结果
  3. 允许用户妥善退出程序
  • Java使用一种称为 异常处理(exception handling) 的错误捕获机制处理。
  • 使用 断言,可以有选择地启用检测:测试期间的检测,虽然将检测删掉或者需要的时候粘贴回来。
  • 日志:程序出现错误,记录出现的问题,以备分析。

 

  • 处理错误:
  1. 程序中可能出现的错误:
  1. 用户输入错误
  2. 设备错误
  3. 物理限制:磁盘满了,存储空间用完
  4. 代码错误
  1. 在Java中,如果某个方法不能采用正常的途径完成它的任务,就可以通过另一个路径退出方法。此情况之下,方法不返回任何值,而是抛出一个 封装错误信息的对象。这个方法无法继续执行,异常处理机制则开始搜索能够处理这种异常的 异常处理器
  2. 异常具有自己的语法和特定的继承结构。
  • 异常分类:
  1. 在Java中,异常对象都是派生于 Throwable类 的一个实例。
  2. 如果内置的异常类不能满足需求,用户可以创建自己的异常类

java断点打了进不去 java断点异常_抛出异常

  • Error类层次结构描述了Java运行时 系统的内部错误资源耗尽错误。应用程序不该抛出这种类型的对象。若出现,通告用户并尽量安全退出。
  • 程序错误导致的异常属于 RuntimeException ;而程序本身没有问题,但由于像IO错误这类问题导致的异常属于 其他异常
  • 派生于 RuntimeException 的异常包括:
  • 错误的类型转换
  • 数组访问越界
  • 访问null指针
  • 等等
  • 不是派生于RuntimeException 的异常包括:
  • 试图在文件尾部后面读取数据
  • 试图打开一个不存在的文件(文件是否存在取决于环境而不是你的代码)
  • 试图根据给定的字符串查找Class对象,而这个字符串表示的类不存在
  • 如果出现 RuntimeException 异常,那么就一定是你的问题:可以通过修改程序来避免此类异常的发生。
  • Java将所有派生于 Error类 或 RuntimeException类 的所有异常称为 非受查(unchecked)异常。其他所有的异常称为受查(checked)异常
  • RuntimeException异常的名字易让人混淆,现在讨论的所有异常都发生于运行时
  • 一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误
  • 需要记住下面四种情况时会抛出异常:
  • 调用抛出受查异常方法
  • 程序运行过程中发现错误,并且利用throw语句抛出一个受查异常
  • 程序出现错误,如数组越界会抛出一个非受查异常。
  • Java虚拟机和运行时库出现的内部错误。
  • 如果出现前两种情况,必须告诉调用这个方法的程序员有可能抛出异常。抛出异常如果没有处理器捕获这个异常,当前执行的线程就会结束
  • 不需要声明Java的内部错误(从Error继承的错误:不可控制),也不应该声明从RuntimeException继承的那些非受查异常(可以避免发生)。
  • 如果在子类中覆盖了超类的一个方法,子类声明的受查异常不能比超类方法中声明的异常更通用。子类可以抛出更特定的异常或者不抛出异常。如果超类方法中没有抛出受查异常,那么子类也不能抛出。

 

  • 如何抛出异常:
String readData( Scanner in ) throws EOFException
{
    ...
    while(...){
        if(!in.hasNext()){
            if( n < len )
                throw new EOFException();
        }
        ...
    }
    return s;
}
  1. 对于一个已经存在的异常类,将其抛出很容易:
  1. 找到一个合适的异常类
  2. 创建这个类的一个对象
  3. 将对象抛出
  1. 一旦方法抛出了异常,这个方法就不能返回到调用者。不必为返回的默认值或错误代码担忧。

 

  • 创建异常类:
  1. java.lang.Throwable:
  1. Throwable()
  2. Throwable( String message )
  3. String getMessage()
  1. 只需定义一个派生于Exception的类,或者派生于Exception子类的类。
  • 捕获异常:
  1. 如果某个异常发生的时候没有任何地方进行捕获,那么程序就会终止执行,并在控制台上打印出异常信息(异常的类型和堆栈的内容)。
  2. 要想捕获一个异常,必须设置 try/catch 语句块。
  3. 若try语句块抛出了一个在catch子句中说明的异常类:
  1. 程序跳过 try语句块的其余代码
  2. 程序将执行catch子句中的处理器代码
  1. 通常,应该捕获那些知道如何处理的异常,而将那些不知道怎么样处理的异常继续进行传递。如果像传递一个异常,就必须在方法的首部添加一个throws说明符
  2. 编译器严格执行throws说明符,如果调用了一个抛出受查异常的方法,就必须对其处理或者继续传递
  3. 如果覆盖一个超类的方法,而这个方法又没有抛出异常,那么这个方法就必须捕获代码中出现的所有的受查异常。不允许子类的throws说明符中出现超过超类方法所列出的异常类范围
  • 捕获多个异常:
try
{
    code that might throw exceptions
}
catch( FileNotFoundException e )
{
    emergency action for missing files
}
catch( UnknownHostException e )
{
    emergency action for unknown hosts
}
catch( IOException e )
{
    emergency action for all other IO problems
}
  • 同一个catch子句中可以捕获多个异常类型(不存在子类关系):
try
{
    code that might throw exceptions
}
catch( FileNotFoundException | UnknownHostException e )
{
    emergency action for missing files and unknown hosts
}
catch( IOException e )
{
    emergency action for all other IO problems
}

 

  • 再次抛出异常与异常链:
  • catch子句可以抛出一个异常,目的:改变异常的类型
try
{
    access the database
}
catch( SQLException e )
{
    Throwable se = new ServletException("database error");
    se.initCause(e);
    throw se;
}
  • 强烈建议使用上述的包装技术,可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。
  • 如果在一个方法中发生了受查异常,而不允许抛出它,就可以利用包装技术捕获受查异常,并包装成运行时异常
  • 有时只想记录一个异常,再将其重新抛出,不做改变:
try
{
    access the database
}
catch( Exception e )
{
    logger.log( level, message, e );
    throw e;
}

 

  • finally子句:
  1. 不管是否有异常被捕获,finally子句中的代码被执行。
  2. try语句可以只有finally子句,而没有catch子句。
  3. 强烈建议解耦合try/catch和try/finally语句块。(提高代码清晰度)
  4. 警告:若finally子句包含return语句,假设try语句执行了return退出, 方法返回前,finally子句的内容依然被执行,再执行finally子句中的return会覆盖原始的返回值。
  • 带资源的try语句:
  1. 假设资源属于一个实现了 AutoCloseable接口 的类,AutoCloseable接口有一个方法:void close() throws Exception 。
  2. Closeable接口 是 AutoCloseable 的 子接口。
  3. try( Resourse res = ... ) { work with res } 。try退出时,会自动调用res.close()。
  4. 可以指定多个资源。用 ";" 分隔 。
  5. 如果try块抛出异常,同时close方法也抛出异常,带资源的try块会很好的解决这个问题:原来的异常会被重新抛出,而close方法抛出的异常会“被抑制”(捕获并由addSuppressed方法添加到原来的异常,通过getSuppressed方法可获得被抑制的异常列表
  6. 带资源的try语句自身也可以有catch子句和一个finally子句,这些子句会在关闭资源之后执行。

 

  • 分析堆栈轨迹元素:
  1. 堆栈轨迹是一个方法调用过程的列表,包含方法调用的特定位置。
  2. printStackTrace方法 和 getStackTrace方法(得到 StackTraceElement对象 的一个 数组)。
  3. StackTraceElement类 可以通过公有接口获得 文件名,当前执行的代码行号类名方法名
  4. 示例程序:(计算n的阶乘,并同时打印出堆栈的轨迹)
package stackTrace;

import java.util.*;
public class StackTraceTest {
	public static void main( String[] args ) {
		Scanner in = new Scanner(System.in);
		System.out.println("Enter n to calulate factorial(n):");
		int n = in.nextInt();
		factorial(n);
		in.close();
	}
	
	public static int factorial( int n ) {
		System.out.println( "factorial(" + n + "):");
		Throwable t = new Throwable();
		StackTraceElement[] frames = t.getStackTrace();
		for( StackTraceElement f : frames )
			System.out.println(f);
		int r;
		if( n <= 1 ) r = 1;
		else r = n * factorial(n-1);
		System.out.println( "return " + r );
		return r;
	}
}
  • java.lang.Throwable:
  • Throwable( Throwable cause )
  • Throwable( String message, Throwable cause )
  • Throwable initCause( Throwable cause )
  • Throwable getCause()
  • StackTraceElement[] getStackTrace()
  • void addSuppressed( Throwable t )
  • Throwable[] getSuppressed()
  • 使用异常机制的技巧:
  1. 异常处理不能代替简单的测试。(与执行简单的测试相比,捕获异常花费的时间大大超过了前者)
  2. 不要过分地细化异常(将整个任务包装在try块中)
  3. 利用异常层次机构:将一种异常转换成另一种更加适合的异常时不要犹豫。(更易读 更易于维护)
  4. 不要压制异常。在Java中,往往强烈地倾向关闭异常。
  5. 在检测错误时,苛刻比放任更好。
  6. 不要羞于传递异常。

 

 

  • 使用断言:
  1. 断言的概念:断言机制允许在测试期间向代码中插入一些检查语句。当代码发布时,这些插入的检测语句会被自动移走
  2. Java语言引入了 关键字assert。两种形式:
  1.  assert 条件;
  2. assert 条件 : 表达式 ;
  1. 以上两种形式都会对条件进行检测,如果结果为 false,则抛出一个 AssertionException 。“表达式”部分的唯一目的产生一个消息字符串。
  2. 启用和禁用断言:
  1. 默认情况下,断言被禁用。-enableassertions 或 -ea 启用。例如: java -enableassertions MyApp 。
  2. 启用或禁用断言不必重新编译。启用或禁用断言是类加载器的功能。当断言被禁用时,类加载器将跳过断言代码。
  3. 也可以某个类或整个包中使用断言: java -ea:MyClass -ea:com.mycompany.mylib... MyApp 。
  4. -disableassertions 或 -da 禁用。
  1. 前置条件。如果调用者在调用方法时没有提供满足前置条件,所有的断言都会失效,并且这个方法可以执行它想做的任何操作(难以预料的结果)。
  2. java.lang.ClassLoader:
  1. void setDefaultAssertionStatus( boolean b )
  2. void setClassAssertionStatus( String className, boolean b )
  3. void setPackageAssertionStatus( String packageName, boolean b )
  4. void clearAssertionStatus()