深入了解 Java 中的异常处理 + 面试题

# 深入了解 Java 中的异常处理 + 面试题

在程序开发中,异常处理也是我们经常使用到的模块,只是平常很少去深究异常模块的一些知识点。比如,try-catch 处理要遵循的原则是什么,finally 为什么总是能执行,try-catch 为什么比较消耗程序的执行性能等问题,我们本讲内容都会给出相应的答案,当然还有面试中经常被问到的异常模块的一些面试题,也是我们本篇要讲解的重点内容。

异常处理基础介绍

先来看看异常处理的语法格式

try{ ... } catch(Exception e){ ... } finally{ ... }

其中,

  • try:是用来监测可能会出现异常的代码段。
  • catch:是用来捕获 try 代码块中某些代码引发的异常,如果 try 里面没有异常发生,那么 catch 也一定不会执行。在 Java 语言中,try 后面可以有多个 catch 代码块,用来捕获不同类型的异常,需要注意的是前面的 catch 捕获异常类型一定不能包含后面的异常类型,这样的话,编译器会报错。
  • finally:不论 try-catch 如何执行,finally 一定是最后执行的代码块,所有通常用来处理一些资源的释放,比如关闭数据库连接、关闭打开的系统资源等。

异常处理的基本使用,具体可以参考下面的代码段:

try {
    int i = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println(e);
} finally {
    System.out.println("finally");
}

多 catch 的使用,具体可以参考下面的代码段:

try {
    int i = Integer.parseInt(null);
} catch (ArithmeticException ae) {
    System.out.println("ArithmeticException");
} catch (NullPointerException ne) {
    System.out.println("NullPointerException");
} catch (Exception e) {
    System.out.println("Exception");
}

需要注意的是 Java 虚拟机会从上往下匹配错误类型,因此前面的 catch 异常类型不能包含后面的异常类型。比如上面的代码如果把 Exception 放在最前面编译器就会报错,具体可以参考下面的图片。

异常处理的发展

随着 Java 语言的发展,JDK 7 的时候引入了一些更加便利的特性,用来更方便的处理异常信息,如 try-with-resources 和 multiple catch,具体可以参考下面的代码段:

try (FileReader fileReader = new FileReader("");
     FileWriter fileWriter = new FileWriter("")) { // try-with-resources
    System.out.println("try");
} catch (IOException | NullPointerException e) { // multiple catch
    System.out.println(e);
}

异常处理的基本原则

先来看下面这段代码,有没有发现一些问题?

try {
  // ...
  int i = Integer.parseInt(null);
} catch (Exception e) {
}

以上的这段代码,看似“正常”,却违背了异常处理的两个基本原则:

  • 第一,尽量不要捕获通用异常,也就是像 Exception 这样的异常,而是应该捕获特定异常,这样更有助于你发现问题;
  • 第二,不要忽略异常,像上面的这段代码只是加了 catch,但没有进行如何的错误处理,信息就已经输出了,这样在程序出现问题的时候,根本找不到问题出现的原因,因此要切记不要直接忽略异常。

异常处理对程序性能的影响

异常处理固然好用,但一定不要滥用,比如下面的代码片段:

// 使用 com.alibaba.fastjson
JSONArray array = new JSONArray();
String jsonStr = "{'name':'laowang'}";
try {
    array = JSONArray.parseArray(jsonStr);
} catch (Exception e) {
    array.add(JSONObject.parse(jsonStr));
}
System.out.println(array.size());

这段代码是借助了 try-catch 去处理程序的业务逻辑,通常是不可取的,原因包括下列两个方面。

  • try-catch 代码段会产生额外的性能开销,或者换个角度说,它往往会影响 JVM 对代码进行优化,因此建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;与此同时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句(if/else、switch)要低效。
  • Java 每实例化一个 Exception,都会对当时的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽略了。

以上使用 try-catch 处理业务的代码,可以修改为下列代码:

// 使用 com.alibaba.fastjson
JSONArray array = new JSONArray();
String jsonStr = "{'name':'laowang'}";
if (null != jsonStr && !jsonStr.equals("")) {
    String firstChar = jsonStr.substring(0, 1);
    if (firstChar.equals("{")) {
        array.add(JSONObject.parse(jsonStr));
    } else if (firstChar.equals("[")) {
        array = JSONArray.parseArray(jsonStr);
    }
}
System.out.println(array.size());

相关面试题

1. try 可以单独使用吗?

答:try 不能单独使用,否则就失去了 try 的意义和价值。

2. 以下 try-catch 可以正常运行吗?

try {
    int i = 10 / 0;
} catch {
    System.out.println("last");
}

答:不能正常运行,catch 后必须包含异常信息,如 catch (Exception e)。

3. 以下 try-finally 可以正常运行吗?

try {
    int i = 10 / 0;
} finally {
    System.out.println("last");
}

答:可以正常运行。

4. 以下代码 catch 里也发生了异常,程序会怎么执行?

