静态代码块、构造代码块、构造器

导读

今天在作公司的项目,即统计实例化对象的存活个数,例如如下的代码java

package com.zbystudy;

/**
 * Created By zby on 14:27 2019/4/12
 */
public class StaticFiled {

    public static int count = 0;

    private final int objCount = count++;

    public void printObjCount() {
        System.out.println("生成对象的个数为: " + objCount);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new StaticFiled().printObjCount();
        }
    }
}

输出如图所示:this

静态属性直接作加法运算

在静态代码块中加法

可是,我若是把代码修改为这样的:spa

public class StaticFiled {

   public static int count = 0;
    
   static {
       count++;
   }

    private final int objCount = count;

    。。。。。。
}

在静态代码块中作加法

你会发现,其只输出1,为何会这样呢?会在下文讲解。code

在构造代码块中加法

咱们再把代码修改为这样的,结果又会不同:对象

public class StaticFiled {

    public static int count = 0;

    {
        count++;
    }
    
    private final int objCount = count;

    。。。。。。
}

其输出结果是这样的:get

在代码块中加减

你会发现,其跳过了0,而直接从1输出,哈哈,很奇怪,对吧?我也觉着很奇怪,不过,会在下文做详细介绍。it

概念

构造器

概念

假想咱们没有构造器,可是咱们还须要建立对象。由于,java是经过消息来请求其余对象。要想请求对象,势必向该对象发送一条消息,换句话说,能够把消息当作某个特定对象的方法的调用。于是,对象仍是很重要的。若是咱们手动去写一个方法,即初始化建立对象的方法,这势必会很是繁琐的。class

于是,咱们就想到了java自带的构造器,其就是为了初始化对象的。请求


构造代码块

概念

定义在类的成员位置上,使用"{}"括起来的代码。构造代码块会在每次类被调用,或者被实例化时就会被执行。其优于用以实例化对象的构造器,如代码所示:程序

/**
 * Created By zby on 16:49 2019/4/12
 */
public class Child{

    private String name;

    public Child(String name) {
        super(name);
        this.name = name;
    }

    public Child(){
        System.out.println("子类Child类的构造器");
    }

    {
        System.out.println("子类Child类的   第一个  构造代码块");
    }

    {
        System.out.println("子类Child类的  第二个  构造代码块");
    }

    public static void main(String[] args) {
        new Child();
    }
}

其输出结果如图所示:

代码块和构造器

你会发现,程序先执行构造代码块,而后再执行构造器,也就是说,构造代码块的优先级比构造器的优先级高。

这也解决了咱们上面的问题,为何程序的输出会跳过0,直接从1开始输出呢?由于,咱们首次实例化StaticFiled对象以前,构造代码块就已执行了一遍,此时的count是1,而不是0了。

同时,你也会发现,构造代码块的执行自己也是有前后顺序的,先写的先输出,后写的后输出。

父类中的构造代码块

可是,若是父类中有构造代码块,子类输出又是什么样的呢?这样,咱们定义一个父类,如代码所示:

public class Parent {

    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    public Parent() {
        System.out.println("父类Parent类的构造器");
    }

    {
        System.out.println("父类Parent类的  第一个  构造代码块");
    }

    {
        System.out.println("父类Parent类的  第一个  构造代码块");
    }
}

咱们再次执行Child类,观察此时的输出结果:

父类构造代码块和子类代码块的关系

你会神奇的发现,首先输出父类的构造代码块和构造器,再次输出子类的构造代码块和构造器。

于是,咱们能够得出两个结论:

  • 在实例化子类对象时,会执行父类中全部未加载的构造代码块和与子类相同的构造器。

与子类相同的构造器是什么意思?也就是说,若是咱们把Child类中main方法,修改成这样的: public static void main(String[] args){new Child("hhhh");},其输出结果是这样的:

子类相同的构造器的结果

  • 父级和子类构造代码块和构造器之间的优先级:父级构造代码块 --》 父级构造器 --》 子类构造代码块 --》 子类构造器

静态代码块

概念

静态代码块只执行一次,是在某个特定类第一次建立对象的时候执行,此后再也不执行该静态代码块,如代码所示:

/**
 * Created By zby on 16:49 2019/4/12
 */
public class Child extends Parent {

    private String name;

    public Child() {
//        System.out.println("子类Child类的构造器");
    }

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

    public static void main(String[] args) {
        for (int i=0;i<10;i++){
            new Child();
        }
    }
}

咱们建立了10个子类对象,但只输出一次静态代码块中的数据,结果如图所示:

静态代码块的输出结果

这也解决了上面的问题,为何咱们在静态代码块中执行这个指令: static {count++;} ,其输出结果始终是 1 的缘由了

父类静态代码块

可是若是父类中存在静态代码块,子类的输出又是什么样的呢?

在Parent类中添加代码: static {System.out.println("父类Parent类的静态代码块");}

在Child类中添加代码: static {System.out.println("子类Child类的静态代码块");}

咱们执行子类的代码,获得的截图:

父类静态

由上图,咱们得出告终论:

首先执行父类中的静态代码块,再执行子类的静态代码块。

三者综合应用

父类中存在静态代码块、构造代码块、构造器,子类中也存在静态代码块、构造代码块、构造器,那么,输出结果是什么样的呢?

输出结果如图说示:

三者综合应用的输出结果

咱们得出了这个结论三者的优先级:

父类静态代码块 --》 子类静态代码块 --》父类构造代码块 --》 父类的构造器 --》子类构造代码块 --》子类构造器

总结

咱们只有明白了父类静态代码块、子类静态代码块、父类构造代码块、父类的构造器、子类构造代码块、子类构造器的关系,才能作更多的事情。就像是盖房子,地基打得越牢固,盖的房子越稳定。不然,盖得越高,危险性越大。