热闹 热闹 标签 标签

Java类初始化全过程

湖外 评论(0) 浏览(309)

Java初始化简介

一般来说,在Java中,每个类产生的编译代码都存在于它自己的独立文件中,该文件只有在使用程序代码时才会被加载,也就是说,类的代码只有在初次使用时才加载。但是,如果存在static的话,就不一样了,当访问static字段或static方法时,也会发生加载。最常见的static方法是构造器方法了,虽然没有显示写明static关键字。所以,更准确地讲,Java的类应该是在其任何static成员被访问时加载的。

Java继承的概念

Java面向对象的三大特征是继承、封装、多态。继承的概念顾名思义,类似父子关系,子继承父亲。继承通常被认为是is-a关系,即子类是一个父累,可以这样理解。例如,大黄鸭是一个鸭子。虽然继承是面向对象的一个重大特性,但是对于项目的架构来说,继承应该慎用,常用的模式有组合,组合即一个类中持有另外一个类的对象,例如我们要构造一个汽车时,总要持有发动机、门、窗户、轮子等这些类的具体对象,显然汽车不是一个发动机,也不是一个轮子,它们的关系是has-a的关系,因此需要使用组合模式来设计。在OOP中,使用继承的一个优点是它可以向上转型(upcasting),即子类可以持有父类的方法,或者说子类是一个父类,通过向上转型,进而引出了多态的优点,即当有方法被覆盖时,父类类型的子类对象可以从子类依次向上查找,知道遇到该方法便可调用,这一优点为程序的可扩展性奠定了基础,如要新建一个子类,只用重新覆盖父类中已有的某个方法即可,在调用时即可实现多态的特性。然而,继承的使用场合应该尽量谨慎,使用前应该问问自己是否需要向上转型,如果后续业务中,必须要进行向上转型,那么就使用继承;否则,大可不必使用继承。

那么,在继承这种设计模式中,程序的加载过程是怎么进行的呢?父类的构造方法会不会加载?如果有static字段或方法,又是如何加载呢?

含有继承的程序加载过程

下面,我们以一个继承关系的两个类来探讨该问题,类中包含有static字段。

首先,写出基类的代码:


?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package demo0812.demo1;
 
public class SupClass {
 
        private int i=9;
        protected int j;
        public SupClass() {
            System.out.println("i="+i+",j="+j);
            j=39;
        }
        private static int x1=printInit("SupClass.x1 initialized");
        protected static int printInit(String str) {
            System.out.println(str);
            return 47;
        }
}

写出子类的代码:



?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package demo0812.demo1;
 
public class SubClass extends SupClass {
 
    private int k=printInit("SubClass.k initialized");
    public SubClass() {
        System.out.println("k="+k);
        System.out.println("j="+j);
    }
     
    private static int x2=printInit("SubClass.x2 initialized");
    public static void main(String[] args) {
        System.out.println("SubClass constructor");
        SubClass subClass = new SubClass();
    }
}

从上面的两个类代码可以看出,基类中含有一个static方法和一个static字段,另外还包含一个显示的构造方法。那么在程序执行过程中,当然先访问SubClass.main()方法,这是程序的入口,于是加载器开始启动并找出SubClass的编译代码(SubClass.class文件),然后,注意到该类有个关键字extends,说明它有个父类,于是,加载器便去加载父类的代码,即SupClass.class文件。加载器发现父类中含有一个静态字段,于是加载器先加载这个静态字段,同时也加载了静态方法,于是输出

?

1
SupClass.x1 initialized

,同时将x1的值赋为47。在加载完父类的静态区域以后,开始加载子类的静态区域,加载器于是看到,在子类中,也有一个静态字段x2,并且对它进行初始化,于是输出

?

1
SubClass.x2 initialized

,同时,将x2的值赋为47。这时,父类和子类的静态区都已经加载完成,于是,接着读取main函数,首先输出main中的第一行命令

?

1
SubClass constructor

接着,执行第二行命令,即对子类进行实例化,注意对子类实例化之前一定会对父类进行实例化,于是输出

?

1
i=9,j=0

接着,对子类进行按序执行,首先输出

?

1
SubClass.k initialized

并对k赋值为47,最后输出


?

1
2
k=47
j=39



0