共计 2859 个字符,预计需要花费 8 分钟才能阅读完成。
导读
今天在做公司的项目,即统计实例化对象的存活个数,例如以下的代码
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();
}
}
}
输出如图所示:
在静态代码块中加法
但是,我如果把代码修改成这样的:
public class StaticFiled {
public static int count = 0;
static {
count++;
}
private final int objCount = count;
。。。。。。
}
你会发现,其只输出 1,为什么会这样呢?会在下文讲解。
在构造代码块中加法
我们再把代码修改成这样的,结果又会不一样:
public class StaticFiled {
public static int count = 0;
{
count++;
}
private final int objCount = count;
。。。。。。
}
其输出结果是这样的:
你会发现,其跳过了 0,而直接从 1 输出,哈哈,很奇怪,对吧?我也觉着很奇怪,不过,会在下文作详细介绍。
概念
构造器
概念
假想我们没有构造器,但是我们还需要创建对象。因为,java 是通过消息来请求其他对象。要想请求对象,势必向该对象发送一条消息,换句话说,可以把消息当做某个特定对象的方法的调用。因而,对象还是很重要的。如果我们手动去写一个方法,即初始化创建对象的方法,这势必会非常繁琐的。
因而,我们就想到了 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 类的静态代码块 ”);}
我们执行子类的代码,得到的截图:
由上图,我们得出了结论:
首先执行父类中的静态代码块,再执行子类的静态代码块。
三者综合应用
父类中存在静态代码块、构造代码块、构造器,子类中也存在静态代码块、构造代码块、构造器,那么,输出结果是什么样的呢?
输出结果如图说示:
我们得出了这个结论三者的优先级:
父类静态代码块 –》子类静态代码块 –》父类构造代码块 –》父类的构造器 –》子类构造代码块 –》子类构造器
总结
我们只有明白了父类静态代码块、子类静态代码块、父类构造代码块、父类的构造器、子类构造代码块、子类构造器的关系,才能做跟朵的事情。就像是盖房子那样,地基打得越牢固,盖的房子越稳定。否则,盖得越高,危险性越大。