`

Java object Initialization (class Instantiation) | 对象的初始化(即类的实例化)

阅读更多
     
类实例即对象。对象的初始化过程也就是类的实例化过程。

inside JVM作者Bill Venners关于对象初始化的文章很棒:
Object Initialization in Java:
http://www.artima.com/designtechniques/initializationP.html


一些名词概念:
Class Instance Creation Expressions:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.9
用来创建类实例对象。
格式简单表述: new nameOfClass(argument list)
Explicit Constructor Invocations:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.8.7.1
引用
指在一个构造方法中显式调用其他的构造方法。分两种:
1 Alternate constructor invocations - 通过 this(args)调用本类其他重载的构造方法
2 Superclass constructor invocations  - 通过 super(args)调用(直接)基类的构造方法。
Instance initialization methods : <init>
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.9
在编译阶段,编译器为类的每个构造方法创建一个名为<init>的方法。该<init>方法返回值为void,参数与对应的构造方法一致。<init>方法称为Instance initialization methods(实例初始化方法)。从字节码文件(.class)的角度考虑,<init>方法就是对象初始化的入口。
需要注意的是,<init>方法并不简单等同于其对应的构造方法。比如如果是在类内构造方法链的最后一个构造方法对应的<init>方法里编译器会将initializers (这里指Instance Initializers & Instance Variable Initializers)的代码添加入该<init>方法,并对父类构造方法对应的<init>方法做显式的调用。

Instance Variable Initializers
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.2
给个例子就明白了:
class CoffeeCup {
    private int innerCoffee = 355; // "= 355" is an Instance Variable Initializers
    // no constructor here
    // ...
}
Instance Initializers
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6
即实例初始化块(Instance Initialization Blocks)、非静态初始化块(Non-static Initialization Blocks)。
实例初始化块中不能有return语句。
引用
http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html 里有这样一句话:
The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.
(java在编译的时候,会将初始化块拷贝到每个构造方法里。所以初始化块可以用来为类的多个构造方法提供共享的代码块)。
这句话是错误的。编译时不是将Instance Initializers拷贝到每个构造函数中!假设成立,那在子类中存在Constructor Chaining时,岂不是Instance Initializers要被多次执行!详见:
http://stackoverflow.com/questions/12253712/java-explicit-constructor-invocation-instance-initializer
静态初始化块(Static Initialization Blocks)见:http://wuaner.iteye.com/blog/1669127

Instance Variable Initializers 和Instance Initializers在对象初始化过程中的行为表现是一致的。如果我们统称它们为Initializers:
1 在一个对象的初始化过程中,每个Initializers只会被执行一次;
2 执行时机:在super(args)调用结束后立即执行。着重强调"立即"二字,意味着其执行时机是先于子类构造方法链的。
3 执行顺序:按Initializers在代码中出现的顺序依次执行。
引用
JLS 8.8.7.1. Explicit Constructor Invocations的最后,找到这段描述可真不容易(Let C be the class being instantiated, and let S be the direct superclass of C.):
Finally, if the superclass constructor invocation statement completes normally, then all instance variable initializers of C and all instance initializers of C are executed. If an instance initializer or instance variable initializer I textually precedes another instance initializer or instance variable initializer J, then I is executed before J.
Execution of instance variable initializers and instance initializers is performed regardless of whether the superclass constructor invocation actually appears as an explicit constructor invocation statement or is provided automatically. (An alternate constructor invocation does not perform this additional implicit execution.)




显式的对象初始化:通过Class Instance Creation Expressions创建类实例。通俗的讲就是通过new关键字创建类实例。
隐式的对象初始化:以下情况下会存在隐式的类实例创建:
引用
Loading of a class or interface that contains a String literal (§3.10.5) may create a new String object to represent that literal. (This might not occur if the same String has previously been interned (§3.10.5).) - 当创建一个包含String字面量的类或接口,且该String字面量未被interned的时候,会(在Method area)中创建一个该String字面量对应的String对象(参考String:http://wuaner.iteye.com/blog/567970)
Execution of an operation that causes boxing conversion (§5.1.7). Boxing conversion may create a new object of a wrapper class associated with one of the primitive types. - 当执行的操作会引发自动装箱时,会为基本类型创建其对应的包装类对象 (自动装箱:http://wuaner.iteye.com/blog/1668172)。
Execution of a string concatenation operator (§15.18.1) that is not part of a constant expression (§15.28) sometimes creates a new String object to represent the result. String concatenation operators may also create temporary wrapper objects for a value of a primitive type.
上述四种情况,都会调用相应类的相应构造方法来做对象的初始化。
以指定构造方法为入口,一个对象的初始化过程分为以下 5 steps(这是个递归的过程):
引用
Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

1 Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

2 If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

3 This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

4 Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

5 Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.
待译。。。
有三种机制确保对象被正确的初始化:
  • constructors
  • instance variable initializers
  • instance initializers



构造方法 Constructors
1 用来创建类的实例对象,与类同名且没有返回值
2 若类中未定义任何构造方法,java默认提供一个方法体为空、没有throws子句的无参构造方法。该默认构造方法拥有和类一样的访问修饰符。这种机制保证了java中的每个类都有至少一个构造方法;若类中定义了构造方法,则不再提供(你定义的肯定也可以是无参的,此种情况下你可以理解成是对默认提供的无参构造方法的覆盖)。
3 使用super(args)或this(args),可以在构造方法中对(直接)父类或本类重载构造方法做显式构造方法调用(概念定义见上面);这种调用必须是在当前构造方法的第一行(at begin of a Constructor body)(因为只能在第一行调,所以只能调一次,因为如果再调另一个构造方法,那就不是第一行了)。
使用this(args)调当前类的重载构造方法时需要注意:不能存在形如下的递归调用环:
引用
	//一 自己调自己,产生单个构造方法的递归调用环路
	Child() {
		this();
		System.out.println("Child no-argument Constructor");
		
	}
        //二 3个构造方法组成的递归调用环路
	Child() {
		this(2);
		System.out.println("Child no-argument Constructor");
		
	}
	Child(int i) {
		this(10, i);
		System.out.println("Child 1-argument Constructor");
	}
	Child(int i, int j) {
		this();
		System.out.println("Child 2-argument Constructor");
	}

4 如果构造方法(包括java默认提供的无参构造方法,虽然无参构造方法内肯定没有显式构造方法调用)内没有显式构造方法调用,则构造方法的第一行隐式调用(直接)父类的无参构造方法super();反之,若构造方法内存在显式构造方法调用,则不存在该隐式调用。
5 构造方法不能被继承,因此不能重写。
6 构造方法可以重载。
从以上基本点我们可以得出以下一些结论:
引用
1 除了Object类的的那一个无参构造方法外,Java世界中所有类的所有构造方法都有且只有一次对其他构造方法(或父类的或本类的)的调用(或显式或隐式);
2 在一次子类对象实例化的过程中,子类的构造方法对父类构造方法的调用肯定有且只有一次(或显式或隐式)。(这里肯定排除Object类,因为它没有父类、不是任何类的子类)
3 对“首行构造方法调用(或显式或隐式)”调用的为父构造方法的子类构造方法来说:因为子类对父类构造方法的隐式调用调用的是其无参构造方法,所以当父类中没有无参构造方法时,这种调用就必须是显式的;或者理解为:如果子类构造方法中既没有显式调用基类构造方法,而基类中又没有无参的构造方法,则编译出错。
4 构造方法的调用组成一条构造方法链 Constructor chaining。如下:
class Parent {
	Parent() {
		System.out.println("Parent non-argument Constructor");
	}
}

class Child extends Parent {
	{
		System.out.println("Child Instance Initialization Block");
	}
	Child() {
		this(1);
		System.out.println("Child no-argument Constructor");
		
	}
	Child(int i) {
		this(10, i);
		System.out.println("Child 1-argument Constructor");
	}
	Child(int i, int j) {
		System.out.println("Child 2-argument Constructor");
	}
	Child(String s) {
	}
}
其对应的Constructor Chaining:
Srcs:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.8
http://docs.oracle.com/javase/tutorial/java/javaOO/constructors.html



this & super:
this
表示当前对象的引用,只能用在实例初始化块(Instance Initializer)、非静态方法(instance method)和构造方法中。有两个用途:
this.的方式,访问当前对象的成员members(static fields/non-static fiels/class method/instance method)。一个常见的使用场景是:当实例方法(或构造方法)参数与实例变量同名,造成实例变量被遮蔽时,实例方法对实例变量的访问必须通过 this.
	int i;
	
	//必须使用 this.
	void m(int i) {
		this.i = i;
	}
二 用来做显式的构造方法调用(explicit constructor invocation):类中存在多个重载的构造方法时,在某一构造方法中使用this(arguments)的方式调用本类其他(一定是其他的!不能自己调自己)的构造方法:
public class ThisSuperTest {
	private int i;
	        
	public ThisSuperTest() {
		this(11);
	}

	public ThisSuperTest(int i) {
		this.i = i;
	}

	public static void main(String[] args) {
		ThisSuperTest o = new ThisSuperTest();
		System.out.println(o.i); //11
	}
}
因为通过这样的方式可以显式调用构造方法,那到底创建了本类的几个对象那?答案还是一个,输出为11就说明了这一点。所以可以得出一个结论:一个类构造方法的被调用次数不等同于该类对象的创建个数
Srcs:
JLS 15.8.3. this http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.8.3
Java Tutorials - Using the this Keyword:http://docs.oracle.com/javase/tutorial/java/javaOO/thiskey.html
super
super表示什么那?这个不好说,你不能简单的说它是父类对象的引用,因为如果父类是抽象的话,你可以通过super调用父抽象类的非抽象方法,但父抽象类肯定没有对象实例存在。给super下定义很难,只有从其用途上掌握它。同this一样,只能用在实例初始化块(Instance Initializer)、非静态方法(instance method)和构造方法中。有两个用途:
一 通过 super. 的方式,访问层级继承树中上游的类的成员members(static fields/non-static fiels/class method/instance method)。最常见的使用场景是当子类重写父类方法时,使用 super. 调用父类的被重写方法。
class Super {
	
	String instanceStr = "superInstanceStr";
	static String staticStr;
	
	static {
		staticStr = "supberStaticStr";
	}
	
	void instanceMethod() {
		System.out.println("Super instanceMethod() ");
	}
	
	static void staticMethod() {
		System.out.println("Super staticMethod() ");
	}
}

public class Sub extends Super {
	
	String instanceStr = "subInstanceStr";
	
	void m() {
		System.out.println(super.instanceStr);
		System.out.println(super.staticStr);//warning
		super.staticMethod();
		super.instanceMethod(); //调用父类的被重写方法
		//间接基类Object的成员也同样可以通过super调
		System.out.println(super.getClass());
	}
	
	@Override
	void instanceMethod() {
		System.out.println("Sub instanceMethod() ");
	}
	
	public static void main(String[] args) {
		new Sub().m();
	}
}
二 用来做显式的构造方法调用(explicit constructor invocation):在子类构造方法中,通过 super(args) 的方式,显式调用基类的构造方法:
class Father {
	Father(String str) {
		
	}
}

class Son extends Father {
	Son() {
		super("aaa");
	}
}
Srcs:
Java Toturials - Using the Keyword super:
http://docs.oracle.com/javase/tutorial/java/IandI/super.html
JLS 15.11.2. Accessing Superclass Members using super:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.11.2



示例代码:
public class ObjectInitializeTest {
	public static void main(String[] args) throws Exception {
		Parent p = new Parent();
		System.out.println();
		Child c = new Child();
		System.out.println();
		
		//InitialBlockTest  cc = new InitialBlockTest();  //StackOverflowError
	}
}

class Parent {   
	{
		System.out.println("Parent Instance Initializers 1");
	}
    static {
        System.out.println("Parent Static Initializers 1");    
    }    
    {
		System.out.println("Parent Instance Initializers 2");
	}
    static {
        System.out.println("Parent Static Initializers 2");    
    }    
    public Parent(){    
        System.out.println("Parent 0-argument constructor");    
    }
}    
    
class Child extends Parent {
	int i;
	{
		System.out.println("Child Instance Initializers 1");
	}
    static {
        System.out.println("Child Static Initializers 1");    
    }    
    {
		System.out.println("Child Instance Initializers 2");
	}
    static {
        System.out.println("Child Static Initializers 2");    
    }    
	Child() {
		this(1);
		System.out.println("Child 0-argument Constructor");
	}
	
	Child(int i) {
		this(10, i);
		System.out.println("Child 1-argument Constructor");
	}
	
	Child(int i, int j) {
		this.i = 1;
		System.out.println("Child 2-argument Constructor");
	}
	
	Child(String s) {
		i = 2;
	}
}

//StackOverflowError
class InitialBlockTest {
	{
		System.out.println("InitialBlockTest - Non-static Instance Initializers ");
		new InitialBlockTest();
	}
	static {
		System.out.println("InitialBlockTest - Static Initializers ");
	}
	InitialBlockTest() {
		System.out.println("InitialBlockTest - Constructor");
	}
}
输出:
引用
Parent Static Initializers 1
Parent Static Initializers 2
Parent Instance Initializers 1
Parent Instance Initializers 2
Parent 0-argument constructor

Child Static Initializers 1
Child Static Initializers 2
Parent Instance Initializers 1
Parent Instance Initializers 2
Parent 0-argument constructor
Child Instance Initializers 1
Child Instance Initializers 2
Child 2-argument Constructor
Child 1-argument Constructor
Child 0-argument Constructor





Sources:
JLS 12.5. Creation of New Class Instances:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5
Java Tutorials - Creating Objects:
http://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html
  • 大小: 27.8 KB
分享到:
评论

相关推荐

    java中类的初始化顺序

    ### Java中类的初始化顺序详解 #### 一、概述 在Java编程语言中,类的初始化是一个非常重要的概念。类的初始化涉及到多个方面,包括静态成员变量、实例成员变量、静态初始化块、实例初始化块以及构造函数等。本文...

    java object create process

    在Java编程语言中,对象创建的过程是一个至关重要的...通过分析`Cupboard.java`、`StaticInitialization.java`、`Table.java`和`Bowl.java`这些文件,我们可以学习到如何定义类、创建对象以及如何初始化和管理它们。

    ts-class-initializable:TypeScript抽象类,允许从普通对象初始化实例属性

    ts-class-initializable 一个小助手,用于从纯对象启用类实例属性的初始化。 扩展一个Initializable类,您的类构造函数将接受一个普通对象文字作为其第一个参数,并将这些值分配给实例属性。例子class Foo extends ...

    类和对象的初始化过程.zip

    在Java编程语言中,类和对象的初始化是程序运行的基础,因为它们构成了面向对象编程的核心。这个压缩包文件包含三个源代码文件:StaticTest.java、Test.java和Father.java,它们可能用于演示类和对象初始化的不同...

    C++全局变量初始化的一点总结

    静态初始化发生在程序加载时,主要针对那些可以用常量表达的初始化,例如零初始化(zero initialization)和常量初始化(const initialization)。零初始化的变量通常存放在BSS段,而常量初始化的变量则位于数据段,...

    深入介绍Java对象初始化

    Java对象初始化是一个重要的概念,它涉及到对象生命周期的早期阶段,确保对象在被使用前具备了正确的状态。在Java中,对象初始化的过程分为几个步骤,包括默认初始化、显式初始化和构造器初始化。 **默认初始化**是...

    Java虚拟机(加载,链接,初始化)1

    在JVM中,类的加载、链接和初始化是至关重要的三个步骤,它们共同确保了程序的正常运行。 1. **加载(Loading)** 加载阶段是JVM寻找和导入类或接口的二进制数据的开始。这个过程通常是从类路径下的`.class`文件中...

    学习java静态数据初始化.doc

    在Java中,静态初始化主要用于在类被加载时初始化类的静态成员。这种初始化只会在类首次被加载时发生一次,之后创建该类的新实例时不会再次触发。静态初始化通过`static`关键字标记,可以用于声明静态变量、静态方法...

    深入java虚拟机加载初始化

    在本例中,由于`Singleton`类中`singleton`变量的初始化发生在类初始化阶段之前,这意味着在`Singleton`对象被创建时,静态变量`a`和`b`尚未被初始化。因此,`a++`和`b++`实际上是在默认值的基础上进行的,即`a`的...

    Java中初始化块详解及实例代码

    Java中初始化块是Java语言中的一种特殊的代码块,它可以在类加载或对象创建时执行某些操作。本文将详细介绍Java中初始化块的概念、种类、特点和应用场景。 什么是初始化块 初始化块是Java语言中的一种特殊的代码块...

    C++对象初始化[文].pdf

    在C++中,对象初始化可以分为两类:初始化和赋值,它们之间有着本质的区别。 初始化是在创建对象的同时为其赋予初始值,而赋值则是替换对象当前的值。在C++中,对象初始化的方法多种多样,包括直接初始化、复制初始...

    java对象创建过程

    3. **初始化(Initialization)**:执行类的初始化代码块和静态初始化器。 #### 二、对象实例化 一旦类被正确地加载和初始化后,就可以通过以下步骤来创建对象实例: 1. **分配内存**:JVM会在堆内存中为新创建的...

    详解Spring 中如何控制2个bean中的初始化顺序

    我们可以在业务层自己控制 A 和 B 的初始化顺序,在 A 中设置一个“是否初始化的”标记,B 初始化前检测 A 是否得以初始化,如果没有则调用 A 的初始化方法,所谓的 check-and-act。 这种方法的优点是可以做到 lazy...

    CSS样式初始化commonInitialize.css

    在"commonInitialize.css"中,通常会包含以下几类初始化内容: 1. **重置默认样式**:这一步是为了消除浏览器之间的样式差异,如设置`*{margin:0;padding:0;}`,这将清空所有元素的内外边距,确保元素布局的一致性...

    LS-DYNA3D中的应力初始化_lsdyna_应力初始化_

    9. **应用实例**:应力初始化常应用于预应力结构分析、疲劳裂纹扩展、地质力学问题、爆炸冲击分析等。在这些领域,初始应力的精确模拟对于预测行为和设计决策至关重要。 总之,LS-DYNA3D中的应力初始化是一个涉及多...

    Java中的静态块初始化块及main方法.doc

    Java编程语言中,静态块(static block)和初始化块(instance initialization block)是两种特殊的代码块,它们在程序运行的不同阶段被执行,对于类和对象的初始化有着重要作用。同时,`main`方法是Java程序的入口...

    程序设计中基于构造函数的对象初始化方法研究.zip_C++_suddenh4l

    在C++编程语言中,对象初始化是一个至关重要的过程,它涉及到类的实例化以及成员变量的赋初值。本文将深入探讨基于构造函数的对象初始化方法,以帮助开发者更好地理解和运用这一核心概念。 构造函数是C++中一种特殊...

    JAVA类加载机制与动态代理

    类加载机制负责将描述类的数据从`.class`文件加载到内存,并进行必要的校验、转换解析和初始化,使之成为可以被Java虚拟机直接使用的Java类型。根据Java虚拟机规范的规定,在以下几种情况下必须对类进行初始化: 1....

    C++有子对象的派生类的构造函数

    当一个类含有子对象(即嵌套类或成员对象)时,这些子对象在派生类的实例化过程中也需要被正确地初始化。本篇文章将深入探讨含有子对象的C++派生类构造函数的细节,以及如何有效地管理它们。 一、构造函数的基本...

    初始化Initialization

    3. **ReLU激活函数的初始化**:由于ReLU函数在负区间为零,如果权重初始化得过大,可能导致大量的神经元“死亡”,即始终处于非激活状态。因此,如Kaiming初始化(也称为He初始化)推荐使用标准差为`sqrt(2 / n_in)`...

Global site tag (gtag.js) - Google Analytics