如何从 Java 的生产代码中删除调试语句

2022-09-03 04:42:53

编译器是否可以从生产代码中删除用于调试目的(如日志记录)的语句?调试语句需要以某种方式标记,可能使用注释。

设置属性(debug = true)并在每个调试语句中检查它很容易,但这会降低性能。如果编译器只是使调试语句消失,那就太好了。


答案 1

两个建议。

首先:对于真正的日志记录,请使用现代日志记录包,如log4j或java自己的内置日志记录。不要太担心性能,日志记录级别检查在纳秒左右。(这是一个整数比较)。

如果您有多个日志语句,请保护整个块:

(log4j,例如:)

if (logger.isDebugEnabled()) {

  // perform expensive operations
  // build string to log

  logger.debug("....");
}

这为您提供了在运行时控制日志记录的附加功能。必须重新启动并运行调试版本可能非常不方便。

第二:

你可能会发现断言更符合你的需求。断言是一个语句,其计算结果为布尔结果,并带有可选消息:

 assert (sky.state != FALLING) : "The sky is falling!";

每当断言导致 false 时,断言就会失败,并且会引发包含消息的 AssertionError(这是一个未经检查的异常,旨在退出应用程序)。

巧妙的是,JVM对这些内容进行了特殊处理,并且可以在运行时使用VM参数(无需重新编译)切换到类级别。如果未启用,则开销为零。


答案 2
public abstract class Config
{
    public static final boolean ENABLELOGGING = true;
}

import static Config.*;

public class MyClass
{
    public myMethod()
    {
        System.out.println("Hello, non-logging world");

        if (ENABLELOGGING)
        {
            log("Hello, logging world.");
        }
    }
}

如果ENABLE_LOGGING设置为 true,则编译器将删除其中带有“Hello, logging world.”的代码块,因为它是静态最终值。如果您使用诸如 proguard 之类的混淆器,则 Config 类也将消失。

混淆器也会允许这样的事情:

public class MyClass
{
    public myMethod()
    {
        System.out.println("Hello, non-logging world");

        Log.log("Hello, logging world.");
    }
}

import static Config.*;

public abstract class Log
{
    public static void log(String s)
    {
        if (ENABLELOGGING)
        {
            log(s);
        }
    }
}

方法 Log#log 在编译器中将减少到零,并由混淆器删除,以及对该方法的任何调用,最终甚至 Log 类本身也将被删除。