说明

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 {
@Override
public void checkPermission(Permission perm) {
// allow anything.
} @Override
public void checkPermission(Permission perm, Object context) {
// allow anything.
} @Override
public 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() 与安全策略的更多相关文章

  1. JAVA System.exit(0) 和 System.exit(1) 的区别

    System.exit(int state) 方法都是来结束当前运行的java虚拟机.所有System.exit(1).System.exit(0) 执行后都会退出程序. state为0时时正常退出, ...

  2. Java 中的System.exit

    在java 中退出程序,经常会使用System.exit(1) 或 System.exit(0). 查看System.exit()方法的源码,如下 /** * Terminates the curre ...

  3. java拦截处理System.exit(0)

    在使用TestNG做单元测试时,需要测试的代码中出现System.exit(0),导致单元测试还未结束程序就停止了.解决方法如下: public class TestMain { public sta ...

  4. Android 与Java 进程退出 killProcess与System.exit

    android所有activity都在主进程中,在清单文件Androidmanifest.xml中可以设置启动不同进程,Service需要指定运行在单独进程?主进程中的主线程?还是主进程中的其他线程? ...

  5. [Java123] Java中的System.exit

    参考:http://www.cnblogs.com/xwdreamer/archive/2011/01/07/2297045.html System.exit(int  status) 方法 java ...

  6. 对于System.exit(0)和System.exit(1)的一般理解

    public static void exit(int status) 终止当前正在运行的 Java 虚拟机.参数用作状态码:根据惯例,非 0 的状态码表示异常终止. 该方法调用 Runtime 类中 ...

  7. System.exit()方法的作用

    查看java.lang.System的源码.我们能够看到System.exit()这种方法等价于Runtime.exit(),代码例如以下: /** * Terminates the currentl ...

  8. 关于JAVA System常见类的一些总结

    一.JAVA System类概述 1.概述: System 类是一个抽象类,所有的字段和方法都是静态的,即不能被实例化.其中包含一些有用的类字段和方法,它不能被实例化.在 System 类提供的设施中 ...

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

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

随机推荐

  1. C#基础知识-使用XML完成一个小程序(十一)

    上一篇中讲到XML基本的结构,还有增删改查的方法,这一篇中我们就来利用XML来完成一个简单的订单系统,主要是实现一个简单学生名单的增删改查,如果想要应用到实际的环境中建议考虑数据量的问题,如果数据量大 ...

  2. 1.文本编辑器-->CKEditor+CKFinder使用与配置

    一.CKEditor介绍 官网地址:http://ckeditor.com CKEditor下载地址:http://ckeditor.com/download CKFinder(免费版本)下载地址:h ...

  3. Lucene学习之四:Lucene的索引文件格式(2)

    本文转载自:http://www.cnblogs.com/forfuture1978/archive/2009/12/14/1623599.html  略有删减和补充 四.具体格式 上面曾经交代过,L ...

  4. 三:SpringTransaction

    一:什么是事务: 事务逻辑上的一组操作,组成这组操作的各个逻辑单元,要么一起成功,要么一起失败. 二:事务特性(ACID): 原子性(Atomicity) :强调事务的不可分割. 一致性(Consis ...

  5. AngularJS 指令 实现文本水平滚动效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. No.4一步步学习vuejs之表单输入绑定

    基础用法 你可以用 v-model 指令在表单控件元素上创建双向数据绑定.它会根据控件类型自动选取正确的方法来更新元素.尽管有些神奇,但 v-model 本质上不过是语法糖,它负责监听用户的输入事件以 ...

  7. java swing画图片爱心

    第一次用swing做一个可视化程序,写第一篇随笔,有写的不好的地方请多多见谅.上个星期三在网上看到一个画爱心的软件,就想着自己用java也实现一个程序,画爱心用到的数学函数知识在网上百度的,不是本人原 ...

  8. URL传递中文:Server.UrlEncode与Server.UrlDecode

    1.设置web.config文件. <system.web>  ......  <globalization requestEncoding="gb2312" r ...

  9. Aysnc-callback with future in distributed system

    Aysnc-callback with future in distributed system

  10. 花1台的钱入手2台【最能抗DDoS】阿里云主机【攻略】

    花1台的钱入手2台[最能抗DDoS]阿里云主机[攻略]: 第一步:先申请0元半年 http://click.aliyun.com/m/335/:注:0元机器只有新帐号可申请第二步:再买6折37/月 h ...