NullPointerException | enum构造函数中的`this`导致NPE

public class Test { public static void main(String[] args) { Platform1 p1=Platform1.FACEBOOK; //giving NullPointerException. Platform2 p2=Platform2.FACEBOOK; //NO NPE why? } } 

 enum Platform1{ FACEBOOK,YOUTUBE,INSTAGRAM; Platform1(){ initialize(this); }; public void initialize(Platform1 platform){ switch (platform) { //platform is not constructed yet,so getting `NPE`. //ie. we doing something like -> switch (null) causing NPE.Fine! case FACEBOOK: System.out.println("THIS IS FACEBOOK"); break; default: break; } } } 

 enum Platform2{ FACEBOOK("fb"),YOUTUBE("yt"),INSTAGRAM("ig"); private String displayName; Platform2(String displayName){ this.displayName=displayName; initialize(this); }; public void initialize(Platform2 platform){ switch (platform.displayName) { //platform not constructed,even No `NPE` & able to access its properties. //switch (null.displayName) -> No Exception Why? case "fb": System.out.println("THIS IS FACEBOOK"); break; default: break; } } } 

任何人都可以解释为什么在Platform1NullPointerException但在Platform2没有。 在第二种情况下,我们如何能够访问枚举对象及其属性,甚至在构造对象之前?

究竟。 正如@PeterS在正确构造之前提到的那样使用枚举导致NPE,因为在未构造的枚举上调用了values()方法。

还有一点,我想在此补充说, Platform1Platform2都试图在switch()中使用未构造的枚举,但NPE仅在Platform1 。 这背后的原因如下: –

