Java 返回分配与分配然后返回。 (例如在单身人士中)
Java returning assignement vs assigning then returning. (E.g. in Singletons)
基本上,你可以有这样的代码:
发件人:Baeldung.com
public static synchronized ClassSingleton getInstance() {
if(instance == null) {
instance = new ClassSingleton();
}
return instance;
}
你可以有这样的代码:
我的版本:
public static synchronized ClassSingleton getInstance() {
return instance = (instance == null) ? new ClassSingleton() : instance;
}
下面的代码在任何方面都更简洁,但是 SonarLints 规则 java:S1121 认为这是不合规的(主要,代码味道)
除了 SonarLint 所说的可读性之外,这背后还有更多内容吗?
我有一种奇怪的感觉,我的版本总是在返回之前做一个赋值,这会不会影响性能?
很高兴听到你们的意见。
是的,它可能会对性能产生影响[可能可以忽略不计]。这可以通过尝试编译它并查看结果来测试。这是来自 Javac 16.0.1 的字节码:
Compiled from "ClassSingleton.java"
public class ClassSingleton {
public ClassSingleton();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static synchronized ClassSingleton getInstance();
Code:
0: getstatic #7 // Field instance:LClassSingleton;
3: ifnonnull 16
6: new #8 // class ClassSingleton
9: dup
10: invokespecial #13 // Method "<init>":()V
13: putstatic #7 // Field instance:LClassSingleton;
16: getstatic #7 // Field instance:LClassSingleton;
19: areturn
public static synchronized ClassSingleton getInstanceShort();
Code:
0: getstatic #7 // Field instance:LClassSingleton;
3: ifnonnull 16
6: new #8 // class ClassSingleton
9: dup
10: invokespecial #13 // Method "<init>":()V
13: goto 19
16: getstatic #7 // Field instance:LClassSingleton;
19: dup
20: putstatic #7 // Field instance:LClassSingleton;
23: areturn
}
通过 long=bad 的旧指标,第二个版本显然更差。不过,严肃地说,它在所有情况下都只执行 2 条额外指令。首先 dup
复制堆栈中的最后一项,然后它可以将该额外的项目用于 putstatic
,后者将 instance
分配给堆栈中的顶部值。我们可以推测,如果另一个线程未在 ClassSingleton
上同步,并且 instance
具有正确的属性,那么理论上我们可能会遇到一些奇怪的行为,其中 instance
可能未正确设置。然而,考虑到 synchronized
为我们处理了大部分,这似乎不太可能。
虽然最后,JIT 编译器可能会通过使用寄存器消除对 dup
的需求,并且它有很大的机会弄清楚它可以摆脱额外的 putstatic
作为出色地。但是,我对 JIT 编译器的经验不足,只能推测它的行为方式。
也就是说,只使用第一个版本。它更容易阅读并生成更短的字节码。
基本上,你可以有这样的代码:
发件人:Baeldung.com
public static synchronized ClassSingleton getInstance() {
if(instance == null) {
instance = new ClassSingleton();
}
return instance;
}
你可以有这样的代码:
我的版本:
public static synchronized ClassSingleton getInstance() {
return instance = (instance == null) ? new ClassSingleton() : instance;
}
下面的代码在任何方面都更简洁,但是 SonarLints 规则 java:S1121 认为这是不合规的(主要,代码味道)
除了 SonarLint 所说的可读性之外,这背后还有更多内容吗?
我有一种奇怪的感觉,我的版本总是在返回之前做一个赋值,这会不会影响性能?
很高兴听到你们的意见。
是的,它可能会对性能产生影响[可能可以忽略不计]。这可以通过尝试编译它并查看结果来测试。这是来自 Javac 16.0.1 的字节码:
Compiled from "ClassSingleton.java"
public class ClassSingleton {
public ClassSingleton();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static synchronized ClassSingleton getInstance();
Code:
0: getstatic #7 // Field instance:LClassSingleton;
3: ifnonnull 16
6: new #8 // class ClassSingleton
9: dup
10: invokespecial #13 // Method "<init>":()V
13: putstatic #7 // Field instance:LClassSingleton;
16: getstatic #7 // Field instance:LClassSingleton;
19: areturn
public static synchronized ClassSingleton getInstanceShort();
Code:
0: getstatic #7 // Field instance:LClassSingleton;
3: ifnonnull 16
6: new #8 // class ClassSingleton
9: dup
10: invokespecial #13 // Method "<init>":()V
13: goto 19
16: getstatic #7 // Field instance:LClassSingleton;
19: dup
20: putstatic #7 // Field instance:LClassSingleton;
23: areturn
}
通过 long=bad 的旧指标,第二个版本显然更差。不过,严肃地说,它在所有情况下都只执行 2 条额外指令。首先 dup
复制堆栈中的最后一项,然后它可以将该额外的项目用于 putstatic
,后者将 instance
分配给堆栈中的顶部值。我们可以推测,如果另一个线程未在 ClassSingleton
上同步,并且 instance
具有正确的属性,那么理论上我们可能会遇到一些奇怪的行为,其中 instance
可能未正确设置。然而,考虑到 synchronized
为我们处理了大部分,这似乎不太可能。
虽然最后,JIT 编译器可能会通过使用寄存器消除对 dup
的需求,并且它有很大的机会弄清楚它可以摆脱额外的 putstatic
作为出色地。但是,我对 JIT 编译器的经验不足,只能推测它的行为方式。
也就是说,只使用第一个版本。它更容易阅读并生成更短的字节码。