带有静态变量的 NullPointerException [英] NullPointerException with static variables

查看:54
本文介绍了带有静态变量的 NullPointerException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚遇到了非常奇怪的(对我来说)java 的行为.我有以下课程:

I just hit very strange (to me) behaviour of java. I have following classes:

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 都是静态初始化的单例,所以在类加载期间.构造函数是私有的,所以它们不能是在其他任何地方初始化.
  2. Unit 类包含对 MKUnit.INSTANCE 的静态 final 引用和MeterUnit.INSTANCE
  1. MKUnit and MeterUnit are both singletons initialized statically, so during class loading. Constructors are private, so they can't be initialized anywhere else.
  2. Unit class contains static final references to MKUnit.INSTANCE and MeterUnit.INSTANCE

我希望:

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

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

But when I run my test case in console with maven my result is:

 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 并且在控制台中我有:

And the funny part is that, when I run this test from eclipse via JUnit runner everything is fine, I have no NullPointerException and in console I have:

Unit.METERS: m
Unit.KM: km

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

So the question is: what can be the reason that KM variable in Unit is null (and in the same time METERS is not null)

推荐答案

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

Static initialization can be tricky. You have an interdependency between A -> B and B -> A. The reason this is a bad idea is because of how the JVM starts loading statics from the top down in a class - if it hits a new class it has yet to initialize, it waits until it initializes that class, and its dependencies, recursively, until everything is ready, then proceeds.

除非它已经加载了一个类.如果 A 引用 B,而 B 引用 A,则不能再次开始加载 A,否则将无限循环(因为 A 会再次加载 B,从而加载 A).所以,在那种情况下,它基本上说已经开始加载,没什么可做的,继续".

Except when it's already loading a class. If A refers to B, and B refers to A, it can't start loading A a second time, or it would be an infinite loop (because A would load B again, which loads A). So, in that case, it basically says "already started loading that, nothing more to do, continuing on".

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

Moral of the story: depending on the ordering of the loading of classes, KMUnit.INSTANCE may not be initialized when you hit this line:

public static final Unit KM = KMUnit.INSTANCE;

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

Imagine that you're the JVM and you start loading KMUnit. It would have to load Unit, the first time it sees it, in order to, e.g. create a object that is a subclass of Unit when we get to the first creation (or perhaps before - I'm a touch foggy on JVM static loading). But that in turn triggers initialization of statics in Unit including this:

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

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

Ok. Now Unit is done loading, and we finish constructing a KMUnit for KMUnit.INSTANCE... but wait - we already set KM = KMUnit.INSTANCE, which was null at the time. So it remains null. Oops.

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

On the other hand, if Unit loads first, then it waits for KMUnit to load before initializing, so KMUnit.INSTANCE is set when we actually run the initializer.

我想.我有点睡眠不足,而且我不是类加载方面的专家.

I think. I'm a bit sleep deprived, and I'm not an expert in classloading.

这篇关于带有静态变量的 NullPointerException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