`
pterodactyl
  • 浏览: 771811 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java 变量的初始化

    博客分类:
  • java
阅读更多
Java:初始化类、变量、程序块加载探讨 

--------------------------------------------------------------------------------

2007-03-19 19:40:12 标签:初始化 加载 java   [推送到技术圈]


版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://zhangjunhd.blog.51cto.com/113473/20927
整理了Think in Java 3rd中所有关于初始化的段落,将其连贯成一片文章,分析Java中初始化类、变量、程序块加载问题。
author: ZJ 07-3-19
Blog: http://zhangjunhd.blog.51cto.com/
1.基本类型数据的初始值
InitialValues.java
public class InitialValues {
    boolean t;
    char c;
    byte b;
    short s;
    int i;
    long l;
    float f;
    double d;

    void print(String s) {
       System.out.println(s);
    }

    void printInitialValues() {
       print("boolean  " + t);
       print("char  " + c);
       print("byte  " + b);
       print("short  " + s);
       print("int  " + i);
       print("long  " + l);
       print("float  " + f);
       print("double  " + d);
    }

    public static void main(String[] args) {
       InitialValues iv = new InitialValues();
       iv.printInitialValues();
    }
}


结果:
boolean  false
char  _
byte  0
short  0
int  0
long  0
float  0.0
double  0.0
2.变量初始化
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧在任何方法(包括构造器)被调用之前得到初始化。看下面的代码:
OrderOfInitialzation.java(执行顺序在代码中已标出,按类标注,罗马字母标注主类中执行顺序。)
class Tag {
    Tag(int marker) {
       System.out.println("Tag(" + marker + ")");
    }
}

class Card {
    Tag t1 = new Tag(1);// Ⅰ①

    Card() {
       System.out.println("Card()");// Ⅰ④
       t3 = new Tag(33);// Ⅰ⑤
    }

    Tag t2 = new Tag(2);// Ⅰ②

    void f() {
       System.out.println("f()");// Ⅱ⑥
    }

    Tag t3 = new Tag(3);// Ⅰ③
}

public class OrderOfInitialzation {
    public static void main(String[] args) {
       Card t = new Card();// Ⅰ
       t.f();// Ⅱ
    }
}


结果:
Tag(1)
Tag(2)
Tag(3)
Card()
Tag(33)
f()
3.静态数据初始化
看下面的代码:
StaticInitialization .java
class Bowl {
    Bowl(int marker) {
       System.out.println("Bowl(" + marker + ")");
    }

    void f(int marker) {
       System.out.println("f(" + marker + ")");
    }
}

class Table {
    static Bowl b1 = new Bowl(1);// Ⅰ①

    Table() {
       System.out.println("Table()");// Ⅰ③
       b2.f(1);// Ⅰ④
    }

    void f2(int marker) {
       System.out.println("f2(" + marker + ")");
    }

    static Bowl b2 = new Bowl(2);// Ⅰ②
}

class Cupboard {
    Bowl b3 = new Bowl(3);// Ⅱ③,Ⅳ①,Ⅵ①

    static Bowl b4 = new Bowl(4);// Ⅱ①

    Cupboard() {
       System.out.println("Cupboard");// Ⅱ④,Ⅳ②,Ⅵ②
       b4.f(2);// Ⅱ⑤,Ⅳ③,Ⅵ③
    }

    void f3(int marker) {
       System.out.println("f3(" + marker + ")");
    }

    static Bowl b5 = new Bowl(5);// Ⅱ②
}

public class StaticInitialization {
    public static void main(String[] args) {
       System.out.println("Creating new Cupboard() in main");// Ⅲ
       new Cupboard();// Ⅳ
       System.out.println("Creating new Cupboard() in main");// Ⅴ
       new Cupboard();// Ⅵ
       t2.f2(1);// Ⅶ
       t3.f3(1);// Ⅷ
    }

    static Table t2 = new Table();// Ⅰ

    static Cupboard t3 = new Cupboard();// Ⅱ
}


结果:
Bowl(1)
Bowl(2)
Table()
f(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard
f(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard
f(2)
f2(1)
f3(1)
由输出可见,静态初始化只有在必要时刻才会进行。如果不创建Table 对象,也不引用Table.b1或Table.b2,那么静态的Bowl b1 和b2 永远都不会被创建。只有在第一个Table 对象被创建(或者第一次访问静态数据)的时候,它们才会被初始化。此后,静态对象不会再次被初始化。
初始化的顺序是先“静态”,(如果它们尚未因前面的对象创建过程而被初始化),后“非静态”。从输出结果中可以观察到这一点。
4.静态块的初始化
Java 允许你将多个静态初始化动作组织成一个特殊的“静态子句”(有时也叫作“静态块”)。与其他静态初始化动作一样,这段代码仅执行一次:当你首次生成这个类的一个对象时,或者首次访问属于那个类的一个静态成员时(即便从未生成过那个类的对象)。看下面的代码:
class Cup {
    Cup(int marker) {
       System.out.println("Cup(" + marker + ")");
    }

    void f(int marker) {
       System.out.println("f(" + marker + ")");
    }
}

class Cups {
    static Cup c1;

    static Cup c2;
    static {
       c1 = new Cup(1);
       c2 = new Cup(2);
    }

    Cups() {
       System.out.println("Cups()");
    }
}

public class ExpilicitStatic {
    public static void main(String[] args) {
       System.out.println("Inside main()");
       Cups.c1.f(99);// (1)
    }
    // static Cups x=new Cups();//(2)
    // static Cups y=new Cups();//(2)
}


结果:
Inside main()
Cup(1)
Cup(2)
f(99)
无论是通过标为(1)的那行程序访问静态的 c1对象,还是把(1)注释掉,让它去运行标为(2) 的那行,Cups 的静态初始化动作都会得到执行。如果把(1)和(2)同时注释掉,Cups 的静态初始化动作就不会进行。此外,激活一行还是两行(2)代码都无关紧要,静态初始化动作只进行一次。
5.非静态实例初始化
看下面的代码:
class Mug {
    Mug(int marker) {
       System.out.println("Mug(" + marker + ")");
    }

    void f(int marker) {
       System.out.println("f(" + marker + ")");
    }
}

public class Mugs {
    Mug c1;

    Mug c2;
    {
       c1 = new Mug(1);
       c2 = new Mug(2);
       System.out.println("c1&c2 initialized");
    }

    Mugs() {
       System.out.println("Mugs()");
    }

    public static void main(String[] args) {
       System.out.println("Inside main()");
       new Mugs();
       System.out.println("===new Mugs again===");
       new Mugs();
    }
}


结果:
Inside main()
Mug(1)
Mug(2)
c1&c2 initialized
Mugs()
===new Mugs again===
Mug(1)
Mug(2)
c1&c2 initialized
Mugs()
从结果可以看到,非静态的代码块被执行了2次。所以只要实例化一个类,该类中的非静态代码块就会被执行一次。
6.数组初始化
注意区分基本类型数据与类数据的初始化。看以下代码:
int[] a; 
a = new int[rand.nextInt(20)];


Integer[] a = new Integer[rand.nextInt(20)]; 
for(int i = 0; i < a.length; i++) { 
     a[i] = new Integer(rand.nextInt(500)); 
}

对于类数据类型的初始化,每个数组子成员都要重新new一下。
7.涉及继承关系的初始化
当创建一个导出类的对象时,该对象包含了一个基类的子对象。看下面代码:
class Art {
    Art() {
       System.out.println("Art constructor");
    }
}

class Drawing extends Art {
    Drawing() {
       System.out.println("Drawing constructor");
    }
}

public class Cartoon extends Drawing {
    public Cartoon() {
       System.out.println("Cartoon constructor");
    }

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


结果:
Art constructor
Drawing constructor
Cartoon constructor
可以发现,构建过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问它之前,就已经完成了初始化。
如果类没有缺省的参数,或者想调用一个带参数的基类构造器,就必须用关键字super显示地调用基类构造器的语句,并且配以适当的参数列表。看下面代码:
class Game {
    Game(int i) {
       System.out.println("Game constructor");
    }
}

class BoardGame extends Game {
    BoardGame(int i) {
       super(i);
       System.out.println("BoardGame constructor");
    }
}

public class Chess extends BoardGame {
    Chess() {
       super(11);
       System.out.println("Chess constructor");
    }

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


结果:
Game constructor
BoardGame constructor
Chess constructor
如果不在BoardGame()中调用基类构造器,编译器将无法找到符合Game()形式的构造器。而且,调用基类构造器必须是你在导出类构造器中要做的第一件事。
8.构造器与多态
8.1构造器的调用顺序
基类的构造器总是在导出类的构造过程中被调用的,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用。这样做是有意义的,因为构造器具有一项特殊的任务:检查对象是否被正确地构造。导出类只能访问它自己的成员,不能访问基类中的成员(基类成员通常是private类型)。只有基类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。因此,必须令所有构造器都得到调用,否则就不可能正确构造完整对象。这正是编译器为什么要强制每个导出类部分都必须调用构造器的原因。

8.2构造器内部的多态方法的行为
看下面代码:
abstract class Glyph {
    abstract void draw();

    Glyph() {
       System.out.println("Glyph() before draw()");
       draw();
       System.out.println("Glyph() after draw()");
    }
}

class RoundGlyph extends Glyph {
    private int radius = 1;

    RoundGlyph(int r) {
       radius = r;
       System.out.println("RoundGlyph.RoundGlyph(),radius=" + radius);
    }

    void draw() {
       System.out.println("RoundGlyph.draw(),radius=" + radius);
    }
}

public class PolyConstructors {
    public static void main(String[] args) {
       new RoundGlyph(5);
    }
}


结果:
Glyph() before draw()
RoundGlyph.draw(),radius=0
Glyph() after draw()
RoundGlyph.RoundGlyph(),radius=5
在Glyph中,draw()方法是抽象的,这样设计是为了覆盖该方法。我们确实在RoungGlyph中强制覆盖了draw()。但是Glyph构造器会调用这个方法,结果导致了对RoundGlyph.draw()的调用,这看起来似乎是我们的目的。但是如果看到输出结果,我们会发现当Glyph的构造器调用draw()方法时,radius不是默认初始值1,而是0。
解决这个问题的关键是初始化的实际过程:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制零。
2)如前所述那样调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,我们此时会发现radius的值为0。
3)按照声明的顺序调用成员的初始化方法。
4)调用导出类的构造器主体。
9.初始化及类的加载
看以下代码:
class Tag {
    Tag(int marker) {
       System.out.println("Tag(" + marker + ")");
    }
}

class Insect {
    private int i = 9;

    protected int j, m;

    Insect() {
       System.out.println("i = " + i + ", j = " + j);
       j = 39;
    }

    private static int x1 = print("static Insect.x1 initialized");

    static int print(String s) {
       System.out.println(s);
       return 47;
    }

    Tag t1 = new Tag(1);
}

public class Beetle extends Insect {
    private int k = print("Beetle.k initialized");

    public Beetle() {
       System.out.println("k = " + k);
       System.out.println("j = " + j);
       System.out.println("m = " + m);
    }

    private static int x2 = print("static Beetle.x2 initialized");

    public static void main(String[] args) {
       System.out.println("Beetle constructor");
       Beetle b = new Beetle();
    }

    Tag t2 = new Tag(2);
}


结果:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
Tag(1)
i = 9, j = 0
Beetle.k initialized
Tag(2)
k = 47
j = 39
m = 0
你在Beetle 上运行Java 时,所发生的第一件事情就是你试图访问Beetle.main( ) (一个static 方法),于是加载器开始启动并找出 Beetle 类被编译的程序代码(它被编译到了一个名为Beetle .class 的文件之中)。在对它进行加载的过程中,编译器注意到它有一个基类(这是由关键字 extends  告知的),于是它继续进行加载。不管你是否打算产生一个该基类的对象,这都要发生。
如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类中的静态初始化(在此例中为Insect)即会被执行,然后是下一个导出类,以此类推。这种方式很重要,因为导出类的静态初始化可能会依赖于基类成员能否被正确初始化的。
至此为止,必要的类都已加载完毕(静态变量和静态块),对象就可以被创建了。
首先,对象中所有的原始类型都会被设为缺省值,对象引用被设为null——这是通过将对象内存设为二进制零值而一举生成的。然后,基类的构造器会被调用。在本例中,它是被自动调用的。但你也可以用super 来指定对基类构造器的调用。基类构造器和导出类的构造器一样,以相同的顺序来经历相同的过程,即向上寻找基类构造器。在基类构造器完成之后(即根部构造器找到之后),实例变量(instance variables )按其次序被初始化(注意观察代码中的Tag())。最后,构造器的其余部分被执行。
10.参考资料
Thinking in Java 3rd
本文出自 “子 孑” 博客,请务必保留此出处http://zhangjunhd.blog.51cto.com/113473/20927

分享到:
评论

相关推荐

    Java变量初始化

    Java 变量初始化详解 Java 变量初始化是 Java 语言的基础知识点之一,但也往往被学习者所忽略。 Java 变量初始化的时机是指在 Java 语言中变量的初始化过程,包括变量的声明、初始化和赋值的步骤。 Java 变量声明 ...

    Java变量初始化的时机

    以下是对java变量初始化的时机的分析。  【java变量执行初始化的步骤】  java是一门强类型语言,因此java语言规定每个变量必须先声明,然后才能使用,声明变量时必须指定该变量的数据类型。首先看下面这条语句的...

    java数组初始化详解

    需要注意的是,在 Java 中,数组变量必须在使用前进行初始化,否则将会抛出 NullPointerException。 二维数组的初始化 在 Java 中,二维数组可以通过多种方式进行初始化。 1. 声明数组变量:`int[][] a;`,这只...

    java类变量初始化顺序

    详细讲解java类中静态变量,普通标量,对象、基本类型的初始化顺序。

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

    理解ClassLoader的工作机制以及类变量初始化的顺序对于深入理解Java运行时环境至关重要。这篇博文将探讨这两个主题。 首先,让我们深入了解Java的ClassLoader。ClassLoader是一个抽象类,它是Java中的一个关键组件...

    java程序初始化顺序

    在Java编程语言中,程序初始化的顺序是一个关键概念,它涉及到类加载、对象创建以及执行流程的安排。了解这些顺序对于编写高效、无错误的代码至关重要。以下是对Java程序初始化顺序的详细说明: 1. **类加载阶段**...

    Java类加载器:静态变量初始化.docx

    Java 类加载器静态变量初始化机制详解 Java 类加载器是 Java 语言的核心组件之一,负责将 Java 字节码文件加载到内存中,以便 JVM 可以执行它们。在 Java 中,类加载器是通过委派机制来实现的,即一个类加载器可以...

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

    静态初始化块常用于设置类级别的变量,或者执行只应执行一次的初始化操作。例如,如果一个类需要在程序运行前设置一个常量数组,静态初始化块就是合适的选择。 ```java public class InitFiledBlockStatic { ...

    java代码的初始化顺序demo

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

    浅谈Java变量的初始化顺序详解

    总结一下Java变量初始化顺序的要点: 1. **静态变量与静态初始化块**:首先按照在类中定义的顺序初始化所有静态变量和执行静态初始化块。这是在类被加载到内存时发生的,与对象创建无关。 2. **非静态变量与初始化...

    Java中static静态变量的初始化完全解析

    ### 静态变量初始化的基本规则: 1. **默认初始化**:当类被加载时,静态变量首先会被赋予其数据类型的默认值。例如,`int`类型的静态变量会被初始化为`0`,`boolean`为`false`,引用类型为`null`。 2. **显式初始化...

    Java类继承初始化顺序

    4. **子类成员变量初始化**:最后,执行子类的非静态成员变量初始化。 在实际编程中,`super`关键字用于引用父类的成员。例如,`super.someMethod()`调用父类的`someMethod()`方法,`super.someVariable`访问父类的...

    java 初始化与方法

    在Java中,类的初始化通常发生在以下几个时刻:当类被首次加载、创建类的实例、访问静态变量或调用静态方法时。初始化过程包括了静态块和实例块的执行,以及构造函数的调用。静态初始化块用于在类加载时执行一次的...

    6种方法初始化JAVA中的list集合

    本文将详细介绍6种初始化Java List集合的方法,并通过代码示例帮助理解每种方法的使用和特点。 1. 常规方式 这是最常见的初始化List的方式,通过创建ArrayList实例并逐个添加元素。自JDK 7以后,可以省略泛型类型的...

    java中类的初始化顺序

    // 实例成员变量初始化 public static TestB b = new TestB(); public static void main(String[] args) { new TestOrder(); } } class TestA { public TestA() { System.out.println("Test--A"); } } ...

    java面试题-类的初始化顺序.doc

    这个顺序表明,无论类之间的继承关系如何,初始化顺序始终是:静态变量和静态初始化块先于非静态成员。在创建对象时,父类的初始化先于子类。这是Java语言规范所规定的,确保在子类访问父类的静态或非静态成员时,...

    关于Java静态成员变量和静态初始化块等的初始化顺序的详细介绍

    详细介绍了Java的静态成员变量、静态数据块、非静态成员变量和非静态成员变量等初始化顺序

    Java变量的初始化及静态方法的实现

    Java 变量的初始化及静态方法的实现 ...本文详细介绍了 Java 变量的初始化和静态方法的实现,通过示例代码和分析,阐述了变量初始化和静态方法的应用场景和重要性。希望本文能够对读者的学习和工作产生帮助和影响。

    java代码初始化流程研究

    Java代码的初始化流程是编程者在开发过程中需要深入了解的关键环节,它涉及到类加载、静态变量初始化、构造函数执行等多个步骤。下面将详细讲解这个过程。 首先,Java代码的执行始于类加载。当程序运行时,Java...

    Java静态和非静态成员变量初始化过程解析

    Java静态和非静态成员变量初始化过程解析 Java静态和非静态成员变量初始化过程解析是Java语言中一个非常重要的概念。了解Java静态和非静态成员变量的初始化过程对大家的学习或者工作具有一定的参考学习价值。 首先...

Global site tag (gtag.js) - Google Analytics