java类对象创建过程,装载与实例化分析

来源:互联网 发布:三星c5网络权限设置 编辑:程序博客网 时间:2024/06/05 14:16

之前在用forName()时有过疑问,只加载了类却为调用类的构造方法,看资料了解到加载类和实例化是初始化的一部分。

今天又查找了相关资料,有了新的认识。

首先要知道类的初始化过程:虚拟机如果是首次加载Java类,会对静态代码块进行一次初始化,且只在第一次进行加载。然后进行实例化,调用构造方法。

还是先贴代码,分析过程写在注释,总结在下面,代码可以直接复制运行,看起来更方便

class lala{
    {
        
      System.out.println("lala的构造块");
    }

    static{
        System.out.println("lala的静态代码块");
        }

    public lala(){
        System.out.println("lala的构造方法");
        }
    }
class haha extends lala{
    public static int ha1=1;
    public static int ha2=1;
    static{
        
        System.out.println("haha的静态代码块");
        }

        public haha(){
             System.out.println("haha的构造方法");
             ha2++;
             System.out.println(ha1);
             System.out.println(ha2);
            }
        {
            System.out.println("haha的构造块");    
           }
}
class hehe {
    static{
        System.out.println("hehe的静态代码块");
    }
    {hehe1=10;}//构造块
    private static hehe sin = new hehe();//构造方法为私有,所以这样进行类的实例化
    //写到给hehe2赋值下面,构造方法打印的hehe2就为10了,说明最后为静态变量赋值也是按顺序来的   
    public static int hehe1;
    public static int hehe2=10;
    public int hehe3=100;
    
    private hehe() {//构造方法设置成私有,如果想实例化类,只能通过调用getInstance()
        System.out.println("\n未经过构造方法加工时");
        System.out.println(hehe1);//10,这里能输出,说明已经创建了静态变量,为10,因为构造块先执行,构造块的作用就是给静态变量赋值
        System.out.println(hehe2);//0,说明静态变量只是创建但并未赋值,采用的类变量默认值,默认是0,这里构造方法的值不受静态赋值影响,是因为还没执行到赋值语句
        System.out.println(hehe3);//100,说明非静态变量是直接赋值的,如果不赋值同样默认值为0
        hehe1++;
        hehe2++;
        System.out.println("\n构造方法加工后");
        System.out.println(hehe1);
        System.out.println(hehe2);
        
    }

    public static hehe getInstance() {//调用此方法获得的类对象是同一个,可以避免实例化出多个同样的对象
        return sin;
    }
}
public class Test{


     static{
        System.out.println("Test的静态代码块");//最先输出,说明先执行main函数所在类的静态代码块
        }

        public Test(){
             System.out.println("Test的构造方法");
            }
        {
            System.out.println("Test的构造块");    
           }
      public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
           
          System.out.println("1分割");
          Class.forName("lala");//说明此处仅加载了lala,只执行了静态代码块
            System.out.println("\n2分割");
            Class.forName("lala").newInstance(); // 实例化才执行构造块与构造方法,静态代码块仅在加载时执行了一次
            System.out.println("\n3分割");
            new lala();    
            System.out.println("\n4分割");
            new Test();
            System.out.println("\n5分割");
            Class.forName("haha");  //此处仅加载了haha,只执行了静态代码块
            System.out.println(haha.ha1);//1,说明执行了静态代码并进行了正确赋值
            System.out.println(haha.ha2);
            System.out.println("\n6分割");
            Class.forName("haha").newInstance(); //实例化了haha,先执行了父类的构造快和构造方法,再执行子类的
            System.out.println(haha.ha1);//1,说明执行了静态代码并进行了正确赋值
            System.out.println(haha.ha2);//2,说明这里构造方法的值不会被静态赋值改变
            System.out.println("\n7分割");
            Class.forName("hehe"); //在类里进行实例化,使forName()在这里加载+实例化了类hehe
            System.out.println("\n直接调用静态变量");
            System.out.println(hehe.hehe1);//仍为11,
            System.out.println(hehe.hehe2);//10,这里执行完构造方法后才执行静态变量赋值语句,这里开始看好像和前面矛盾了,静态变量赋值中插入了调用构造方法,其实是静态变量赋值进行了实例化,调用的静态方法,所以之后的语句又重新给变量进行了赋值
            
            hehe he=hehe.getInstance();
            System.out.println("\n调用实例化方法后的静态变量");
            System.out.println(he.hehe1);//直接输出1,说明未调用构造块与构造方法,只是赋值了引用,未再次实例化新对象
            System.out.println(he.hehe2);
            System.out.println(he.hehe3);
          }
    }


打印:

Test的静态代码块
1分割
lala的静态代码块

2分割
lala的构造块
lala的构造方法

3分割
lala的构造块
lala的构造方法

4分割
Test的构造块
Test的构造方法

5分割
haha的静态代码块
1
1

6分割
lala的构造块
lala的构造方法
haha的构造块
haha的构造方法
1
2
1
2

7分割
hehe的静态代码块

未经过构造方法加工时
10
0
100

构造方法加工后
11
1

直接调用静态变量
11
10

调用实例化方法后的静态变量
11
10
100


经过这些分析,可以总结出的结论:


1,类对象的创建过程分两步,加载与实例化,在new与newInstance()区别那篇也分析过

2,加载的过程又分三步,首先将类的.class文件中的数据读入内存,在堆创建一个Class对象,可以进行反射操作;然后连接这个类,首先确保类的正确性,开始执行静态代码块,分配内存,但这里初始化为默认值,再把类中符号引用转换为直接引用,最后才给静态变量正确赋值。

3,静态代码块只在首次加载类时执行且执行一次,静态代码块指以static声明的变量,方法,还有上面用到的代码块

4,静态代码块加载仅创建了变量与方法,赋值为默认值,变量默认值为0

5,普通变量创建时就进行了赋值,但是在实例化时才执行

6,实例化一个未加载的类时,执行顺序是静态代码块,构造代码块,构造方法

7,通过将构造方法设置成私有,静态引用实例化类,就只能通过调用getter()方法得到引用,使的操作的类对象是同一个,可以避免实例化出多个同样的对象,省内存(猜测),但要注意静态变量赋值应写在实例化前,否则构造方法得到的静态变量是默认值

8,如果想要forName()时就调用构造方法,可以将实例化写成静态

9,如果实例化的类有父类,先执行父类的构造块和构造方法,再执行子类的



0 0