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

构造器初始化

阅读更多
可以用构造器来进行初始化。在运行时刻,可以调用方法或执行某些动作来确定初值,这为编程带来了更大的灵活性。但要牢记:无法阻止自动初始化的进行,它将在构造器被调用之前发生。例如下述代码:
public class Counter{
	int i;
	Counter(){ i = 7; }
	//......
}

那么i首先会被置为0,然后变为7。对于所有基本类型和对象引用,包括在定义时已经指定初值的变量,这种情况都成立;因此,编译器不会强制你一定要在构造方法的某个地方或在使用它们之前对元素进行初始化——因为初始化早已得到了保证

1、初始化顺序

在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。例如:
class Window{
	Window(int marker){System.out.println("Window("+marker+")");}
}

class House{
	Window w1 = new Window(1);
	House(){
		System.out.println("House()");
		w3 = new Window(33);
	}
	Window w2 = new Window(2);
	void f(){System.out.println("f()");}
	Window w3 = new Window(3);
}

public class OrderOfInitialization{
	public static void main(String args[]){
		House h = new House();
		h.f();
	}
}

程序运行结果如下:



在House类中,故意把几个Window对象的定义散布到各处,以证明它们全都会在调用构造器或其他方法之前得到初始化。此外,w3在构造器内再次被初始化。

由输出可见,w3这个引用会被初始化两次:一次在调用构造器前,一次在调用期间(第一次引用的对象将被丢弃,并作为垃圾回收)。试想,如果定义了一个重载构造器,它没有初始化w3,;同时在w3的定义里也没有指定默认值,那会产生什么后果呢?所以尽管这种方法似乎效率不高,但它的确能使初始化得到保证。

2、静态数据的初始化

无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本数据类型的标准初值;如果它是一个对象引用,那么它的默认初始化值就是null。

如果想在定义处进行初始化,采取的方法与非静态数据没什么不同。

要想了解静态存储区域是何时进行初始化的,请看下面的例子:
class Bowl{
	Bowl(int marker){
		System.out.println("Bowl("+marker+")");
	}
	void f1(int marker){
		System.out.println("f1("+marker+")");
	}
}

class Table{
	static Bowl bowl1 = new Bowl(1);
	Table(){
		System.out.println("Table()");
		bowl2.f1(1);
	}
	void f2(int marker){
		System.out.println("f2("+marker+")");
	}
	static Bowl bowl2 = new Bowl(2);
}

class Cupboard{
	Bowl bowl3 = new Bowl(3);
	static Bowl bowl4 = new Bowl(4);
	Cupboard(){
		System.out.println("Cupboard()");
		bowl4.f1(2);
	}
	void f3(int marker){
		System.out.println("f3("+marker+")");
	}
	static Bowl bowl5 = 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();
		table.f2(1);
		cupboard.f3(1);
	}
	static Table table = new Table();
	static Cupboard cupboard = new Cupboard();
}

程序运行结果如下:



Bowl类使得看到类的创建,而Table类和Cupboard类在它们的类定义中加入了Bowl类型的静态数据成员。注意,在静态数据成员定义之前,CupBoard类先定义了一个Bowl类型的非静态数据成员bowl3。

由输出可见,静态初始化只有在必要时刻才会进行。如果不创建Table对象,也不引用Table.bowl1或Table.bowl2,那么静态的Bowl bowl1和bowl2永远都不会被创建。只有在第一个Table对象被初创建(或者第一次访问静态数据)的时候,它们才会被初始化。此后,静态对象不会再次被初始化

初始化的顺序是先静态对象(如果它们尚未因前面的对象创建过程而被初始化),而后是“非静态”对象。从输出结果中可以观察到这一点。要执行main()(静态方法),必须加载StaticInitialization类,然后其静态域table和cupboard被初始化,这将导致它们对应的类也被加载,并且由于它们也都包含静态的Bowl对象,因此Bowl随后也被加载。这样,在这个特殊的程序中的所有类在main()开始之前就都被加载了。实际情况通常并非如此,因为在典型的程序中,不会像在本例中所做的那样,将所有的事物都通过static联系起来。

总结一下对象创建的过程,假设有个名为Dog的类:

(1)即使没有显式地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类的路径,以定位Dog.class文件。

(2)然后载入Dog.class,有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次

(3)当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。

(4)这块存储空间会被清零,这就自动地将Dog对象中的所有基本数据类型数据都设置成了默认值(对数字来说就是0,对于boolean和char也相同),而引用则被设置成了null。

(5)执行所有出现于字段定义处的初始化动作。

(6)执行构造器。

