码迷,mamicode.com
首页 > 其他好文 > 详细

第十一章 异常,日志,断言和调试

时间:2015-09-10 21:18:40      阅读:272      评论:0      收藏:0      [点我收藏+]

标签:

第十一章 异常,日志,断言,调试

    由于程序的错误或一些外部环境的影响造成用户数据的丢失,用户就有可能不再使用这个程序了。为了避免,应该做到以下几点:

  • 向用户通告错误

  • 保存所有的操作结果

  • 允许用户以适当的形式退出程序。

11.1 处理异常

    当由于出现错误而使得某些操作没有完成,程序应该:

  • 返回到一种安全状态,并能够让用户执行一些其他命令;或者

  • 允许用户保存所有操作的结果,以适当的方式终止程序

    异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种错误的处理器。程序中可能出现的错误和问题:

  1. 用户输入错误

  2. 设备错误

  3. 物理限制(磁盘满了)

  4. 代码错误,例如(方法返回一个错误答案,或错误调用其他方法;使用了一个无效的数组下标;试图查找一个在散列表中不存在的数据项;试图对一个空栈进行退栈。)

11.1.1 异常分类

异常对象都是派生与Throwable类的一个实例,如果java内置的异常类不能够满足需求,用户还可以创建自己的异常类。

技术分享

    Error类层次结构描叙了java运行时系统的内部错误和资源耗尽错误。

    在java设计是,需要关注Exception层次结构。两个分支:

  • 派生于RuntimeException(程序错误导致)

    • 错误的类型转换

    • 数组访问越界(通过检查数组下标,避免ArrayIndexOutOfBoundsException)

    • 访问空指针(在使用变量之前检查是否为空来杜绝NullPointerException)

  • 包含其他异常(程序本身没问题,像类似于I/O错误这类问题导致的异常)

    • 试图在文件尾部后读取数据

    • 试图打开一个错误格式的URL

    • 试图根据给定的字符串查找Class对象,而这个字符串表示的类不存在

    如果出现RuntimeException异常,就一定是你的问题。

    异常的分类:

  • 未检查异常

    • 派生于Error

    • 派生于RuntimeException

  • 其他异常称为已检查异常。

11.1.2 声明已检查异常

    一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误。方法应该在其收不声明所有可能排除的异常。这样可以从首部反映这个方法可能抛出哪类已检查异常。

    自己编写方法是,不必将可能抛出的异常都进行声明。需要记住下面四种情况应该抛出异常:

  • 调用一个抛出已检查异常的方法,例如FileInputStream

  • 程序运行过程中发现错误,并且利用throw语句抛出一个已检查异常

  • 程序出现错误,例如a[-1] = 0 会抛出一个ArrayIndexOutOfBoundsException

  • java虚拟机和运行时库出现内部异常

    如果出现前两中情况,必须告诉调用这个方法的程序员有可能抛出异常。因为任何一个抛出异常的方法都有可能是一个死亡陷阱,如果没有处理器捕获这个异常,当前执行的线程就会结束。

    对于那些可能被他人使用的java方法,应该根据异常规范,在方法首部声明这个方法可能抛出的异常。

class MyAnimation
{
    ...
    public Iamge loadImage(String s) throws IOException
    {
        ...
    }
}

抛出多个已检查异常,必须在首部列出所有的异常类,逗号隔开

class MyAnimation
{
    ...
    public Iamge loadImage(String s) throws EOFExceoption,MalformedURLException
    {
        ...
    }
}

    不需要声明java内部错误,即Error派生的错误。也不应该声明从RuntimeException派生的那些未检查异常。这些错误应该多花时间在修正程序中的错误上,而不是说明这些错误发生的可能性上。

    总之,一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控制(Error),要么应该避免发生(RuntimeException)。

    除了声明异常外,还可以捕获异常。这样会使异常不被抛到方法之外,也不需要throws规范。

    警告:如果子类中覆盖了超类的一个方法,子类声明的已检查异常不能超过超类方法中什么的异常范围。如果超类方法没有抛出任何已检查异常,子类也不应该抛出任何已检查异常。

