Java——程式碼塊
前言
在程式編寫之中可以直接使用
{...}
定義的一段語句就是程式碼塊。根據程式碼塊的位置以及關鍵字的不同可以分為4種:普通程式碼塊、構造塊、靜態塊以及同步程式碼塊(多執行緒相關)。下面將先介紹前3種以及Java類的初始化順序:
- 對於一個類(沒有繼承)的初始化情況
寫在方法裡面的程式碼塊就是普通程式碼塊
public static void main(String args[]){ { int num = 0; } int num=100; }
{...}
表示的是一個作用域,內部定義的變數的可以起作用的範圍僅在{...}
這個範圍內。
上面程式碼中{int num=0;}
的num在離開{...}
後就被銷燬了,於是可以在外部又可以定義int num=100
。
若是寫成以下:
public static void main(String args[]){ int num=100; { int num = 0;//報錯:Duplicate local variable num } }
因為外部也存在num這個變數,且有效。所以,這樣定義會出錯。
普通程式碼塊的作用就是為了防止在方法中編寫程式碼過多,產生變數重名,於是對一個方法中的程式碼進行區域性的分割 。但是建議一個方法中的程式碼不要太長,儘量不使用普通程式碼塊
如果將一個程式碼塊放在類裡面,那麼就是一個構造塊。
構造塊的作用是為了給物件進行初始化 。我們知道建構函式的作用也是為了給物件進行初始化,那麼這兩者有什麼區別呢?
publicclass Student { private String name; private int age; //無參建構函式 public Student() { System.out.println("constructor with no args "); System.out.println("name:"+this.name + " age:"+this.age); this.name = "no name"; this.age = 18; } //有參建構函式 public Student(String name, int age){ System.out.println("constructor with args"); System.out.println("name:"+this.name + " age:"+this.age); this.name = name; this.age = age; } //構造塊 { System.out.println("constructor block "); name = "cbname"; age = 20; } public static void main(String[] args) { new Student(); System.out.println("=========="); new Student("sakura", 19); } } /* output: constructor block constructor with no args name:cbname age:20 ========== constructor block constructor with args name:cbname age:20 */
可以看出每次建立物件時,都會呼叫一次構造塊,並且構造塊的優先於建構函式執行 。有物件的建立,才會呼叫構造快,類是不能呼叫構造塊的。
構造塊與建構函式的區別在於:每個物件被構造塊初始化的那部分變數擁有的初始值是一樣的,構造塊對所有物件的效果是一樣的 。然而每個物件可能會使用不同建構函式,不同的建構函式初始化物件的方式是不同的 。
使用static修飾的程式碼塊就叫做靜態程式碼塊或者直接叫靜態塊。
前面在介紹ofollow,noindex"> static關鍵字 (可以回顧檢視)時,介紹了一部分static修飾程式碼塊的知識。
-
靜態塊在類載入時執行,且只會執行一次 ,執行順序優先主函式、建構函式和構造塊 。
-
靜態程式碼塊主要用於初始化類中的static屬性(類屬性) ,而構造塊是初始化物件中的屬性
-
一個類中可以有多個靜態程式碼塊, 執行順序依照靜態程式碼塊的宣告順序。靜態程式碼塊可以在類的任意位置定義,在方法中不可以宣告靜態塊 。
對於一個類(沒有繼承)的初始化情況
publicclass Student { private String name="no name"; private int age=18; private static int id=1; //無參建構函式 public Student() { System.out.println("======"); System.out.println("無參建構函式"); System.out.println("姓名:"+name+" 年齡:"+age); } //有參建構函式 public Student(String name, int age){ System.out.println("======"); System.out.println("有參建構函式"); System.out.println("姓名:"+this.name+" 年齡:"+this.age); this.name = name; this.age = age; System.out.println("姓名:"+this.name+" 年齡:"+this.age); } //構造塊 { System.out.println("======"); System.out.println("構造塊"); System.out.println("姓名:"+this.name+" 年齡:"+this.age); this.name = "cbname"; this.age = 18; } //靜態程式碼塊 static { System.out.println("======"); System.out.println("靜態塊"); System.out.println("靜態變數id="+id); } public static void main(String[] args) { System.out.println("======"); System.out.println("主方法"); new Student(); new Student("小王",20); } } /* output: ====== 靜態塊 靜態變數id=1 ====== 主方法 ====== 構造塊 姓名:no name 年齡:18 ====== 無參建構函式 姓名:cbname 年齡:18 ====== 構造塊 姓名:no name 年齡:18 ====== 有參建構函式 姓名:cbname 年齡:18 姓名:小王 年齡:20 */
對於一個類而言:
靜態程式碼塊、構造程式碼塊、建構函式和主函式的執行順序為:
靜態程式碼塊>主函式>構造程式碼塊>建構函式
在加上靜態屬性、普通屬性,他們的初始化執行順序就為:
靜態變數、靜態程式碼塊 > 主函式 > 指定初始值的屬性 > 構造程式碼塊 > 建構函式
class Person{ private String name="Person沒有名字"; private int age=10; private static int id=1; //無參建構函式 public Person() { System.out.println("======"); System.out.println("Person無參建構函式"); System.out.println("Person 姓名:"+this.name+" 年齡:"+this.age); } //構造塊 { System.out.println("======"); System.out.println("Person 構造塊"); System.out.println("Person 姓名:"+this.name+" 年齡:"+this.age); this.name = "pcbname"; this.age =11 ; } //靜態程式碼塊 static { System.out.println("======"); System.out.println("Person 靜態塊"); System.out.println("Person 靜態變數id="+id); } } publicclass Student extends Person{ private String name="Student沒有名字"; private int age=18; private static int id=2; //無參建構函式 public Student() { //自動呼叫父類的無參建構函式 super(); System.out.println("======"); System.out.println("Student無參建構函式"); System.out.println("Student 姓名:"+this.name+" 年齡:"+this.age); } //有參建構函式 public Student(String name, int age) { //自動呼叫父類的無參建構函式 super(); System.out.println("======"); System.out.println("Student有參建構函式"); System.out.println("Student 姓名:"+this.name+" 年齡:"+this.age); this.name = name; this.age = age; } //構造塊 { System.out.println("======"); System.out.println("Student 構造塊"); System.out.println("Student 姓名:"+this.name+" 年齡:"+this.age); this.name = "scbname"; this.age = 19; } //靜態程式碼塊 static { System.out.println("======"); System.out.println("Student 靜態塊"); System.out.println("Student 靜態變數id="+id); } public static void main(String[] args) { System.out.println("======"); System.out.println("主方法"); System.out.println("\n--------第一次建立Studet物件--------"); new Student(); System.out.println("\n--------第二次建立Studet物件--------"); new Student("小夏",20); } } /* ====== Person 靜態塊 Person 靜態變數id=1 ====== Student 靜態塊 Student 靜態變數id=2 ====== 主方法 --------第一次建立Studet物件-------- ====== Person 構造塊 Person 姓名:Person沒有名字 年齡:10 ====== Person無參建構函式 Person 姓名:pcbname 年齡:11 ====== Student 構造塊 Student 姓名:Student沒有名字 年齡:18 ====== Student無參建構函式 Student 姓名:scbname 年齡:19 --------第二次建立Studet物件-------- ====== Person 構造塊 Person 姓名:Person沒有名字 年齡:10 ====== Person無參建構函式 Person 姓名:pcbname 年齡:11 ====== Student 構造塊 Student 姓名:Student沒有名字 年齡:18 ====== Student有參建構函式 Student 姓名:scbname 年齡:19 */
觀察程式碼結果,分析,對於有繼承關係的類,初始化順序按如下進行:
-
執行父類的靜態程式碼塊,並初始化父類靜態成員變數
2. 執行子類的靜態程式碼塊,並初始化子類靜態成員變數
3. 執行父類的構造程式碼塊,執行父類的建構函式,若普通成員變數指定了初始值則先執行初始值的賦值,然後返回執行建構函式
4.執行子類的構造程式碼塊,執行子類的建構函式,若普通成員變數指定了初始值則先執行初始值的賦值,然後返回執行建構函式
小結
本文介紹的三種程式碼塊,普通塊和構造塊都不建議使用,靜態塊可以使用用於初始化靜態變數。
理清Java程式初始化的執行順序可以很清楚地掌握程式的執行過程。
對一個類而言,靜態變數和靜態程式碼塊> 主函式 > 指定初始值的屬性 > 構造程式碼塊 > 建構函式
對於有繼承的來說,
父類靜態塊和靜態變數 > 子類靜態塊和靜態變數 > 父類指定初始值的屬性 > 父類構造塊 > 父類建構函式 > 子類指定初始值的屬性 > 子類構造塊 > 子類建構函式