本文转自:http://blog.csdn.net/macheng365/article/details/6403050
对于JAVA中类的初始化是一个很基础的问题,其中的一些问题也是易被学习者所忽略。当在编写代码的时候碰到时,常被这些问题引发的错误,感觉莫名其妙。而且现在许多大公司的面试题,对于这方面的考查也是屡试不爽。不管基于什么原因,我认为,对于java类中的初始化问题,有必要深入的了解。Java类的初始化,其实就是它在JVM的初始化问题(类加载的问题),对于它在JVM中的初始化是一个相当复杂的问题,是给专家们来探讨的,所以在这里我只是对一些容易忽略的问题,发表一下个人观点:
1,在一个类的内部(不考虑它是另一个类的派生类):很多人认为,类的成员变量是在构造方法调用之后再初始化的,先不考虑这种观点的正确性,先看一下下面的代码:
- class Test01...{
- public Test01(int i)...{
- System.out.println("Test01 of constractor : " + i);
- }
- }
- public class Test02 ...{
- private Test01 t1 = new Test01(1);
- private int n = 10;
- public Test02()...{
- System.out.println("Test02 of constructor : " + n);
- }
- private Test01 t2 = new Test01(2);
- public static void main(String[] args) ...{
- Test02 test = new Test02();
- }
- }
- 输出的结果为:
- Test01 of constractor : 1
- Test01 of constractor : 2
- Test02 of constructor : 10
通过输出,可见当生成Test02的实例test时,它并不是首先调用其构造方法而是先是成员变量的初始化,而且成员的初始化的顺序以成员变量的定义顺序有关,先定义的先初始化,初始化后再调用构造方法。其实成员变量的初始化,在类的所有方法调用之前进行,包括构造方法
当类中有Static 修饰的成员呢?测试下面一段代码:
- public class Test03 ...{
- private int i1 = printCommon();
- private static int i2 = printStatic();
- public Test03()...{
- }
- public static int printCommon()...{
- System.out.println("i1 is init!");
- return 1;
- }
- public static int printStatic()...{
- System.out.println("i2 is init!");
- return 2;
- }
- public static void main(String[] args) ...{
- Test03 t = new Test03();
- }
- }
- 输出结果为:
- i2 is init!
- i1 is init!
可见static的成员比普通的成员变量先初始化。
我们都知道,如果一个类的成员变量没有在定义时,系统会给予系统默认的值,有=号的就直接给予右值,系统在给予初值和=号给予值这2中方式,在执行时间上有先后吗?为了测试,我编写了如下代码:
- public class Test04 ...{
- private static Test04 t1 = new Test04();
- private static int i1;
- private static int i2 = 2;
- public Test04()...{
- i1++;
- i2++;
- }
- public static void main(String[] args) ...{
- Test04 t2 = new Test04();
- System.out.println("t2.i1 = " + t2.i1);
- System.out.println("t2.i2 = " + t2.i2);
- }
- }
- 我们先预计一下输出,可能有几种答案:2和3,3和3,2和2
- 执行代码后:
- t2.i1 = 2
- t2.i2 = 3
为什么是2和3呢?其实代码的执行顺序是这样的:首先执行给t1,i1,i2分别给予初始值null,0,0,再执行
Test04 t1 =new Test04(),这样i1++,i2++被执行,i1,i2都变为1,执行完毕后接着执行int i1; i1,i2的值仍然是1,1,当执行int i2 = 2时i2被赋予了值,即i1 = 1,i2=2;再执行Test04 t2 = new Test04(),i1,i2再执行++,此时i1 =2,i2 =3,输出i1,i2,结果就是:t2.i1 = 2,t2.i2 = 3。 通过上面的代码我们可以认为系统默认值的给予比通过等号的赋予先执行。
2,一个类还有上层的类,即父类:
当生成一个子类时,大家到知道会调用父类的构造方法。如果子类和父类中都有Static的成员变量呢,其实我们在深入分析一个类的内部初始化后,对于存在父类的类的初始化其实原理都一样,具体以下面的代码为例:
- class SuperClass ...{
- static...{
- System.out.println("SuperClass of static block");
- }
- public SuperClass()...{
- System.out.println("SuperClass of constracutor");
- }
- }
- public class SubClass extends SuperClass...{
- static...{
- System.out.println("SubClass of static block");
- }
- public SubClass()...{
- System.out.println("SubClass of constracutor");
- }
- public static void main(String[] args)...{
- SuperClass t = new SubClass();
- }
- }
- 输出结果:
- SuperClass of static block
- SubClass of static block
- SuperClass of constracutor
- SubClass of constracutor
可见当父类,和子类有Static时,先初始化Static,再初始化子类的Static,再初始化父类的其他成员变量->父类构造方法->子类其他成员变量->子类的构造方法。
父类上层还有父类时,总是先执行最顶层父类的Static-->派生类Static-->派生类Static-->.......-->子类Static-->顶层父类的其他成员变量-->父类构造方法--> 派生类的其他成员变量 --> 派生类构造方法--> ...............-->子类其他成员变量-->子类构造方法
讨论到继承,就不得提一下多态:
如果父类构造方法的代码中有子类中被重写得方法,当执行这样的语句
SuperClass super = new SubClass();
初始化时调用父类的构造方法,是执行父类的原方法,还是执行子类中被重写的方法呢?
- class SuperClass...{
- public SuperClass()...{
- System.out.println("SuperClass of constructor");
- m();
- }
- public void m()...{
- System.out.println("SuperClass.m()");
- }
- }
- public class SubClassTest extends SuperClass ...{
- private int i = 10;
- public SubClassTest()...{
- System.out.println("SubClass of constructor");
- super.m();
- m();
- }
- public void m()...{
- System.out.println("SubClass.m(): i = " + i);
- }
- public static void main(String[] args)...{
- SuperClass t = new SubClassTest();
- }
- }
- 可能很多人会认为输出为:
- SuperClass of constructor
- SubClass.m(): i = 10
- SubClass of constructor
- SuperClass.m()
- SubClass.m(): i = 10
- 其实不然!
- 正确输出为:
- SuperClass of constructor
- SubClass.m(): i = 0
- SubClass of constructor
- SuperClass.m()
- SubClass.m(): i = 10
- 在生成对象时,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的privte int i 也被父类访问到,这不是和我们说private的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到SubClass的private成员,一点都不奇怪,只是此时还没执行 i = 10,所以在父类的构造方法中调用m()时,系统只能将i赋予系统初值0。
- 下面是我设计的一道完整的初始化例子,可测试你对类的初始化问题是否完整掌握:
- 写出程序运行的结果:
- class A...{
- private int i = 9;
- protected static int j;
- static...{
- System.out.println("-- Load First SuperClass of static block start!-- ");
- System.out.println("j = " + j);
- System.out.println("-- Load First SuperClass of static block End -- ");
- }
- public A()...{
- System.out.println("------- Load SuperClass of structor start --------");
- System.out.println("Frist print j = " + j);
- j = 10;
- m();
- System.out.println("k = " + k);
- System.out.println("Second print j = " + j);
- System.out.println("----------- Load SuperClass End ----------- ");
- }
- private static int k = getInt();
- public static int getInt()...{
- System.out.println("Load SuperClass.getInt() ");
- return 11;
- }
- static...{
- System.out.println("--- Load Second SuperClass of static block!-------");
- System.out.println("j = " + j);
- System.out.println("k = " + k);
- System.out.println("-- Load Second SuperClass of static block End -- ");
- }
- public void m()...{
- System.out.println("SuperClass.m() , " + "j = " +j);
- }
- }
- class B extends A ...{
- private int a = 10;
- static...{
- System.out.println("---- Load SubClass of static block!------");
- System.out.println("-- Load SubClass of static block End -- ");
- }
- public B()...{
- System.out.println("Load SubClass of structor");
- m();
- System.out.println("--- Load SubClass End ---- ");
- }
- public void m()...{
- System.out.println("SubClass.m() ," + "a = " + a );
- }
- }
- public class Test1...{
- public static void main(String[] args)...{
- A a = new B();
- }
- }
- 正确的答案为:
- -- Load First SuperClass of static block start!--
- j = 0
- -- Load First SuperClass of static block End --
- Load SuperClass.getInt()
- --- Load Second SuperClass of static block!-------
- j = 0
- k = 11
- -- Load Second SuperClass of static block End --
- ---- Load SubClass of static block!------
- -- Load SubClass of static block End --
- ------- Load SuperClass of structor start --------
- Frist print j = 0
- SubClass.m() ,a = 0
- k = 11
- Second print j = 10
- ----------- Load SuperClass End -----------
- Load SubClass of structor
- SubClass.m() ,a = 10
- --- Load SubClass End ----
下面需要说明的一点也是至关重要的一点:那就是成员变量的初始化和非static初始化块之间的执行顺序是按照他们出现的先后顺序来执行的
- public class Test04
- {
- //下面的这两行代码放置的顺序,跟执行结果是有关系的
- private String t1 = test();
- {
- System.out.println("初始化快!");
- }
- //上面的这两行代码放置的顺序,跟执行结果是有关系的
- private String test(){
- System.out.println("实例变量的执行过程");
- return "test";
- }
- public Test04()
- {
- System.out.println("构造方法!");
- }
- public static void main(String[] args)
- {
- Test04 t2 = new Test04();
- }
- }
相关推荐
Java 变量初始化详解 Java 变量初始化是 Java 语言的基础知识点之一,但也往往被学习者所忽略。 Java 变量初始化的时机是指在 Java 语言中变量的初始化过程,包括变量的声明、初始化和赋值的步骤。 Java 变量声明 ...
在探讨继承中子类与父类构造函数及静态块的执行顺序时,我们首先需要理解构造函数和静态块的基本概念及其在Java中的作用。构造函数主要用于初始化对象的状态,而静态块则是在类加载到内存中时执行的一段代码,通常...
总之,Java代码的初始化顺序是类加载的必然过程,涉及到静态和实例初始化块、构造函数、成员变量初始化以及继承关系的影响。这个demo是学习和理解这些概念的重要工具,通过实际操作可以加深对Java内存管理和对象生命...
### Java 类中静态域、块,非静态域、块,构造函数的初始化顺序 #### 一、概述 在 Java 编程语言中,类的初始化顺序对于理解程序的行为至关重要。特别是当涉及到静态域(静态变量)、非静态域(实例变量)、静态块...
理解ClassLoader的工作机制以及类变量初始化的顺序对于深入理解Java运行时环境至关重要。这篇博文将探讨这两个主题。 首先,让我们深入了解Java的ClassLoader。ClassLoader是一个抽象类,它是Java中的一个关键组件...
这是因为首先调用了`Component`的构造函数来初始化`Aggregator`的成员变量`component`,然后才是`Aggregator`自身的构造函数。 这个顺序对于理解对象生命周期和依赖关系至关重要。如果在构造过程中,依赖于其他对象...
在深入探讨Java类的完整构造执行顺序之前,我们先来明确一下Java中构造函数与类初始化的基本概念。构造函数在创建对象时被调用,用于初始化该对象的状态。而类初始化则是指在类首次被使用时,JVM执行的一系列初始化...
Java代码的初始化流程是编程者在开发过程中需要深入了解的关键环节,它涉及到类加载、静态变量初始化、构造函数执行等多个步骤。下面将详细讲解这个过程。 首先,Java代码的执行始于类加载。当程序运行时,Java...
4. **子类非静态成员初始化**:接着是子类的非静态成员变量初始化。 5. **基类构造函数调用**:通过`super()`调用基类的构造函数。 6. **子类构造函数调用**:最后执行子类自身的构造函数。 ### 初始化过程详解 ##...
它们可以被视为构造函数的补充,提供额外的初始化逻辑,特别是当多个构造函数需要执行相同的初始化操作时。 ```java public class InitFiledBlockStatic { int instanceVar; { // 对象初始化块中的代码 ...
// 实例成员变量初始化 public static TestB b = new TestB(); public static void main(String[] args) { new TestOrder(); } } class TestA { public TestA() { System.out.println("Test--A"); } } ...
java 静态_非静态 字段_方法_代码块 子类父类构造_初始化顺序! 三个class 让你清清楚楚 第一个class java代码如下: package initialOrder; class Parent { // 静态变量 public static String p_StaticField...
### 静态变量初始化的基本规则: 1. **默认初始化**:当类被加载时,静态变量首先会被赋予其数据类型的默认值。例如,`int`类型的静态变量会被初始化为`0`,`boolean`为`false`,引用类型为`null`。 2. **显式初始化...
在 Java 中,静态变量的初始化过程可以被分为三个阶段:静态初始化、非静态初始化和执行构造函数。静态初始化是指在类加载时对静态变量的初始化。非静态初始化是指在实例创建时对实例变量的初始化。执行构造函数是指...
在Java中,静态初始化块的执行顺序非常重要,因为它会影响类的静态变量和静态方法的初始化。在实际开发中,了解静态初始化块的执行顺序非常重要,否则可能会导致类的静态变量和静态方法不能正确地初始化。 下面是一...
5. **实例化顺序**:在实例化过程中,成员变量首先被初始化,然后是成员函数(如果存在初始化函数),最后是构造函数。这是为了确保对象的状态在构造函数执行前已经完全准备就绪。 6. **静态成员函数和实例成员函数...
构造函数是在创建对象时执行,而静态初始化块是在类加载时执行。两者不会相互影响,它们各自负责不同的初始化任务。 4. **多线程环境**:如果多个线程同时加载同一个类,静态初始化块会被同步执行,确保线程安全。...
3. **初始化块与成员变量初始化的顺序**:如果静态成员变量定义与静态初始化块同时存在,则先执行静态成员变量的初始化;同理,对于非静态成员变量也是如此。 掌握这些原则可以帮助开发者更准确地控制类的初始化...
2. 非静态数据成员:在构造函数初始化列表中,成员变量按照它们在类声明中的顺序被初始化。即使初始化列表中的顺序不同,也会按照声明的顺序进行。 例如: ```cpp class MyClass { public: int a; int b; static...