3、显式的静态初始化

Java允许将多个静态初始化动作组织成一个特殊的“静态句子”(有时也叫做“静态块”)。就像下面这样:
public class Spoon{
	static int i;
	static{
		i =7;
	}
}

尽管上面的代码看起来像一个方法,但实际只是一段跟在static关键字后面的代码。与其他静态初始化动作一样,这段代码仅执行一次:当首次生成这个类的一个对象时,或者首次访问属于那个类的静态数据成员时(即便从未生成过那个类的对象)。例如:
class Cup{
	Cup(int marker){
		System.out.println("Cup("+marker+")");
	}
	void f(int marker){
		System.out.println("f("+marker+")");
	}
}
class Cups{
	static Cup cup1;
	static Cup cup2;
	static{
		cup1 = new Cup(1);
		cup2 = new Cup(2);
	}
	Cups(){
		System.out.println("Cups()");
	}
}
public class ExplicitStatic{
	public static void main(String args[]){
		System.out.println("Inside main()");
		Cups.cup1.f(99);//(1)
	}
	//static Cups cups1 = new Cups();//(2)
	//static Cups cups2 = new Cups();//(2)
}

运行结果1(注释掉(2)):



运行结果2(注释掉(1)):



运行结果3(注释掉(1)和(2)):



运行结果4((1)和(2)均不注释掉):



从上述结果可以看出:无论是通过标为(1)的那行代码访问静态的cup1对象,还是把标为(1)的行注释掉,让它去运行标为(2)的那行代码,Cups的静态初始化动作都会得到执行。如果把标为(1)和(2)的行同时注释掉,Cups静态初始化动作就不会进行,就像在输出中看到的那样。此外,激活一行还是两行标为(2)的代码都无关紧要,静态初始化动作只进行一次

4、非静态实例初始化

Java中也有被称为实例初始化的类似语法,用来初始化每一个对象的非静态变量。例如:
class Mug{
	Mug(int marker){
		System.out.println("Mug("+marker+")");
	}
	void f(int marker){
		System.out.println("f("+marker+")");
	}
}
public class Mugs{
	Mug mug1;
	Mug mug2;
	{
		mug1 = new Mug(1);
		mug2 = new Mug(2);
		System.out.println("mug1&mug2 initialized");
	}
	Mugs(){
		System.out.println("Mugs()");
	}
	Mugs(int i){
		System.out.println("Mugs(int)");
	}
	public static void main(String args[]){
		System.out.println("Inside main()");
		new Mugs();
		System.out.println("new Mugs() completed");
		new Mugs(1);
		System.out.println("new Mugs(1) completed");
	}
}

程序运行的结果如下:



你可以看到实例初始化子句:
{
	mug1 = new Mug(1);
	mug2 = new Mug(2);
	System.out.println("mug1&mug2 initialized");
}

看起来它与静态初始化子句一模一样,只不过少了static关键字。这种语法对于支持“匿名内部类”的初始化是必须的,但是它也使得你可以保证无论调用了哪个显式构造器,某些操作都会发生。从输出中可以看到实例初始化子句是在两个构造器之前执行的。

  • 大小: 48.1 KB
  • 大小: 27.9 KB
  • 大小: 29.1 KB
  • 大小: 25.7 KB
  • 大小: 28.9 KB
  • 大小: 40 KB
  • 大小: 24.5 KB
分享到:
评论

