`
wsmajunfeng
  • 浏览: 496913 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java的异常限制

阅读更多
    在Java中如果某个方法可能会抛出检查型异常(比如打开一个文件),那么Java编译器会强制在定义该方法的时候必须声明抛出该异常或者该异常的父类异常,否则不能通过编译,这叫做“异常说明”,其形式如void f() throws TooBig,TooSmall{…}。这是种优雅的做法,它使得调用此方法者能确切知道写什么样的代码可以捕获所有潜在的异常。异常限制主要是指针对在继承中当发生覆盖方法的时候,Java编译器要求子类里的覆盖方法中的定义要么没有异常说明,要么有的话则异常说明里的异常必须包含在基类被覆盖方法的异常说明里列出来的那些异常之中。请看下面的代码:
class Exception1 extends Exception{}
class Exception2 extends Exception{}
class Exception3 extends Exception{}
class A{
void f() throws Exception1, Exception2{}
}
public class B extends A{
//1: void f(){System.out.println(“throw no Exception”);}
//2: void f() throws Exception1{System.out.println(“throw Exception1”);}
//3: void f() throws Exception2{System.out.println(“throw Exception2”);}
//4: void f() throws Exception1, Exception2{
//        System.out.println(“throw Exception1”);
//  }
//!5: void f() throws Exception3{System.out.println(“throw Exception3”);}
}

    这段代码中自定义了三个异常Exception1、Exception2和Exception3,类B继承了类A,A中的方法f()声明抛出两个异常Exception1和Exception2。在B中标记了覆盖方法f()的五种书写形式(它们都被注释掉了)。其中标记1、2、3、4所标记的方法f()的书写都是正确的,它们的定义中要么没有异常说明(标记1),要么异常说明里的异常包含在基类方法的异常说明里列出的那些异常之中(标记2、3、4),但是标记5所标记的方法f()是不正确的,不能通过编译器的编译,因为它声明抛出了一个基类方法中没有声明的异常Exception3。
     当然,这只是异常限制很简单的一面,稍后我们将看到更多的异常限制。
1、异常限制的原因
     一个方法的定义中如果有异常说明,那么在调用该方法时需要捕获此方法异常说明中的异常(或父类异常)做相应处理。在Java中的异常捕获处理是通过try-catch语句来实现的,如上文类A中的方法f()处理方式如下:
try{
 f();
}catch(Exception1 exception1){//…
}catch(Exception2 exception2){//…
}

    所以如果我们编写的代码只是同基类A打交道,去调用A中的f(),那么我们就可以将调用f()的语句放入try块中,并随后跟随两个catch语句分别捕捉Exception1和Exception2,而且我们也只需要两个catch语句(因为A中的f()声明只抛出两个异常)。其形式如下:

public class C{
    void g(A a){
try{
           a.f();
}catch(Exception1 exception1){//…
}catch(Exception2 exception2){//…
}
}
}

    类C中的方法g接受一个类A对象的引用作为参数,并调用类A方法f(),我们只需要有两个catch语句去分别捕捉Exception1和Exception2即可。
    现在我们考虑向类C方法g传递的参数不是类A对象的引用而是A的子类对象的引用(这时发生了向上转型),比如是类B对象的引用,那么由于Java的多态性a.f()实际调用的是类B中的方法f()。下面我们来真正的思考为什么Java要对覆盖方法做异常限制:如果说类B中的覆盖方法f()声明抛出了一个基类A中的被覆盖方法f()没有声明抛出的异常Exception3,那么当向类C方法g传递类B对象的引用,然后执行上述代码时,a.f()就可能会抛出异常Exception3,但是随后的异常处理代码(catch语句)中并不能捕获处理异常Exception3,于是程序就失灵了。因此,子类B中的覆盖方法f()绝不能抛出基类A中的被覆盖方法f()没有声明抛出的异常。子类B中的覆盖方法f()要么没有声明抛出异常,要么有的话则声明抛出的异常必须包含在基类被覆盖方法的异常说明里列出的那些异常之中。当然,考虑把上述这种情况换到类实现接口上也是一致的。
    综上所述,Java中的异常限制的基础在于继承和Java中方法的后期绑定(正是由于Java中方法的后期绑定带来了多态性),其根本原因在于为了防止当程序中发生向上转型时可能带来的异常处理错误从而导致的程序的失灵和崩溃。
2、理解异常限制的重要性
    强大的异常处理机制是Java 的一大优势,正确、合理地使用Java 异常处理能够使程序更健壮。异常限制是Java的异常处理机制中一个重要的知识点,而Java的异常限制又是基于继承和多态性的,领悟Java异常限制的内在原因有助于我们更好的理解继承和多态。
    继承是面向对象语言的基本特征之一,用面向对象语言Java编写的稍复杂的程序都会用到继承,而一个可靠健壮的程序也少不了异常处理代码,因此在我们使用Java过程中必然会遇到异常限制的问题,理解异常限制并能熟练运用异常限制对于我们编写正确的Java程序以及提高编程的效率都是十分有利的。
3、异常限制的详细阐述
    分析了异常限制的原因后我们通过一个例子来阐述异常限制的详细内容(代码中被注释掉的都是不能通过编译的):
class A extends Exception{}
class A1 extends A{}
class A2 extends A{}
abstract class B{
     public void B() throws A{}
     public void f() throws A{}
     public void b1() throws A1,A2{}
     public void b2(){}
}
class C extends Exception{}
class A11 extends A1{}
interface D{
public void f() throws C;
}
class BD extends B implements D{
     public BD() throws C,A{}                     //1
     //! public void b2() throws A11{}                //2
     //! public void f() throws C{}                   //3
     public void f(){}                             //4
     public void b1() throws A11{}                  //5
}

    类BD继承了类B,并实现了接口D,因为在子类的构造器中基类的构造器必须首先被调用,所以如果基类的构造器中有异常说明,那么子类构造器也必须有异常说明,并且子类构造器的异常说明里的异常必须包含基类构造器的异常说明里的异常。在上述代码中基类B的构造器声明抛出异常A,因此子类BD的构造器中也必须声明抛出异常A。请看标记1处BD的构造器中的异常说明不仅有A,还有一个基类B的构造器中没有声明的异常C,这样做可以吗?思考一下异常限制的定义,因为异常限制是针对覆盖方法的,而子类中的构造器并没有覆盖基类的构造器,所以这样做显然是可以的,即子类构造器可以声明抛出基类构造器的异常说明里没有的异常。
    类BD中的方法b2()不能通过编译(标记2处),思考一下异常限制的原因,这是因为:类BD中的b2()覆盖了基类B中的b2(),基类B中的b2()没有声明抛出任何异常,而BD的b2()却声明抛出异常A11。如果编译器允许这么做的话,那么当我们在调用B中的b2()的时候不需要做任何异常处理(因为它并没有声明抛出任何异常),而当发生方法的后期绑定把它替换成B的子类BD的对象时,这个方法却有可能会抛出异常A11,但实际上我们并没有做任何异常处理,所以这个时候程序就可能会出现问题了。因此,如果基类的原方法没有异常说明那么子类中的覆盖方法也不能有异常说明。
    类BD中的方法f()很特殊,它的来源有两个,在基类B和接口D中都有方法f()。基类B中的f()声明抛出了异常C,而接口D中的f()没有声明抛出异常,那么子类BD中的f()的异常说明应该是怎样的呢?从代码中我们可以看到声明其抛出异常C是不能通过编译的(标记3处),而没有异常说明是正确的(标记4处)。我们来分析一下原因:因为类BD中f()的来源有两个,它既覆盖了基类B的f()又实现了接口D中的f(),所以它既要遵守基类B的f()带来的异常限制(不能声明抛出异常C以外的异常),又要遵守接口D中的f()带来的异常限制(不能声明抛出异常)。因此综合起来,它能声明抛出的异常就是基类B的f()声明的异常与接口D中的f()声明的异常的“交集”,在这里是个空集,即BD中的f()不能声明抛出异常。于是,标记4处的代码能通过编译,而标记3处的不能。
    最后我们来看类BD中的覆盖方法b1(),基类B中的被覆盖方法b1()声明抛出了异常A1和A2,而BD中的b1()抛出了异常A1的子类异常A11(标记5处),这是允许的,因为如果我们可以捕捉异常A1,那么我们自然也就可以捕捉到它的子类异常A11。因此,子类中的覆盖方法可以声明抛出基类中被覆盖方法声明抛出的异常的子类异常。
4、总结
    根据以上对异常限制的详细阐述,下面我们对异常限制的具体内容做一总结:
   (1)如果基类构造器有异常说明,那么子类构造器也必须声明抛出基类构造器中声明的那些异常(或这些异常的父类异常),另外,子类构造器的异常说明中也可以有基类构造器的异常说明中没有的异常。
   (2)如果基类的被覆盖方法没有异常说明,那么子类里的覆盖方法也不能有异常说明;如果基类的被覆盖方法有异常说明,那么子类里的覆盖方法中的定义要么没有异常说明,要么有的话则异常说明里的异常必须包含在基类被覆盖方法的异常说明里列出的那些异常之中(或是这些异常的子类异常)。
   (3)如果子类不仅继承了一个基类还实现了一个或多个接口,而且该覆盖方法在两个或两个以上的接口(基类)中存在,那么子类中的覆盖方法声明抛出的异常应为存在该方法的那些接口(基类)中的该方法声明抛出的异常的交集。
    总之,如果方法被覆盖,要求被覆盖的方法一定不能声明抛出新的异常或比原方法范畴更广的异常。
1
0
分享到:
评论

相关推荐

    java 文件名大小限制异常类java 文件名大小限制异常类java 文件名大小限制异常类

    java 文件名大小限制异常类java 文件名大小限制异常类java 文件名大小限制异常类java 文件名大小限制异常类java 文件名大小限制异常类java 文件名大小限制异常类java 文件名大小限制异常类java 文件名大小限制异常类...

    java异常分析及解决办法

    Java异常处理是编程中至关重要的一个环节,它帮助开发者识别并修复程序运行时出现的问题。在Java中,异常是程序执行期间发生的不正常情况,通常会导致程序中断。下面将详细解释给定文件中提到的一些常见Java异常,并...

    JAVA异常PPT

    主要讲述JAVA中的异常,自己定义异常类,及其使用方法。

    java 文件名称超长限制异常类 java 文件名称超长限制异常类

    java 文件名称超长限制异常类 java 文件名称超长限制异常类java 文件名称超长限制异常类 java 文件名称超长限制异常类java 文件名称超长限制异常类 java 文件名称超长限制异常类java 文件名称超长限制异常类 java ...

    Java\课件\JAVA的异常处理机制.ppt

    以下是关于Java异常处理机制的详细说明: 1. **什么是异常处理机制**: Java的异常处理机制是一种用于处理程序运行时错误的机制,它将原本需要程序员手动检查和处理的错误情况,转变为由系统自动检测和报告。当...

    java异常ppt

    Java异常处理是编程中至关重要的一个环节,它用于处理运行时出现的问题,使得程序更加健壮和稳定。异常(Exception)是程序运行时发生的错误,它们可以由用户输入错误、设备故障、物理限制或者代码本身的错误引发。...

    保证Java精确异常的指令调度技术

    为了解决上述问题,本文提出了一种新的算法,旨在打破Java精确异常的要求对指令调度的限制,同时又能保证异常发生时精确异常的要求不被破坏。该算法的核心思想是在确保满足精确异常要求的前提下,尽可能地提高指令级...

    Java中常见异常类型及分析.pdf

    ### Java中常见异常类型及分析 #### 一、概述 在Java编程中,异常处理是一项重要的技术,它有助于开发者在程序运行过程中及时发现并处理错误,确保程序的稳定性和健壮性。Java语言中提供了丰富的异常处理机制,...

    Java常见异常大全.pdf

    这些异常通常属于JVM层面的异常,或者是由程序逻辑错误和资源限制等因素引起的。 了解和掌握这些异常,对于编写健壮的Java程序至关重要。开发人员需要根据异常的类型和上下文信息来合理处理这些异常,或者通过抛出...

    Java异常大全

    ### Java异常大全 在Java编程过程中,异常处理是不可或缺的一部分,它可以帮助开发者更好地管理程序运行时可能出现的问题。本文将对常见的Java异常进行详细介绍,并提供一些基本的处理策略。 #### 1. `java.lang....

    JAVA中常见的异常

    以上列举了几种常见的Java异常类型及其处理策略。理解这些异常的触发原因以及如何避免它们对于编写健壮可靠的Java程序至关重要。除了上述异常之外,Java还提供了许多其他类型的异常来处理不同的错误情况,开发者应该...

    Java常见异常类型及原因分析(下).pdf

    在文档《Java常见异常类型及原因分析(下)》中,介绍了几种常见的Java异常类型及其产生原因,并提供了相应的分析。 首先,ArrayIndexOutOfBoundsException异常是在进行数组操作时最常见的异常之一。这个异常发生在...

    java 动态换ip限制 ip频繁限制

    在这种背景下,Java开发者可能需要处理“动态换IP限制 IP频繁限制”的问题。本文将深入探讨如何在Java环境中应对这类限制,以确保合法的爬虫或者服务请求能够正常进行。 首先,我们需要理解IP限制的工作原理。通常...

    java异常汇总.txt

    ### Java异常概述与详解 在Java编程中,异常处理是一种重要的机制,用于处理程序执行过程中可能出现的错误或异常情况。Java中的异常分为两大类:**受检异常**(Checked Exceptions)和**非受检异常**(Unchecked ...

    Java异常应用指导.docx

    Java异常处理是编程中至关重要的一个环节,它确保了程序在遇到错误时能够优雅地处理问题,而不是突然崩溃。本文将深入探讨Java异常的应用和理解。 首先,异常机制是Java程序设计中的一个核心特性,它提供了一种处理...

    JAVA实验十一 异常处理与集合类

    【异常处理】是Java编程中一个重要的概念,用于处理程序运行时可能出现的错误情况。异常是程序执行过程中发生的不正常事件,可能导致程序中断。在Java中,异常通过`try-catch`块进行捕获和处理,可以避免程序因错误...

    Java异常处理的设计原则.pdf

    Java异常处理是编程中至关重要的一个方面,它用于处理程序运行时出现的错误和异常状况。以下是基于给定内容总结的Java异常处理的设计原则: 1. **早处理**:异常不应该用于控制正常程序流程,而应该仅用于处理非...

    Java语言程序设计:第五章 异常.ppt

    在Java程序执行过程中,可能会出现各种错误,如文件错误、设备错误、物理限制、代码错误等。在出现错误时,我们期望在出现错误的时候,程序能返回一种安全状态,以适当的形式终止程序。异常处理的任务就是将控制权从...

    JAVA常见异常解析

    在Java编程中,异常处理是一项重要的技能,它帮助开发者捕获并处理程序运行时可能出现的问题。以下是对Java中一些常见异常的详细解析: 1. **java.lang.NullPointerException**: 当尝试访问或操作一个null引用的...

    解决低版本java解密加密的【key长度异常】

    然而,有时在使用低版本的Java运行环境时,可能会遇到“key长度异常”的问题,这通常与Java加密的默认限制有关。本文将深入探讨这个问题,并提供解决方案。 Java的安全策略在早期版本中对加密密钥的长度进行了严格...

Global site tag (gtag.js) - Google Analytics