美文网首页java基础回顾
Java 异常面试问题与解答

Java 异常面试问题与解答

作者: 淡定_蜗牛 | 来源:发表于2019-11-01 11:10 被阅读0次

    Java 提供了一种健壮且面向对象的方法来处理称为 Java异常处理的异常情况。

    1. Java中的异常是什么?

    异常是在程序执行期间可能发生的错误事件,它会破坏其正常流程。异常可能源于各种情况,例如用户输入的错误数据,硬件故障,网络连接故障等。

    每当执行 Java 语句时发生任何错误,都会创建一个异常对象,然后 JRE尝试查找异常处理程序来处理该异常。如果找到了合适的异常处理程序,则将异常对象传递到处理程序代码以处理异常,称为捕获异常。如果未找到处理程序,则应用程序将异常抛出给运行时环境,并且 JRE 终止程序。

    Java 异常处理框架仅用于处理运行时错误,异常处理框架不处理编译时错误。

    2.Java 中的异常处理关键字是什么?

    java 异常处理中使用了四个关键字。

    1. throw:有时我们明确地想要创建异常对象,然后将其抛出以停止程序的正常处理。throw 关键字用于向运行时抛出异常以进行处理。

    2. throws:当我们在方法中抛出任何已检查的异常并且不对其进行处理时,我们需要在方法签名时使用 throws 关键字,以使调用方程序知道该方法可能抛出的异常。调用方方法可以处理这些异常,也可以使用throws关键字将其传播到其调用方方法。我们可以在 throws 子句中提供多个异常,它也可以与 main()方法一起使用。

    3. try-catch:我们在代码中使用 try-catch 块进行异常处理。try 是块的开始,catch 是 try 块的末尾,用于处理异常。我们可以使用 try 捕获多个 catch 块,并且 try-catch 块也可以嵌套。catch 块需要一个应为 Exception 类型的参数。

    4. finally:finally 块是可选的,只能与 try-catch 块一起使用。由于异常会暂停执行过程,因此我们可能会打开一些不会关闭的资源,因此可以使用 finally 块。无论是否发生异常,finally 块都会始终执行。

    3.解释Java异常层次结构?

    Java 异常是分层的,继承用于对不同类型的异常进行分类。Throwable是 Java 异常层次结构的父类,它有两个子对象– ErrorException。异常进一步分为检查异常和运行时异常。

    Error是超出应用程序范围的特殊情况,无法预见并从中恢复,例如硬件故障,JVM 崩溃或内存不足错误。

    Checked Exception 是我们可以在程序中预期并尝试从程序中恢复的异常情况,例如 FileNotFoundException。我们应该捕获该异常,并向用户提供有用的消息,并正确记录下来以进行调试。Exception是所有 “检查的异常” 的父类。

    Runtime Exception是由错误的编程引起的,例如,尝试从 Array 中检索元素。在尝试检索元素之前,我们应该首先检查数组的长度,否则它可能ArrayIndexOutOfBoundException在运行时抛出。RuntimeException是所有运行时异常的父类。

    4.Java异常类的重要方法是什么?

    Exception及其所有子类均未提供任何特定方法,并且所有方法均在基类 Throwable 中定义。

    1. String getMessage() –此方法返回 Throwable 消息字符串,并且可以在通过其构造函数创建异常时提供该消息。
    2. String getLocalizedMessage() –提供此方法,以便子类可以重写它以向调用程序提供特定于语言环境的消息。此方法的 Throwable 类实现只需使用getMessage()方法即可返回异常消息。
    3. synchronized Throwable getCause() - 此方法返回异常原因或 null (原因未知)。
    4. String toString() –此方法以 String 格式返回有关 Throwable 的信息,返回的 String 包含 Throwable 类的名称和本地化消息。
    5. void printStackTrace() –此方法将堆栈跟踪信息打印到标准错误流,此方法已重载,我们可以传递 PrintStream 或 PrintWriter 作为参数,以将堆栈跟踪信息写入文件或流。

    5.解释 Java 7 ARM Feature和多捕获块?

    如果您在单个 try 块中捕获了很多异常,则您会注意到 catch 块代码看起来非常丑陋,并且主要由用于记录错误的冗余代码组成,请记住,Java 7 的功能之一就是多捕获块我们可以在单个 catch 块中捕获多个异常。具有此功能的 catch 块如下所示:

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

    在大多数情况下,我们使用 finally 块只是为了关闭资源,有时我们忘记关闭它们并在资源耗尽时获取运行时异常。这些异常很难调试,我们可能需要调查使用该类型资源的每个位置,以确保我们将其关闭。因此,java 7 的改进之一是 try-with-resources,我们可以在 try 语句本身中创建资源,并在 try-catch 块内使用它。当执行从 try-catch 块执行时,运行时环境会自动关闭这些资源。具有这种改进的 try-catch 块示例为:

    try (MyResource mr = new MyResource()) {
        System.out.println("MyResource created in try-with-resources");
    } catch (Exception e) {
        e.printStackTrace();
    }
    

    6.Java中的 Checked 和 Unchecked 异常有什么区别?

    1、检查异常应在代码中使用 try-catch 块进行处理,否则方法应使用 throws 关键字使调用者知道该方法可能抛出的检查异常。未经检查的异常不需要在程序中处理,也不需要在方法的 throws 子句中提及。

    2.、Exception是所有Checked 异常的超类,而RuntimeException是所有Unchecked 的异常的超类。请注意,RuntimeException 是 Exception 的子类。

    3、Checked 异常是需要在代码中处理的错误方案,否则您将获得编译时错误。例如,如果您使用 FileReader 读取文件,则可能会抛出该文件FileNotFoundException,我们必须将其在 try-catch 块中捕获,或再次将其抛出给调用方方法。Unchecked 异常通常是由不良的编程引起的,例如,在调用对象引用中的方法而不确保其不为 null 时,会引发 NullPointerException。例如,我可以编写一种方法来删除字符串中的所有元音。确保不传递空字符串对象是调用者的责任。我可能会更改处理这些情况的方法,但理想情况下,调用方应注意这一点。

    7.Java中 throw 和 throws 之间的区别是什么?

    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 中的 OutOfMemoryError?

    Java 中的 OutOfMemoryError 是 java.lang.VirtualMachineError 的子类,当 JVM 堆内存不足时,它会被 JVM 抛出。我们可以通过修改 java 选项提供更多内存来解决此错误。

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

    10.有哪些不同的情况导致 “主线程异常”?

    一些常见的主线程异常情况是:

    • main 线程中的 java.lang.UnsupportedClassVersionError 异常:当您的 Java 类是从另一个 JDK 版本编译的,而您试图从另一个 Java 版本运行它时,将发生此异常。
    • main 线程中的 java.lang.NoClassDefFoundError 异常:此异常有两种变体。第一个是您以. class 扩展名提供类全名的位置。第二种情况是找不到类时。
    • main 线程中的 java.lang.NoSuchMethodError 异常:当您尝试运行不具有 main 方法的类时,将发生此异常。
    • main 线程中的 java.lang.ArithmeticException 异常:每当从 main 方法抛出任何异常时,它都会打印控制台异常。第一部分说明从 main 方法抛出异常,第二部分打印异常类名称,然后在冒号后打印异常消息。

    11.Java中的 final,finally 和 finalize 有什么区别?

    final 和 finally 是 Java 中的关键字,而 finalize 是一种方法。

    • final 关键字可以与类变量一起使用,以使它们不能被重新分配; class 可以避免通过类进行扩展; final
      关键字可以与方法避免被子类覆盖;

    • finally 关键字可以与 try-catch 块一起使用,以提供将始终执行的语句即使出现某些异常,通常最终还是会用来关闭资源。

    • finalize()方法在对象被销毁之前由垃圾回收器执行,这是确保关闭所有全局资源的好方法。

    在这三个中,只有finally 与 Java 异常处理有关。

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

    当 main()方法引发异常时,Java Runtime 将终止程序并在系统控制台中打印异常消息和堆栈跟踪。

    13.我们可以有一个空的捕获块吗?

    我们可以有一个空的 catch 块,但这是最糟糕的编程示例。我们永远不应该有空的 catch 块,因为如果异常被该块捕获,我们将没有有关该异常的信息,调试它将是一场噩梦。至少应该有一条日志记录语句,以将异常详细信息记录在控制台或日志文件中。

    14.提供一些 Java 异常处理最佳实践吗?

    与 Java 异常处理有关的一些最佳实践是:

    • 捕获特定异常可以简化调试。
    • 在程序中尽早抛出异常(Fast-Fast)。
    • 在程序后期捕获异常,让调用者处理异常。
    • 使用 Java 7 ARM 功能来确保资源被关闭,或者使用 finally 块来正确地关闭它们。
    • 始终记录异常消息以进行调试。
    • 使用多捕获块让代码更加清洁。
    • 使用自定义异常可以从应用程序 API 中引发单一类型的异常。
    • 遵循命名约定,始终以 Exception 结尾。
    • 使用 javadoc 中的 @throws 记录由方法引发的异常。
    • 异常的代价很高,因此仅在有意义时才抛出异常。否则,您可以捕获它们并返回null或不响应。

    15.以下程序有什么问题,我们该如何解决?

    在这里,我们将研究与 Java 异常相关的一些编程问题。

    1). 下面的程序有什么问题?

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

    另一种方法是从多捕获块中删除 FileNotFoundException。

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

    您可以根据情况选择任何一种方法。

    2). 下面的程序有什么问题?

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

    该程序将编译错误,因为 FileNotFoundException 是 IOException 的子类,因此 FileNotFoundException 的 catch 块不可访问,并且您将收到错误消息 “ Unreachable catch block for FileNotFoundException. It is already handled by the catch block for IOException”。

    您需要修复 catch 块顺序才能解决此问题。

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

    请注意,JAXBException 与 IOException 或 FileNotFoundException 不相关,可以放置在以上 catch 块层次结构中的任何位置。

    3). 下面的程序有什么问题?

    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()方法应抛出此异常以捕获调用方法。您将收到错误消息 “ JAXBException 无法访问的捕获块。不会从 try 语句主体中引发此异常。

    要解决此问题,您将必须删除 JAXBException 的 catch 块。

    注意,捕获 NullPointerException 是有效的,因为它是未经检查的异常。

    4). 下面的程序有什么问题?

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

    这是一个技巧性的问题,代码没有问题,它将成功编译。我们总是可以捕获 Exception 或任何未经检查的异常,即使它不在方法的 throws 子句中也是如此。

    同样,如果方法(foo)在 throws 子句中声明未经检查的异常,则在程序中处理该异常不是强制性的。

    5). 下面的程序有什么问题?

    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()方法签名不同。要解决此问题,我们可以将子类中的方法特性更改为与超类完全相同,也可以从子类方法中删除throws子句,如下所示。

    @Override
    public void start(){
    }
    

    6). 下面的程序有什么问题?

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

    上面的程序无法编译,因为多捕获块中的异常对象是最终对象,我们无法更改其值。由于“无法分配多捕获块的参数e”,将导致编译时错误。

    我们必须删除对新异常对象的“ e”分配以解决此错误。


    “不积跬步,无以至千里”,希望未来的你能:有梦为马 随处可栖!加油,少年!

    关注公众号:「Java 知己」,每天更新Java知识哦,期待你的到来!

    • 发送「Group」,与 10 万程序员一起进步。
    • 发送「面试」,领取BATJ面试资料、面试视频攻略。
    • 发送「玩转算法」,领取《玩转算法》系列视频教程。
    • 千万不要发送「1024」...
      每日福利

    相关文章

      网友评论

        本文标题:Java 异常面试问题与解答

        本文链接:https://www.haomeiwen.com/subject/zorfbctx.html