在没有退出命令的情况下进入 exit() 方法

exit() method entered without exit command

我的 Processing 3 代码表现出一些奇怪的行为。我定义了一个 void exit() 方法,它在随机时间执行,而用户实际上没有告诉代码退出。方法如下:

void exit()
{
 println("clearing buffer and closing file");
 if (output != null) {
   print("output is not null");
   try {
     output.close();
   } 
   catch (IOException e) {
     println("Error while closing the writer");
   }
 }
 super.exit();
}

如您所见,它所做的唯一一件事就是尝试关闭一个名为 output 的缓冲写入器。冲洗这个作家并不重要,所以现在我只是把它从我的草图中删除。但从长远来看,我很好奇这是怎么发生的。我的代码中没有其他地方明确调用了退出方法。 IE,代码无法决定退出。仅当用户使用 X 关闭问题时。

注意:我无法上传此方法的完整代码,因为它太长了。我认为也许更好的方式来表达我的问题是这样的:

"Hi, I am a noob that doesn't know anything about the exit method. Is there anyway that this method could somehow get called without me explicitly calling it or hitting the exit button?"

简而言之,您的问题的答案是肯定的。

您的方法可以通过同一 class 加载器中的任何 class 或层次结构中位于下方的任何其他加载器使用反射动态找到和调用。

此外,它具有默认访问权限。因此它可以被同一包中的任何 class 静态调用。

+1 @Andres,反射是一种可能性。

您是否尝试过在方法上使用断点并查看线程的堆栈跟踪?

就我个人而言,我不使用断点(只是我的风格)并且会尝试以编程方式查看线程。也许下面的一些代码可以帮助您查看线程并了解发生了什么:

public class ThreadUtil {

    /** Blocked constructor **/
    private ThreadUtil() {
    }

    /**
     * Get the stackstrace of the current {@link Thread}.
     * The stacktrace will be returned in the form of a string.
     */
    public static String getStackTrace() {
        return getStackTrace(Thread.currentThread());
    }

    /**
     * Get the stackstrace of a {@link Thread}.
     * The stacktrace will be returned in the form of a string.
     */
    public static String getStackTrace(Thread thread) {
        StringBuilder sb = new StringBuilder();
        StackTraceElement[] currThreadStackTraceElementList = thread.getStackTrace();
        appendStackTrace(sb, currThreadStackTraceElementList);
        return sb.toString();
    }

    public static String getAllStackTraces() {
        StringBuilder sb = new StringBuilder();
        Map<Thread, StackTraceElement[]> threadList = Thread.getAllStackTraces();
        for (StackTraceElement[] currThreadStackTraceElementList : threadList.values()) {
            appendStackTrace(sb, currThreadStackTraceElementList);
        }
        return sb.toString();
    }

    private static void appendStackTrace(StringBuilder sb,
            StackTraceElement[] currThreadStackTraceElementList) {
        sb.append("Thread stack trace: \n");
        for (StackTraceElement currThreadStackTraceElement : currThreadStackTraceElementList) {
            sb.append("\t" + currThreadStackTraceElement + "\n");
        }
        sb.append("\n");
    }
}

将此添加到 exit() 方法的开头。

    new Exception().printStackTrace();

生成的堆栈跟踪应该可以让您弄清楚是什么在调用您的 exit() 方法。

或者如果您不能调整代码,您可以 运行 使用调试器的应用程序并在 exit() 方法的开头设置断点。


要回答您关于是否可能的问题,答案取决于您所说的 "without me explicitly calling" 是什么意思。调用方法有多种方式,其中一些非常晦涩;例如

  • 您可以使用反射从声明 class 中获取 exit 方法的 Method 对象,然后对其调用 invoke(...)
  • 您可以通过 JNI 或 JNA api 从本机代码调用 Java 方法。
  • 您可以生成包含 exit() 调用的 Java 源代码,编译它,加载它,然后 运行 它。
  • 您可以使用 BCEL 或类似方法将 exit() 调用插入到 "innocent" 方法中。

还有...

  • 如果有附加到 JVM 的调试代理,调试器可以在 JVM 中的某个线程上调用 exit()

这是一个特定于处理的东西。

void exit() 是在 PApplet.java

中处理已经定义的方法

reference 中所述:

Rather than terminating immediately, exit() will cause the sketch to exit after draw() has completed (or after setup() completes if called during the setup() function).

For Java programmers, this is not the same as System.exit(). Further, System.exit() should not be used because closing out an application while draw() is running may cause a crash (particularly with P3D).

exit() 预计会这样使用:

void draw() {
  line(mouseX, mouseY, 50, 50);
}

void mousePressed() {
  exit(); 
}

它在 PApplet.java 中的几个地方被调用,特别是在 handleKeyEvent 中,以便在按下 ESC 或按下 ⌘w 时关闭草图。

只需将您的方法重命名为 exit() 以外的名称