如何以多种方式定义私有静态最终字段很重要

时间:2016-05-25 17:00:38

标签: java

之间有什么区别
 private static final String JDBC_URL = getURL();

然后将getURL()定义为:

private static String getURL() {
        return "jdbc:mysql://localhost:3306/";
    }

使用以下代码:

private static final String JDBC_URL = "jdbc:mysql://localhost:3306/";

我发现使用上述内容编写mock测试更容易。我可能错了。只是想知道两种方式是否相同。感谢。

3 个答案:

答案 0 :(得分:2)

变量值的唯一区别在于,设置变量的方式以及在运行时如何解释变量的方式不同。

使用第一个变量通过调用函数并使用该函数返回值来设置变量,因此在程序运行之前不会设置变量。

在第二个调用中,您只是通过使用赋值而不是调用函数并将该函数的返回值赋给变量来立即设置,以便在编译程序时也设置变量,而不是在运行时设置。

答案 1 :(得分:2)

正如其他答案所述,在第一种情况下,变量是在运行时分配的,而在第二种情况下,大多数是在编译时内联的。即使不使用反射,这也很重要。

两个具体案例,提供了相互矛盾的建议:

案例1:循环引用

在一些涉及循环类引用的罕见情况下,使用文字字符串初始化更好,因为使用表达式初始化的常量可能无法在需要时立即初始化,从而导致令人惊讶的{ {1}}秒。

E.g。以下代码的输出是

null

PKG / tmp.java:

1null
2foo

PKG / tmp2.java:

package pkg;
class tmp {
  static { tmp2.go(1); }
  public static final String FOO = foo();
  static { tmp2.go(2); }
  private static String foo() { return "foo"; }
  public static void main(String[] args) { }
}

案例2:单独编译

如果编译定义常量的package pkg; class tmp2 { public static void go(int n) { System.out.println(n + tmp.FOO); } } ,则单独编译使用常量的Class1Class2中的常量可能会在未来版本的代码中更新,它是最好通过方法调用初始化以防止编译器内联值。如果常量是从文字字符串初始化的,那么Class1将在编译时将旧的常量值放入其中,并且根本不会在运行时引用Class2中的字段。

一个常见的情况是,如果有问题的常量在库中,并且您针对库的v1.0编译项目,但是使用库的v2.0进行部署。更糟糕的是,如果在库的v2.0中常量从表达式初始化,则无法通过查看库的v2.0来发现问题。

其他说明

  • 两个字符串文字与Class1的串联将在编译时进行评估,被视为字符串文字,并且如此内联。
  • JDK附带了一个名为+的程序,它可以从类中提取和反汇编字节码,这对于跟踪这样的问题非常有用。您的IDE可能提供类似的反汇编/反编译功能。

答案 2 :(得分:1)

第二种方式是编译时常量,所以在引用该值时代码中的其他地方,它实际上不会引用该字段 - 它只会将值烘焙。

除非你通过反射开始搞乱这个字段或类似讨厌的东西你不应该注意到任何语义差异,但是字节码的差异。