相关推荐

    实例解析Java中的构造器初始化

    主要通过实例解析Java中的构造器初始化,代码很简单,叙述很明确,需要的朋友可以了解下。

    初始化和清理.ppt初始化和清理.ppt初始化和清理.ppt初始化和清理.ppt

    初始化和清理是编程中至关重要的概念,特别是...总之,初始化和清理是Java编程中必不可少的环节,包括正确使用构造器初始化对象、理解对象创建过程、掌握垃圾回收机制及其原理,这些都对编写高效、稳定的代码至关重要。

    java 构造器的调用

    在Java编程语言中,构造器(Constructor)是一个特殊的方法,主要负责对象的初始化工作。当创建一个新的类实例时,构造器会被自动调用。构造器的名称必须与类名完全相同,且没有返回类型,包括void。理解并熟练运用...

    详解Java的初始化与清理

    本文将深入探讨Java中的初始化过程,包括对象的构造器初始化以及类成员的初始化顺序,同时也会涉及Java的垃圾回收机制,即内存的自动清理。 首先,让我们详细了解一下Java中的构造器初始化。构造器是Java中用于初始...

    JAVA面试题解惑系列——类的初始化顺序

    只有在子类的静态成员初始化完成后,才开始父类的非静态成员和构造器的初始化,以及子类的构造器初始化。 这个顺序可能会导致一些有趣的现象,例如,在父类的构造器中使用某个还未初始化的子类静态成员变量(如果...

    Java中初始化数据域的四种方法

    2. **构造器初始化** 当需要根据对象创建时的不同条件来设置初始值时,可以使用构造器。在`InitFiledByConstructor.java`文件中,我们可能会看到: ```java public class InitFiledByConstructor { private ...

    JAVA经典继承与父类调用 geter、seter访问器 构造器

    在`Testoo.java`文件中,很可能包含了测试这些概念的代码,通过创建不同类型的对象,调用构造器初始化它们,然后通过getter和setter方法来访问和修改成员变量的值。这样的测试有助于验证类的设计是否正确,以及继承...

    C#在无参构造器中初始化成员变量

    `this()`关键字调用了无参构造器,确保了即使通过有参数的构造器创建对象,成员变量也会先由无参构造器初始化,然后在有参构造器中进行进一步的设置。 4. **多参数构造器** `class A`还展示了如何处理多个参数的...

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

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

    java中对象创建、初始化、引用

    - **初始化对象**:通过构造器初始化新创建的对象,设置其初始状态。 - **赋值**:最后,将创建好的对象的引用赋给之前声明的引用变量。 例如,下面的代码展示了如何创建一个`A`类的对象: ```java A a1 = new A()...

    深入理解java构造器机理

    在 Java 编程语言中,构造器是一种特殊的方法,用于初始化对象的创建。它是 Java 类中最重要的一个概念。下面将深入讨论构造器的机理、执行顺序、作用及与其他概念的区别。 一、构造器的机理 构造器是一种特殊的...

    对象初始化流程梳理对象初始化流程梳理

    Java中的对象初始化流程是编程实践中一个非常重要的概念,它涉及到类加载、静态初始化块、实例初始化块、构造器等多个方面。下面将详细解释这个过程。 首先,对象初始化流程的起点是程序的入口点,即`main`方法。当...

    Java-objects-initial.rar_objects

    - **构造器初始化**:在构造器中对字段的赋值也是初始化的一部分,这通常发生在对象实例化过程中。 2. **实例初始化块**: - 如果在类中定义了`{}`包裹的代码块,且不包含任何条件,这就是实例初始化块。每当创建...

    理解构造器--构造器和方法的区别

    在这里,无参构造器`Platypus()`通过`this("John/Mary Doe")`调用了带参数的构造器来初始化`name`。 关键字`super`在构造器和方法中的用途也不同。在方法中,`super`用于调用超类中被重写的方法,而在构造器中,`...

    java程序初始化顺序

    - 构造器主体:最后,执行当前类的构造器主体,完成对象的具体初始化。 4. **多线程下的初始化**: - 当多个线程同时尝试初始化同一个类时,Java保证只会执行一次类的初始化过程。这是由JVM的同步机制保证的,...

    JAVA面试题解惑系列

    执行上述代码,输出结果会清晰地展示出JAVA在处理继承时的初始化顺序,证实了静态变量和静态初始化块优先于实例变量和构造器初始化的规则。 #### 总结 理解和掌握JAVA中静态变量与实例变量的初始化顺序是JAVA开发...

    WPF 对象初始化器_1 对象初始化器_1

    ### WPF 对象初始化器详解 #### 一、对象初始化器概述 对象初始化器是C# 3.0引入的一项新特性,它简化了对象创建的过程。在传统的面向对象编程中,创建对象后通常需要手动设置各个属性。这种方式不仅繁琐,而且...

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

    如果在构造器或初始化块中依赖其他静态或非静态成员,必须确保这些成员在需要之前已经被正确初始化。否则,可能会导致程序运行时错误或者逻辑错误。 此外,静态初始化块只在类加载时执行一次,而初始化块(也称为...

    Java类继承初始化顺序

    当我们谈论“Java类继承初始化顺序”时,涉及到的关键知识点包括构造器、成员变量的初始化、super关键字以及方法的覆盖。 首先,了解类初始化的顺序至关重要。当创建一个子类实例时,初始化过程遵循以下步骤: 1. ...

    Java入门理解构造器

    - **自定义构造器**:开发者可以根据需求定义一个或多个构造器来满足不同的初始化需求。自定义构造器可以是带参数的,也可以是无参数的。 #### 三、构造器的作用 构造器的作用主要体现在以下几个方面: - **初始...

Global site tag (gtag.js) - Google Analytics