说明

System.exit() 的本质是通知 JVM 关闭。

一般来说,有两种禁用 System.exit() 的办法:

  • 安全管理器
  • 安全策略

本质都是JRE 提供的本地实现,在执行之前进行权限判断。

因为System.exit() 是一种很暴力的手段,如果在 Client 模式下自己写个小程序无所谓,但是在 Server 上多个程序、或者多线程时就会有很大的麻烦。

底层源码

1.先来看看静态方法 System.exit() 的源码:

// System.exit()
public static void exit(int status) {Runtime.getRuntime().exit(status);
}

应该说很简单, 只是简单地调用运行时的 exit 方法.

2.然后我们看运行时的实例方法 exit:

// Runtime.exit()
public void exit(int status) {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkExit(status);}Shutdown.exit(status);
}

如果有安全管理器,那么就让安全管理器执行 checkExit退出权限检查。

如果检查不通过,安全管理器就会抛出异常(这就是约定!)。

然后当前线程就会往外一路抛异常,如果不捕获,那么该线程就会退出。

此时如果没有其他的前台线程正在运行,那么JVM也会跟着退出。

3.Shutdownjava.lang 包下面的一个类。

访问权限是 default, 所以我们在API中是不能调用的。

// Shutdown.exit()
static void exit(int status) {boolean runMoreFinalizers = false;synchronized (lock) {if (status != 0) runFinalizersOnExit = false;switch (state) {case RUNNING:       /* Initiate shutdown */state = HOOKS;break;case HOOKS:         /* Stall and halt */break;case FINALIZERS:if (status != 0) {/* Halt immediately on nonzero status */halt(status);} else {/* Compatibility with old behavior:* Run more finalizers and then halt*/runMoreFinalizers = runFinalizersOnExit;}break;}}if (runMoreFinalizers) {runAllFinalizers();halt(status);}synchronized (Shutdown.class) {/* Synchronize on the class object, causing any other thread* that attempts to initiate shutdown to stall indefinitely*/sequence();halt(status);}
}

其中有一些同步方法进行锁定。 退出逻辑是调用了 halt 方法。

// Shutdown.halt()
static void halt(int status) {synchronized (haltLock) {halt0(status);}
}static native void halt0(int status);

然后就是调用 native 的 halt0() 方法让 JVM “自杀“了。

示例

使用安全管理器的实现代码如下所示:

1.定义异常类, 继承自 SecurityException

ExitException.java

package com.cncounter.security;public class ExitException extends SecurityException {private static final long serialVersionUID = 1L;public final int status;public ExitException(int status) {super("忽略 Exit方法调用!");this.status = status;}
}

2.定义安全管理器类, 继承自 SecurityManager

NoExitSecurityManager.java

