[关闭]
@lzb1096101803 2016-02-15T11:18:02.000000Z 字数 7463 阅读 546

18. 异常

Java 面试


如果问异常是什么,要当场画出异常的继承结构
http://www.233.com/Java/zhuanye/20110219/154033310.html

  1. Java中的检查型异常和非检查型异常有什么区别?
    检查型异常和非检查型异常的主要区别在于其处理方式。
    检查型异常需要使用try,catch和finally关键字在编译期进行处理,否则会出现编译
    器会报错。对于非检查型异常则不需要这样做。
    Java中所有继承自java.lang.Exception类的异常都是检查型异常,所有继承自RuntimeException的异常都被称为非检查型异常。

  2. NullPointerException和ArrayIndexOutOfBoundException之间有什么相同之处?
    这两个异常都是非检查型异常,都继承自RuntimeException。
    该问题可能会引出另一个问题,即Java和C的数组有什么不同之处,因为C里面的数组是没有大小限制的,绝对不会抛出ArrayIndexOutOfBoundException。

  3. 在Java异常处理的过程中,你遵循的那些最好的实践是什么?
    1) 调用方法的时候返回布尔值来代替返回null,这样可以 避免“恶心的”NullPointerException。
    2) catch块里别不写代码。空catch块是异常处理里的错误事件,因为它只是捕获了异常,却没有任何处理或者提示。通常你起码要打印出异常信息/记录日志,当然你最好根据需求对异常信息进行处理。
    3)能抛受控异常(checked Exception)就尽量不抛受非控异常(checked Exception)。通过去掉重复的异常处理代码,可以提高代码的可读性。
    4) 绝对不要让你的数据库相关异常显示到客户端。由于绝大多数数据库和SQLException异常都是受控异常,在Java中,你应该在DAO层把异常信息处理,然后返回处理过的能让用户看懂并根据异常提示信息改正操作的异常信息
    5) 在Java中,一定要在数据库连接,数据库查询,流处理后,在finally块中调用close()方法。当然这是以前的,现在是用框架,不过框架内部也是这样做

    使用具体的异常方便调试
    程序中早点抛出异常
    捕获异常后先让调用者处理异常
    使用Java 7 ARM功能确保资源关闭或者用finally子句正确地关闭它们
    为了调试需要总是记录异常信息
    用多个catch子句实现更完全的关闭
    你自己的应用API中用自定义的异常来抛出单种类型异常
    遵循命名规定,以异常结束
    在Javadoc中用@throws来标注方法抛出的异常
    处理异常是有花销的,因此只有在必要时才抛出。否则,你会扑空或毫无收获。
    
  4. 既然我们可以用RuntimeException来处理错误,那么你认为为什么Java中还存在检查型异常?
    存在检查型异常是一个设计上的决定,
    绝大多数检查型异常位于java.io包内,这是合乎情理的,因为在你请求了
    不存在的系统资源的时候,一段强壮的程序必须能够优雅的处理这种情况。通过把IOException声明为检查型异常,Java确保了你能够优雅的对异常进行处理。另一个可能的理由是,可以使用catch或finally来确保数量受限的系统资源(比如文件描述符)在你使用后尽早得到释放。

  5. throw 和 throws这两个关键字在java中有什么不同?
    throws总是出现在一个函数头中,用来标明该成员函数可能抛出的各种异常, 你也可以申明未检查的异常,但这不是编译器强制的。
    如果方法抛出了异常那么调用这个方法的时候就需要将这个异常处理。另一个关键字 throw 是用来抛出任意异常的,按照语法你可以抛出任意 Throwable (i.e. Throwable 或任何Throwable的衍生类) , throw可以中断程序运行,因此可以用来代替return .

  6. 什么是“异常链”?
      “异常链”是Java中非常流行的异常处理概念,是指在进行一个异常处理时抛出了另外一个异常,由此产生了一个异常链条。该技术大多用于将“ 受检查异常” ( checked exception)封装成为“非受检查异常”(unchecked exception)或者RuntimeException。
    顺便说一下,如果因为因为异常你决定抛出一个新的异常,你一定要包含原有的异常,这样,处理程序才可以通过getCause()和initCause()方法来访问异常最终的根源。
    异常链过长会导致代码维护相对较难

  7. 你曾经自定义实现过异常吗?怎么写的?
    我们绝大多数都写过自定义或者业务异常,像AccountNotFoundException
    通过为每一个特定的情况创建一个特定的异常,你就为调用者更好的处理异常提供了更好的选择。相比通用异常(general exception),我更倾向更为精确的异常。大量的创建自定义异常会增加项目class的个数,因此,在自定义异常和通用异常之间维持一个平衡是成功的关键。

  8. JDK7中对异常处理做了什么改变?
    一是在一个catch块中可以出来多个异常,就像原来用多个catch块一样。
    另一个是自动化资源管理(ARM),也称为try-with-resource块。
    这2个特性都可以在处理异常时减少代码量,同时提高代码的可读性。对于这些特性了解,不仅帮助开发者写出更好的异常处理的代码,也让你在面试中显的更突出。

    catch (IOException|SQLException ex) {  
        logger.log(ex);  
        throw ex;  
    }  
    
    在java7中try后面可以跟上资源对象的声明,可以实现在finally中释放资源的功能,如下代码示例:
    
    //try-with-resource statement
    try (PrintWriter out2 = new PrintWriter(
                new BufferedWriter(
                new FileWriter("out.txt", true)))) {
        out2.println("the text");
    } catch (IOException e) {
        e.printStackTrace();
    }
    上面的代码实际等同于如下代码:
    
    //close() is in finally clause
    PrintWriter out = null;
    try {
        out = new PrintWriter(
            new BufferedWriter(
            new FileWriter("out.txt", true)));
        out.println("the text");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (out != null) {
            out.close();
        }
    }
    
  9. 你遇到过 OutOfMemoryError 错误嘛?你是怎么搞定的?
    这个面试题会在面试高级程序员的时候用,面试官想知道你是怎么处理这个危险的OutOfMemoryError错误的。必须承认的是,不管你做什么项目,你都会碰到这个问题。所以你要是说没遇到过,面试官肯定不会买账。要是你对这个问题不熟悉,甚至就是没碰到过,而你又有3、4年的Java经验了,那么准备好处理这个问题吧。在回答这个问题的同时,你也可以借机向面试秀一下你处理内存泄露、调优和调试方面的牛逼技能。我发现掌握这些技术的人都能给面试官留下深刻的印象。

  10. 如果执行finally代码块之前方法返回了结果,或者JVM退出了,finally块中的代码还会执行吗?
    这个问题也可以换个方式问:“如果在try或者finally的代码块中调用了System.exit(),结果会是怎样”。
    了解finally块是怎么执行的,即使是try里面已经使用了return返回结果的情况,对了解Java的异常处理都非常有价值。只有在try里面是有System.exit(0)来退出JVM的情况下finally块中的代码才不会执行
    可以从class文件入手。


  1. 下面的代码都有哪些错误:

    01     public static void start() throws IOException, RuntimeException{
    02      
    03        throw new RuntimeException(“Not able to Start”);
    04     }
    05      
    06     public static void main(String args[]) {
    07        try {
    08              start();
    09        } catch (Exception ex) {
    10                ex.printStackTrace();
    11        } catch (RuntimeException re) {
    12                re.printStackTrace();
    13        }
    14     }
    

      这段代码会在捕捉异常代码块的RuntimeException类型变量“re”里抛出编译异常错误。因为Exception是RuntimeException的超类,在start方法中所有的RuntimeException会被第一个捕捉异常块捕捉,这样就无法到达第二个捕捉块,这就是抛出“exception java.lang.RuntimeException has already been caught”的编译错误原因。
      

  2. 下面的Java代码都有哪些错误:

    01     public classSuperClass { 
    02         public void start() throws IOException{
    03             throw new IOException(“Not able to open file”);
    04         }
    05     }
    06      
    07     public class SubClass extendsSuperClass{ 
    08         public void start() throws Exception{
    09             throw new Exception(“Not able to start”);
    10         }
    11     }
    

      这段代码编译器将对子类覆盖start方法产生不满。因为每个Java中方法的覆盖是有规则的,一个覆盖的方法不能抛出的异常比原方法继承关系高。因为这里的start方法在超类中抛出了IOException,所有在子类中的start方法只能抛出要么是IOExcepition或是其子类,但不能是其超类,如Exception。
      
      

  3. 下面的Java异常代码有什么错误:

    01     public static void start(){
    02        System.out.println(“Java Exception interivew question Answers for Programmers”);
    03     }
    04      
    05     public static void main(String args[]) {
    06        try{
    07           start();
    08        }catch(IOException ioe){
    09           ioe.printStackTrace();
    10        }
    11     }
    

    上面的Java异常例子代码中,编译器将在处理IOException时报错,因为IOException是受检查异常,而start方法并没有抛出IOException,所以编译器将抛出“异常, java.io.IOException 不会在try语句体中抛出”,但是如果你将IOException改为Exception,编译器报错将消失,因为Exception可以用来捕捉所有运行时异常,这样就不需要声明抛出语句。

  4. 下面这段代码有什么问题呢?

    package com.journaldev.exceptions;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    
    public class TestException {
        public static void main(String[] args) {         
            try {             
                testExceptions();
            } catch (FileNotFoundException | IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void testExceptions() throws IOException,
                FileNotFoundException {
        }
    }
    

    上面这段代码将不能被编译,并且会得到:The exception FileNotFoundException is already caught by the alternative IOException这样的错误信息,这是因为FileNotFoundException是IOException的子类。有两种方法来解决此问题:第一种是用两个catch子句来处理这两个异常。代码如下:

    try {     
        testExceptions(); 
    }catch(FileNotFoundException e){
        e.printStackTrace(); 
    }catch (IOException  e) {
        e.printStackTrace(); 
    }
    

    另一种方法就是在catch子句中移除FileNotFoundException,只用IOException。如:

    try {
         testExceptions(); 
    }catch (IOException  e) {
         e.printStackTrace(); 
    }
    

    你可以根据自己的catch子句情况选择上面的任一方法。

  5. 下面这段代码又有什么问题呢?

    package com.journaldev.exceptions;   
    import java.io.FileNotFoundException; 
    import java.io.IOException;   
    import javax.xml.bind.JAXBException;   
    
    public class TestException1 {
           public static void main(String[] args) {
                 try {
                     go();
                 } catch (IOException e) {
                     e.printStackTrace();
                 } catch (FileNotFoundException e) {
                     e.printStackTrace();
                 } catch (JAXBException e) {
                     e.printStackTrace();
                 }
         }
    
           public static void go() throws IOException, JAXBException, FileNotFoundException{
            }
     }
    

    跟A代码一样,代码将不能编译,因为FileNotFoundException是IOException的子类,所以,FileNotFoundException的catch子句将被隐藏,同时,你会得到这样的:Unreachable catch block for FileNotFoundException.的错误信息。因为异常已被IOException的catch子句处理。你需要改变catch子句的顺序来修复程序。代码如下:

    try {
      go();
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    } catch (JAXBException e) {
    e.printStackTrace();
    }
    

    JAXBException跟FileNotFoundException和IOException不相关,它能放在catch块层级的任何位置。

  6. 下面的代码存在什么问题呢?

    package com.journaldev.exceptions;
    
    public class TestException3 {
           public static void main(String[] args) {
             try{
             bar();
             }catch(NullPointerException e){
                 e.printStackTrace();
             }catch(Exception e){
                 e.printStackTrace();
             }
                       foo();     
           }
    
           public static void bar(){
    
            }
    
              public static void foo() throws NullPointerException{
             } 
    }
    

    这代码是个幌子,根本没问题,能被正确编译。我们能捕获到一般异常或者是不被检查的异常,即使在throws语句中没被提及。
    同样,如果程序中的一个方法foo()在throws中声明了不被检查的异常,程序中也不一定要处理这个异常。

  7. 下面这段代码同样存在瑕疵。

        package com.journaldev.exceptions;
    
        import java.io.IOException;   
    
        public class TestException4 {
               public void start() throws IOException{
    
               }
    
                public void foo() throws NullPointerException{     } 
        }  
    
         class TestException5 extends TestException4{
                   public void start() throws Exception{     }
                   public void foo() throws RuntimeException{
                       } 
        }
    

    这段代码不能被编译,因为父类中start的方法签名与子类中的start方法签名不相同。为纠正这错误,我们可以修改子类的方法签名使之与超类相同,我们也可以像下面代码那样移除子类中throws关键字。

        @Override    
        public void start(){
    
        }
    
  8. 下面的代码存在什么问题呢?

    package com.journaldev.exceptions;
    import java.io.IOException;   
    import javax.xml.bind.JAXBException;   
    
    public class TestException6 {
           public static void main(String[] args) {
             try {
                 foo();
             } catch (IOException | JAXBException e) {
                 e = new Exception("");
                 e.printStackTrace();
             }catch(Exception e){
                 e = new Exception("");
                 e.printStackTrace();
             }
         }
    
           public static void foo() throws IOException, JAXBException{
                   }
     }
    

    这段代码同样不能编译,因为在多个catch子句中的异常对象是不可变的,我们不能改变其值。你会得到这样的:The parameter e of a multi-catch block cannot be assigned编译时错误信息。我们需要删掉将e赋值给新异常对象这句来修正错误。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注