導讀
今天在做公司的項目,即統計實例化對象的存活個數,例如以下的代碼
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();
}
}
}
查看其輸出結果:
**這也解決了上面的問題,爲什麼我們在靜態代碼塊中執行這個指令: static {count++;}
,其輸出結果始終是一的原因了。
父類靜態代碼塊
但是如果父類中存在靜態代碼塊,子類的輸出又是什麼樣的呢?
在Parent類中添加代碼: static {System.out.println("父類Parent類的靜態代碼塊");}
在Parent類中添加代碼: static {System.out.println("子類Child類的靜態代碼塊");}
我們執行子類的代碼,得到的截圖:
由上圖,我們得出了結論:
首先執行父類中的靜態代碼塊,在執行子類的靜態代碼塊。
三者綜合應用
父類中存在靜態代碼塊、構造代碼塊、構造器,子類中也存在靜態代碼塊、構造代碼塊、構造器,那麼,輸出結果是什麼樣的呢?
輸出結果如圖說示:
我們得出了這個結論三者的優先級:
父類靜態代碼塊 --》 子類靜態代碼塊 --》父類構造代碼塊 --》 父類的構造器 --》子類構造代碼塊 --》子類構造器
總結
我們只有明白了父類靜態代碼塊、子類靜態代碼塊、父類構造代碼塊、父類的構造器、子類構造代碼塊、子類構造器的關係,才能做跟朵的事情。就像是蓋房子那樣,地基打得越牢固,蓋的房子越穩定。否則,蓋得越高,危險性越大。