java类的生命周期2类的初始化

来源:互联网 发布:js点击文字跳转页面 编辑:程序博客网 时间:2024/06/06 05:47
                                                          类的初始化
在初始化阶段,java虚拟机执行类的初始化语句,为类的静态变量赋予初始值。在程序中,静态变量的初始化有两种路径:
(1)在静态变量的声明处初始化
(2)在静态代码块中进行初始化
静态变量的声明语句,以及静态代码块都被看做类的初始化语句,Java虚拟机会按照初始化语句在类文件中的先后顺序来依次执行它们
public class Sample
{
    public static int number;
    static{number = 1;}
    static{number = 2;}
}
//number是2
Java虚拟机初始化一个类包含以下步骤
(1)假如这个类还没有被加载或连接,那就先执行加载和连接
(2)假如这个类存在直接的父类,并且这个类还没有被初始化,那么先初始化直接的父类
(3)假如类中存在初始化语句,那就依次执行这些初始化语句
当初始化一个类的直接父类时,也需要重复执行以上步骤,这会确保当程序主动使用一个类时,这个类及所有父类(包括直接父类和间接父类)都已经被初始化。
public class Base
{
    public static int a = 1;
    static
    {
        System.out.println("Base");
    }
}
public class Son extends Base
{
    public static int b = 2;
    static
    {
        System.out.println("son");
    }
}
public class GrandSon extends Son
{
    public static int c = 2;
    static
    {
        System.out.println("GrandSon");
    }
    public static void main(String[] args)
    {
        
    }
}
结果是:
Base
son
GrandSon

******************************************************************类的初始化时机**************************************************
Java虚拟机只有在程序主动使用一个类或接口时才会初始化它。只有6中活动被看做是程序对类或接口的主动使用
1.创建类的实例。创建类的实例的途径包括:用new语句创建实例,或者通过反射,克隆及反序列手段来创建实例
2.调用类的实例方法
3.访问某个类或接口的静态变量,或者对该静态变量赋值
4.调用Java API的某些反射方法,比如调用Class.forName("Worker")方法,假如Worker类还没有被初始化,那么forName()方法就会初始化Worker类,然后返回代表这个Worker类的Class实例。forName()方法时java.lang.Class类的静态方法
5.初始化一个类的子类
6.Java虚拟机启动时被表明为启动类的类。例如执行"java Base",Base就是启动类,Java虚拟机会先初始化它
除了上述6种类型,其他使用Java类的方式都被看做是被动使用,都不会导致类的初始化

1.对于final类型的静态常量,如果在编译时就能计算出变量的取值,那么这种变量被看做是编译时常量。java程序中对类的编译时常量的使用,被看作是对类的被动使用,不会导致类的初始化
public class Father
{
    public static final int NUMBER = 1;
    static{
        System.out.println("Father");
    }
}
public class Test
{
    public static void main(String[] args)
    {
        System.out.println(Father.NUMBER);
    }
}
打印结果是:1
当方法的字节码流中保存一个表示"Father.NUMBER"的符号引用,而是直接在字节码流中嵌入常量值1,因此当程序访问"Father.NUMBER"时,无需初始化Father类
**当Java虚拟机加载并连接Father类时,不会在方法区内为它的编译时常量a分配内存
2.对于final类型的静态常量,如果在编译时不能计算出变量的取值,那么程序对类的这种变量的使用,会被看做时对类的主动使用,会导致类的初始化
public class Father
{
    public static final int NUMBER = (int)(Math.random() * 10);    //不是编译时常量
    static{
        System.out.println("Father");
    }
}
public class Test
{
    public static void main(String[] args)
    {
        System.out.println(Father.NUMBER);
    }
}
打印结果是
Father
随机生成的数字
3.当Java虚拟机初始化一个类时,要求它的所有父类都会被初始化,但是这条规则并不适用于接口
在初始化一个类时,并不会先初始化它所实现的接口
在初始化一个接口时,并不会先初始化它的父接口
因此,一个父接口并不会因为它的子接口或者实现类的初始化而被初始化,只有当程序首次调用特定接口的静态变量时,才会导致该接口的初始化
4.只有当程序访问的静态变量或静态方法的确在当前类或接口中定义时,才可以看作是对类或接口的主动使用
class Father
{
    public static int number = 1;
    static{
        System.out.println("Father");
    }
    public static void print()
    {
        System.out.println("Father's static method");
    }
}
class Son extends Father
{
    static {
        System.out.println("Son");
    }
}
public class Sample
{
    public static void main(String[] args)
    {
        //System.out.println(Father.number);    //会初始化Father
        Father.print();                        //会初始化Father
    }
}
打印结果是:
Father
Father's static method
5.调用ClassLoader类的loadClass方法加载一个类时,并不是对类的主动使用,不会导致类的初始化,
class ClassA
{
    static {
        System.out.println("InitClassA");
    }
}
public class ClassB
{
    public static void main(String[] args) throws Exception
    {
        ClassLoader loader = ClassLoader.getSystemClassLoader();   //获得系统加载器
        Class objClass = loader.loadClass("ClassA");               //加载ClassA
        System.out.println("after load ClassA");                   
        System.out.println("before init ClassA");
        objClass = Class.forName("ClassA");                        //初始化ClassA
    }
}
打印结果是:
after load ClassA
before Init ClassA
InitClassA
原创粉丝点击