`
hesihua
  • 浏览: 233752 次
  • 性别: Icon_minigender_2
  • 来自: 武汉
社区版块
存档分类
最新评论

Java类中构造方法的执行顺序和变量初始化

    博客分类:
  • java
 
阅读更多
看下面的代码先不要运行而尝试给出输出:
class A {
        public A() {
                init();
        }
        public void init() {
        }
}
public class B extends A {
        int i;
        int s = 0;
        public void init() {
                i = 100;
                s = 100;
        }
        public void println() {
                System.out.println(i);
                System.out.println(s);
        }
        public static void main(String[] arg) {
                new B().println();
        }
}
它的输出是什么呢?为什么不输出 100 100,而输出 100 0呢?
可以用下面的代码来尝试解释:
class A {
        public A() {
                System.out.println("enter A()");
                init();
                System.out.println("exit A()");
        }
        public void init() {
                System.out.println("enter A.init");
                System.out.println("exit A.init");
        }
}
public class B extends A {
        public B() {
                System.out.println("enter B()");
                System.out.println("exit B()");
        }
        int i;
        int s = inits();
        public static int inits() {
                System.out.println("enter B.inits");
                System.out.println("exit B.inits");
                return 0;
        }
        public void init() {
                System.out.println("enter B.init");
                i = 100;
                s = 100;
                System.out.println("exit B.init");
        }
        public void println() {
                System.out.println("enter B.println");
                System.out.println(i);
                System.out.println(s);
                System.out.println("exit B.println");
        }
        public static void main(String[] arg) {
                new B().println();
        }
}

上面的代码输出如下:
enter A()
enter B.init
exit B.init
exit A()
enter B.inits
exit B.inits
enter B()
exit B()
enter B.println
100
0
exit B.println

由此可以看出大致执行顺序如下:
main的new B()
->class B的public B()的第一行(首先调用基类构造函数,隐含的super()调用),第二行还没执行又
->class A的public A()第一行,第二行init()去调用class B的init()而不是class A的init()所以
  这里i=100,s=100(运行时多态性),public A()完了之后
->public B()的第一行,下面先执行实例变量的初始化。(此处在下面继续讨论)
  下来是s=inits()结果s=0,i没变还是100,最后才执行public B()的两条输出,到这里new B()才算完,
  下面就是B的println()。

关于i和s在类初始化方面的赋值方面的问题,请继续看下面的例子:

class Base {
    Base() {
        System.out.println("Base() before print()");
        print();
        System.out.println("Base() after print()");
    }
    public void print() {
        System.out.println("Base.print()");
    }
}
class Derived extends Base {
    int value = 100;
    Derived() {
        System.out.println("Derived() With " + value);
    }
    public void print() {
        System.out.println("Derived.print() with " + value);
    }
}
public class Main {
    public static void main(String[] args) {
        new Derived();
    }
}
如果变量有定义初始化值,如value=100,则先赋初始值,然后运行构造函数,那么在这个程
序的任何位置value都应该是100,但事实却非如此,输出结果如下:
Base() before print()
Derived.print() with 0   <---------这里是0而不是100
Base() after print()
Derived() With 100

会不会比较容易让人迷惑?
总结一下吧,顺序当然是很容易就推出,没什么好讨论的。
实际上例子只是说明,
int i; != int i = 0;
一般的初学者都会认为两者是相同的。
但是实际上不但是在顺序上不一样,而且javac对两者的编译是完全不一样。
前者只是申明一个变量,在初始化对象变量(这里指int i = 0;)的时候并不会编译成初始化指令。
而这些初始化对象变量的指令,会在本类构造函数里面的第一条指令(注意不是构造函数之前)
之前执行,而在此之前可能已经执行了父类的构造函数。
所以我们不难推出最开始那个例子的结果为什么一个是100,一个是0。

还有要注意的是构造函数实际上并没有分配空间(尽管我们通常都会认为)。
对于一般的对象生成(用new关键字,其他情况要另外分析)。
javac会把它编译成new #number 这个指令,#number指向的是类在常数池的索引。
这个new指令就是分配对象空间,并根据类里面所声明的变量进行空间分配,
并把他们赋值成初始化的值(就是大家都知道的,int(0),objct(null))。

举个简单的例子。对于一般的语句:比如说new A();
实际上执行顺序如下:
        new #A的索引
//然后是下面大括号的指令,它们都是A的构造函数(这里的构造函数并不等同于我们代码
                                里面的public A() {.. },实际上是大于,然后
                                根据里面的代码生成A的构造函数字节代码段。)
        {
         执行父类构造函数字节代码段
         本类对象变量的初始化指令(比如int i = 10;这些指令是在编译时确定的)
         然后下面的指令就是public A() {...}里面代码的指令
              {
                ...
                ...
              }
        }

实际上,假如你只是在类申明了int i;而在以后的代码都不引用它的话,
javac是不会把它编译到class里面的。这也许是javac的优化结果。
上文摘抄自: http://www.cnitblog.com/flydream/archive/2006/11/11/19074.aspx
上面的总结下来就是如下
  1. 如果父类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块
  2. 如果类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块
  3. 将类的成员赋予初值(原始类型的成员的值为规定值,例如int型为0,float型为0.0f,boolean型为false;对象类型的初始值为null)
  4. 如果构造方法中存在this()调用(可以是其它带参数的this()调用)则执行之,执行完毕后进入第7步继续执行,如果没有this调用则进行下一步。(这个有可能存在递归调用其它的构造方法)
  5. 执行显式的super()调用(可以是其它带参数的super()调用)或者隐式的super()调用(缺省构造方法),此步骤又进入一个父类的构造过程并一直上推至Object对象的构造。
  6. 执行类申明中的成员赋值和初始化块。
  7. 执行构造方法中的其它语句。
分享到:
评论

相关推荐

    探究java的ClassLoader及类变量初始化顺序

    总之,深入理解Java的ClassLoader机制和类变量初始化顺序是提升Java编程技能的重要步骤。通过学习这些知识点,开发者可以更好地优化代码、设计更健壮的系统,并解决与类加载和初始化相关的复杂问题。

    Java类继承初始化顺序

    总之,Java类继承初始化顺序涉及到静态和非静态初始化块、构造器的调用以及方法的覆盖。理解这些概念对于编写健壮的、易于维护的Java代码至关重要。在实际编程中,应合理利用继承特性,同时注意避免不必要的复杂性和...

    java中类的初始化顺序

    ### Java中类的初始化顺序详解 #### 一、概述 在Java编程语言中,类的初始化是一个非常重要的概念。类的初始化涉及到多个方面,包括静态成员变量、实例成员变量、静态初始化块、实例初始化块以及构造函数等。本文...

    java代码的初始化顺序demo

    总之,Java代码的初始化顺序是类加载的必然过程,涉及到静态和实例初始化块、构造函数、成员变量初始化以及继承关系的影响。这个demo是学习和理解这些概念的重要工具,通过实际操作可以加深对Java内存管理和对象生命...

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

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

    java程序初始化顺序

    - 实例初始化块({}):然后执行类中的实例初始化块,这些代码会为实例变量赋值或执行其他初始化任务。 - 构造器主体:最后,执行当前类的构造器主体,完成对象的具体初始化。 4. **多线程下的初始化**: - 当多...

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

    在Java编程语言中,初始化块是程序执行时用于初始化对象或类的重要机制。这里我们将深入探讨两种类型的初始化块:静态初始化块(Static Initializer Block)和对象初始化块(Instance Initializer Block)。这两种...

    java 构造方法的资源

    了解和熟练使用构造方法是Java面向对象编程的基础,下面将详细阐述Java构造方法的相关知识点。 一、构造方法的作用 构造方法的主要任务是在创建对象时设置对象的初始状态,为对象成员变量赋值。当一个类被实例化时...

    java类中静态域、块,非静态域、块,构造函数的初始化顺序

    ### Java 类中静态域、块,非静态域、块,构造函数的初始化顺序 #### 一、概述 在 Java 编程语言中,类的初始化顺序对于理解程序的行为至关重要。特别是当涉及到静态域(静态变量)、非静态域(实例变量)、静态块...

    Java常见笔试、面试题目深度剖析,方法重写详解、静态代码块与构造方法执行顺序问题

    对于构造方法执行顺序,要能够分析和解释复杂的继承结构中对象初始化的过程。这些都是衡量开发者对Java面向对象特性理解程度的重要指标。 总之,掌握这些Java基础知识不仅可以帮助你在面试中脱颖而出,也能使你在...

    类继承的初始化顺序类,继承的初始化顺序

    4. **子类非静态成员初始化**:接着是子类的非静态成员变量初始化。 5. **基类构造函数调用**:通过`super()`调用基类的构造函数。 6. **子类构造函数调用**:最后执行子类自身的构造函数。 ### 初始化过程详解 ##...

    类执行顺序小结.doc

    如果类中存在多个构造代码块或成员变量初始化,它们将按照在代码中出现的顺序依次执行。 #### 三、构造方法的执行 构造方法用于初始化类的实例,它们在创建对象时被调用。构造方法的执行顺序遵循以下规则: - ...

    java构造方法

    这意味着如果一个类中有多个成员变量,它们的初始化顺序将按照代码中声明的顺序进行。 #### 八、总结 构造方法是Java编程语言中一个重要的概念,它对于对象的初始化至关重要。理解构造方法的工作原理、如何正确...

    深入剖析java类的构造方式

    字节码揭示了类的内部操作,包括方法调用、变量初始化等。 总的来说,Java类的构造方式是一个涉及内存分配、初始化、继承和多态的重要主题。理解和掌握这一过程对于编写高效、可靠的Java代码至关重要。通过实例和...

    Java初始化顺序1

    在上面的示例代码中,我们可以看到,类变量和实例变量的初始化顺序是按照定义的顺序进行的。同时,我们还可以看到,静态变量的初始化顺序是按照定义的顺序,并且只在第一次访问时初始化。 在 Java 中,static ...

    类初始化顺序示例讲解

    3. **初始化块与成员变量初始化的顺序**:如果静态成员变量定义与静态初始化块同时存在,则先执行静态成员变量的初始化;同理,对于非静态成员变量也是如此。 掌握这些原则可以帮助开发者更准确地控制类的初始化...

    java类中元素初始化顺序详解

    首先,Java 解释器会执行类中的静态变量初始化和静态初始化块。这些静态元素的初始化只会在类加载时执行一次,并且按照它们在源代码中的顺序进行。在给定的例子中,我们看到 `parent` 类和 `child` 类都有静态变量...

    Java类初始化顺序

    Java类的初始化顺序是编程中一个非常重要的概念,它涉及到对象的创建过程和成员变量的初始化。当一个Java类被实例化或者其静态成员被访问时,类的初始化过程就开始了。以下详细解释了Java类的初始化顺序: 1. **...

    JAVA面试题解惑系列(一)——类的初始化顺序-JAVA程序员JAVA工程师面试必看.pdf,这是一份不错的文件

    在 JAVA 中,类的初始化顺序可以分为四个阶段:静态变量、静态初始化块、变量、初始化块和构造器。其中,静态变量和静态初始化块的初始化顺序是最高的,接着是变量和初始化块,最后是构造器。 在了解类的初始化顺序...

Global site tag (gtag.js) - Google Analytics