Java类初始化深入分析 初始化(initialization)其实包含两部分:
1.类的初始化(initialization class & interface);
2.对象的创建(creation of new class instances)。
类的初始化其实是类加载(loading of classes),它是"对象的创建"的第一步。
顺序:类的加载肯定是第一步的,所以类的初始化在前,然后是对象的初始化。大体的初始化顺序是:类初始化 ->对象的初始化
(1)类初始化类初始化分为两个部分:
一是初始化类的静态成员或者静态块;
另外就是初始化接口,也就是初始化接口里的成员;
A) 初始化类的静态成员。
例如:
class
{
static int a = 0;
static
{
int a = 1;
}
}
B)初始化接口里的成员。
例如:
public interface A
{
MyClass myClass = new MyClass();
}
class Initialization implements A{}
类初始化原则:
--initialization classes 时,该class的superclass 将首先被初始化,但其实现的interface则不会。
--initialization classes时,该class的superclass,以及superlcass的superclass 会首先被递归地初始化,一直到java.lang.Object为止。但initialiazation interface的时候,却不需如此,只会初始化该interface本身。
--对于由引用类变量(class field)所引发的初始化,只会初始化真正定义该field的class。
--如果一个static field是编译时常量(compile-time constant),则对它的引用不会引起定义它的类的初始化。 --类变量的初始化是先把静态成员设置默认值,然后再对其赋值。 引起定义它的类的初始化。 --类变量的初始化是先把静态成员设置默认值,然后再对其赋值。
可以看下面的例子:
例一:
class One
{
One(String str)
{
System.out.println(str);
}
}
class Two
{
static int i = 0;
One one_1 = new One("one-1");
static One one_2 = new One("one-2");
static One one_3 = new One("one-3");
Two(String str)
{
System.out.println(str);
}
}
public class TestStatic
{
public static void main(String[] args)
{
System.out.println("Test main() start...");
System.out.println("Two.i = " + Two.i);
}
}
首先java虚拟机要执行TestStatic里面的static方法main(),这会导致TestStatic这个类的加载,从而会进行类变量的初始化。由于TestStatic里面没有静态成员,也没有继承自某个类,所以该过程什么事情都不做;
第二执行TestStatic里面的main();打印出“"Test main() start...”;
第三执行Two.i,由于引用到Two的静态成员,所以会对Two这个类的加载,从而要对它的静态成员进行初始化,初始化过程为:先把静态成员变量设置为默认值;然后对i,one_2,one_3进行显示的初始化,打印出“one-2”,“one-3”;
第四静态成员初始化完成后,执行返回到main()方法的System.out.println("Two.i = " + Two.i);语句,打印出“Two.i = 0”
例二:
public class Singleton
{
private static Singleton obj = new Singleton();
public static int count1;
public static int count2 = 0;
private Singleton()
{
count1++;
count2++;
}
public static Singleton getInstance()
{
return obj;
}
public static void main(String[] args)
{
Singleton obj = getInstance();
System.out.println("Inside Singleton,count1=" + obj.count1);
System.out.println("Inside Singleton,count2=" + obj.count2);
}
}
首先JVM要执行Singleton里面的main(),这会导致对Singleton的加载,执行静态成员的初始化;
第二给成员变量分配空间,并且赋默认值。把obj置为null,count1=0, count2=0;
第三顺序执行赋值语句,首先执行private static Singleton obj = new Singleton();结果count1=1, count2=1;
第四执行count2=0;赋值语句;结果变成count1=1, count2=0;
注意:由于count1不存在赋值语句,所以count1是不会发生变化的。
(2)对象创建
具体步骤如下:
(A) 所有的成员变量—包括该类,及它的父类中的成员变量--被分配内存空间,并赋予默认值。
(这里是第一次初始化成员变量)
(B) 为所调用的构造函数初始化其参数变量。(如果有参数)
(C) 如果在构造函数中用this 调用了同类中的其他构造函数,则按照步骤(B)~(F)去处理被调用到的构造函数。
(D) 如果在构造函数中用super调用了其父类的构造函数,则按照步骤(B)~(F)去处理被调用到的父类构造函数。
(E) 按照书写顺序,执行赋值语句或者初始化块来初始化成员变量。(这里是第二次初始化成员变量)
(F) 按照书写顺序,执行constructor的其余部分。
*注意*
成员变量其实都被初始化2次,第一次是赋予默认值,第二次才是你想要设定的值。
例一:
class Insect
{
int i = 9;
int j;
Insect()
{
System.out.println("Insect initialized");
prt("i = " + i + ",j = " + j);
j = 39;
}
static int x1 = prt("static Insect.x1 initialized");
static int prt(String s)
{
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect
{
int k = prt("Beetle.k initialized");
Beetle()
{
System.out.println("Beetle initialized");
prt("k = " + k);
prt("j = " + j);
}
static int x2 = prt("static Beetle.x2 initialized");
public static void main(String[] args)
{
prt("Beetle constructor");
Beetle b = new Beetle();
}
}
首先JVM执行Beetle里面的静态方法main(),这会导致对Beetle类的加载,加载Beetle后发现它还有基类Insect,接着JVM会加载基类Insect,如果Insect还有基类,这个加载过程会一直递归下去,直到父类为Object为止;
第二类加载完成后,变量的内存空间已经分配并且初始值为默认值,接着以从基类到子类的顺序进行静态成员的赋值;所以打印出“static Insect.x1 initialized”,“static Beetle.x2 initialized”;
第三静态成员变量赋值完成后,程序执行点回到main(),打印“Beetle constructor”;
第四创建一个Beetle对象,这会调用Beetle的构造方法,由于Beetle有基类,故会调用基类的构造方法,这个过程会一直递归下去,直到父类为Object为止。注意在调用过程中构造方法的方法体是没有执行的;
第五对基类Insect的非静态成员进行显示赋值。这样i = 9; j=0;然后执行基类的构造方法的方法体,打印出“I = 9,j = 0”,并给j 赋值为39;
第六基类非静态成员成员初始化完后会对子类Beetle的非静态成员赋值,故打印出“Beetle.k initialized”,并且k被赋值为47;赋值操作完成后执行构造方法体,打印出“Beetle initialized”,“k=47”,“j=39”,初始化完成。
例二:
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();
}
}
首先JVM执行CtorDemo3里面的静态方法main(),这会导致对CtorDemo3类的加载。由于CtorDemo3没有静态成员,故不用做静态成员的初始化;
第二创建B的对象,这会加载B类,因为B类有基类A,所以也要加载基类A;
第三类加载完毕后所有成员变量都分配了内存空间,赋了默认的初值。由于基类和子类都没有静态成员,所以都不用做静态成员的初始化;
第四调用构造方法B(),由于方法中this(10),故会调用B(int i)这个构造方法,在B(int i)这个方法中有super(i)这条调用语句,所以会调用类A的构造方法A(int i),在这个构造方法中有this();这条语句,所以会调用A(),打印出“A.A called”,接着执行A(int i)的构造方法体“A.A(int) called”;
第五调用返回到类B后会执行B的赋值语句和初始化块,打印“B.f called”,并且给i赋值47,执行初始化块会打印“initialization block executed”;
第六执行B(int i)构造方法的方法体,打印“B.B(int) called”;
第七执行B()构造方法的方法体,打印“B.B() called”;
分享到:
相关推荐
Java 数组初始化详解 Java 数组初始化是 Java 编程语言中的一种基本概念,它允许开发者创建和初始化数组,以便于存储和操作数据。在本文中,我们将对 Java 数组初始化进行详细的介绍,包括一维数组和二维数组的声明...
类--初始化块"); 39. } 40. // 构造器 41. public SubClass() { 42. System.out.println("子类--构造器"); 43. } 44. 45. public static void main(String[]...因此,掌握类的初始化顺序是每个Java开发者必备的知识点。
Java类初始化时机测试方法解析 Java类初始化时机测试方法解析是Java编程语言中一个重要的概念,涉及到类的加载、链接、初始化等过程。下面我们将对Java类初始化时机测试方法进行详细的解析。 首先,我们需要了解...
Java类的初始化顺序是编程中一个非常重要的概念,它涉及到对象的创建过程和成员变量的初始化。当一个Java类被实例化或者其静态成员被访问时,类的初始化过程就开始了。以下详细解释了Java类的初始化顺序: 1. **...
5. **初始化类的子类**。 6. **JVM启动时作为主类的类**。 例如,如果我们有一个`Singleton`类,其中包含静态变量和静态初始化代码块,如下所示: ```java class Singleton { private static Singleton singleton...
Java类初始化和实例化是程序开发中至关重要的环节,它涉及到类加载、静态变量的初始化以及构造器的执行。在Java中,类的初始化通常在以下几个情况发生: 1. 使用`new`关键字创建实例。 2. 访问或修改类的静态字段...
在Java编程语言中,程序初始化的顺序是一个关键概念,它涉及到类加载、对象创建以及执行流程的安排。了解这些顺序对于编写高效、无错误的代码至关重要。以下是对Java程序初始化顺序的详细说明: 1. **类加载阶段**...
静态初始化块用于初始化类级别的静态变量,它们只在类加载时执行一次。 2. **成员变量初始化**:接下来,初始化父类的非静态成员变量,这是通过调用父类的构造器实现的。Java规定每个子类实例在构造之前必须先调用...
首先,需要了解Java类初始化的基本规则,即在类的静态变量、静态初始化块、变量、初始化块、构造器这五个部分中,它们的初始化顺序依次是:静态变量、静态初始化块、变量、初始化块、构造器。这个顺序在单个类中是...
在Java编程语言中,初始化块是程序执行时用于初始化对象或类的重要机制。这里我们将深入探讨两种类型的初始化块:静态初始化块(Static Initializer Block)和对象初始化块(Instance Initializer Block)。这两种...
Java 变量初始化的时机可以分为两类:实例变量初始化和类变量初始化。 实例变量初始化 实例变量初始化可以在三个地方进行: 1. 定义实例变量的时候指定初始值; 2. 非静态初始化代码块中对实例变量指定初值; 3. ...
### Java中类的初始化顺序详解 #### 一、概述 在Java编程语言中,类的初始化是一个非常重要的概念。类的初始化涉及到多个方面,包括静态成员变量、实例成员变量、静态初始化块、实例初始化块以及构造函数等。本文...
它用于初始化类级别的静态变量。在`init`目录下的文件可能包含了不同静态初始化的示例。 3. **实例化阶段**: 当我们创建一个新的对象时,Java会进行实例初始化。首先,分配内存空间,然后调用构造函数。实例初始...
详细讲解java类中静态变量,普通标量,对象、基本类型的初始化顺序。
构造方法用于初始化类的新实例,普通方法执行特定任务,静态方法与类关联而非实例,抽象方法则在接口或抽象类中定义,需由子类实现。方法的参数传递、返回值和重载也是重要的知识点,理解这些能帮助我们编写更灵活...
Java类的初始化和实例化详解 Java类的初始化和实例化是Java编程语言中两个非常重要的概念,它们都是Java类生命周期的重要组成部分。下面我们将详细介绍Java类的初始化和实例化的过程,并解释其中的细节。 一、Java...
本文将详细介绍6种初始化Java List集合的方法,并通过代码示例帮助理解每种方法的使用和特点。 1. 常规方式 这是最常见的初始化List的方式,通过创建ArrayList实例并逐个添加元素。自JDK 7以后,可以省略泛型类型的...