`
liujiawinds
  • 浏览: 136978 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

构造方法的初始化

    博客分类:
  • java
阅读更多

想像一下你正在用java写程序,并且用下面的代码初始化类 A 和 B 的对象:
  
 class A {
        int a = f();
        int f() {
            return 1;
        }
    }  
    class B extends A {
        int b = a;
        int f() {
            return 2;
        }
    }   
    public class CtorDemo1 {
        public static void main(String args[]) {
            B bobj = new B();
            System.out.println(bobj.b);
        }
    }


现在,好像很明显的当初始化完成后,bobj.b的值将是1。毕竟,类B中的b 的值是用类A中的a的值初始化的,而a 是用f 的值初始化的,而它的值为1,对吗?
实际上, bobj.b 的值是2,要知道为什么需要知道对象初始化的问题。
当一个对象被创建时,初始化是以下面的顺序完成的:
1.         设置成员的值为缺省的初始值 (0, false, null)
2.         调用对象的构造方法 (但是还没有执行构造方法体)
3.         调用父类的构造方法
4.         使用初始化程序和初始块初始化成员
5.         执行构造方法体

看看在实际中是如何一步一步完成的,看看下面的例子:
  
 class A {
        A() {
            System.out.println("A.A called");
        }
    }
   
    class B extends A {
        int i = f();
        int j;
   
        {
            j = 37;
            System.out.println("initialization block executed");
        }
   
        B() {
            System.out.println("B.B called");
        }
   
        int f() {
            System.out.println("B.f called");
            return 47;
        }
    }
   
    public class CtorDemo2 {
        public static void main(String args[]) {
            B bobj = new B();
        }
    }
 

程序的输出是:

    A.A called
    B.f called
    initialization block executed
    B.B called

B 的构造方法被调用,但是最先做的事情是隐含的调用父类的构造方法。父类必须自己负责初始化它自己的状态而不是让子类来做。
然后B对象的成员被初始化,这包含一个对B.f 的调用和包围在{}中的初始块的执行。最后B的构造方法体被执行。
你可能会问“什么是对父类的构造方法的隐含调用”。这意味着如果你的构造方法的第一行不是下面内容之一:

   super();

   super(args);

   this();

   this(args);

则有下面的调用:

   super();

提供给构造方法的第一行。

如果类没有构造方法呢?在这种情况下,一个缺省的构造方法(也叫"无参构造方法")由java编译器自动生成。缺省构造方法只有在类没有任何其它的构造方法时才产生。
更深入的明白这个,假设在文件A.java中有这样的代码:
    public class A {
        public static void main(String args[]) {
            A aref = new A();
        }
    }
 

如果你想编译然后列出A.class 中的字节码,输入下面的内容:

    $ javac A.java
    $ javap -c -classpath . A

输出:

    Compiled from A.java
    public class A extends java.lang.Object {
        public A();
        public static void main(java.lang.String[]);
    }
  
    Method A()
       0 aload_0
       1 invokespecial #1 <Method java.lang.Object()>
       4 return
  
    Method void main(java.lang.String[])
       0 new #2 <Class A>
       3 dup
       4 invokespecial #3 <Method A()>
       7 astore_1
       8 return

在main 中,注意对 A 的构造方法的调用(就是invokespecial 行),以及A的构造方法中产生的类似的对Object 构造方法的调用。
如果父类没有缺省构造方法,你必须明确使用"super(args)"调用父类的某个构造方法,例如,下面是一个错误的用法:
    class A {
        A(int i) {}
    }
 
    class B extends A {}


在上面的情况下, A 没有缺省的构造方法,但是B的构造方法必须调用A的某个构造方法。
让我们来看看初始化的另一个例子:

  
 class A {
        A() {
            System.out.println("A.A called");
        }
        A(int i) {
            this();
            System.out.println("A.A(int) called");
        }
    }
   
    class B extends A {
        int i = f();
        int j;
   
        {
            j = 37;
            System.out.println("initialization block executed");
        }
   
        B() {
            this(10);
            System.out.println("B.B() called");
        }
   
        B(int i) {
            super(i);
            System.out.println("B.B(int) called");
        }
   
        int f() {
            System.out.println("B.f called");
            return 47;
        }
    }
   
    public class CtorDemo3 {
        public static void main(String args[]) {
            B bobj = new B();
        }
    }
 

程序的输出是:

    A.A called
    A.A(int) called
    B.f called
    initialization block executed
    B.B(int) called
    B.B() called

这个例子明确使用super() 和 this() 调用。this()调用是调用同一个类中的另一个构造方法;这个方法被称为“显式构造方法调用”。当那样的构造方法被调用,它将执行通常的super() 过程以及后续的操作。这意味着A.A 的方法体在A.A(int)之前执行,而这两个都在B.B(int) 和B.B 前执行。
如果返回第一个例子,你就可以回答为什么打印的是2而不是1。B 没有构造方法,因此生成一个缺省构造方法,然后它调用super(),然后调用A 产生的缺省构造方法。
然后A中的成员被初始化,成员a 被设置为方法f()的值,但是因为B 对象正被初始化,f() 返回值2。换句话说,调用的是B中的f()方法。
A产生的构造方法体被执行,然后B的成员被初始化,而b 被赋予值a,也就是2。最后,B的构造方法被执行。
最后一个例子说明了第一个例子的一个小小的变异版本:

   
class A {
        int a = f();
        int f() {
            return 1;
        }
    }
   
    class B extends A {
        int b = 37;
        int f() {
            return b;
        }
    }
   
    public class CtorDemo4 {
        public static void main(String args[]) {
            B bobj = new B();
            System.out.println(bobj.a);
            System.out.println(bobj.f());
        }
    }


程序的输出是:

    0
    37

你可能会期望输出的两个值bobj.a 和bobj.f()是一样的,但是正如你看到的他们不一样。这是正确的,即使是在a是从B的f方法中初始化的并且打印的是a 和 B的 f 方法的值。
这儿的问题是当a通过对B的f方法调用而初始化,而该方法返回成员b的值,而该成员还没有被初始化。因为这个,b的值就是刚开始的初始值0。
这些例子解释了编程中重要的一点――在对象的构造阶段调用可重载的方法是不明智的。
分享到:
评论

相关推荐

    浅谈Java程序中的构造方法调用.pdf

    当父类和子类都定义了构造方法时,如果父类的构造方法初始化父类定义的成员,子类的构造方法初始化子类定义的成员,在创建子类的对象时,这两个构造方法都要执行。这种情况下,必须在子类的构造方法中使用关键字 ...

    Java零基础-构造方法.md

    2. 调用构造方法初始化对象。 3. 返回对象引用。 在这个过程中,构造方法被自动调用,负责完成对象状态的初始化工作。 #### 总结 通过本文的学习,读者应能深刻理解Java中构造方法的作用和使用方式,掌握构造方法...

    包含圆类的程序(含几种构造方法)java语言实现

    1. 创建默认的`Circle`对象`O`,然后通过用户输入的坐标和半径,调用`Circle`的构造方法初始化`O`的属性,并打印出其信息,计算并显示周长与面积。 2. 接着,再次获取用户输入,修改`O`的圆心坐标和半径,再次打印...

    Java程序设计课程实验8-封装、构造方法

    例如,创建一个类,使用封装来保护数据,然后通过构造方法初始化对象,同时利用this关键字进行方法调用。实验报告将要求学生阐述他们的实现过程,解释封装和构造方法的重要性,以及在实际编程中的应用。 实验过程中...

    Java面向对象程序设计方案练习题(1).doc

    【练习题05】:定义`Person`类,包含私有属性姓名`name`和年龄`age`,并用构造方法初始化,提供显示方法`display`。在`main`中创建`Person`对象并显示信息,练习了构造方法和访问私有属性的方法。 【练习题06】:这...

    构造方法JAVA构造方法

    Java构造方法是面向对象编程中的一个关键概念,用于初始化新创建的对象。在Java类中,构造方法是一个特殊的方法,它的名字必须与类名完全相同,没有返回类型,甚至不包括void关键字。当我们创建一个类的新实例时,...

    java 静态非静态 字段方法 子类父类构造_初始化顺序!

    java 静态_非静态 字段_方法_代码块 子类父类构造_初始化顺序! 三个class 让你清清楚楚 第一个class java代码如下: package initialOrder; class Parent { // 静态变量 public static String p_StaticField...

    JAVA第四章类与对象.pptx

    Java第四章类与对象,介绍类与对象的使用,关系,Java是面向对象的程序设计语言。封装性、继承性、多态性

    java 构造方法的资源

    在Java编程语言中,构造方法(Constructor)是一个特殊的方法,它的主要作用是初始化新创建的对象。构造方法与类名相同,没有返回类型,也不需要在方法签名中声明void。了解和熟练使用构造方法是Java面向对象编程的...

    java 初始化与方法

    构造方法用于初始化类的新实例,普通方法执行特定任务,静态方法与类关联而非实例,抽象方法则在接口或抽象类中定义,需由子类实现。方法的参数传递、返回值和重载也是重要的知识点,理解这些能帮助我们编写更灵活...

    java构造方法

    无参构造方法用于简单的初始化,而有参构造方法允许在创建对象时传递参数,这样可以在对象创建时设定更复杂的初始状态。 #### 四、构造方法的调用 构造方法在使用`new`关键字创建对象时被自动调用。此外,在一个类...

    SpringBoot项目启动时实现调用一次初始化方法.docx

    1. 对象初始化——构造方法 2. 对象初始化——`@PostConstruct`注解的方法 3. 对象初始化——实现了`InitializingBean`接口的`afterPropertiesSet`方法 4. 对象初始化——自定义的`init`方法 5. ---容器启动完毕后.....

    C++全局变量初始化的一点总结

    动态初始化则涉及运行时的动作,比如需要函数调用或类构造函数的初始化。这些变量的初始化在main函数执行之前进行,但晚于静态初始化。这意味着如果一个全局变量依赖于其他全局变量的初始化结果,必须确保这些依赖的...

    C#中结构(struct)的部分初始化和完全初始化实例分析

    总结来说,C# 中的结构要求在调用任何方法之前进行完全初始化,也就是说,所有字段都需要有明确的初始值。这可以是通过直接为每个字段赋值,或者是通过构造函数进行初始化。不进行完全初始化可能导致编译错误或运行...

    java中带有不同构造方法的程序内存分析

    总的来说,Java中带有不同构造方法的程序内存分析涉及构造方法的选择、内存的分配(包括栈和堆)、对象的初始化以及垃圾回收机制的理解。通过深入研究这些概念,开发者可以更好地控制和优化程序的内存使用,提高程序...

    苏坤基础提高视频笔记

    【苏坤基础提高视频笔记】是...这些笔记内容提供了学习C#基础的重要指导,帮助理解类的结构、对象的生命周期、属性的封装以及如何通过构造方法初始化对象。掌握这些知识点对于进一步深入学习C#的面向对象编程至关重要。

    Java静态初始化块和对象初始化块

    它们可以被视为构造函数的补充,提供额外的初始化逻辑,特别是当多个构造函数需要执行相同的初始化操作时。 ```java public class InitFiledBlockStatic { int instanceVar; { // 对象初始化块中的代码 ...

    Java基础学习,对Java基本内容的一个总结

    * 初始化:使用 new 创建对象时,会调用构造方法初始化对象。 Java 访问实例变量和方法 通过已经创建的对象来访问成员变量和成员方法。 Java 源文件声明规则 一个源文件只能有一个 public 类,一个源文件可以有...

    Java面向对象编程上机-练习题汇总.doc

    编写构造方法初始化其成员属性。 * Car 类:继承于 Vehicles 类,增加 int 型成员属性 seats(座位),增加成员方法 showCar(显示小汽车的信息),编写构造方法。 * Truck 类:继承于 Vehicles 类,增加 float 型...

Global site tag (gtag.js) - Google Analytics