是否需要调用构造函数来将变量初始化为默认值?

时间:2018-11-02 07:10:47

标签: java initialization

在这里,我尝试遍历几乎所有有关变量初始化的文章,但我仍然不了解有关变量初始化的几件事。对于我来说,尚不清楚我们是否必须调用构造函数将实例变量初始化为默认值,还是可以在不调用构造函数的情况下发生?例如,约定是我们用Java编写的每个类都在其构造函数中调用超类构造函数以“初始化超类的变量”。这到底是什么意思?我们是否必须调用超类构造函数将超类的实例变量初始化为默认值?甚至没有invokin超级类构造函数实例变量都具有默认值,而我们正在执行此操作以将构造函数中描述的值或在声明期间给定的值传递给它们?第二件事是,我们可以用几种方式初始化变量,方法是不给它们赋值(它们将被设置为默认值),而在声明中赋值,例如:

private int number = 10;

或在构造函数中指定值。对我来说很明显,我们必须调用构造函数将变量初始化为构造函数中指定的值,但是其他两个示例又如何呢?我们还必须调用构造函数将实例变量初始化为这些值吗?有人还可以给我命令以简单的方式创建此命令吗?我知道这里有很多关于对象实例化和初始化的顺序的文章,我已经阅读了全部,但是我仍然不了解那里的很多内容。 并告诉我是否错,但据我所知,问题的答案是我们必须调用超类构造函数将超类字段初始化为其默认值,因为否则,将没有意义进行调用如果超类的实例变量已经使用默认值初始化而没有调用构造函数,则必须使用超类构造函数-因为我们不必担心我们的变量如果未初始化-则使用默认值。

@EDIT:明确地说,我的主要问题是: 实例变量何时获得默认值?在构造函数之前还是在构造函数之前?是否必须调用构造函数来初始化实例变量,或者甚至在构造函数调用之前就已指定默认值?

3 个答案:

答案 0 :(得分:1)

在面向对象的编程语言中,“对象”扮演关键角色。因此,必须启动该对象。我无法完全理解您的要求。.据我所知。

您必须实例化一个类以使用其字段和方法。为此,您要像这样分配

Class_Name object = new Class_Name();

没问题,是否在调用类中创建默认构造函数,它将创建一个默认构造函数。

但是,一旦使用参数创建了构造函数,就必须创建一个默认构造函数来初始化上述对象。

您可以按照描述并通过参数化构造函数为类中的变量分配值。最好编写私有修饰符来初始化变量(封装概念)。

您可以通过在构造函数中传递参数来更改实例化变量的值。

如果尚未为变量输入任何值。编译器将为此设置默认值。 看看这篇文章 https://www.javaworld.com/article/2076614/core-java/object-initialization-in-java.html

答案 1 :(得分:1)

根据JLS 4.12.5

  

程序中的每个变量都必须有一个值,然后才是   已使用:

     

每个类变量,实例变量或数组组件为   在创建时用默认值初始化(第15.9节,第15.20.2节)

     

对于字节类型,默认值为零,即   (字节)0。

     

对于short类型,默认值为零,即   (短)0。

     

对于int类型,默认值为零,即0。

     

对于long类型,默认值为零,即0L。

     

对于float类型,默认值为正零,即0.0f。

     

对于double类型,默认值为正零,即0.0d。

     

对于char类型,默认值为空字符,即   '\ u0000'。

     

对于布尔类型,默认值为false。

     

对于所有引用类型(第4.3节),默认值为null。

     

每个方法参数(第8.4.1节)初始化为对应的   方法的调用者提供的参数值(第15.12节)。

     

每个构造函数参数(第8.8.1节)都初始化为   类实例创建提供的相应参数值   表达式(第15.9节)或显式构造函数调用(第8.8.7节)。

     

将异常参数(第14.20节)初始化为引发的对象   代表例外(第11.3节,第14.18节)。

     

必须明确给局部变量(第14.4节,第14.14节)赋值   在使用之前,通过初始化(第14.4节)或分配   (§15.26),可以使用确定规则进行验证   分配(§16(确定分配))。

因此,如果您的字段在使用前未在任何地方初始化,则对象(引用)类型的初始(或默认)值为null,图元false的初始值为boolean类型,或其他任何原始类型的00的{​​{1}}是char字符)。

如果该字段已初始化,那么我们需要查看顺序。

null

这将生成:

private class A {
    protected int a = 2;

    public A() {
        System.out.println("Printing from constructor of A");
        printValues();
        System.out.println();
    }

    public void printValues() {
        System.out.println("a = " + a);
    }
}

private class B extends A {
    private int b = 3;
    private int c = initC();

    public B() {
        super();

        System.out.println("Printing from constructor of B");
        printValues();
        System.out.println();
    }

    @Override
    public void printValues() {
        super.printValues(); // Call parent implementation

        System.out.println("b = " + b);
        System.out.println("c = " + c);
    }

    private int initC() {
        System.out.println("Printing from initC()");
        printValues();
        System.out.println();

        return 4;
    }

    public static void main(String[] args) {
        new B();
    }
}

Printing from constructor of A a = 2 b = 0 c = 0 Printing from initC() a = 2 b = 3 c = 0 Printing from constructor of B a = 2 b = 3 c = 4 (它是父类)的构造函数中,A(属于a)已经用A初始化了。其他2个字段保持未初始化状态,返回JLS 4.12.5指定的值。

然后2的构造函数完成并返回到A的构造函数(子类)。您可能希望它进入B构造函数部分,但是在此之前发生了其他事情-调用B。此时,我们可以看到initC()也已经初始化,但是b尚未初始化,因为c应该返回用于初始化initC()的值。 / p>

最后,我们看到所有3个字段都已初始化。

这是顺序:

  1. 大多数超类'字段首先被初始化。
  2. 从超类的构造函数返回时,子类将初始化其自己的字段。
  3. 构造函数继续执行,这使您可以使用初始化的值。

因此,在字段声明中初始化内联允许您确保即使在构造函数中使用该字段时,该字段也具有该值,而在构造函数中初始化只能确保在 之后退出该值构造函数(子类也可以确保也已初始化)。

答案 2 :(得分:1)

  

何时实例变量获取默认值?在构造函数中还是在构造函数之前?

在构造函数之前。如果它们在构造函数之后得到其默认值 ,那么在构造函数内部将它们设置为其他值将毫无意义。

  

是否必须调用构造函数来初始化实例变量,还是在构造函数调用之前分配默认值?

没关系,当您创建新实例时,构造函数总是被调用 。如果编写的子类没有显式调用其超类的构造函数,则该超类的no-args构造函数将被自动调用。

例如,这个:

class B extends A {
     public B() {}
}

等效于此:

class B extends A {
     public B() {
         super();
     }
}

如果不显式调用超类构造函数,则将调用超类的无参数构造函数。如果不存在这样的构造函数,则您的代码将无法编译。

总是调用一个构造函数。