Java核心技术面试精讲
杨晓峰
前Oracle首席工程师
立即订阅
43250 人已学习
课程目录
已完结 43 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 以面试题为切入点,有效提升你的Java内功
免费
模块一 Java基础 (14讲)
第1讲 | 谈谈你对Java平台的理解?
第2讲 | Exception和Error有什么区别?
第3讲 | 谈谈final、finally、 finalize有什么不同?
第4讲 | 强引用、软引用、弱引用、幻象引用有什么区别?
第5讲 | String、StringBuffer、StringBuilder有什么区别?
第6讲 | 动态代理是基于什么原理?
第7讲 | int和Integer有什么区别?
第8讲 | 对比Vector、ArrayList、LinkedList有何区别?
第9讲 | 对比Hashtable、HashMap、TreeMap有什么不同?
第10讲 | 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?
第11讲 | Java提供了哪些IO方式? NIO如何实现多路复用?
第12讲 | Java有几种文件拷贝方式?哪一种最高效?
第13讲 | 谈谈接口和抽象类有什么区别?
第14讲 | 谈谈你知道的设计模式?
模块二 Java进阶 (16讲)
第15讲 | synchronized和ReentrantLock有什么区别呢?
第16讲 | synchronized底层如何实现?什么是锁的升级、降级?
第17讲 | 一个线程两次调用start()方法会出现什么情况?
第18讲 | 什么情况下Java程序会产生死锁?如何定位、修复?
第19讲 | Java并发包提供了哪些并发工具类?
第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
第21讲 | Java并发类库提供的线程池有哪几种? 分别有什么特点?
第22讲 | AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
第23讲 | 请介绍类加载过程,什么是双亲委派模型?
第24讲 | 有哪些方法可以在运行时动态生成一个Java类?
第25讲 | 谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?
第26讲 | 如何监控和诊断JVM堆内和堆外内存使用?
第27讲 | Java常见的垃圾收集器有哪些?
第28讲 | 谈谈你的GC调优思路?
第29讲 | Java内存模型中的happen-before是什么?
第30讲 | Java程序运行在Docker等容器环境有哪些新问题?
模块三 Java安全基础 (2讲)
第31讲 | 你了解Java应用开发中的注入攻击吗?
第32讲 | 如何写出安全的Java代码?
模块四 Java性能基础 (3讲)
第33讲 | 后台服务出现明显“变慢”,谈谈你的诊断思路?
第34讲 | 有人说“Lambda能让Java程序慢30倍”,你怎么看?
第35讲 | JVM优化Java代码时都做了什么?
模块5 Java应用开发扩展 (4讲)
第36讲 | 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
第37讲 | 谈谈Spring Bean的生命周期和作用域?
第38讲 | 对比Java标准NIO类库,你知道Netty是如何实现更高性能的吗?
第39讲 | 谈谈常用的分布式ID的设计方案?Snowflake是否受冬令时切换影响?
周末福利 (2讲)
周末福利 | 谈谈我对Java学习和面试的看法
周末福利 | 一份Java工程师必读书单
结束语 (1讲)
结束语 | 技术没有终点
Java核心技术面试精讲
登录|注册

第2讲 | Exception和Error有什么区别?

杨晓峰 2018-05-08
世界上存在永远不会出错的程序吗?也许这只会出现在程序员的梦中。随着编程语言和软件的诞生,异常情况就如影随形地纠缠着我们,只有正确处理好意外情况,才能保证程序的可靠性。
Java 语言在设计之初就提供了相对完善的异常处理机制,这也是 Java 得以大行其道的原因之一,因为这种机制大大降低了编写和维护可靠程序的门槛。如今,异常处理机制已经成为现代编程语言的标配。
今天我要问你的问题是,请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别?

典型回答

Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。
Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的 Error,是 Throwable 不是 Exception。
不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

考点分析

