在 Java 中,为什么声明一个日志记录器 static final是最佳实践?
static final
private static final Logger S_LOGGER
您希望何时更改字段的值?
如果您永远不会更改值,那么将字段设置为 final 将使其成为 很明显,从而永远不会更改值。
因为这种功能通常可以在对象的所有实例中共享。对同一类的两个实例使用不同的日志记录器(90% 的情况下)没有多大意义。
但是,有时也可以看到声明为单例的日志记录器类,或者甚至只是提供静态函数来记录内容。
通常,您使用类名初始化日志记录器以记录日志——这意味着如果它们不是静态的,那么最终类的每个实例都会有一个它的实例(高内存占用) ,但是所有这些日志记录器将共享相同的配置并且行为完全相同。这就是 static位背后的原因。另外,由于每个 Logger都是使用类名初始化的,为了防止与子类发生冲突,您声明它为 private,因此它不能被继承。 final的出现是因为在执行过程中通常不会更改 Logger——所以一旦初始化,就永远不会“重新配置”它——在这种情况下,最终确保没有人可以更改它(无论是错误还是其他原因)是有意义的。 当然,如果您打算以不同的方式使用 Logger,那么您可能需要 没有来使用 static final——但是我大胆地猜测,80% 的应用程序将使用上面解释的日志记录。
static
Logger
private
final
此外,我更喜欢名称 log尽可能简单,但描述性。
log
编辑: 然而,这些规则有一个有趣的例外:
protected final Logger log = LoggerFactory.getLogger(getClass());
而不是:
private static final Logger log = LoggerFactory.getLogger(Foo.class);
前一种方法允许您在整个继承层次结构中的所有类中使用相同的日志记录器名称(实际类的名称)。因此,如果 Bar扩展了 Foo,两者都将记录到 Bar日志记录器。有些人觉得更直观。
Bar
Foo
static 意味着每个类只创建一个 Logger,而不是每个类的 例子创建一个 Logger。一般来说,这就是您想要的——因为记录程序往往会根据类的不同而有所不同。
final 意味着不会更改 logger变量的值。这是正确的,因为您几乎总是将所有日志消息(从一个类)抛给同一个日志记录器。即使在极少数情况下,一个类可能想要向另一个日志记录器发送一些消息,创建另一个日志记录器变量(例如 widgetDetailLogger)也比动态变更静态变量的值要清楚得多。
logger
widgetDetailLogger
在大多数情况下,您不会更改引用,并且 final修饰符会标记它。对于每个类实例,您不需要单独的实例-因此 static。和 首先,这是为了表演-它可以很好地优化(最终)和节省内存(静态)。
要回答这个问题,您应该问自己“ static”和“ final”是用来做什么的。
对于 Logger,(我假设您谈到的是 Log4J Logger 类)您希望每个类都有一个类别。这应该会导致这样一个事实,即您只分配它一次,而且每个类不需要多个实例。而且可能没有理由将一个类的 Logger 对象公开给另一个类,所以为什么不将其设置为私有并遵循一些 OO 原则呢。
您还应该注意到,编译器能够从中获益。因此您的代码执行得更好一些:)
看看这篇博文: 去除 Java 静态日志记录器。这是你如何使用 slf4j 和 Jcabi-log:
import com.jcabi.log.Logger; class Foo { void save(File f) { Logger.info(this, "file %s saved successfully", f); } }
再也不要用那种静电噪音了。
除了在其他答案中给出的原因之外,我遇到的一个问题是,如果我的日志记录器既不是静态的,也不是最终的:
... public Logger logger = LoggerFactory.getLogger(DataSummary.class); public String toJson() { GsonBuilder gsonBuilder = new GsonBuilder(); return gsonBuilder.create().toJsonTree(this).toString(); } ...
在某些情况下(当我使用 Gson 库时) ,我会得到 stackoverflow 异常。我的具体情况是实例化包含非静态非最终日志记录器的类。然后调用调用 GsonBuilder 的 toJson 方法:
... DataSummary ds = new DataSummary(data); System.out.println(ds.toJson()); ...
这段代码是脆弱的,但是在 Java7之后,我们可以使用 < code > Logger lgr = LoggerFactory.getLogger (MethodHandles.lookup () . lookupClass ()) ; 而不是静态记录器。
理想情况下,Logger 应该遵循 Java7,因为它不提供声纳,并提供符合规范的代码: Private: 永远不能在其父类之外访问。如果另一个类需要记录某些内容,它应该实例化自己的日志记录器。 不依赖于类(对象)的实例。当记录某些内容时,上下文信息当然可以在消息中提供,但是应该在类级别创建日志记录器,以防止在每个对象中创建日志记录器,从而防止高内存占用。 Final: 每个类只创建一次。
实际上,静态日志记录器可能是“有害的”,因为它们应该在静态上下文中工作。当有一个动态的环境,例如。OSGi 可能有助于使用非静态日志记录器。由于一些日志实现在内部缓存日志记录器(至少是 log4j) ,因此性能影响可以忽略不计。
静态日志记录器的一个缺点是垃圾收集(当一个类只使用一次时,例如在初始化期间,日志记录器仍然保留)。
详情请参阅:
参见:
根据我在互联网上读到的关于使日志记录器静态与否的信息,最佳实践是根据用例使用它。
有两个主要论点:
当您将其设置为静态时,它不会被垃圾收集(内存使用和性能)。
当您不使它成为静态的时候,它会为每个类实例创建(内存使用)
因此,当您为一个单例创建一个日志记录器时,您不需要使它成为静态的。因为只有一个实例,所以只有一个日志记录器。
另一方面,如果要为模型或实体类创建日志记录器,则应将其设置为静态,以避免创建重复的日志记录器。
我们用
Private -这样它仍然是类的私有数据成员(我们通常希望每个类级别变量都是这样)。
Static -这很重要。我们希望整个类只有一个日志记录器实例,而不是类的每个新实例/对象都生成一个新的日志记录器。Java 中的静态关键字也是如此。因此我们声明它是静态的
Final -我们不希望更改 logger 变量的值,而是希望它在整个类生命周期中保持不变。