java类和对象的初始化

JAVA类与对象的初始化

JVM通过加装、连接和初始化一个Java类型,使该类型可以被正在运行的Java程序所使用。类型的生命周期如下图所示:
这里写图片描述

类的初始化和对象的初始化(类的实例化、创建一个对象)是两个不同的事情,类的初始化在类的实例化之前。
1类的初始化
类初始化阶段,主要是为类变量(静态变量)赋予正确的初始值。这里的“正确”初始值指的是程序员希望这个类变量所具备的起始值。一个正确的初始值是通过类变量初始化语句或者静态初始化块给出的。初始化一个类包含两个步骤:
  1) 如果类存在直接父类的话,且直接父类还没有被初始化,就先初始化直接父类。
  2) 如果类存在一个类初始化方法,就执行此方法。
类初始化时机:Java 虚拟机规范为类的初始化时机做了严格定义:在首次主动使用时初始化。
  1) 创建类的新实例;
  2) 调用类的静态方法;
  3) 操作类或接口的静态字段(final字段除外);
  4) 调用Java的特定的反射方法;
  5) 初始化一个类的子类;
  6) 指定一个类作为Java虚拟机启动时的初始化类,含有main()方法的类启动时
除了以上六种情形以外,所有其它的方式都是被动使用的,不会导致类的初始化。
一旦一个类被装载、连接和初始化,它就随时可以使用了。
2对象的实例化,对象实例化和初始化是就是对象生命的起始阶段的活动。
2.1对象何时被初始化
Java对象在其被创建时初始化,在Java代码中,有两种行为可以引起对象的创建。其中比较直观的一种,也就是通常所说的显式对象创建,就是通过new关键字来调用一个类的构造函数,通过构造函数来创建一个对象,这种方式在java规范中被称为“由执行类实例创建表达式而引起的对象创建”。
当然,除了显式地创建对象,以下的几种行为也会引起对象的创建,但是并不是通过new关键字来完成的,因此被称作隐式对象创建,他们分别是:
· 加载一个包含String字面量的类或者接口会引起一个新的String对象被创建,除非包含相同字面量的String对象已经存在与虚拟机内了(JVM会在内存中会为所有碰到String字面量维护一份列表,程序中使用的相同字面量都会指向同一个String对象),比如,
Java代码

1.class StringLiteral {  
2.    private String str = "literal";  
3.    private static String sstr = "s_literal";   
4.}  

· 自动装箱机制可能会引起一个原子类型的包装类对象被创建,比如,
Java代码

1.class PrimitiveWrapper {  
2.    private Integer iWrapper = 1;  //Integer iWrapper=Integer.valueOf(1);
3.}  

· String连接符也可能会引起新的String或者StringBuilder对象被创建,同时还可能引起原子类型的包装对象被创建
2.2对象初始化的方式
构造函数
实例初始化器
实例变量初始化器
每一个Java中的对象都至少会有一个构造函数,如果我们没有显式定义构造函数,那么Java编译器会为我们自动生成一个构造函数,这个默认的构造器仅仅调用父类的无参构造器。构造函数与类中定义的其他方法基本一样,除了构造函数没有返回值,名字与类名一样之外。在生成的字节码中,这些构造函数会被命名成方法,参数列表与Java语言书写的构造函数的参数列表相同(这样的方法名在Java语言中是非法的,但是对于JVM来说,是合法的)。另外,构造函数也可以被重载。
Java要求一个对象被初始化之前,其超类也必须被初始化,这一点是在构造函数中保证的。Java强制要求Object对象(Object是Java的顶层对象,没有超类)之外的所有对象构造函数的第一条语句必须是超类构造函数的调用语句或者是类中定义的其他的构造函数,如果我们即没有调用其他的构造函数,也没有显式调用超类的构造函数,那么编译器会为我们自动生成一个对超类构造函数的调用指令。Java对构造函数作出这种限制,目的是为了要保证一个类中的实例变量在被使用之前已经被正确地初始化,不会导致程序执行过程中的错误。

1.class Foo {  
2.    int i;  
3.      
4.    Foo() {  
5.        i = 1;  
6.        int x = getValue();  
7.        System.out.println(x);  
8.    }  
9.      
10.    protected int getValue() {  
11.        return i;  
12.    }  
13.}  
14.  
15.class Bar extends Foo {  
16.    int j;  
17.      
18.    Bar() {  
19.        j = 2;  
20.    }  
21.      
22.    @Override  
23.    protected int getValue() {  
24.        return j;  
25.    }  
26.}  
27.  
28.public class ConstructorExample {  
29.    public static void main(String... args) {  
30.        Bar bar = new Bar();  
31.    }  
32.}  
1.public class InstanceInitializer {  
2.      
3.    private int i = 1;  //实例变量初始化器
4.    private int j;  
5.      
6.    {  
7.        j = 2;  //实例初始化器
8.    }  
9.}  

实例变量初始化器和实例初始化器代码会先于构造函数被执行。
如果我们定义了实例变量初始化器与实例初始化器,那么编译器会将其中的代码放到类的构造函数中去,这些代码会被放在对超类构造函数的调用语句之后(还记得吗?Java要求构造函数的第一条语句必须是超类构造函数的调用语句),构造函数本身的代码之前。

对比总结
类初始化 对象初始化
为静态变量赋初值 实例变量和从超类继承的实例变量
静态变量初始化语句和静态初始化块 构造函数、实例变量初始化器、实例初始化器
在首次“主动使用”是初始化,且只进行一次 对象创建时初始化(对应对象创建的几种情况),一个实例变量最多可以被初始化4次

对象的创建过程
1即使没有显示地使用static关键字,构造器实际上也是静态方法。因此,当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者类的静态方法、静态域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件
2载入Dog.class文件,有关静态初始化的所有动作执行。因此,静态初始化只在Class对象首次加载时进行一次
3当用new Dog()创建对象时,首先在堆上为Dog对象分配足够的存储空间
4这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成默认值(对数字来说就是0,对布尔型和字符型也相同),而引用则被设置成null
5执行所有处于字段定义处的初始化动作
6执行构造器。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值