Java 如何以多种方式定义私有静态final字段很重要
两者的区别是什么Java 如何以多种方式定义私有静态final字段很重要,java,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/"; 我发现使用上面
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
测试更容易。我可能错了。只是想知道这两种方法是否相同。谢谢。第二种方法是编译时常量,因此在代码的其他地方,当您引用该值时,它实际上不会引用该字段-它只会烘焙该值
除非您开始通过反射或类似讨厌的方式来处理字段,否则您不应该注意到任何语义差异,但这将是字节码的差异。变量的值是什么根本没有区别,唯一的区别是如何设置变量以及在运行时如何解释变量 对于第一个变量,您通过调用函数并使用该函数返回值来设置变量,这样直到程序运行后才设置变量
在第二次调用中,您只是通过使用赋值立即进行设置,而不是调用函数,并将该函数的返回值赋给变量,以便在编译程序时而不是在运行时设置该变量。正如其他答案所述,在第一种情况下,变量是在运行时分配的,而在第二种情况下,变量大部分是在编译时内联的。即使不使用反射,这也很重要 两个具体案例提供了相互矛盾的建议: 案例1:循环参考 在一些涉及循环类引用的罕见情况下,最好使用文本字符串初始化,因为使用表达式初始化的常量可能不会在需要时立即初始化,从而导致意外的
null
s
例如,下面代码的输出是
1null
2foo
pkg/tmp.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) { }
}
pkg/tmp2.java:
package pkg;
class tmp2 {
public static void go(int n) {
System.out.println(n + tmp.FOO);
}
}
案例2:单独汇编
如果编译定义常量的Class1
,然后分别编译使用该常量的Class2
,并且Class1
中的常量可能会在未来版本的代码中更新,那么最好通过方法调用进行初始化,以防止编译器内联该值。如果常量是从文本字符串初始化的,那么Class2
将在编译时将旧的常量值放入其中,并且在运行时根本不会引用Class1
中的字段
一种常见的情况是,所讨论的常量位于库中,您根据库的v1.0编译项目,但使用库的v2.0部署它。更糟糕的是,如果在库的v2.0中,常量是从表达式初始化的,那么查看库的v2.0就无法发现问题
其他注释
- 两个字符串文字与
的串联将在编译时进行计算,被视为字符串文字,并作为字符串文字进行内联+
- JDK附带了一个名为
的程序,它可以从类中提取和分解字节码,这对于跟踪类似问题非常有用。IDE可能提供类似的反汇编/反编译功能javap
getURL()
,那么在初始化JDBC\u URL
时也可以使用它,以保持一致性。但可能并没有什么真正的区别,似乎第二种方法更有效readable@JakubBibro是的,但如果他添加了一个URL设置程序,需要稍后获取URL的值,该怎么办?因此,如果修改getter,则第一个表达式将更加模块化。@jgr208没错,这是一个非常重要的区别,第二个表达式是一个常量表达式。@SotiriosDelimanolis这是真的。因此,它确实在运行时节省了一点时间,但就超出范围而言,除非他在嵌入式系统上,或者有很大的限制,否则不应该对性能产生影响