是否应在大写字母中声明“静态最终记录器”?


243

在Java中,静态最终变量是常量,并且约定应使用大写形式。但是,我已经看到大多数人以小写形式声明记录器,这在PMD中是违反的。

例如:

private static final Logger logger = Logger.getLogger(MyClass.class);

只需在GoogleSO中搜索“静态最终记录器”,您便会自己看到。

我们应该改用LOGGER吗?


PMD或Checkstyle是过早的幼稚尝试,旨在提高可读性,但弊大于利。最易读的样式可以根据上下文的情况而改变。参见Guava或JDK src,它们没有遵循任何严格的样式模板,但是由专业人员制作而成,这是毫无疑问的。示例:DelegatedExecutorService @ docjar.com/html/api/java/util/concurrent/Executors.java.html
DanielHári17年

声纳规则(rules.sonarsource.com/java/tag/convention/RSPEC-1312)的名称也为private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);
Kenston Choi

Answers:


306

记录器引用不是常量,而是最终引用,并且不应大写。常数VALUE应该为大写。

private static final Logger logger = Logger.getLogger(MyClass.class);

private static final double MY_CONSTANT = 0.0;

42
如果静态最终引用是不变的,则它们是常量。按照这种逻辑,您将永远不会有常量字符串,因为任何静态最终字符串都是引用。
Jeffrey Blattman 2013年

30
但是java.lang.String 不可变的,并且还是一种特殊的类(请参阅String.intern(),有关Sring池的文档等)
Aleksander Adamowski

3
不可变是指对象的状态在构造后无法更改。请参阅下面的我的帖子。记录器不一定是可变的。
Jeffrey Blattman

4
如果有人仍然在意这个问题,请在github.com/checkstyle/checkstyle/issues/23上分享想法,以区分需求大写的地方和需求不大的地方。
罗曼·伊万诺夫

2
@Jeach我不认为不变性与状态如何变化有关,而与状态如何变化无关。而且,什么是用户?外部用户正在运行该程序?您是否会区分用户按下按钮所修改的状态与计时器以某个随机间隔触发的状态所进行的区分?(我不这么认为)。
Jeffrey Blattman 2014年

235

为了给Crunchdog的答案增加更多价值,《Java编码样式指南》在3.3字段命名中对此进行了说明。

用作常量的字段名称应全部为大写,并用下划线将单词分开。以下内容被视为常量:

  1. 所有static final原始类型(请记住,所有接口字段本质上都是static final)。
  2. 所有static final对象引用类型,其后绝不能使用“ .”(点)。
  3. static final绝不跟在“ [” 后的所有数组(方括号)。

例子:

MIN_VALUE, MAX_BUFFER_SIZE, OPTIONS_FILE_NAME

根据该约定,logger是一个static final对象的引用作为第2点规定,而是因为它跟着“ .”你用它每次,它不能被看作是一个常数,因此应该是小写。


11
我见过的最佳定义。链接的文档似乎已移至更新cs.bilgi.edu.tr/pages/standards_project/…–
罗伯特

15
我不明白第2点。什么是对象类型的示例,其后不加点。所有对象类型都继承自Object,您可以调用诸如此类的方法.equals
dogbane 2013年

6
你是对的。并且,当查看一些Java常量(例如Boolean.TRUE,Boolean.FALSE,TimeUnit.MINUTES,String.CASE_INSENSITIVE_ORDER或Collections.EMPTY_LIST)时,也可以跟在它们后面.
cbliard

5
@RomanIvanov我在这里再次发现它:scribd.com/doc/15884743/Java-Coding-Style-by-Achut-Reddy由Achut Reddy撰写,最后更新时间为2000
cbliard

1
我相信2的目的是指定仅将要比较的类视为常量。该类不打算被“使用”。当我看到SOME_CLASS.doStuff()时,我总是畏缩。这只是丑陋的编码。唯一的问题是在常量对象(字符串是常见示例)的常见情况下,该对象仅用于比较,但为了避免空检查,使用了yoda样式编码,因此在常量上调用了equals()。我想我会把这作为对2的警告
Robin

44

从有效的Java,第二版开始,

先前规则的唯一例外是“常量字段”,其名称应包含一个或多个大写单词,并用下划线字符分隔,例如VALUES或NEGATIVE_INFINITY。常量字段是静态最终字段,其值是不可变的。如果静态的final字段具有原始类型或不可变的引用类型(第15项),则它是一个常量字段。例如,枚举常量是常量字段。如果静态最终字段具有可变的引用类型,但如果引用的对象是不可变的,则它仍可以是常量字段。

总而言之,常量==静态最终形式,加上引用(相对于简单类型)的不变性。

查看slf4j记录器,http: //www.slf4j.org/api/org/slf4j/Logger.html

这是一成不变的。另一方面,JUL记录器是可变的。log4j记录器也是可变的。因此正确的是,如果您使用的是log4j或JUL,则应为“ logger”,如果您使用的是slf4j,则应为LOGGER。

请注意,上面链接的slf4j javadocs页面有一个示例,其中他们使用“ logger”,而不是“ LOGGER”。

这些当然只是约定,不是规则。如果您碰巧正在使用slf4j,并且您想使用“记录器”,因为您已经习惯了其他框架,或者如果键入起来更容易,或者出于可读性考虑,请继续。


2
基于这种推理,checkstyle的简单定义是不合适的吗?
罗伯特2012年

3
我不知道检查样式的规则。如果只是坚持认为任何静态的final应该是大写的,那么是的,那是错误的。
杰弗里·布拉特曼

5
Logger 接口 到底是如何不可变的?只有final classStringInteger)可以保证不变性。即使找不到SLF4J的任何可变实现Logger,也没有人可以阻止您自己编写。
Costi Ciudatu '16

