Java之类。实例初始化

类初始化

类被加载内存后,会在方法区创建一个Class对象(后面反射章节详细学习)来存储该类的所有信息。此时会为类的静态变量分配内存,然后为类变量进行初始化。那么,实际上,类初始化的过程时在调用一个<clinit>()方法,而这个方法是编译器自动生成的。编译器会将如下两部分的所有代码,按顺序合并到类初始化<clinit>()方法体中。

(1)静态类成员变量的显式赋值语句

(2)静态代码块中的语句

整个类初始化只会进行一次,如果子类初始化时,发现父类没有初始化,那么会先初始化父类。

public class Test{
    public static void main(String[] args){
        Father.test();
    }
}
class Father{
    private static int a = getNumber();
    static{
        System.out.println("Father(1)");
    }
    private static int b = getNumber();
    static{
        System.out.println("Father(2)");
    }
    
    public static int getNumber(){
        System.out.println("getNumber()");
        return 1;
    }
    
    public static void test(){
        System.out.println("Father:test()");
    }
}
View Code
运行结果:
getNumber()
Father(1)
getNumber()
Father(2)
Father:test()
	private static int a = getNumber();
	static{
		System.out.println("Father(1)");
	}
	private static int b = getNumber();
	static{
		System.out.println("Father(2)");
	}

顺序改变 输出内容也改变

    static{
        System.out.println("Father(1)");
    }
    private static int a = getNumber();
    private static int b = getNumber();
    static{
        System.out.println("Father(2)");
    } 
类加载时候:Father.test();第一次使用的时候类就加载,此时静态变量赋值和静态初始化按书写顺序同时进行
类加载要比实例化靠前 对象实力化=执行构造器=new 
类初始化块在类加载时进行,static静态变量也在类加载时赋值
实例初始化块在实例化对象时进行,同事执行构造器 

区别有继承关系的时候,继承关系时候父类优先子类空间运行

父子类

public class Test{
    public static void main(String[] args){
        Son.test();
        System.out.println("-----------------------------");
        Son.test();
    }
}
class Father{
    private static int a = getNumber();
    static{
        System.out.println("Father(1)");
    }
    private static int b = getNumber();
    static{
        System.out.println("Father(2)");
    }
    
    public static int getNumber(){
        System.out.println("Father:getNumber()");
        return 1;
    }
}
class Son extends Father{
    private static int a = getNumber();
    static{
        System.out.println("Son(1)");
    }
    private static int b = getNumber();
    static{
        System.out.println("Son(2)");
    }
    
    public static int getNumber(){
        System.out.println("Son:getNumber()");
        return 1;
    }
    
    public static void test(){
        System.out.println("Son:test()");
    }    
}
View Code
运行结果:
Father:getNumber()
Father(1)
Father:getNumber()
Father(2)
Son:getNumber()
Son(1)
Son:getNumber()
Son(2)
Son:test()
-----------------------------
Son:test()

结论:

每一个类都有一个类初始化方法<clinit>()方法,然后子类初始化时,如果发现父类加载和没有初始化,会先加载和初始化父类,然后再加载和初始化子类。一个类,只会初始化一次。

实例初始化

语法格式:在类中方法外,一个类中可以出现多个

【修饰符】 class 类名{
    {
        非静态代码块语句;
    }
}

 

实际上我们编写的代码在编译时,会自动处理代码,整理出一个<clinit>()的类初始化方法,还会整理出一个或多个的<init>(...)实例初始化方法。一个类有几个实例初始化方法,由这个类有几个构造器决定。

实例初始化方法的方法体,由四部分构成:

(1)super()或super(实参列表) 这里选择哪个,看原来构造器首行是哪句,没写,默认就是super()

(2)非静态实例变量的显示赋值语句

(3)非静态代码块

(4)对应构造器中的代码

特别说明:其中(2)和(3)是按顺序合并的,(1)一定在最前面(4)一定在最后面

执行特点:

  • 创建对象时,才会执行,

  • 调用哪个构造器,就是指定它对应的实例初始化方法

  • 创建子类对象时,父类对应的实例初始化会被先执行,执行父类哪个实例初始化方法,看用super()还是super(实参列表)

public class Test{
    public static void main(String[] args){
        Father f1 = new Father();
//        Father f2 = new Father("atguigu");
    }
}
class Father{
    private int a = getNumber();
    private String info;
    {
        System.out.println("Father(1)");
    }
    Father(){
        System.out.println("Father()无参构造");
    }
    Father(String info){
        this.info = info;
        System.out.println("Father(info)有参构造");
    }
    private int b = getNumber();
    {
        System.out.println("Father(2)");
    }

    public int getNumber(){
        System.out.println("Father:getNumber()");
        return 1;
    }
}
View Code
Father:getNumber()
Father(1)
Father:getNumber()
Father(2)
Father()无参构造

 

结论:

类初始化肯定优先于实例初始化。

类初始化只做一次。

实例初始化是每次创建对象都要进行。

构造器和非静态代码块

从某种程度上来看,非静态代码块是对构造器的补充,非静态代码块总是在构造器执行之前执行。与构造器不同的是,非静态代码块是一段固定执行的代码,它不能接收任何参数。因此非静态代码块对同一个类的所有对象所进行的初始化处理完全相同。基于这个原因,不难发现非静态代码块的基本用法,如果有一段初始化处理代码对所有对象完全相同,且无须接收任何参数,就可以把这段初始化处理代码提取到非静态代码块中。

即如果每个构造器中有相同的初始化代码,且这些初始化代码无须接收参数,就可以把它们放在非静态代码块中定义。通过把多个构造器中相同代码提取到非静态代码块中定义,能更好地提高初始代码的复用,提高整个应用的可维护性。

 

 

  

 

 

posted @ 2021-03-12 23:58  亚洲哈登  阅读(49)  评论(0编辑  收藏  举报