Подтвердить что ты не робот

Почему NullPointerException?

У меня есть абстрактный класс и производный класс. Посмотрите на предоставленный код: -

public abstract class Parent{
    public Parent(){
        init();
    }

    public abstract void init();    
}



public class Child extends Parent{
    private String mTitle = null;

    public Child(){
        super();
        System.out.println(mTitle.toString());
    }       

    public void init(){
        mTitle = "It' a test";
    }    
}

Когда я исполню вышеуказанный код, он выкинет NullPointerException при печати значения mTitle. Если вы проверите код в конструкторе родителя, я вызвал абстрактный метод, который будет вызван методом init производного класса, В абстрактном методе я инициализирую значение значения mTitle как = "Это тест";

После вызова родительского конструктора производного класса необходимо вызвать System.out.println.

Если это так, то почему он бросает исключение NullPointerException.

Но, если я просто оставлю задание mTitle, он не будет выбрасывать исключение, например: -

private String mTitle;

Если инициализация переменной возникает при вызове contruct класса, и мы знаем, что по умолчанию глобальный объект инициализируется в null. Но в этом случае он не будет выбрасывать исключение.

4b9b3361

Ответ 1

Как и в JLS §12.5 (Создание экземпляров нового класса), следующая процедура используется при создании экземпляра:

  • Назначьте аргументы для конструктора вновь созданным переменным параметра для этого вызова конструктора.

  • Если этот конструктор начинается с явного вызова конструктора (§8.8.7.1) другого конструктора в том же классе (с использованием этого), то затем оценивайте аргументы и обрабатывайте вызов конструктора рекурсивно, используя эти пять шагов. Если вызов конструктора завершается внезапно, то эта процедура завершается внезапно по той же причине; в противном случае, перейдите к шагу 5.

  • Этот конструктор не начинается с явного вызова конструктора другого конструктора в том же классе (используя это). Если этот конструктор относится к классу, отличному от Object, то этот конструктор начнет с явного или неявного вызова конструктора суперкласса (используя super). Оцените аргументы и обработайте вызов конструктора суперкласса рекурсивно, используя эти пять шагов. Если вызов конструктора завершается внезапно, то эта процедура завершается внезапно по той же причине. В противном случае перейдите к шагу 4.

  • Выполните инициализаторы экземпляра и инициализаторы переменных экземпляра для этого класса, присваивая значения инициализаторов переменной экземпляра соответствующим переменным экземпляра в порядке слева направо, в котором они отображаются в текстовом виде в исходном коде для класс. Если выполнение любого из этих инициализаторов приводит к исключению, то никакие новые инициализаторы не обрабатываются, и эта процедура завершается внезапно с тем же исключением. В противном случае перейдите к шагу 5.

  • Выполните оставшуюся часть тела этого конструктора. Если это выполнение завершается внезапно, то эта процедура завершается внезапно по той же причине. В противном случае эта процедура завершится нормально.

Это означает, что ваш вызов super() и последующий вызов переопределенного метода init() выполняются до инициализации переменной экземпляра с помощью null, которая отбрасывает результат метода init и перезаписывает любое значение, являющееся присвоенный mTitle значением null.

Это приводит к следующему Золотому правилу: никогда не вызывайте неконфиденциальные методы в конструкторе!

Ответ 2

Согласно разделу 12.5 JLS, конструктор суперкласса будет запускаться перед инициализатором для mTitle, что означает, что он будет установлен вернитесь к null после того, как он установлен на "It a test".

Ответ 3

Согласно раздел 12.5 JLS, конструктор суперкласса будет выполняться перед конструктором производного класса.

Инициализация глобальной переменной - это вызов во время вызова constuctor. Поэтому, когда конструктор суперкласса вызывает абстрактный метод и устанавливает значение mTitle в реализации метода abstarct, он будет устанавливать первое значение this = "это тест". После завершения суперклассического класса он вызовет конструктор производного класса и когда вызов конструктора производного класса сначала инициализирует свою глобальную переменную, которая устанавливает значение mTitle в значение null. Как упоминается в коде

private String mTitle = null;

Но во втором случае mTitle не присваивают никакого значения в коде как упоминание

private String mTitle;

поэтому он примет значение по умолчанию, которое мы назначили в реализованном abstarct-методе init, "это тест", поэтому он не будет генерировать исключение.