`
supportopensource
  • 浏览: 515045 次
  • 性别: 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中的构造器初始化,代码很简单,叙述很明确,需要的朋友可以了解下。

    java 构造器的调用

    java 构造器初始化成员变量 是否提供默认构造器 继承中构造器的调用

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

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

    通过三点用Java求三角形周长和面积

    ②重载构造器初始化对角线上的点,public Point(double x); ③编写distance()方法,计算当前点到原点的距离:public double distance(); ④重载double distance(Point p)方法,计算当前点到另外一个点p的距离。 ...

    C#高级编程_Lambda表达式和LINQ的原理和使用

    静态字段在静态构造器初始化。 字段是与类或类的实例关联的变量。 使用 static 修饰符声明的字段定义了一个静态字段 (static field)。一个静态字段只标识一个存储位置。无论对一个类创建多少个实例,它的静态字段...

    编程思想下篇

    5.7 构造器初始化 5.7.1 初始化顺序 5.7.2. 静态数据的初始化 5.7.3. 显式的静态初始化 5.7.4. 非静态实例初始化 5.8 数组初始化 5.8.1 可变参数列表 5.9 枚举类型 5.10 总结 第6章 访问权限控制 第7章 复用类 第8章...

    Thinking in java4(中文高清版)-java的'圣经'

    5.6.1 指定初始化 5.7 构造器初始化 5.7.1 初始化顺序 5.7.2. 静态数据的初始化 5.7.3. 显式的静态初始化 5.7.4. 非静态实例初始化 5.8 数组初始化 5.8.1 可变参数列表 5.9 枚举类型 5.10 总结 第6章 访问权限控制 ...

    深入理解java构造器机理

    java构造方法是java类中最重要的一个概念,这篇文档涵盖了,java对象初始化过程中构造器调用的顺序,及作用。

    java基础核心总结归纳---参考手册--心得手册-学习资料-总结经验

    构造器初始化 15 初始化顺序 16 this 和 super 16 访问控制权限 16 继承 17 多态 17 代理 17 Static 17 Final 17 接⼝和抽象类 18 接⼝ 18 抽象类 18 异常 18 认识 Exception 18 什么是 Throwable 18 常⻅的 ...

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

    我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。我们也可以通过下面的测试代码来验证这一点:

    Java 基础核心总结 +经典算法大全.rar

    类的初始化 成员初始化 构造器初始化初始化顺序 数组初始化 对象的销毁 对象作用域 this 和 super 访问控制权限继承 多态组合代理 向上转型static final 接口和抽象类接口 抽象类异常 认 识 Exception 什么是 ...

    C#关于构造函数初始化器的使用

    有时,在一个类中有几个构造函数,以容纳某些可选参数,这些构造函数都包含一些共同的代码。  例如,下面的情况: class Car { private string description; private uint nWheels; public Car(string model,...

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

    using System; #region /* /// /// 类定义 /// class Person { //成员变量 int name; int height; //成员方法 void eat() { } } class Test { static void Main() { ... class

    Python构造器用法解析.docx

    构造器概述 Python中的构造器是一种特殊类型的方法,它被用于创建和初始化对象。构造器是在对象被创建之前自动调用的方法,它可以接受参数、返回值和执行任意代码。Python中的构造器由特殊方法`__init__`定义,它...

    C#与.NET技术平台实战演练.part2

    initializer10-5初始化只读数据10-6在构造器中使用out与ref10-7struct构造器10-7-1struct构造器的限制10-8static构造器10-8-1使用static构造器初始化静态成员10-8-2static构造器的限制10-9对象与内存10-9-l对象的...

    C#与.NET技术平台实战演练.part1

    initializer10-5初始化只读数据10-6在构造器中使用out与ref10-7struct构造器10-7-1struct构造器的限制10-8static构造器10-8-1使用static构造器初始化静态成员10-8-2static构造器的限制10-9对象与内存10-9-l对象的...

    【05-面向对象(下)】

    •在java类里只能包含Field,方法,构造器,初始化块,内部类(接口、枚举)等5种成员。 用static修饰的类成员属 于类成员,类Field既可通过类来访问,也可以通过类的对象来访问。当通过对象来访问类属性时,系统会在底...

    类初始化和实例初始化1

    实例初始化过程:实例初始化就是执行()方法:()方法可能重载有多个,有几个构造器就有几个方法()方法由非静态实例

    Scala进阶_构造器

    类的构造器 当创建类对象的时候,会自动调用类的构造器。之前使用的都是默认构造器,我们接下来要学习如何自定义构造器。 主构造器 我们学习过,Java的构造... // 初始化成员变量 this.name = name; this.age = age;

    c# 引用类型构造器

    创建一个引用类型的实例时, 首先为实例的数据字段分配内存, 然后初始化对象的附加字段(对象指针、同步块索引), 最后调用类型中定义的实例构造器来设置对象的初始化状态。 构造引用类型的对象时,在调用类型的...

Global site tag (gtag.js) - Google Analytics