快速入门Java(五)异常

如果一个用户在运行程序期间,由于程序的错误或一些外部环境的影响造成用户数据的丢失。为了避免这类事件的发生,至少应该做到以下几点:

  • 向用户通告错误
  • 保存所有的工作结果
  • 允许用户已妥善的形式退出程序

一、处理异常

在Java中,如果某个方法不能采取正常的途径完成它的任务,就可以通过另外一个路径退出方法。在这种情况下,方法并不会返回任何值,而是抛出一个封装了错误信息的对象。需要注意的是,这个方法将会立即退出,并不返回任何值。此外,调用这个方法的代码也将无法继续执行,取而代之的是,异常处理机制开始搜索能够处理这种异常状况的异常处理器。

二、异常分类

在Java中,异常对象都是派生于Throwable对象类的一个实例。如果Java中内置的异常类不能够满足需求,用户可以创建自己的异常类。
在这里插入图片描述
所有的异常都是由Throwable继承而来。

Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止。

由程序错误导致的异常属于RuntimeException;而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常。派生于RuntimeException的异常包含下面几种情况:

  • 错误的类型转换
  • 数组访问越界
  • 访问null指针

不是派生于RuntimeException的异常包括:

  • 试图在文件尾部后面读取数据
  • 试图打开一个不存在的文件
  • 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在

“如果出现RuntimeException异常,那么就一定是你的问题”是一条相当有道理的规则。Java语言规范将派生于Error类或RuntimeException类的所有异常称为非受查异常,所有其他的异常称为受查异常。编辑器将核查是否为所有的受查异常提供了异常处理器。

至于什么时候需要在方法中用throws子句声明异常,什么异常必须使用throws子句声明,需要记住在遇到下面4种情况时应该抛出异常:

  1. 调用一个抛出异常受查的方法,例如,FileInputStream构造器
  2. 程序运行过程种发现错误,并且利用throw语句抛出一个受查异常
  3. 程序出现错误,例如,a[-1]=会抛出一个ArrayIndexOutOfBoundsException这样的非受查异常。
  4. Java虚拟机和运行时库出现的内部错误

如果抛出了一个异常,而没有处理器捕获这个异常,当前执行的线程就会结束。也不应该声明从RuntimeException继承的那些非受查异常。这些运行时错误完全在我们的控制之下。如果特别关注数组下标引发的错误,就应该将更多的时间花费在修正程序中的错误上,而不是说明这些错误发生的可能性上。

总之,一个方法必须声明所有可能抛出的受查异常,而非受查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。如果方法没有声明所有可能发生的受查异常,编译器就会发出一个错误信息。

如果在子类中覆盖了超类的一个方法,子类方法中声明的受查异常不能比超类方法中声明的异常更通用。特别需要说明的是,如果超类方法没有抛出受查异常,子类也不能抛出任何受查异常。另外,在C++中,如果没有给出throw说明,函数可能会抛出任何异常。而在Java中,没有throws说明符的方法将不能抛出任何受查异常。

三、创建异常类

在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。在这种情况下,创建自己的异常类就是一件顺理成章的事情了。我们需要做的只是定义一个派生于Exception的类,或者派生于Exception子类的类。

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

例如,定义一个派生于IOException的类。习惯上,定义的类应该包含两个构造器,一个是默认的构造器;另一个是带有详细描述信息的构造器。

class FileFormatException extends IOException
{
     public FileFormatException(){}
     public FileFormatException(String gripe)
    {
      super(gripe);
     }
}

现在,就可以抛出自己定义的异常类型了。

String readData(BufferedReader in)throws FileFormatException
{
   if(n<len)
   throw new FileFormatException();
}

四、捕获异常

如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台打印出异常信息,其中包括异常的类型和堆栈的内容。

要想捕获一个异常,必须设置try/catch语句块。

try
{
 code
}
catch(ExceptionType e)
{
  handler for this type
}

如果在try语句块中的任何代码抛出了一个在catch子句中说明的异常类,那么

  1. 程序将跳过try语句块的其余代码
  2. 程序将执行catch子句中的处理器代码

如果方法中的任何代码抛出了一个在catch子句中没有声明的异常类型,那么这个方法就会立刻退出。如果调用了一个抛出受查异常的方法,就必须对它进行处理,或者继续传递。通常,应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常继续进行传递。

捕获多个异常

在一个try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。可以按照下列方式为每个异常类型使用一个单独的catch子句:

try
{
 code that minght throw exceptions 
}
catch(FileNotFoundException e)
{
 code
}
catch(UnknownHostException e)
{
 code
}
catch(IOException e)
{
 code
}

五、使用异常机制的技巧

1)异常处理不能代替简单的测试

使用异常的基本规则是:只在异常情况下使用异常机制。

2)不要过分地细化异常

有必要将整个任务包装在一个try语句块中,这样,当任何一个操作出现问题时,整个任务都可以取消。

3)利用异常层次结构

不要只抛出RuntimeException异常。应该寻找更加适当的子类或创建自己的异常类。不要只捕获Thowable异常,否则,会使程序代码更难读,更难维护。

4)不要羞于传递异常

很多程序员都感觉应该捕获抛出的全部异常。如果调用了一个抛出异常的方法,例如,FileInputStream构造器或readLine方法这些方法就会本能地捕获这些可能产生的异常。其实,传递异常要比捕获这些异常更好:

public void readStuff(String filename)throws IOException
{
  InputStream in=new FileInputStream(filename);
 . . .
}

让高层次的方法通知用户发生了错误,或者放弃不成功的命令更加适宜。

六、使用断言

断言机制允许在测试期间向代码插入一些检查语句。当代码发布时,这些插入的检测语句将会被自动地移走。
Java语言引入了关键字assert。这个关键字有两种形式:assert 条件和assert 条件:表达式;。这两种形式都会对条件进行检测,如果结果为false,则抛出一个AssertionError异常。在第二种形式中,表达式将被传入AssertionError的构造器,并转换为一个消息字符串。

在Java中,给出了3种处理系统错误的机制:

  • 抛出一个异常
  • 日志
  • 使用断言

什么时候应该选择使用断言?
断言失败是致命的,不可恢复的错误。断言检查只用于开发和测试阶段。不应该使用断言向程序的其他部分通告发生了可恢复性的错误,或者,不应该作为程序向用户通告问题的手段。断言是一种测试和调试阶段所使用的战术性工具;而日志记录是一种在程序的整个生命周期都可以使用的策略性工具。

七、调试技巧

1)可以用下面的方法打印或记录任意变量的值:
System.out.println(“x”+x);或Logger.getGlobal().info(“x=”+=);

要想获得隐式参数对象的状态,就可以打印this对象的状态。
Logger.getGlobal().info(“this=”+this);

2)一个不太为人所知但却非常有效的技巧是在每一个类中放置一个单独的main方法。这样就可以对每一个类进行单元测试。

3)利用Throwable类提供的printStackTrace方法,可以从任何一个异常对象中获得堆栈情况。下面的代码将捕获任何异常,打印异常对象和堆栈轨迹,然后,重新抛出异常,以便找到相应的处理器。

  try
    {
     . . .
    }
    catch(Throwable t)
    {
     t.printStackTrace();
     throw t;
    }

不一定要通过捕获异常来生成堆栈轨迹。只要在代码的任何位置插入下面这条语句就可以获得堆栈轨迹:Thread.dumpStack();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值