因为界面中的方法本质上不允许突变。您是对的,尽管您可以实现该接口以实现可变的副作用。
杰弗里·布拉特曼

检查样式规则不够成熟,无法暗示可读性。通过模板化样式无法实现可读性,根据上下文的不同,可读性可能会有所不同。请参阅JDK代码,它不遵循任何样式模板,而是由专业人员制作的,可以显示某些内容。
DanielHári17年

37

我喜欢Google的支持(Google Java Style

每个常量都是一个静态最终字段,但并非所有静态最终字段都是常量。选择常量大小写之前,请考虑该字段是否真的像常量。例如,如果该实例的可观察状态中的任何一个可以更改,那么几乎可以肯定它不是常数。仅企图从不改变对象通常是不够的。

例子:

// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final Joiner COMMA_JOINER = Joiner.on(',');  // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }

// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final Set<String> mutableCollection = new HashSet<String>();
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};

6
我认为第一句话简洁地概括了这一点:“每个常量都是静态的最终字段,但并非所有静态的最终字段都是常量。” 使用机械思维很容易,并且每个静态的final字段都以大写形式(并且直到现在我一直在这样做),但这会错过语言的精妙之处。
ayahuasca 2015年

根据那句话,可以归结为该字段“真的感觉”像一个常数。我们是工程师,而不是精神科医生。
杰弗里·布拉特曼

“考虑一下……如果真的感觉像一个常数”。确实不应有人的想法进入工程领域。
Jeffrey Blattman,

然后在Guava的代码中,它们的名称为private static final Logger logger = Logger.getLogger(Finalizer.class.getName());
Kenston Choi

10

如果您使用自动化工具来检查您的编码标准,但违反了上述标准,则应对其进行固定或对这些标准进行修复。如果您使用的是外部标准,请修复代码。

Sun Java中的约定对于公共静态常量是大写的。显然,记录器不是恒定不变的,而是代表可变的东西(否则就没有指向它的方法调用的希望,以免发生某些事情)。对于非恒定最终字段没有特定的标准。


10
为什么说记录器不是恒定的?看来确实确实如此。日志记录的产生是调用其方法的副作用,但不要更改其可观察的状态。我错过了什么?
KLE

检查API。它确实有一对添加/获取方法。但是无论如何,你的推理还是有缺陷的。可以观察到日志记录(否则,有什么意义)。
汤姆·霍顿

3
如果它是一个StringBuilder而不是一个记录器,那么它可能显然是非恒定的。即使对于记录器,诸如Logger.setLevel()之类的方法也可以明显改变接收者。通常,大写是针对那些被语言视为常量并将内联的常量。
Pete Kirkham