package com.cncounter.security;import java.security.Permission;public class NoExitSecurityManager extends SecurityManager {@Overridepublic void checkPermission(Permission perm) {// allow anything.}@Overridepublic void checkPermission(Permission perm, Object context) {// allow anything.}@Overridepublic void checkExit(int status) {super.checkExit(status);throw new ExitException(status);}
}

其中直接拒绝系统退出。

3.增加一个辅助和测试类,实际使用时你也可以自己进行控制。

NoExitHelper.java

package com.cncounter.security;public class NoExitHelper {/*** 设置不允许调用 System.exit(status)* * @throws Exception*/public static void setNoExit() throws Exception {System.setSecurityManager(new NoExitSecurityManager());}public static void main(String[] args) throws Exception {setNoExit();testNoExit();testExit();testNoExit();}public static void testNoExit() throws Exception {System.out.println("Printing works");}public static void testExit() throws Exception {try {System.exit(42);} catch (ExitException e) {//System.out.println("退出的状态码为: " + e.status);}}
}

在其中,使用了一个 main 方法来做简单的测试。 控制台输出结果如下:

Printing works
退出的状态码为: 42
Printing works

原问题

原来的问题如下:

I’ve got a few methods that should call System.exit() on certain inputs. Unfortunately, testing these cases causes JUnit to terminate! Putting the method calls in a new Thread doesn’t seem to help, since System.exit() terminates the JVM, not just the current thread. Are there any common patterns for dealing with this? For example, can I subsitute a stub for System.exit()?

大意是:

有一些方法需要测试, 但是在某些特定的输入时就会调用 System.exit()。这就杯具了,这时候 JUnit 测试也跟着退出了! 用一个新线程来调用这种方法也没什么用, 因为 System.exit() 会停止JVM , 而不是退出当前线程。有什么通用的模式来处理这种情况吗? 例如,我能替换掉 System.exit() 方法吗?

建议如下:

Instead of terminating with System.exit(whateverValue), why not throw an unchecked exception? In normal use it will drift all the way out to the JVM’s last-ditch catcher and shut your script down (unless you decide to catch it somewhere along the way, which might be useful someday).

In the JUnit scenario it will be caught by the JUnit framework, which will report that such-and-such test failed and move smoothly along to the next.

翻译如下:

在程序中调用 System.exit(whateverValue) 是一种很不好的编程习惯, 这种情况为什么不抛出一个未检测的异常(unchecked exception)呢? 如果程序中不进行捕获(catch), 抛出的异常会一路漂移到 JVM , 然后就会退出程序(只有主线程的话)。

在 JUnit 的测试场景中异常会被 JUnit 框架捕获, 然后就会报告说某某某测试执行失败,然后就继续下一个单元测试了。

当然,给出的解决方案就是前面的那段代码. 你还可以阅读下面的参考文章,查找其他的解决方案。

参考文章:

  • Java: 如何单元测试那种会调用 System.exit() 的方法?

  • Java中如何禁止 API 调用 System.exit()

  • 如何配置 Tomcat 的安全管理器

日期: 2015年08月25日

人员: 铁锚 http://blog.csdn.net/renfufei

Java: System.exit() 与安全策略相关推荐

  1. [转载] Java——System.exit()用法及个人理解

    参考链接: Java中的System.exit() 该方法的原型是:System.exit(int status). 取值及作用 status由使用者自取,一般可取0,1或者其他数:当其为System ...

  2. exit java_没有System.exit,如何退出Java程序?(通过用户输入?)

    这是我的代码.我不确定如何使用返回值退出程序.有任何想法吗?这是我完成任务的最后一步.重要区域标有我听到了返回的消息,但是当我将main中的void更改为int时,程序会说main必须为void. i ...

  3. android java 退出程序_android开发两种退出程序方式(killProcess,System.exit)

    KillProcess: 在android中我们如果想要程序的进程结束可以这样写: android.os.Process.killProcess(android.os.Process.myPid()) ...

  4. Java System类exit()方法及示例

    系统类exit()方法 (System class exit() method) exit() method is available in java.lang package. exit()方法在j ...

  5. bat 命令行执行 java(jar)获取返回值 【ERRORLEVEL、System.exit(code)】

    ■前言 bat调用java,如何判断java运行正常终了 ■实现 1.命令行中, 使用 %ERRORLEVEL% ↓ERRORLEVEL的介绍 https://blog.csdn.net/sxzlc/ ...

  6. Java中的System.exit()

    The System.exit() method terminates the Java virtual machine. This method takes an argument, which i ...

  7. Java中System.exit()方法的功能简介说明

    转自: Java中System.exit()方法的功能简介说明 下文笔者讲述java代码中System.exit()方法的功能简介说明,如下所示: Java中System.exit()方法功能:此方法 ...

  8. Java笔记(6)-Math、BIgInteger、DecimalFormat、Pattern和Macth、Scanner、System.exit()

    我是天空里的一片云, 偶尔投影在你的波心-- 你不必讶异, 更无须欢喜-- 在转瞬间消灭了踪影. 你我相逢在黑夜的海上, 你有你的,我有我的,方向: 你记得也好, 最好你忘掉, 在这交会时互放的光亮. ...

  9. java的System.exit(0)的作用以及和System.exit(1)区别。

    System.exit(int  status)这个方法是用来结束当前正在运行中的java虚拟机. status是非零参数,那么表示是非正常退出. System.exit(0)是正常退出程序,而Sys ...

最新文章

  1. pandas中一列拆分成两列
  2. 支持断线重连、永久watcher、递归操作并且能跨平台(.NET Core)的ZooKeeper异步客户端
  3. 使用VS Code 从零开始开发并调试.NET Core 应用程序
  4. linux 内存一直在增加,linux – 缓存内存和共享内存总和超过总内...
  5. Arm华为NXP睿赛德大咖云集!2020中国嵌入式技术大会嘉宾揭晓
  6. excel字符串反转
  7. 注塑机网关 HFCL-EDGE
  8. Validation 使用
  9. 第130章 SQL函数 SQRT
  10. MacOS上BeyondCompare无限试用的方法
  11. 呼叫中心的软电话架构
  12. 如何进行滤波器设计软件选择
  13. 界面仅允许扫码枪录入禁止手工键盘输入
  14. linux系统各服务相关设置
  15. javase能做什么项目
  16. Altium高版本(19以上)Comment批量修改成Value或者MPN的方法
  17. 15个精美的 HTML5 单页网站作品欣赏
  18. word中表格文字居中
  19. 电信网连微软服务器有问题,宽带有问题?找我们就对了。
  20. “东数西算”带来的数据安全挑战有哪些?

热门文章

  1. 四章: CentOS6.5 配置连接Windows远程桌面、创建新用户、禁用root在本地或远程使用ssh登录
  2. jq ajax异步上传图片插件,jQuery异步上传文件插件ajaxFileUpload详细介绍
  3. linux系统硬盘数据恢复软件下载,Linux硬盘数据恢复软件
  4. mysql数据库备份报错145_mysql数据库导出时报错mysqldump: Got error: 145的解决方法
  5. Linux操作系统安装LAMP环境
  6. 这是Nginx的负载均衡配置方法
  7. 日志中的秘密 Windows登录类型都有哪些
  8. Metasploit的三种启动方式
  9. Linux应用程序设计之网络基础编程
  10. Linear Algebra lecture6 note