如何忽略反射调用中ClassB#main()的System#exit(),避免JVM终止
Absolutely, you can pull this off without rewriting tons of code! Let me break down two reliable approaches depending on your Java version and use case:
System.exit() with a Custom Security Manager (Java 8 and earlier) This is the lightest-weight option if you want to keep everything in the same JVM. The idea is to override the default SecurityManager to block calls to System.exit() and throw an exception instead, which you can catch in ClassA to resume execution.
First, create a tiny custom SecurityManager:
class NoExitSecurityManager extends SecurityManager { @Override public void checkExit(int status) { // Throw an exception to stop the exit from happening throw new SecurityException("Blocked attempt to exit JVM"); } }
Then, wrap your reflection call in ClassA with logic to swap the SecurityManager temporarily:
public class ClassA { public static void main(String[] args) { // Save the original SecurityManager to restore later SecurityManager originalSM = System.getSecurityManager(); try { // Activate our custom manager System.setSecurityManager(new NoExitSecurityManager()); // Call ClassB's main via reflection as you normally would Class<?> classB = Class.forName("ClassB"); Method mainMethod = classB.getMethod("main", String[].class); mainMethod.invoke(null, (Object) new String[]{}); } catch (SecurityException e) { // This catches the block from our SecurityManager System.out.println("Caught and ignored ClassB's System.exit() call!"); } catch (Exception e) { // Handle other reflection-related errors e.printStackTrace(); } finally { // Always restore the original SecurityManager System.setSecurityManager(originalSM); } // This line will run even after ClassB tried to exit! System.out.println("ClassA's main is still chugging along."); } }
- Pros:
- Zero changes needed to ClassB
- No extra process overhead
- Runs entirely within the same JVM
Note: For Java 9+, SecurityManager is marked deprecated, but it still works (you'll just get a warning). If you want a deprecation-free option for newer Java versions, go with the next approach.
If you want complete isolation (and avoid deprecated APIs in Java 9+), spin up a separate JVM process to run ClassB. When ClassB calls System.exit(), it only terminates the child process—not the parent JVM where ClassA is running.
Here's how to implement this in ClassA:
public class ClassA { public static void main(String[] args) { try { // Build a process to run ClassB ProcessBuilder pb = new ProcessBuilder( // Path to your Java executable System.getProperty("java.home") + "/bin/java", // The fully qualified name of ClassB "ClassB" ); // Redirect the child process's output/error to the parent's console (optional) pb.inheritIO(); // Start the process and wait for it to finish Process childProcess = pb.start(); int exitCode = childProcess.waitFor(); System.out.println("ClassB exited with code: " + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); } // This line runs regardless of ClassB's exit call System.out.println("ClassA's main keeps on running!"); } }
- Pros:
- Works on every Java version (no deprecation warnings)
- Complete isolation—ClassB can't crash or terminate ClassA's JVM
- No changes needed to ClassB
- Cons:
- Minor performance overhead from starting a new process
- Sharing data between ClassA and ClassB requires extra work (like sockets or files) if needed
Both approaches let you ignore ClassB's System.exit() without modifying ClassB itself. Pick the first one if you need same-JVM performance, or the second if you want future-proof, isolated execution.
内容的提问来源于stack exchange,提问作者vkourt