5
记录器不是常量,因为它是对对象的引用。常量是无法更改的值。对象引用是最终的(因此无法更改对它的引用,例如,与其他对象交换或设置为null),但是对象本身可以。
Spoike

1
@JeffreyBlattman我不同意所有最终引用都应使用大写字母,但您可以自由采用自己喜欢的任何编码标准。很抱歉,您发现“可变对象”和“代表易变事物的对象”之间的区别令人困惑;一个示例可能是您的后帐号,它本身不会改变,但用于访问可变余额。查找指示符和有效数之间的区别以获取更多详细信息,或者查找莱布尼茨的单子,以了解不可变的事物如何表示可变性。
Pete Kirkham 2013年

7

如果您对此进行搜索,则可能会发现在某些情况下,记录器未定义为静态最终记录器。为此添加一些快速的复制粘贴功能,这可能可以解释它。

我们在所有代码中都使用LOGGER,这符合我们的命名约定(我们的CheckStyle对此很满意)。


我们甚至更进一步,利用Eclipse中严格的命名约定。我们使用以下代码模板创建一个新类:

    // private static final Logger LOGGER = Logger.getLogger(${enclosing_type}.class);

记录器已被注释掉,因为最初我们不需要它。但是,如果以后需要它,我们就取消注释。

然后在代码中,我们使用期望该记录器存在的代码模板。try-catch模板的示例:

    try {
      ${cursor} or some other template
    } catch (Exception t) {
      LOGGER.error("${methodName} ${method parameters}", t);
    }

我们还有更多使用它的模板。

严格的惯例使我们的工作效率和连贯的代码模板


5
捕获Throwable是不好的做法,除非您记录并重新抛出它。记住错误:OutOfMemeoryError等。事件异常不是很安全,在多线程应用程序中由您自己捕获和处理。
m_vitaly

2
Eclipse语法为:Logger.getLogger($ {enclosing_type} .class);
dogbane

@fahdshariff感谢您提供精确的语法。我更新了答案。
KLE

如果CheckStyle或PMD的“严格约定”有所帮助,那么为什么Guava和JDK源没有任何已应用的通用样式?例如,它们的来源在需要的地方有很多完整的内联块。可读性取决于上下文,因此对所有内容使用严格的样式约定会破坏基于上下文的决策,从而降低可读性。
DanielHári17年

6

我个人认为大写看起来确实很大。而且,由于它是一个与类行为没有直接关系的类,因此我认为使用logger代替不是主要问题LOGGER。但是,如果您要严格学习,请使用LOGGER


4

不要忘记PMD会尊重您的评论

// NOPMD

在里面。这将导致PMD从其检查中跳过该行,这将允许您选择所需的样式。


6
或不使用PMD,它们总是错误的,并且您的代码是完美的
IAdapter,2009年

1
如果您每次都需要排除检查,那么检查就没有意义了。
keiki 2014年

无法同意更多-但是...了解排除意见很有用
Fortyrunner 2014年

3

通常常量是大写的。

但是,如果使用slf4j外观,则记录器不应是静态的,而是应查找包含类的每个“新”类。这样可以避免在Web容器中出现一些令人讨厌的类加载器问题,此外,它还允许logger框架根据调用上下文执行特殊的操作。


2

我更喜欢“ logger”,即小写。原因不是它是一个常数还是一个常数(可变或不可变)。如果使用该推理,则如果我们更改日志记录框架(或者如果框架更改记录器的可变性),则必须重命名变量。

对我来说,其他原因更为重要。

  1. 记录器是类中的影子对象,不应突出显示,因为它未实现主要逻辑。如果我们使用“ LOGGER”,那么它在代码中就非常吸引眼球。

  2. 有时,记录器是在实例级别声明的(即不是静态的),甚至被注入为依赖项。如果我决定更改获取记录器的方式,则我不想更改代码。代码稳定性。这种(在许多情况下都是假设的)更改是我更喜欢小写字母的另一个原因。


1

如果您的编码标准-如果有的话-说它应该是大写的,那么可以。

我看不出任何一种严格的理由可以选择一种方法。我认为这完全取决于您的个人喜好。您公司的编码标准。

顺便说一句:我更喜欢“ LOGGER” ;-)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.