分析 Exception 和 Error 的区别,是从概念角度考察了 Java 处理机制。总的来说,还处于理解的层面,面试者只要阐述清楚就好了。
我们在日常编程中,如何处理好异常是比较考验功底的,我觉得需要掌握两个方面。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java核心技术面试精讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(165)

  • 迷途知返
    我比较菜 在听到“NoClassDefFoundError 和 ClassNotFoundException 有什么区别,这也是个经典的入门题目。“ 这一段的时候 我以为会讲这两个的区别呢 我觉得这个区别详细讲讲 就是干货!文章总结性的语言比较多 并不具体
    2018-05-17
    748
  • 毛毛熊
    NoClassDefFoundError是一个错误(Error),而ClassNOtFoundException是一个异常,在Java中对于错误和异常的处理是不同的,我们可以从异常中恢复程序但却不应该尝试从错误中恢复程序。
    ClassNotFoundException的产生原因:

    Java支持使用Class.forName方法来动态地加载类,任意一个类的类名如果被作为参数传递给这个方法都将导致该类被加载到JVM内存中,如果这个类在类路径中没有被找到,那么此时就会在运行时抛出ClassNotFoundException异常。

    ClassNotFoundException的产生原因:

    Java支持使用Class.forName方法来动态地加载类,任意一个类的类名如果被作为参数传递给这个方法都将导致该类被加载到JVM内存中,如果这个类在类路径中没有被找到,那么此时就会在运行时抛出ClassNotFoundException异常。
    ClassNotFoundException的产生原因主要是:
    Java支持使用反射方式在运行时动态加载类,例如使用Class.forName方法来动态地加载类时,可以将类名作为参数传递给上述方法从而将指定类加载到JVM内存中,如果这个类在类路径中没有被找到,那么此时就会在运行时抛出ClassNotFoundException异常。
    解决该问题需要确保所需的类连同它依赖的包存在于类路径中,常见问题在于类名书写错误。
    另外还有一个导致ClassNotFoundException的原因就是:当一个类已经某个类加载器加载到内存中了,此时另一个类加载器又尝试着动态地从同一个包中加载这个类。通过控制动态类加载过程,可以避免上述情况发生。

    NoClassDefFoundError产生的原因在于:
    如果JVM或者ClassLoader实例尝试加载(可以通过正常的方法调用,也可能是使用new来创建新的对象)类的时候却找不到类的定义。要查找的类在编译的时候是存在的,运行的时候却找不到了。这个时候就会导致NoClassDefFoundError.
    造成该问题的原因可能是打包过程漏掉了部分类,或者jar包出现损坏或者篡改。解决这个问题的办法是查找那些在开发期间存在于类路径下但在运行期间却不在类路径下的类。
    2018-05-21
    4
    428
  • 公号-代码荣耀
    在Java世界里,异常的出现让我们编写的程序运行起来更加的健壮,同时为程序在调试、运行期间发生的一些意外情况,提供了补救机会;即使遇到一些严重错误而无法弥补,异常也会非常忠实的记录所发生的这一切。以下是文章心得感悟:

    1 不要推诿或延迟处理异常,就地解决最好,并且需要实实在在的进行处理,而不是只捕捉,不动作。

    2 一个函数尽管抛出了多个异常,但是只有一个异常可被传播到调用端。最后被抛出的异常时唯一被调用端接收的异常,其他异常都会被吞没掩盖。如果调用端要知道造成失败的最初原因,程序之中就绝不能掩盖任何异常。

    3 不要在finally代码块中处理返回值。

    4 按照我们程序员的惯性认知:当遇到return语句的时候,执行函数会立刻返回。但是,在Java语言中,如果存在finally就会有例外。除了return语句,try代码块中的break或continue语句也可能使控制权进入finally代码块。

    5 请勿在try代码块中调用return、break或continue语句。万一无法避免,一定要确保finally的存在不会改变函数的返回值。

    6 函数返回值有两种类型:值类型与对象引用。对于对象引用,要特别小心,如果在finally代码块中对函数返回的对象成员属性进行了修改,即使不在finally块中显式调用return语句,这个修改也会作用于返回值上。

    7 勿将异常用于控制流。

    8 如无必要,勿用异常。
    2018-05-08
    225
  • adrian-jser
    假如你开车上山,车坏了,你拿出工具箱修一修,修好继续上路(Exception被捕获,从异常中恢复,继续程序的运行),车坏了,你不知道怎么修,打电话告诉修车行,告诉你是什么问题,要车行过来修。(在当前的逻辑背景下,你不知道是怎么样的处理逻辑,把异常抛出去到更高的业务层来处理)。你打电话的时候,要尽量具体,不能只说我车动不了了。那修车行很难定位你的问题。(要补货特定的异常,不能捕获类似Exception的通用异常)。还有一种情况是,你开车上山,山塌了,这你还能修吗?(Error:导致你的运行环境进入不正常的状态,很难恢复)
    ------------------
    思考问题:由于代码堆栈不再是同步调用那种垂直的结构,这里的异常处理和日志需要更加小心,我们看到的往往是特定 executor 的堆栈,而不是业务方法调用关系。对于这种情况,你有什么好的办法吗?
    ------------------
    业务功能模块分配ID,在记录日志是将前后模块的ID进行调用关系的串联,一并跟任务信息,进行日志保存。

    作者回复: 形象!

    2018-09-21
    1
    176
  • coder王
    留言中凸显高手。
    2018-05-08
    85
  • 钱宇祥
    1.异常:这种情况下的异常,可以通过完善任务重试机制,当执行异常时,保存当前任务信息加入重试队列。重试的策略根据业务需要决定,当达到重试上限依然无法成功,记录任务执行失败,同时发出告警。
    2.日志:类比消息中间件,处在不同线程之间的同一任务,简单高效一点的做法可能是用traceId/requestId串联。有些日志系统本身支持MDC/NDC功能,可以串联相关联的日志。

    作者回复: 很棒的总结

    2018-05-08
    74
  • DavidWhom佳传
    提出面试问题,却没有较好的回答,很难受(;_;)
    2018-05-14
    55
  • 欧阳田
    1.Error:系统错误,虚拟机出错,我们处理不了,也不需要我们来处理。
    2.Exception,可以捕获的异常,且作出处理。也就是要么捕获异常并作出处理,要么继续抛出异常。
    3.RuntimeException,经常性出现的错误,可以
    捕获,并作出处理,可以不捕获,也可以不用抛出。ArrayIndexOutOfBoundsException像这种异常可以不捕获,为什么呢?在一个程序里,使用很多数组,如果使用一次捕获一次,则很累。
    4.继承某个异常时,重写方法时,要么不抛出异常,要么抛出一模一样的异常。
    5.当一个try后跟了很多个catch时,必须先捕获小的异常再捕获大的异常。
    6.假如一个异常发生了,控制台打印了许多行信息,是因为程序中进行多层方法调用造成的。关键是看类型和行号。
    7.上传下载不能抛异常。上传下载一定要关流。
    8.异常不是错误。异常控制代码流程不利于代码简单易读。
    9.try catch finally执行流程,与 return,break,continue等混合使用注意代码执行顺序。不是不可以,而是越是厉害的人,代码越容易理解。
    2018-05-08
    1
    50
  • 约书亚
    先说问题外的话,Java的checked exception总是被诟病,可我是从C#转到Java开发上来的,中间经历了go,体验过scala。我觉得Java这种机制并没有什么不好,不同的语言体验下来,错误与异常机制真是各有各的好处和槽点,而Java我觉得处在中间,不极端。当然老师提到lambda这确实是个问题...
    至于响应式编程,我可以泛化为异步编程的概念嘛?一般各种异步编程框架都会对异常的传递和堆栈信息做处理吧?比如promise/future风格的。本质上大致就是把lambda中的异常捕获并封装,再进一步延续异步上下文,或者转同步处理时拿到原始的错误和堆栈信息

    作者回复: 是的,非常棒的总结,归根结底我们需要一堆人合作构建各种规模的程序,Java异常处理有槽点,但实践证明了其能力;
    类似第二点,我个人也觉得可以泛化为异步编程的概念,比如Future Stage之类使用ExecutionException的思路

    2018-05-08
    45
  • 飞云
    能不能讲下怎么捕捉整个项目的全局异常,说实话前两篇额干货都不多,希望点更实在的干货

    作者回复: 谢谢建议,极客课程设计是尽量偏向通用场景,我们掉坑里,往往都不是在高大上的地方;全局异常Spring MVC的方式就很实用;对与干货,你是希望特定场景,特定问题吗?说说你的想法

    2018-05-08
    24
  • 猿工匠
    每天早上学习与复习一下😁😁
    2018-05-08
    23
  • 小绵羊拉拉
    看完文章简单认识一些浅层的意思 但是我关注的 比如try catch源码实现 涉及 以及 文章中提到 try catch 产生 堆栈快照 影响jvm性能等 一笔带过 觉得不太过瘾。只是对于阿里的面试 读懂这篇文章还是不够。还希望作者从面试官的角度由浅入深的剖析异常处理 最后还是 谢谢分享

    作者回复: 谢谢反馈,如果不做jvm或非常底层开发,个人没有看到这些细节的实际意义,如果非要问可以鄙视他:-)
    创建Throwable因为要调用native方法fillInStacktrace;至于try catch finally,jvms第三章有细节,也可以自己写一段程序,用javap反编译看看 goto、异常表等等

    2018-05-08
    18
  • 小猴子吹泡泡
    老师可以在文章末尾推荐一些基础和进阶的Java学习书籍或是资料吗?最好是使用较新版本jdk的
    2018-05-10
    17
  • 涟漪
    非常感谢作者以及评论中的高手们!我很喜欢作者能够精选评论。
    2018-05-09
    14
  • James
    个人觉得checked exception / unchecked exception 分别翻译为 检查型异常/非检查型异常 更加好理解。
    可检查异常容易被理解为可以不检查。

    作者回复: 有道理,谢谢指出

    2018-05-08
    11
  • 拉灯灯
    error指的是不可预料的错误,可能会导致程序宕机;而exception指的是在程序运行中可以预见的异常,而异常分为检查异常与一般异常,检查异常需要在程序中显示捕获并处理,一般异常可以通过程序编码来进行处理,比如数组越界,空指针等;异常处理的两大基本原则:不要捕获泛泛的异常信息,比如直接捕获Exception,这样会在增加代码阅读难度;不要生吞异常;打印异常信息是一个比较重的操作,会导致程序变慢;try catch最好是包括需要检验异常的代码,不要包含过长代码,这样会降低JVM的优化效率;这是学习本节课的部分总结
    2018-05-16
    9
  • 过河
    对于日志里面我们看到的往往是特定 executor 的堆栈,而不是业务方法调用关系这种情况,我在公司推行的是自定义异常,自定义的异常有一个错误码,这个错误码需要细到某个业务的某个方法的某种错,这样排查问题会很方便,但是写的时候就比较麻烦,文档也比较多

    作者回复: 嗯,有些类似trace id的思路,构建树形堆栈也有帮助

    2018-05-08
    9
  • Jerry银银
    由于反应式编程是异步的,基于事件的,所以异常肯定不能直接抛出,如果直接抛出,随便一个异常都会引起程序崩溃,直接影响到对后续事件处理。个人觉得一种处理方式是:当某个事件发生异常时,为了不影响对后续事件的处理,可以对当前发生异常的事件进行拦截处理,然后将异常信息发送出去。

    至于发生异常时,堆栈信息只是关于特定executor框架中的,不知道是否可以将之前事件的“上下文”带到executor,再传递给观察者?
    (对反应式编程不太了解,尝试作答^_^)
    2018-05-08
    9
  • 三军
    Java语言规范将派生于Error类或RuntimeException类的所有异常称为未检查(unchecked)异常,所有其他的异常成为已检查(checked)异常。

    编译器将检查是否为所有的已检查异常提供了异常处理器。

    这是经典,要好好理解。

    平时我们使用throws往外拋错,或者try-catch这类异常处理器不就是处理已检查异常吗😊

    未检查异常就是潜在的,编译器无需提供异常处理器进行处理。
    2018-05-10
    8
  • Monday
    杨老师和各位一起学习的学友们好!
    学习完本节有以下几个小问题,麻烦解惑下,非常感谢:
    1、Java中Exception为什么要分为checked、unchecked Exception?
    2、怎么区分以上两种Exception?仅仅是unchecked Exception都是继承自RuntimeException?
    3、关于NoClassDefFoundError和ClassNotFoundException的问题:
      3.1)求两者区别的典型回答(标准答案)
      3.2)Java中类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。请问NoClassDefFoundError和ClassNotFoundException分别都发生在哪几个阶段?
    2018-09-24
    7
收起评论
99+
返回
顶部