《阿里巴巴Java开发手册》 详解专栏的第 16 节对详细讲述了 JVM的退出时机,给出了几个案例帮助大家理解这个知识点。
该小节给出了一个思考题
public class Demo {
public static void main(String[] args) {
// 省略一些代码 (第 1 处)
try {
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
System.out.println(br.readLine());
br.close();
} catch (Exception e) {
System.exit(2);
} finally {
System.out.println("Exiting the program");
}
}
}如果 try 代码块发生异常,如何在第 1 处代码添加几行代码,使得 finally 代码可以被执行到?
大家如果对虚拟机的退出时机感兴趣,可以购买专栏学习,下文将对该课后答案进行解读。
看到这个问题很多人会很费解,因为如果发生 IO异常,执行 catch 代码块,执行到 System.exit(2) 时,虚拟机退出, finally 就无法执行了。
这可怎么办呢?
虽然可以使用 字节码工具如 ASM 或者 Javassist 来修改字节码在 System.exit(2) 之前添加跳转到 finally 模块,但是并不符合题意。
难道是要阻止 System.exit(2) 的执行??
2.2 源码分析java.lang.System#exit
public static void exit(int status) {
Runtime.getRuntime().exit(status);
}看底层的源码 java.lang.Runtime#exit
public void exit(int status) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExit(status);
}
Shutdown.exit(status);
}注意:很多人比较急躁,看源码不喜欢看源码注释,提醒大家一定要重视注释。
通过该注释我们知道 System#exit(int) 是为了简化调用,是一种更传统和方便的调用方式。
另外 status 非0 表示非正常终止。
另外 如果一个 安全管理器阻止了某个特定 status 的退出,则会抛出 SecurityException。
因此思路来了,我们如果阻止 status ==2 的退出不就好了?
那么如何阻止呢?
public static SecurityManager getSecurityManager() {
return security;
}查看成员变量
private static volatile SecurityManager security = null;
找到设置的地方:
public static
void setSecurityManager(final SecurityManager s) {
try {
s.checkPackageAccess("java.lang");
} catch (Exception e) {
// no-op
}
setSecurityManager0(s);
}因此我们设置一下安全管理器。这里判断 status 为 2 时 ,抛出SecurityExceptio异常。
public static void main(String[] args) {
// 修改 SecurityManager
System.setSecurityManager(new SecurityManager() {
@Override
public void checkExit(int status) {
if(status ==2){
throw new SecurityException("不允许退出");
}
}
});
try {
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
System.out.println(br.readLine());
br.close();
} catch (Exception e) {
System.exit(2);
} finally {
System.out.println("Exiting the program");
}
}因此内部抛出 IO异常之后, catch 代码块执行到 System.exit(2); 内部执行到 security.checkExit(status); 时抛出 SecurityException。
public void exit(int status) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkExit(status);
}
Shutdown.exit(status);
}这样 fianlly 代码块就可以得到执行。
执行效果:
Exiting the program
Exception in thread "main" java.lang.SecurityException: 不允许退出
at com.imooc.basic.learn_exit.ExitTest$1.checkExit(ExitTest.java:12)
at java.lang.Runtime.exit(Runtime.java:107)
at java.lang.System.exit(System.java:971)
at com.imooc.basic.learn_exit.ExitTest.main(ExitTest.java:20)
另外最初catch那里是是 IOException weibo_LittleYul 练习是遇到了问题,进行了探讨,给出了不错的答案。
思考题我跟着一些写,但是结果不一样,会发生一下异常 Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "log4j.xml" "read") 我跟着源码进去看了,奈何没太看懂,SecurityManager#hasAllPermission返回的是false,可以理解自己创建的SecurityManager没有权限吗~
然后提示他此时已经实现了效果,如果不想出现这个异常该怎么做呢?
该同学给出了几种不错的方案:
1 直接修改安全管理器,重写权限检查的功能
2 修改policy文件,提供文件读的权限
需要通过-Djava.security.policy指定policy文件,在里面加上对应文件的io权限就可以了。
之前是我没整明白grant语句的语法,误以为给了全部权限,实际上该grant语句只对file:${{java.ext.dirs}}/*下的代码给力全部权限。
所以我们自己的代码还是没有权限去读取文件,需要自己授权。
可能有些同学会觉得这种题目“没意义”。其实潜台词就是面试的时候不太可能会问到,其次平时开发的时候用的不多。
不知道有没有同学发现,高考对我们带来的影响一直从未消逝。很多人学习技术更多地是为了功利性地考试或者求职,其他的一概是“无用”。
这就导致技术的深度和广度不够,因为稍微深点的东西就不愿意学,觉得没必要;稍微广点的东西,当前不用就觉得不着急学。
然而,不同的面试官的喜好不同,有些面试官可能会问到这些问题。
如果你回答都是别人也会的“标准答案”,那你的优势在哪里?
如果所有技术问题都不深究,不管是校招还是社招又如何能超出面试官的期待呢?
举一个非常简单的例子
如果面试官问你 Integer a = 200,b =200; 问 a==b 的结果。
有一部分同学可能搞不清楚;
有一部分同学看过《阿里巴巴Java开发手册》或者一些面经,窃喜,颇有自信地回答出“标准答案”为 false 因为缓存了 -128 到 127 的整数对象,不在缓存区间所以为false。
真的如此吗?
其实最大值是可以设置的,因此答案也可能是true。
有很大一部分同学由于没有实际去看源码,没有看到有资料提到这一点,而不知道。
很少有资料告诉你为什么要缓存这一区间,缓存的本质是什么?
缓存这一段整数对象是 Java语言规范的要求,某种程度上是因为这一段数据更常用,因此提前构造好。
缓存主要体现了空间换时间的思想。
就像这个问题,面试的时候很多人自己觉得回答的不错,而被淘汰。从回答问题本身而言,深度还不够,还不全面,没有超出面试官的期待,甚至还没回答到问题的关键。
3 总结本文解答 《阿里巴巴Java开发手册》详解专栏的 16节 JVM的退出时机的课后题。
并探讨了研究这个题目的意义,目的之一是帮助大家养成看源码的习惯,培养解决问题的能力,目的之二是希望大家能够有一定的技术追求,主动提升技术的深度和广度。
希望大家在看其他资料或者学专栏是,更加重视分析和解决问题的步骤,而不是记忆答案。
因为分析和解决问题是一种能力,有助于帮助你解决其他问题,而记忆的信息容易遗忘。
---------------------------------
如果本文对你有帮助,欢迎点赞、评论和转发,你的支持是我创作的最大动力。
另外想学习,更多开发和避坑技巧,少走弯路,请关注《阿里巴巴Java 开发手册》详解专栏



