面试题——Java异常架构与异常关键字

面试题——Java异常架构与异常关键字,第1张

面试题——Java异常架构与异常关键字

文章目录

1.Java异常简介2.Java异常架构3. Java异常关键字4. Java异常处理5.常见异常处理方式6.Error 和 Exception 区别是什么?7. JVM 是如何处理异常的?8. throw 和 throws 的区别是什么?9. final、finally、finalize 有什么区别?10.NoClassDefFoundError 和 ClassNotFoundException 区别?11.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?12.异常处理-阿里巴巴Java开发手册


1.Java异常简介

Java异常是Java提供的一种识别及响应错误的一致性机制。
Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程
序健壮性。在有效使用异常的情况下,异常能清晰的回答what, where, why这3个问题:异常类型
回答了“什么”被抛出,异常堆栈跟踪回答了“在哪”抛出,异常信息回答了“为什么”会抛出。

2.Java异常架构


1. Throwable

Throwable 是 Java 语言中所有错误与异常的超类。Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示发生了异常情况。Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。

2. Error(错误)

定义:Error 类及其子类。程序中无法处理的错误,表示运行应用程序中出现了严重的错误。特点:此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!
3. Exception(异常)
程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异常。

运行时异常
定义:RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。
特点:Java 编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过
throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。比如NullPointerException空指针异常、ArrayIndexOutBoundException数组下标越界异常、ClassCastException类型转换异常、ArithmeticExecption算术异常。此类异常属于不受检异常,
一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。虽然 Java 编译器不
会检查运行时异常,但是我们也可以通过 throws 进行声明抛出,也可以通过 try-catch 对它进行
捕获处理。如果产生运行时异常,则需要通过修改代码来进行避免。例如,若会发生除数为零的情
况,则需要通过代码避免该情况的发生!
RuntimeException 异常会由 Java 虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行
时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进代码。

编译时异常

定义: Exception 中除 RuntimeException 及其子类之外的异常。特点: Java 编译器会检查它。如果程序中出现此类异常,比如 ClassNotFoundException(没有找
到指定的类异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过trycatch进行捕获处理,否则不能通过编译。在程序中,通常不会自定义该类异常,而是直接使用系
统提供的异常类。该异常我们必须手动在代码里添加捕获语句来处理该异常。 4. 受检异常与非受检异常
Java 的所有异常可以分为受检异常(checked exception)和非受检异常(unchecked
exception)。

受检异常

编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除 RuntimeException 及其子类外,其他的Exception 异常都属于受检异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用方法签名中用 throws 关键字抛出,否则编译不通过。

非受检异常

编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和错误(Error)。 3. Java异常关键字

try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生
异常时,异常就被抛出。catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、
网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或 者- - throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直
接停止。throw – 用于抛出异常。throws – 用在方法签名中,用于声明该方法可能抛出的异常。 4. Java异常处理

Java 通过面向对象的方法进行异常处理,一旦方法抛出异常,系统自动根据该异常对象寻找合适异常处理器(Exception Handler)来处理该异常,把各种不同的异常进行分类,并提供了良好的接口。在 Java 中,每个异常都是一个对象,它是 Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java 的异常处理是通过5 个关键词来实现的:try、catch、throw、throws和 finally。在Java应用中,异常的处理机制分为声明异常,抛出异常和捕获异常。 5.常见异常处理方式

直接抛出异常

通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。

private static void readFile(String filePath) throws IOException { 
File file = new File(filePath); 
String result; BufferedReader reader = new BufferedReader(new FileReader(file)); 
	while((result = reader.readLine())!=null) {
	 System.out.println(result); 
	 }
 reader.close(); 
 }

封装异常再抛出

有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。

private static void readFile(String filePath) throws MyException { 
try {// code 
} 
catch (IOException e) { 
MyException ex = new MyException("read file failed."); 
ex.initCause(e); 
throw ex; 
	} 
}
6.Error 和 Exception 区别是什么?

Error 类型的错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这
类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序
会被终止,仅靠应用程序本身无法恢复;Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处
理,使应用程序可以继续正常运行。 7. JVM 是如何处理异常的?

在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异
常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出
异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。

JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。当 JVM发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。

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

throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出。throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常。 9. final、finally、finalize 有什么区别?

final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、
修饰变量表示该变量是一个常量不能被重新赋值。finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法
finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代
码。finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用
finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。 10.NoClassDefFoundError 和 ClassNotFoundException 区别?

NoClassDefFoundError 是一个 Error 类型的异常,是由 JVM 引起的,不应该尝试捕获这个异
常。引起该异常的原因是 JVM 或 ClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是变异后被删除了等原因导致;ClassNotFoundException 是一个受查异常,需要显式地使用 try-catch 对其进行捕获和处理,或在方法签名中用 throws 关键字进行声明。当使用 Class.forName, ClassLoader.loadClass 或ClassLoader.findSystemClass 动态加载类到内存的时候,通过传入的类路径参数没有找到该类,就会抛出该异常;另一种抛出该异常的可能原因是某个类已经由一个类加载器加载至内存中,另一个加载器又尝试去加载它。 11.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

答:会执行,在 return 前执行。注意:在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。显然,在 finally 中返回或者修改返回值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java中也可以通过提升编译器的语法检查级别来产生警告或错误。 12.异常处理-阿里巴巴Java开发手册

    【强制】Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch
    的方式来处理,比如:NullPointerException,IndexOutOfBoundsException等等。 说明:无法
    通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不通过
    catch NumberFormatException来实现。 正例:
if (obj != null) {
	…
}

反例:

try {
 obj.method(); 
 }
catch (NullPointerException e) {
…
}
public void wrapException(String input) throws MyBusinessException { 
try {
// do something 
} catch (NumberFormatException e) { 
throw new MyBusinessException("A message that describes the error.", e); 
	} 
}
    【强制】异常不要用来做流程控制,条件控制。 说明:异常设计的初衷是解决程序运行中的各种
    意外情况,且异常的处理效率比条件判断方式要低很多。【强制】catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于
    非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。 说明:对大段代码进行trycatch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,这是一种不负责任
    的表现。 正例:用户注册的场景中,如果用户输入非法字符,或用户名称已存在,或用户输入密
    码过于简单,在程序上作出分门别类的判断,并提示给用户。【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该
    异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。【强制】有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事
    务。【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。 说明:如果JDK7
    及以上,可以使用try-with-resources方式。【强制】不要在finally块中使用return。 说明:try块中的return语句执行成功后,并不马上返
    回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try
    块中的返回点。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5703469.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存