在之前的文章中,已经发布了常见的面试题,这里我了点时间整理了一下对应的解答,由于个人能力有限,不一定完全到位,如果有解答的不合理的地方还请指点,在此谢过。
本文主要描述的是java的异常机制,这个在实际面试中,问到的概率不算太大,但是在项目中,如何优雅的使用异常机制却是一个很大的学问。下面先看一个常见的面试题型。
public static void main(String[] args) {
System.out.println(test());
}
private static int test(){
int x= 1;
try {
return ++x;
}catch (Exception ex){
return x++;
}finally {
++x;
}
}
这也算是面试中常见的一类面试题。上面的题目考察的知识点有++运算符,try catch finally 和return。在这里,先来看下上面的知识点后解答题目。
Java 的try catch finally过程:
实际在项目中,我们需要知道的是先执行try里面的内容,如果执行的过程中出现异常执行catch中的内容,最后不管是否执行过程中出现异常都会执行finally中的内容。如下,下面执行流程是:先执行(1)中的内容,最后执行(3)的内容,是否执行(2)的内容取决于(1)中的内容是否执行异常,如果(1)中执行的过程中出现问题就要执行(2)中的内容,如果没有就直接执行(3)的内容。
如果和return结合的话,我们也要执行finally中的内容之后才会return。换句话说,不管怎样,finally的内容都要执行。所以上面的打印内容是2。
请简单介绍JAVA的异常类?运行时异常和受检异常的区别是什么?Error和Exception的区别是什么?
我们通过一个简单的类图来描述一下,java的异常最大的父类是Throwable,其下两个子类是Error和Exception。Error代表的是错误,可能是系统内部环境错误,通过代码是无法解决的问题。这个时候只能通过停止虚拟机,其他别无选择。而exception是代码在设计上的缺陷导致,这个有可能是程序在设计时没有考虑到的场景导致,这个时候不需要停止虚拟机,更多的是希望对异常捕获之后进行异常处理。所以error是比exception更为严重的错误。Exception又可以分为两大类:运行时异常和受检异常;受检异常一般是指在程序中需要捕获的,这种异常一般都需要我们在catch中进行处理,而运行时异常是jvm自己管理的,我们一般不需要在代码中处理,如果发生异常的时候,代码会一直往外抛,直到最上层的代码。
举例说说你平时碰到的Error和Exception?
平时碰到的Error类:
AssertionError:断言错误
IllegalAccessError:非法权限错误
InternalError:java虚拟机内部错误
NoClassDefFoundError:java虚拟机在类初始化时找不到类的定义错误
NoSuchMethodError:类不存在该方法错误
OutOfMemoryError:分配内存时,内存溢出错误
StackOverflowError:栈溢出错误
Exception类:
运行时异常(RuntimeException):
NullPointerException:空指针异常
IndexOutOfBoundsException:下标越界异常
ClassCastException:类强制转换异常
NumberFormatException:数字格式化异常
IllegalArgumentException:参数错误异常
受检异常(checkedException):
SQLException:SQL异常
IOException:IO操作异常,这个是一个很大的异常类,其子类常见的有FIleNotFoundException,EOFException,SocketException;
InterruptedException:中断异常
NoClassDefFoundError 和ClassNotFoundException 的区别是什么?
我们至少可以根据后缀判断出Noclassdeffouderror 是error级别的,它是虚拟机抛出的,而classnotfoundexception是exception级别的,它是由代码中产出。Noclassdefounderror 一般是由于java类在编译完成之后,迁移过程中,class文件的丢死导致,jvm在加载类的时候抛出的错误。而classnotfoundexcption一般是动态加载类的时候,没有在类的路径下找到该类抛出的异常。
Throw和Throws的区别是什么?
当我们在代码中希望明确抛出一个异常的时候,我们使用Throw关键字将异常抛出到函数体上,对外抛出。当我们需要在函数体上对外抛出异常的时候,我们使用throws关键字将函数的各种异常对调用方抛出。我们看一段简单的代码就能明白throw和throws的区别了。
private static int test() throws Exception {//将函数体的异常抛出
int x= 1;
try {
return ++x;
}catch (NullPointerException ex){
throw new Exception("test");//将异常抛至函数体
}finally {
++x;
}
}
final、finally、finalize的区别是什么?
这几个词只是长得有点相同,其实它们在java中没有任何关系。
Final是固定的意思,它的常见用法是用来修饰变量,类,方法,当我们修饰变量的时候final修饰的是常量,当修饰方法的时候,表示方法不能被覆盖,当修饰类的时候,表示类不能继承。当然还有一种是修饰方法内的变量,这种在λ表达式的时候经常会被使用到,这个就是表示传过来的值是不允许改变的。
Finally是最终的意思,这个是在java异常机制中,表示都要执行的意思,这个在上面的内容中已经提到了,这里不多说明。
finalize是java的一个方法,它不是关键字,它是jvm垃圾回收的时候使用。我们知道finalize是object的一个方法,任何一个对象都有该方法,jvm在垃圾回收的时候,回收线程会先判断对象是否不可达(GCRoot不能连接),如果不可达就代表可以回收,这个时候判断该对象是否覆盖了finalize方法,如果没有覆盖,直接回收;如果有覆盖,就将该对象放置在一个F-Queue队列中,由优先级低的线程执行,如果执行完成之后,再次判断该对象是否不可达,如果不可达就直接回收,如果可达的话,就是我们常说的对象逃逸。这是finalize函数的设计初衷,一般我们在实际项目中很少会用到这个方法,毕竟垃圾回收我们希望虚拟机自己管理就好了。
一般在实际项目中,有哪些处理异常的方法,说说你的理解?
- 异常的分类处理,自定义异常的使用。项目中异常的分类很有必要,不同的异常对应着不同的处理方式,有些异常可以忽视的话,就不建议往外抛出。
- 异常类的创建和统一规范打印,项目中规范好异常的打印方式,可以有效的判断出是哪个服务哪个类出问题,并且又能保证关键信息不被泄露。
- 如果是非常重要的方法不允许抛出异常的话,可以在函数的开始直接使用try catch 进行捕获,这个是有效防止运行时异常导致整个程序后续执行出现问题。
- 异常尽量最小化,和前段界面交互的话,尽量将异常描述的用户能懂,不建议直接将异常内容抛出,防止信息泄露
- 可以使用函数式接口将异常的处理进行封装,防止一段代码中异常太多,导致程序的可读性太差。看下面一个例子哈:
//test方法
public void test(){
try {
//do sth
}catch (ExceptionA ex){//不同的exception 异常
}
try {
//do sth
}catch (ExceptionB ex){
}
//....
try {
try {
}catch (ExceptionC ex){
}
}catch (ExceptionD ex){
}
try {
//do sth
}catch (Exception ex){
}
}
使用函数式接口定义异常处理方法:
public static void hand(ExConWithNoReturn consumer){
try {
consumer.hand();
}catch (Exception ex){
log.error("hand ex:",ex);
}
}
切换之后的方法就变成了:
public void test(){
ExceptionHandler.hand(()->{
//do sth
});
ExceptionHandler.hand(()->{
//do sth other
});
ExceptionHandler.hand(()->{
//do sth other
});
}
这样代码看上去可读性会高点。
Java异常处理的问题基本就这么多,本文到这里就基本结束了,如果你觉得对你的学习和面试有些帮助,帮忙点个赞或者转发一下哈,谢谢。
想要了解更多java内容(包含大厂面试题和题解)可以关注公众号,也可以在公众号留言,帮忙内推阿里、腾讯等互联网大厂哈