try {
    int i = 10 / 0;
    System.out.println("try");
} catch (Exception e) {
    int j = 2 / 0;
    System.out.println("catch");
} finally {
    System.out.println("finally");
}
System.out.println("main");

答:程序会打印出 finally 之后抛出异常并终止运行。

5. 以下代码 finally 里也发生了异常,程序会怎么运行?

try {
    System.out.println("try");
} catch (Exception e) {
    System.out.println("catch");
} finally {
    int k = 3 / 0;
    System.out.println("finally");
}
System.out.println("main");

答:程序在输出 try 之后抛出异常并终止运行,不会再执行 finally 异常之后的代码。

6. 常见的运行时异常都有哪些?

答:常见的运行时异常如下:

  • java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象;
  • java.lang.ClassNotFoundException 指定的类找不到;出现原因:类的名称和路径加载错误,通常是程序

试图通过字符串来加载某个类时引发的异常;

  • java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符;
  • java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生;
  • java.lang.ClassCastException 数据类型转换异常;
  • java.lang.NoClassDefFoundException 未找到类定义错误;
  • java.lang.NoSuchMethodException 方法不存在异常;
  • java.lang.IllegalArgumentException 方法传递参数错误。

7. Exception 和 Error 有什么区别?

答:Exception 和 Error 都属于 Throwable 的子类,在 Java 中只有 Throwable 及其之类才能被捕获或抛出,它们的区别如下:

  • Exception(异常)是程序正常运行中,可以预期的意外情况,并且可以使用 try/catch 进行捕获处理的。Exception 又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception),运行时异常编译能通过,但如果运行过程中出现这类未处理的异常,程序会终止运行;而受检查的异常,要么用 try/catch 捕获,要么用 throws 字句声明抛出,否则编译不会通过。
  • Error(错误)是指突发的非正常情况,通常是不可以恢复的,比如 Java 虚拟机内存溢出,诸如此类的问题叫做 Error。

8. throw 和 throws 的区别是什么?

答:它们的区别如下:

  • throw 语句用在方法体内,表示抛出异常由方法体内的语句处理,执行 throw 一定是抛出了某种异常;
  • throws 语句用在方法声明的后面,该方法的调用者要对异常进行处理,throws 代表可能会出现某种异常,并不一定会发生这种异常。

9. Integer.parseInt(null) 和 Double.parseDouble(null) 抛出的异常一样吗?为什么?

答:Integer.parseInt(null) 和 Double.parseDouble(null) 抛出的异常类型不一样,如下所示:

  • Integer.parseInt(null) 抛出的异常是 NumberFormatException;
  • Double.parseDouble(null) 抛出的异常是 NullPointerException。

至于为什么会产生不同的异常,其实没有特殊的原因,主要是由于这两个功能是不同人开发的,因而就产生了两种不同的异常信息。

10. NoClassDefFoundError 和 ClassNoFoundException 有什么区别?

  • NoClassDefFoundError 是 Error(错误)类型,而 ClassNoFoundExcept 是 Exception(异常)类型;
  • ClassNoFoundExcept 是 Java 使用 Class.forName 方法动态加载类,没有加载到,就会抛出 ClassNoFoundExcept 异常;
  • NoClassDefFoundError 是 Java 虚拟机或者 ClassLoader 尝试加载类的时候却找不到类订阅导致的,也就是说要查找的类在编译的时候是存在的,运行的时候却找不到,这个时候就会出现 NoClassDefFoundError 的错误。

11. 使用 try-catch 为什么比较耗费性能?

答:这个问题要从 JVM(Java 虚拟机)层面找答案了。首先 Java 虚拟机在构造异常实例的时候需要生成该异常的栈轨迹,这个操作会逐一访问当前线程的栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常等信息,这就是使用异常捕获耗时的主要原因了。

12. 常见的 OOM 原因有哪些?

答:常见的 OOM 原因有以下几个:

  • 数据库资源没有关闭;
  • 加载特别大的图片;
  • 递归次数过多,并一直操作未释放的变量。

13. 以下程序的返回结果是?

public static int getNumber() {
    try {
        int number = 0 / 1;
        return 2;
    } finally {
        return 3;
    }
}

A:0

B:2

C:3

D:1

答:3

题目解析:程序最后一定会执行 finally 里的代码,会把之前的结果覆盖为 3。

14. finally、finalize 的区别是什么?

答:finally、finalize 的区别如下:

  • finally 是异常处理语句的一部分,表示总是执行;
  • finalize 是 Object 类的一个方法,子类可以覆盖该方法以实现资源清理工作,垃圾回收之前会调用此方法。

15. 为什么 finally 总能被执行?

答:finally 总会被执行,都是编译器的作用,因为编译器在编译 Java 代码时,会复制 finally 代码块的内容,然后分别放在 try-catch 代码块所有的正常执行路径及异常执行路径的出口中,这样 finally 才会不管发生什么情况都会执行。



