Java异常的面试问题及答案

本文由 ImportNew - 韩远青 翻译自 Journaldev。欢迎加入Java小组。转载请参见文章末尾的要求。

Java提供了一个健壮的、面向对象的方法来处理出现异常,称为Java异常处理。 我以前写过一篇长文章来介绍Java异常处理,今天我将列出一些重要的Java异常面试的问题及答案,希望对你们的面试有所帮助。

1.什么是Java异常

答:异常是发生在程序执行过程中阻碍程序正常执行的错误事件。比如:用户输入错误数据、硬件故障、网络阻塞等都会导致出现异常。 只要在Java语句执行中产生了异常,一个异常对象就会被创建,JRE就会试图寻找异常处理程序来处理异常。如果有合适的异常处理程序,异常对象就会被异常处理程序接管,否则,将引发运行环境异常,JRE终止程序执行。 Java异常处理框架只能处理运行时错误,编译错误不在其考虑范围之内。

2.Java异常处理中有哪些关键字?

答:

  • throw:有时我们需要显式地创建并抛出异常对象来终止程序的正常执行。throw关键字用来抛出并处理运行时异常。
  • throws:当我们抛出任何“被检查的异常(checked exception)”并不处理时,需要在方法签名中使用关键字throws来告知调用程序此方法可能会抛出的异常。调用方法可能会处理这些异常,或者同样用throws来将异常传给上一级调用方法。throws关键字后可接多个潜在异常,甚至是在main()中也可以使用throws。
  • try-catch:我们在代码中用try-catch块处理异常。当然,一个try块之后可以有多个catch子句,try-catch块也能嵌套。每个catch块必须接受一个(且仅有一个)代表异常类型的参数。
  • finally:finally块是可选的,并且只能配合try-catch一起使用。虽然异常终止了程序的执行,但是还有一些打开的资源没有被关闭,因此,我们能使用finally进行关闭。不管异常有没有出现,finally块总会被执行。

3.描述一下异常的层级。

答:Java异常是层级的,并通过继承来区分不同种类的异常。

  • Throwable是所有异常的父类,它有两个直接子对象Error,Exception,其中Exception又被继续划分为“被检查的异常(checked exception)”和”运行时的异常(runtime exception,即不受检查的异常)”。 Error表示编译时和系统错误,通常不能预期和恢复,比如硬件故障、JVM崩溃、内存不足等。
  • 被检查的异常(Checked exception)在程序中能预期,并要尝试修复,如FileNotFoundException。我们必须捕获此类异常,并为用户提供有用信息和合适日志来进行调试。Exception是所有被检查的异常的父类。
  • 运行时异常(Runtime Exception)又称为不受检查异常,源于糟糕的编程。比如我们检索数组元素之前必须确认数组的长度,否则就可能会抛出ArrayIndexOutOfBoundException运行时异常。RuntimeException是所有运行时异常的父类。

4.Java异常类有哪些的重要方法?

答:Exception和它的所有子类没有提供任何特殊方法供使用,它们的所有方法都是来自其基类Throwable。

  • String getMessage():方法返回Throwable的String型信息,当异常通过构造器创建后可用。
  • String getLocalizedMessage():此方法通过被重写来得到用本地语言表示的异常信息返回给调用程序。Throwable类通常只是用getMessage()方法来实现返回异常信息。
  • synchronized Throwable getCause():此方法返回异常产生的原因,如果不知道原因的话返回null。(原文有拼写错误 应该是if 不是id)
  • String toString():方法返回String格式的Throwable信息,此信息包括Throwable的名字和本地化信息。
  • void printStackTrace():该方法打印栈轨迹信息到标准错误流。该方法能接受PrintStream 和PrintWriter作为参数实现重载,这样就能实现打印栈轨迹到文件或流中。

5.描述Java 7 ARM(Automatic Resource Management,自动资源管理)特征和多个catch块的使用

答:如果一个try块中有多个异常要被捕获,catch块中的代码会变丑陋的同时还要用多余的代码来记录异常。有鉴于此,Java 7的一个新特征是:一个catch子句中可以捕获多个异常。示例代码如下:

catch(IOException | SQLException | Exception ex){
     logger.error(ex);
     throw new MyException(ex.getMessage());
}

大多数情况下,当忘记关闭资源或因资源耗尽出现运行时异常时,我们只是用finally子句来关闭资源。这些异常很难调试,我们需要深入到资源使用的每一步来确定是否已关闭。因此,Java 7用try-with-resources进行了改进:在try子句中能创建一个资源对象,当程序的执行完try-catch之后,运行环境自动关闭资源。下面是这方面改进的示例代码:

try (MyResource mr = new MyResource()) {
            System.out.println("MyResource created in try-with-resources");
        } catch (Exception e) {
            e.printStackTrace();
        }
想了解更多的Java ARM的资讯,请猛点这里
6.被检查的异常和不受检查的异常有什么区别?
答:

A.被检查的异常应该用try-catch块代码处理,或者在main方法中用throws关键字让JRE了解程序可能抛出哪些异常。不受检查的异常在程序中不要求被处理或用throws语句告知。

