简体   繁体   English

Java抽象类异常行为

[英]Java Abstract class Unusual behavior

Abstract Class: 抽象类:

   public abstract class ParentClass {
    private static ParentClass mpParentClass;

    public ParentClass() {
        mpParentClass = this;
    }

    public abstract void method1();

    public static ParentClass getInstance() {
        return mpParentClass;
    }
}

Child Class : 儿童班:

public class ChildClass extends ParentClass{
    @Override
    public void method1() {
        System.out.print("ChildClass class method");
    }
}

Test Class : 测试类:

public class TestClass {
    public static void main(String[] args) {
         ChildClass cl = new ChildClass();
        ParentClass.getInstance().method1();
    }
}

Here I have created an abstract class and a Child class which extends the parent Abstract class. 在这里,我创建了一个抽象类和一个扩展父抽象类的Child类。

Parent abstract class holds a reference to its own instance and returns the instance through a static method. 父抽象类包含对其自己的实例的引用,并通过静态方法返回实例。

In Test class, if I don't create an object of ChildClass, java throws NullPointerException. 在Test类中,如果我不创建ChildClass的对象,则java抛出NullPointerException。

But after creating object of ChildClass, and then querying instance of ParentClass and invoking abstract method, it calls method implemented by ChildClass. 但是在创建了ChildClass的对象,然后查询ParentClass的实例并调用抽象方法之后,它调用了ChildClass实现的方法。

I am unable to understand this behavior . 我无法理解这种行为。 Please anyone explain. 请有人解释一下。

The first time you Instantiate a ChildClass you use the default constructor of parentClass which instantiate the private field with the ChildClass type . 第一次实例化ChildClass时,使用parentClass的默认构造函数,该构造函数实例化具有ChildClass类型的私有字段。 If you don't do that, the private field mpParentClass is not instantiate. 如果不这样做,私有字段mpParentClass不会实例化。 So you have a NullPointerException 所以你有一个NullPointerException

ParentClass.getInstance() is a static method so it doesn't require an instance of your class to run. ParentClass.getInstance()是一个静态方法,因此它不需要运行类的实例。

By calling this method you will return the static member mpParentClass . 通过调用此方法,您将返回静态成员mpParentClass But by default this member contains a null reference. 但默认情况下,此成员包含null引用。

So without doing anything this will indeed result in a NullPointerException because you didn't call the constructor of ParentClass . 因此,如果不执行任何操作,这确实会导致NullPointerException因为您没有调用ParentClass的构造函数。


In your example you first make an instance of the ChildClass . 在您的示例中,您首先创建ChildClass的实例。

This will call the default constructor of that class. 这将调用该类的默认构造函数。 This default constructor has the standard behavior of calling the default constructor of the super class (by calling super() ). 这个默认构造函数具有调用超类的默认构造函数的标准行为(通过调用super() )。

So by instantiating the ChildClass you call the constructor of ParentClass that will set the mpParentClass datamember to this. 因此,通过实例化ChildClass您可以调用ParentClass的构造函数,该构造函数将mpParentClass datamember设置为this。 And here this refers to the instance of the ChildClass you are creating. 这里指的是您正在创建的ChildClass的实例。

So after construction mpParentClass will contain the newly created instance of the ChildClass . 因此在构造之后, mpParentClass将包含新创建的ChildClass实例。

Here's what's happening. 这是正在发生的事情。

When you call the constructor for your ChildClass , it's implicit that the first actual call in that method is to the superclass constructor. 当您调用ChildClass的构造函数时,隐含的是该方法中的第一个实际调用是超类构造函数。 If you had a superclass constructor that required/allowed alternate arguments, you could call it manually. 如果你有一个超类构造函数需要/允许备用参数,你可以手动调用它。 But it's happening for you. 但它正在发生在你身上。

When the superclass constructor is called, a static reference is being assigned to that new instance, which is a ChildClass instance. 调用超类构造函数时,会为该新实例分配static引用,该实例 ChildClass实例。 (Because that's what this is, in this case.) (因为这就是this ,在这种情况下。)

If you were to call: 如果你打电话:

new ChildClass();
new ParentClass() {
    public void method1() {
        System.out.println("Anonymous class!");
    }
};
ParentClass.getInstance().method1();

You would see "Anonymous class!" 你会看到"Anonymous class!" , because there is one static reference that is going to be reassigned every time you create any instance of a ParentClass implementation. ,因为每次创建ParentClass实现的任何实例时都会有一个静态引用被重新分配。

Regarding your NullPointerException - the only place where mpParentClass is assigned to a value is in the constructor for ParentClass . 关于NullPointerException - 将mpParentClass分配给值的唯一位置是ParentClass的构造函数。 If you never create an instance of a ParentClass , then this code will never be called, and mpParentClass will be left with its original value, which is null . 如果您从未创建ParentClass的实例,则永远不会调用此代码,并且mpParentClass将保留其原始值,即null Attempting to call a method or access a property on a null reference is what produces a NullPointerException . 尝试调用方法或访问null引用上的属性是产生NullPointerException

The question to ask is: If you never instantiate any of your implementations (by 'calling' their constructors), what do you expect the mpParentClass variable to be set to, if not null ? 要问的问题是:如果你从未实例化任何实现(通过'调用'它们的构造函数),你期望mpParentClass变量设置mpParentClass ,如果不是null

Parent class is abstract so you can't use the constructor directly. 父类是抽象的,因此您不能直接使用构造函数。 The static variable instance is null by default which leads to your NullPointerException. 默认情况下,静态变量实例为null,这会导致您的NullPointerException。 The only way to set this variable is by calling the constructor which is only called when a constructor of a child class is called. 设置此变量的唯一方法是调用构造函数,该构造函数仅在调用子类的构造函数时调用。

This is because you are only assigning to the static field mpParentClass in your ParentClass constructor. 这是因为您只是在ParentClass构造函数中指定静态字段mpParentClass Therefore, until you create an instance of ChildClass , mpParentClass is null. 因此,在创建ChildClass实例之前, mpParentClass为null。 In your example, the base class ParentClass constructor is called implicitly when you create an instance of the derived class ChildClass . 在您的示例中,在创建派生类ChildClass的实例时,将隐式调用基类ParentClass构造函数。

Might I suggest that you use the singleton pattern instead? 我建议您使用单例模式吗?

public class Singleton {
  // Private constructor prevents instantiation from other classes
  private Singleton() {}

 /**
   * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
   * or the first access to SingletonHolder.INSTANCE, not before.
   */
  private static class SingletonHolder { 
    private static final Singleton INSTANCE = new Singleton();
  }

  public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}

You're creating a ChildClass then calling its method1() through unnecessary trickery. 你正在创建一个ChildClass然后通过不必要的ChildClass调用它的method1()

You do realize that this will be a ChildClass instance at the ParentClass constructor, right? 您确实意识到this将是ParentClass构造函数中的ChildClass实例,对吧? It has to be, since the parent class is abstract and therefore there can never be a this instance that would represent it. 它必须是,因为父类是抽象的,因此永远不会有一个this实例,将代表它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM