gpt4 book ai didi

java - 带有静态变量的 NullPointerException

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:17:52 28 4
gpt4 key购买 nike

我刚刚遇到了 java 的(对我来说)非常奇怪的行为。我有以下类(class):

public abstract class Unit {
public static final Unit KM = KMUnit.INSTANCE;
public static final Unit METERS = MeterUnit.INSTANCE;
protected Unit() {
}
public abstract double getValueInUnit(double value, Unit unit);
protected abstract double getValueInMeters(double value);
}

和:

public class KMUnit extends Unit {
public static final Unit INSTANCE = new KMUnit();

private KMUnit() {
}
//here are abstract methods overriden
}

public class MeterUnit extends Unit {
public static final Unit INSTANCE = new MeterUnit();

private MeterUnit() {
}

///abstract methods overriden
}

还有我的测试用例:

public class TestMetricUnits extends TestCase {

@Test
public void testConversion() {
System.out.println("Unit.METERS: " + Unit.METERS);
System.out.println("Unit.KM: " + Unit.KM);
double meters = Unit.KM.getValueInUnit(102.11, Unit.METERS);
assertEquals(0.10211, meters, 0.00001);
}
}
  1. MKUnit 和 MeterUnit 都是静态初始化的单例,所以在类加载期间。构造函数是私有(private)的,所以他们不能在其他任何地方初始化。
  2. 单元类包含对 MKUnit.INSTANCE 和MeterUnit.INSTANCE

我希望:

  • 加载 KMUnit 类并创建实例。
  • 加载 MeterUnit 类并创建实例。
  • 单元类已加载,KM 和 METERS 变量已初始化,它们是最终的,因此无法更改。

但是当我使用 maven 在控制台中运行我的测试用例时,我的结果是:

 T E S T S

Running de.audi.echargingstations.tests.TestMetricUnits<br/>
Unit.METERS: m<br/>
Unit.KM: null<br/>
Tests run: 3, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.089 sec <<< FAILURE! - in de.audi.echargingstations.tests.TestMetricUnits<br/>
testConversion(de.audi.echargingstations.tests.TestMetricUnits) Time elapsed: 0.011 sec <<< ERROR!<br/>
java.lang.NullPointerException: null<br/>
at <br/>de.audi.echargingstations.tests.TestMetricUnits.testConversion(TestMetricUnits.java:29)
<br/>

Results :

Tests in error:
TestMetricUnits.testConversion:29 NullPointer

有趣的是,当我通过 JUnit runner 从 eclipse 运行这个测试时,一切都很好,我没有 NullPointerException 并且在控制台中我有:

Unit.METERS: m
Unit.KM: km

所以问题是:Unit 中的 KM 变量为空的原因(同时 METERS 不为空)

最佳答案

静态初始化可能很棘手。您在 A -> B 和 B -> A 之间存在相互依赖关系。这是一个坏主意的原因是 JVM 如何开始在类中自上而下加载静态信息 - 如果它遇到一个尚未初始化的新类,它一直等到它初始化该类及其依赖项,递归地,直到一切准备就绪,然后继续。

除非它已经在加载一个类。如果A引用了B,B又引用了A,就不能再开始加载A了,否则就是死循环(因为A又加载了B,又加载了A)。所以,在那种情况下,它基本上是说“已经开始加载了,没什么可做的,继续”。

故事的寓意:根据类加载的顺序,当您点击此行时,KMUnit.INSTANCE 可能不会初始化:

public static final Unit KM = KMUnit.INSTANCE;

假设您是 JVM,您开始加载 KMUnit。它必须在第一次看到它时加载 Unit,以便,例如当我们第一次创建时(或者可能之前——我对 JVM 静态加载有点迷糊),创建一个 Unit 的子类对象。但这反过来会触发 Unit 中的静态初始化,包括:

public static final Unit KM = KMUnit.INSTANCE;
public static final Unit METERS = MeterUnit.INSTANCE;

好的。现在 Unit 已完成加载,我们完成了为 KMUnit.INSTANCE 构造 KMUnit...但是等等 - 我们已经设置了 KM = KMUnit.INSTANCE,当时它是 null。所以它仍然是空的。糟糕。

另一方面,如果 Unit 首先加载,那么它会在初始化之前等待 KMUnit 加载,因此 KMUnit.INSTANCE 在我们实际运行初始化程序时设置。

我觉得。我有点 sleep 不足,而且我不是类加载方面的专家。

关于java - 带有静态变量的 NullPointerException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19856012/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com