B.Exception是所有被检查异常的基类,然而,RuntimeException是所有不受检查异常的基类。

C.被检查的异常适用于那些不是因程序引起的错误情况,比如:读取文件时文件不存在引发的FileNotFoundException。然而,不被检查的异常通常都是由于糟糕的编程引起的,比如:在对象引用时没有确保对象非空而引起的NullPointerException

7.在Javathrowthrows关键字之间的区别?

答:throws用于在方法签名中声明此方法可能抛出的异常,而throw关键字则是中断程序的执行并移交异常对象到运行时进行处理。

8.Java中怎么写自定义的异常?

答:我们能继承Exception类或其任何子类来实现自己的自定义异常类。这自定义异常类可以有自己变量和方法来传递错误代码或其它异常相关信息来处理异常。

下面是一个简单的自定义异常示例:

package com.journaldev.exceptions;
 
import java.io.IOException;
 
public class MyException extends IOException {
 
    private static final long serialVersionUID = 4664456874499611218L;
 
    private String errorCode="Unknown_Exception";
 
    public MyException(String message, String errorCode){
        super(message);
        this.errorCode=errorCode;
    }
 
    public String getErrorCode(){
        return this.errorCode;
    }
 
}

9.Java中什么是内存不足错误?

答:在Java中,OutOfMemoryError java.lang.VirtualMachineError的一个子类,当堆内存耗尽时会被JVM抛出。我们能通过设置Java选项来提供更大的内存供应用使用来达到修复的目的。

$>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m

10.引发Exception in thread main的各种不同情形?

答:通常的一些主线程异常情形主要有以下几种:

  • Exception in thread main java.lang.UnsupportedClassVersionError:当编译和运行Java类的JDK版本不同的时出现这种异常。
  • Exception in thread main java.lang.NoClassDefFoundError:这种异常出现的原因有两种:第一种是提供类全名时附带有.class;第二种是指定类未找到。
  • Exception in thread main java.lang.NoSuchMethodError: main:当试图运行一个没main方法的类时会出现这种异常。
  • Exception in thread main java.lang.NoSuchMethodError: main:无论何时main方法任何异常,它打印异常到控制台。其第一部分是陈述main方法抛出的异常,第二部分打印异常类名,后接异常类信息。

想了解更多这方面的内容,请猛点这里。

11Javafinal,finally,finalize的区别?

答:finalfinallyJava中是关键字,而finalize则是一个方法。

final关键字使得类变量不可变,避免类被其它类继承或方法被重写。finallytry-catch块一起使用,即使是出现了异常,其子句总会被执行,通常,finally子句用来关闭相关资源。finally方法中的对象被销毁之前会被垃圾回收。

综上三者,只有finally用于异常处理。

12.main方法抛出异常时发生了什么?

答:当main方法抛出异常时,Java运行时间终止并在控制台打印异常信息和栈轨迹。

13.catch子句能为空吗?

答:可以有空的catch子句,但那是最糟糕的编程,因为那样的话,异常即使被捕获,我们也得不到任何的有用信息,对于调试来说会是个噩梦,因此,编程时永远不要有空的catch子句。Catch子句中至少要包含一个日志语句输出到控制台或保存到日志文件中。


14.提供一些Java异常处理的最佳实践。

答:有关Java异常处理的相关最佳实践如下:

  • 使用具体的异常方便调试
  • 程序中早点抛出异常
  • 捕获异常后先让调用者处理异常
  • 使用Java 7 ARM功能确保资源关闭或者用finally子句正确地关闭它们
  • 为了调试需要总是记录异常信息
  • 用多个catch子句实现更完全的关闭
  • 你自己的应用API中用自定义的异常来抛出单种类型异常
  • 遵循命名规定,以异常结束
  • 在Javadoc中用@throws来标注方法抛出的异常
  • 处理异常是有花销的,因此只有在必要时才抛出。否则,你会扑空或毫无收获。

15.下面的程序中有什么问题,该怎么去修改?

答:这里将针对一些跟异常相关的编程问题:

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

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子句情况选择上面的任一方法。

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

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块层级的任何位置。

C.下面的代码同样存在问题。

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

这段代码同样不能编译,因为JAXBException是个受检查的异常,而foo方法应该抛出此异常供调用方法捕获。你将会得到:Unreachable catch block for JAXBException这样的错误信息。这个异常不可能从try子句中抛出。为解决这个错误,只能将JAXBException从catch子句中移除。

也要注意到,NullPointerException的异常捕获是有效的,因为它是个不被检查的异常。

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

 

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中声明了不被检查的异常,程序中也不一定要处理这个异常。

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

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(){
 
}

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

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赋值给新异常对象这句来修正错误。

想了解更多关于Java7多个catch子句的细节,请猛点这里。

这就是所有异常面试的问题,希望你们能喜欢。同时,我也希望将来能在列表中添加更多的问题,以适应未来的需要。

本文转自:http://www.importnew.com/7383.html

你可能感兴趣的:(java基础)