欢迎关注我的公众号,回复关键字“Java” ,将会有大礼相送!!! 祝各位面试成功!!!

原文地址:https://www.cnblogs.com/dailyprogrammer/p/12267585.html

时间: 2024-08-29 07:30:29

深入了解 Java 中的异常处理 + 面试题的相关文章

【转】Java中关于异常处理的十个最佳实践

原文地址:http://www.searchsoa.com.cn/showcontent_71960.htm 导读:异常处理是书写强健Java应用的一个重要部分,Java许你创建新的异常,并通过使用 throw 和 throws关键字抛出它们. 异常处理是书写强健Java应用的一个重要部分,它是关乎每个应用的一个非功能性需求,是为了优雅的处理任何错误状况,比如资源不可访问,非法输入,空输入等等.Java提供了几个异常处理特性,以try,catch和 finally 关键字的形式内建于语言自身之中

Java中的异常处理机制

Java中的异常处理机制 示例 public class test { public static void main(String[] args) { // TODO Auto-generated method stub String s="hello"; int i=Integer.parseInt(s); } } 运行异常结果 在上述代码中Integer.parseInt表示把字符串类型转化成整数类型,同时Integer是Int的封装类:程序会报错,因为计算机没有办法把hello

Java中的异常处理机制的简单原理和应用。

异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间.地点.人物.情节等信息,可以用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息. Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Erro

牛刀小试 - 趣谈Java中的异常处理

概述 顾名思义,通俗来讲异常就是指,那些发生在我们原本考虑和设定的计划之外的意外情况. 生活中总是会存在各种突发情况,如果没有做好准备,就让人措手不及. 你和朋友约好了明天一起去登山,半道上忽然乌云蔽日,下起了磅礴大雨.这就是所谓的异常情况. 你一下子傻眼了,然后看见朋友淡定的从背包里掏出一件雨衣穿上,淫笑着看着你.这就是对异常的处理. 对于一个OO程序猿来讲,所做的工作就是:将需要处理的现实生活中的复杂问题,抽象出来编写成为程序. 既然现实生活中总是存在着各种突然的异常情况,那么对应其抽象出的

Java中实现异常处理的基础知识

异常 (Exception):发生于程序执行期间,表明出现了一个非法的运行状况.许多JDK中的方法在检测到非法情况时,都会抛出一个异常对象. 例如:数组越界和被0除. 源代码示例: package yanzheng; //关于异常  2015.11.11 import javax.swing.*; public class AboutException { public static void main(String[] args) { int i=1, j=0, k; //   k=i/j; 

java中的异常处理原理浅谈

什么是异常呢: 在现实生活中,总会有一些问题,聚焦在生活上,我们会有工作问题,比如工作不满意,工作薪水低,工作没有发展前景,那么我们会成为这是一个“问题”. 在java的世界中,那么我们相对应的是程序,那么程序也有这样或者那样的问题,那么我们成为“异常”.异常就是程序在运行时出现的不正常现象. java把异常封装成对象的描述,来高度的概括. java中Error我们无能为力.我们还是聊聊Exception Exception的分类: 1.编译时异常 必须进行捕获 2.运行时异常(也就是Runti

一些JAVA中有趣的面试题

这几天的JAVA培训课上,不断的唤醒自己对JAVA的理解(时间太长,大学学的快忘干净了),其中有一些很有趣的面试题,都是在抠JAVA细节(唉,我是那种见坑就往下跳的人,一做就错),下面就来分享一下: 1)用JAVA程序写出当你在超市购物花费1.1元时,你给收银员2元钱,收银员找你0.9元的过程: 当时一看题目,好简单啊!迅速写出: public class Sell{ public static void main(String[] args){ double total = 2.0; doub

Java中的异常处理从概念到实例

1.概念 采用新的异常处理机制 在以往的程序开发过程中,经常采用返回值进行处理.例如,在编写一个方法,可以返回一个状态代码,调用者根据状态代码判定出错与否.若状态代码表示一个错误,则调用这进行相应的处理,或显示一个错误页面或错误信息.通过返回值进行处理的方法是有效的,但是有它的许多不足之处. 1. 程序复杂 2. 可靠性差 3. 返回信息有限 4. 返回代码标准化困难 采用错误代码和异常处理相结合的方式的优越性: 1. 把错误代码与常规代码分开 2. 可以在catch中传播错误信息 3. 可以对

初探Java中的异常处理

Java中的异常有下面几种: 1) Error:Java运行时的内部错误: 2) Exception:程序中应该捕获的异常. RuntimeException:由于编程产生的错误: IOException:输入输出产生的错误. 异常处理的方法: 1)try-catch try语句块只能有一个,而catch可以有多个,catch必须紧跟try语句,中间不能有其他代码.如果有多个catch语句,那么捕获父类异常的catch语句必须放在后面,否则它会捕获它的所有子类异常,而使得子类异常catch语句永