  public void initialize(Platform1 platform){ switch (platform) { 

Platform1 enum的上面一段代码是在switch中使用platform enum对象,其中使用了内部$SwitchMap$Platform1[]数组并初始化了这个数组使用了values()方法,因此得到了NPE。 但是在Platform2switch (platform.displayName)displayName上的比较,它已经初始化并且发生了字符串比较,因此没有NPE。

以下是反编译代码的片段: –

PLATFORM1

  static final int $SwitchMap$Platform1[] = new int[Platform1.values().length]; 

PLATFORM2

 switch ((str = platform.displayName).hashCode()) { case 3260: if (str.equals("fb")) { 

在正确构建枚举之前尝试处理枚举时,你不能这样做。 (如在完整的东西中构建)。 您会注意到该错误正在尝试引用枚举的值部分:

 Caused by: java.lang.NullPointerException at Platform1.values 

在处理对象之前,您需要允许对象在内部进行正确的初始化。 这将有效:

 public static void main(String[] args) { Platform1 p1=Platform1.FACEBOOK; p1.initialize(p1); //Platform1.YOUTUBE giving NullPointerException why? Platform2 p2=Platform2.FACEBOOK; //NO NPE } enum Platform1{ FACEBOOK,YOUTUBE,INSTAGRAM; Platform1(){ //initialize(this); }; 

显然,您的初始化函数应该重命名,因为它只是报告值。 您的第二个示例提供了值,因此正常工作。

来自其中一个Java文档:

枚举声明定义了一个类(称为枚举类型)。 枚举类主体可以包括方法和其他字段。 编译器在创建枚举时会自动添加一些特殊方法。 例如,它们有一个静态值方法,该方法返回一个数组,该数组按照声明的顺序包含枚举的所有值。 此方法通常与for-each构造结合使用,以迭代枚举类型的值。 例如,下面Planet类示例中的代码遍历太阳系中的所有行星。

正如已经指出的那样,enums上的switch在内部调用了values方法,但只有在初始化所有枚举常量后才会初始化:

 Caused by: java.lang.NullPointerException at Platform1.values(Test.java:17) at Platform1$1.(Test.java:25) ... 4 more 

Platform2 ,这不会发生,因为switch到字符串。

更面向对象的方法是创建一个initialize方法,构造函数调用该方法并被需要专门初始化的常量覆盖:

 enum Platform3 { FACEBOOK { @Override protected void initialize() { System.out.println("THIS IS FACEBOOK"); } }, YOUTUBE, INSTAGRAM; Platform3() { initialize(); } // this acts as the default branch in the switch protected void initialize() { System.out.println("THIS IS OTHER PLATFORM: " + this.name()); } } 

您正在获取NPE,因为您正在引用尚未构建的实例。 在完成构造FACEBOOK实例的Platform1构造函数之前, Platform1.FACEBOOKnull

Platform1构造函数调用initialize ,其中包含一个switch 。 该switchcasePlatform1.FACEBOOK 。 由于FACEBOOK的构造函数尚未返回,因此FACEBOOK引用为空。 Java语言规范不允许在switch使用null ,它会像您找到的那样抛出运行时exception。

以下示例显示了初始化生命周期:

 public class Test { // v--- assign to `PHASE` after creation static final Serializable PHASE = new Serializable() {{ // v---it is in building and doesn't ready... System.out.println("building:" + PHASE); //NULL System.out.println("created:" + this);//NOT NULL }}; public static void main(String[] args) { // v--- `PHASE` is ready for use System.out.println("ready:" + PHASE); //NOT NULL } } 

简而言之,枚举常量在构建过程中未初始化。 换句话说,当前的枚举实例将被分配给相关的常量,直到整个建筑工作完成。

switch语句将调用values()方法,但是枚举常量在构建中并且没有准备好使用。 为了避免客户端代码更改其内部$VALUES数组, values()克隆其内部数组,因为枚举常量尚未就绪,然后抛出NullPointerException 。 这里是字节码values()方法和静态初始化块:

 static {}; 10: putstatic #14 // Field FACEBOOK:LPlatform1; 23: putstatic #16 // Field YOUTUBE:LPlatform1; // putstatic //Other Fields 61: putstatic #1 // Field $VALUES:[LPlatform1; // `$VALUES` field is initialized at last ---^ 

 public static Platform1[] values(); // v--- return null 0: getstatic #1 // Field $VALUES:[LPlatform1; // v--- null.clone() throws NullPointerException 3: invokevirtual #2 // Method "[LPlatform1;".clone:()Ljava/lang/Object; 

简短回答: 调用初始化方法的地方,当枚举类被类加载器(正在进行)加载时被调用,因此您无法访问类级属性,即静态。 您可以在哪里访问非staic属性。

1.第一次在代码中引用Enum时,会调用enum的构造函数。

 Platform1 p1=Platform1.FACEBOOK; 

该行将使用类加载器为Enum Platform1加载类。 并且将为该枚举中的每个条目/实例调用构造函数,此处为3。

下面的代码将打印三个哈希码。

  enum Platform1{ FACEBOOK,YOUTUBE,INSTAGRAM; Platform1() { initialize(this); }; public void initialize(Platform1 platform){ System.out.println(platform.hashCode()); // it will print three hash codes switch (platform.hashCode()) { case 1: System.out.println(platform); break; default: break; } } } 

因此,简而言之,当调用此初始化方法时,Enum类未完全加载且正在进行中。 因此,您无法在该时间点访问该枚举的任何静态属性或方法。

2.当你使用下面的行时,它调用values()一个静态方法,

  public void initialize(Platform1 platform){ switch (platform) { } } 

只需将静态方法更改为某种等效的非静态方法即可。 喜欢,

  enum Platform1{ FACEBOOK,YOUTUBE,INSTAGRAM; Platform1() { initialize(this); }; public void initialize(Platform1 platform){ System.out.println(platform.hashCode()); switch (platform.toString()) { // toString() is non static method case "FACEBOOK": System.out.println(platform); break; default: break; } } } 

所以你的问题的答案是,当Enum课程被初始化时,你

  • 无法访问任何静态的东西

  • 但可以访问非静态的东西

因此下面的代码对你有用,

 enum Platform2{ FACEBOOK("fb"),YOUTUBE("yt"),INSTAGRAM("ig"); private String displayName; Platform2(String displayName){ this.displayName=displayName; initialize(this); }; public void initialize(Platform2 platform){ switch (platform.displayName) { case "fb": System.out.println("THIS IS FACEBOOK"); break; default: break; } } } 

4.如果你将displayName更改为static,那么事情就会破裂。

  enum Platform2{ FACEBOOK("fb"),YOUTUBE("yt"),INSTAGRAM("ig"); private static String displayName = "FACEBOOK"; Platform2(String displayName){ initialize(this); }; public void initialize(Platform2 platform){ switch (platform.displayName) { case "FACEBOOK": System.out.println(platform); break; default: break; } } }