11.1.3  如何抛出异常

    对于一个已存在的异常类:

  1. 找到一个合适的异常类

  2. 创建这个类的一个对象

  3. 将对象抛出

    一旦方法抛出了异常,这个方法就不可能返回到调用者。

    例如读一个文件首部,读到某个字符后结束了,认为不正常,希望抛出一个异常。

    首先决定抛出什么类型异常,归结为IOException是一种很好的选择,仔细阅读Java API会发现:EOFException是在输入过程中,遇到了一个未预期的EOF后的信号。

String readData(Scanner in) throws EOFException
{
    ...
    while(...)
    {
        if(!in.hasNext()) //EOFException
        {
            if(n<len)
                throws new EOFException;
        }
        ...
    }   
    return s;
}

    EOFException类还有一个字符串型参数的构造器。

String gripe = "Content-length:"+len+". Received: "+n;
throws new EOFException(gripe);

11.1.4 创建异常类

定义一个派生于Exception类,或其子类的类。习惯上,定义这样的类应该包含两个构造器,一个默认;另一个是带有描叙信息的构造器(超类Throwable的toString方法会打印这些详细信息,在调试中非常有用)。

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

 11.2 捕获异常

捕获异常用try/catch语句块。

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

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

  1. 程序将跳过try语句块的其余代码

  2. 程序将执行catch子句中的处理代码

    如果try中没有抛出任何异常,程序将跳过catch子句。如果抛出的是catch子句中没有声明的异常类型,这个方法就会立刻退出。

    如果调用了一个已检查异常的方法,就必须对它进行处理,或者将它传递出去。通常应该捕获那些知道如何处理,而将那些不知道怎样处理的异常传递出去(throws)。一个例外:如果编写一个覆盖超类的方法,而这个方法有没有抛出异常,那么这个方法就必须捕获方法代码中出现的每一个已检查异常。

11.2.1 捕获多个异常

try
{
    code that might throws exceptions
}
catch(MalformedURLException e1)
{
    emergency action for malformed URLs
}
catch(UnkownHostException e2)
{
    emergency action for unknown hosts
}
catch(IOexception e3)
{
    emergency action for all other I/O problems
}

    异常对象(e1,e2,e3)可能包含异常本身有关的信息。想要获得更多信息可用

e1.getMessage()

或得到异常对象的类型

e3.getClass().getName()

11.2.2 再次抛出异常与异常链

    在catch子句中可以抛出一个异常,这样子的目的是改变异常的类型。ServletException例子。执行servlet的代码可能不想知道发生错误的细节,但希望明确知道servlet是否故障。

try
{
    access the database
}
catch(SQLException e)
{
    throw new ServletException("database error: "+e.getMessage());
}

    在java SE 1.4中,有一种更好的处理方法

try
{
    access the database
}
catch(SQLException e)
{
    Throwable se = new ServletException("database error: ");
    se.initCause(e);//重新得到原始异常
    throw se;
}

    强烈建议这种包装技术,可以让用户抛出子系统中的高级异常,而不丢失原始异常细节。如果一个方法中发生类一个已检查异常,而不允许抛出,那包装技术十分有用。捕获这个已检查异常,并将其包装成一个运行时异常。

11.2.3 Finally子句

    如果方法获得一些本地资源,并且只有这个方法自己知道,又如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。finally子句解决这个问题。如果使用java编写数据库程序,就需要使用这个技术关闭与数据库的连接。不管异常是否被捕获,finally子句都会被执行。

Graphics g = image.getGraphics();
try
{
    //1
    code that might throw exception
    //2
}
catch(IOException e )
{
    //3
    show error dialog
    //4
}
finally
{
    //5
    g.dispose();
}

    无论怎样都会执行finally子句。finally子句中运用return语句会覆盖之前的return语句。








第十一章 异常,日志,断言和调试

标签:

原文地址:http://my.oschina.net/liuyang2077/blog/504639

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!