面着面着,就修仙了,还原俺的真实面试场景。
面试官:谈谈异常机制?
我:只要涉及到谈谈,我内心都是崩溃的,不知道该如何说起
一上来,我可能先回答:Error和Exception是Throwable类的派生实例。说到此处,面试官还不得问一下Error?
面试官:给我讲讲什么是Error?
我:其实就是明显的给自己挖坑,哈能咋滴!Error描述了Java运行时系统的内部错误和资源耗尽错误,你比如栈溢出和堆溢出啦?不好,面试官微笑了。
面试官:讲一下什么是栈溢出和堆溢出?
我:哎,中枪了,这咋害能扯到虚拟机上了
- StackOverFlowError:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出此异常。比如,无限递归方法,其实面试官按捺不住的问
面试官:为什么无限递归方法就可以抛出该异常?
我:因为我们知道,每次调用方法所产生资源都存放在了虚拟机栈中,如果无限递归下去,那岂不是?
面试官:虚拟机栈存了什么资源?
我:我真的是!虚拟机栈存了局部变量表、操作数栈、动态链接和方法出口。
面试官:局部变量表中存了什么?
我:啊?还好我会,存放了编译期可知的各种基本数据类型(8大基本类型),对象引用类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置。
面试官:好,开始讲堆溢出
我:害能给我绕回来...如果虚拟机可动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常,当然,如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,也会抛出该异常。比如,我又挖坑,举例子:无限创建线程。这次我主动说原因:操作系统分配给每个进程内存是有限的,比如32位的windows限制为2G。虚拟机提供了参数来控制堆和方法区的内存的最大值,而剩下的内存,忽略其他因素,就由虚拟机栈和本地方法栈“瓜分天下了”。每个线程分配到栈容越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。
面试官:嘿嘿,方法区会溢出吗?
我:嘿嘿,会。比如方法区中有一个运行时常量池,晓得吧?其中String.intern()方法是一个native方法,它(1.6)的作用是:如果字符串常量池中已经包含了此String对象的字符串,则返回代表池中这个字符串String对象;否则,将此String对象所包含的字符串添加到常量池中,并且返回此String对象的引用。在1.7版本就不一样了,而是从堆中实例String对象的引用复制到常量池并返回。当然,还有很多带有反射机制的框架,大量使用反射创建类来填满方法区。
面试官:嘿嘿,直接内存会溢出吗?
我:简直了,太能问了。那肯定也是能的哦,比如DirectByteBuffer。
面试官:可以了,聊Exception
我:无限退出递模式!Exception又分解为RuntimeException(运行时)和程序本身没有问题,由于像IO错误这类问题导致的异常(编译)。
面试官:RuntimeException中有哪些,举一些?
我:好的,比如,NullPointerException
,ArithmeticException
,ClassCastException
,ArrayIndexOutOfBoundsException
等
面试官:什么是受检异常和非受检异常?
我:派生于Error类或RuntimeException类的所有异常称为非受检异常,所有其他的异常称为受检异常。
面试官:如何捕获异常?
我:
-
try
块: 用于捕获异常。其后可接零个或多个catch
块,如果没有catch
块,则必须跟一个finally
块。 -
catch
块: 用于处理try
捕获到的异常。 -
finally
块: 无论是否捕获或处理异常,finally
块里的语句都会被执行。当在try
块或catch
块中遇到return
语句时,finally
语句块将在方法返回之前被执行。
throw 抛出异常,throws是方法可能抛出异常的声明。(用在声明方法时,表示该方法可能要抛出异常)
注意:finally return会覆盖try和catch的返回值
public class Main {
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
int a = 0;
try {
int b = 1 / 0; // 不管发生不发生异常
a++;
return a;
} catch (Exception e) {
a++;
return a;
} finally {
return 5;
}
}
}
// 结果都是5
面试官:可以,我们换一波问题。
我:既然聊到了非受检异常,我还想扯一波Spring事务,Spring事务失效的原因,其中原因之一有非受检异常的原因。
简单这里提一下原因:
-
数据库引擎不支持事务
-
没有被 Spring 管理
-
方法不是 public 的
-
自身调用问题
-
数据源没有配置事务管理器
-
不支持事务(传播机制)
-
异常被吃了(捕获异常)
-
异常类型错误(checked异常失效)