类初始化与实例初始化

  我们的类初始化在类被加载时就会执行,这里类被加载并不一定要实例化类的对象,只要jvm在第一次用到这个类的方法或者属性时发现内存中没有加载过这个类,就会对类进行初始化。

  当类初始化时被加载到内存中的方法区中,并次创建成一个class对象存储类的信息,然后分配内存执行,类中的静态属性的显式赋值语句和静态代码块被放在一个叫<clinit>的方法中执行(按属性和静态代码块在代码中书写的

顺序加载),这里要注意,对于每一个类,<clinit>方法只在类第一次被调用时执行一次,当第二次调用这个类的方法或者属性时,就不会再被执行。

  当被使用的类是某个类的子类时,这时首先初始化父类,然后再初始化子类,同样的,父类也只被能被初始化一次,之后使用不会再被初始化,对于一个类,<clinit>方法只执行一次 

  而实例初始化是在实例化类的对象时进行的,也就是说,只要实例化一个对象,就会进行一次实例初始化。 

  实例初始化过程和类初始化过程相似,但都是非静态的,非静态属性的显式赋值和构造代码块还有构造器(注意注意:构造器是最后被初始化,其他的属性和代码块按代码书写的顺序加载)一起被放在一个叫<init>的方法中执行,每创建一

个对象就初始化一次,这一点和类初始化不同。

 

  下面提供几个例题帮助理解:

  第一题比较综合,可从第二题开始

 1 public class Test1 {
 2     public static void main(String[] args) {
 3         Zi zi = new Zi();//1 首先进入主方法,看到类被实例化,先进行类的初始化,然后进行实例初始化
 4     }
 5 }
 6 class Fu{
 7     private static int i = getNum("(1)i");//3 首先进行静态属性的加载, 执行getNum方法
 8     private int j = getNum("(2)j");//11 实例初始化父类,加载非静态属性
 9     static{
10         print("(3)父类静态代码块");//6 加载静态代码块并打印 (3)父类静态代码块->1,父类实例化完毕
11     }
12     {
13         print("(4)父类非静态代码块,又称为构造代码块");//14 加载构造代码块 打印(4)父类非静态代码块,又称为构造代码块->2
14     }
15     Fu(){
16         print("(5)父类构造器");//15最后调用构造器,打印  (5)父类构造器->2    父类实例初始化完成 ,进入子类
17     }
18     public static void print(String str){//5 打印(1)i->0  //13 (2)j->1
19         System.out.println(str + "->" + i);
20     }
21     public static int getNum(String str){//4  //12
22         print(str);
23         return ++i;
24     }
25 }
26 class Zi extends Fu{//2 找到对象所在类发现存在继承关系,所以转到父类先进行父类的初始化
27     private static int k = getNum("(6)k");//7 进入子类实例化,方法同
28     private int h = getNum("(7)h");//16同父类
29     static{
30         print("(8)子类静态代码块");//10 (8)子类静态代码块->1 子类初始化完毕,开始进行实例初始化
31     }
32     {
33         print("(9)子类非静态代码块,又称为构造代码块");//19 (9)子类非静态代码块,又称为构造代码块->2
34     }
35     Zi(){
36         print("(10)子类构造器");//20 (10)子类构造器->2
37     }
38     public static void print(String str){//9 (6)k->0   //18 (7)h->1
39         System.out.println(str + "->" + k);
40     }
41     public static int getNum(String str){//8  //17
42         print(str);
43         return ++k;
44     }
45 }

执行结果:

(1)i->03)父类静态代码块->16)k->08)子类静态代码块->12)j->14)父类非静态代码块,又称为构造代码块->25)父类构造器->27)h->19)子类非静态代码块,又称为构造代码块->210)子类构造器->2

 

 1 public class Test07 {
 2     public static void main(String[] args) {
 3         Son son = new Son();
 4     }
 5 }
 6 class Father{
 7     static{
 8         System.out.println("(1)父类的静态代码块");
 9     }
10     {
11         System.out.println("(2)父类的非静态代码块");
12     }
13     Father(){
14         System.out.println("(3)父类的无参构造");
15     }
16 }
17 class Son extends Father{
18     static{
19         System.out.println("(4)子类的静态代码块");
20     }
21     {
22         System.out.println("(5)子类的非静态代码块");
23     }
24     Son(){
25         System.out.println("(6)子类的无参构造");
26     }
27 }

 执行结果:

(1)父类的静态代码块
(4)子类的静态代码块
(2)父类的非静态代码块
(3)父类的无参构造
(5)子类的非静态代码块
(6)子类的无参构造

 

 1     public static void main(String[] args) {
 2         Sub s = new Sub();
 3     }
 4 }
 5 class Base{
 6     Base(){
 7         method(100);
 8     }
 9     {
10         System.out.println("base");
11     }
12     public void method(int i){
13         System.out.println("base : " + i);
14     }
15 }
16 class Sub extends Base{
17     Sub(){
18         super();
19         super.method(70);
20     }
21     {
22         System.out.println("sub");
23     }
24     public void method(int j){
25         System.out.println("sub : " + j);
26     }

执行结果:

base
sub : 100
sub
base : 70

这题要注意,执行父类构造时发现一个method方法,这时要看实例化的对象是什么,是子类就要看子类中有没有同名方法,不能盲目调用父类的方法

点赞