該系列博文會告訴你如何從入門到進階,一步步地學習Java基礎知識,並上手進行實戰,接着瞭解每一個Java知識點背後的實現原理,更完整地瞭解整個Java技術體系,造成本身的知識框架。java
構造方法(或構造函數)是類的一種特殊方法,用來初始化類的一個新的對象。Java 中的每一個類都有一個默認的構造方法,它必須具備和類名相同的名稱,並且沒有返回類型。構造方法的默認返回類型就是對象類型自己,而且構造方法不能被 static、final、synchronized、abstract 和 native 修飾。框架
提示:構造方法用於初始化一個新對象,因此用 static 修飾沒有意義;構造方法不能被子類繼承,因此用 final 和 abstract 修飾沒有意義;多個線程不會同時建立內存地址相同的同一個對象,因此用 synchronized 修飾沒有必要。ide
構造方法的語法格式以下:函數
public class Person { /** * 1.構造方法沒有返回值 默認返回類型就是對象類型自己 * 2.構造方法的方法名和類名相同 */ //無參構造方法 public Person() { System.out.println("我是無參構造方法"); } //有參構造方法 public Person(String username,Integer age) { System.out.println("我是有參構造"+"姓名:"+username+" 密碼:"+age); } public static void main(String[] args) { Person p1=new Person();//調用無參構造 Person p2=new Person("小王",12);//調用有參構造 } }
關於構造方法,須要注意:學習
普通代碼塊是咱們用得最多的也是最廣泛的,它就是在方法名後面用{}括起來的代碼段。普通代碼塊是不可以單獨存在的,它必需要緊跟在方法名後面。同時也必需要使用方法名調用它。spa
public class Test { public void test(){ System.out.println("普通代碼塊"); } }
在類中直接定義沒有任何修飾符、前綴、後綴的代碼塊即爲構造代碼塊。咱們明白一個類必須至少有一個構造函數,構造函數在生成對象時被調用。構造代碼塊和構造函數同樣一樣是在生成一個對象時被調用線程
public class Test{ { System.out.println("我是構造代碼塊"); } }
注意:設計
想到靜態咱們就會想到static,靜態代碼塊就是用static修飾的用{}括起來的代碼段,它的主要目的就是對靜態屬性進行初始化。code
public class Test { static{ System.out.println("靜態代碼塊"); } }
注意:對象
A:
public class Test { public Test(){ System.out.println("Test構造函數"); } { System.out.println("Test構造代碼塊"); } static { System.out.println("靜態代碼塊"); } public static void main(String[] args) { } }
結果:
靜態代碼塊
B:
public class Test { public Test(){ System.out.println("Test構造函數"); } { System.out.println("Test構造代碼塊"); } static { System.out.println("靜態代碼塊"); } public static void main(String[] args) { Test t=new Test();//建立了一個對象 } }
這段代碼相比於上述代碼多了一個建立對象的代碼
結果:
靜態代碼塊 Test構造代碼塊 Test構造函數
C:
public class Test { public Test(){ System.out.println("Test構造函數"); } { System.out.println("Test構造代碼塊"); } static { System.out.println("靜態代碼塊"); } public static void main(String[] args) { Test t1=new Test();//建立了一個對象 Test t2=new Test(); } }
結果:
靜態代碼塊 Test構造代碼塊 Test構造函數 Test構造代碼塊 Test構造函數
由此結果能夠看出:靜態代碼塊只會在類加載的時候執行一次,而構造函數和構造代碼塊則會在每次建立對象的都會執行一次
對於一個類而言,按照以下順序執行:
- 執行靜態代碼塊
- 執行構造代碼塊
- 執行構造函數
對於靜態變量、靜態初始化塊、變量、初始化塊、構造器,它們的初始化順序依次是(靜態變量、靜態初始化塊)>(變量、初始化塊)>構造器。
D:
public class Test { //靜態變量 public static String staticField="靜態變量"; //變量 public String field="變量"; //靜態初始化塊 static { System.out.println(staticField); System.out.println("靜態初始化塊"); } { System.out.println(field); System.out.println("初始化塊"); } //構造函數 public Test() { System.out.println("構造函數"); } public static void main(String[] args) { Test t=new Test(); } }
結果:
靜態變量 靜態初始化塊 變量 初始化塊 構造函數
class TestA{ public TestA() { System.out.println("A的構造函數"); } { System.out.println("A的構造代碼塊"); } static { System.out.println("A的靜態代碼塊"); } } public class TestB extends TestA { public TestB() { System.out.println("B的構造函數"); } { System.out.println("B的構造代碼塊"); } static { System.out.println("B的靜態代碼塊"); } public static void main(String[] args) { TestB t=new TestB(); } }
這裏有兩個類,屬於繼承的關係,讀者先不要看答案,本身思考一下結果是啥?
1 A的靜態代碼塊 2 B的靜態代碼塊 3 A的構造代碼塊 4 A的構造函數 5 B的構造代碼塊 6 B的構造函數
當設計到繼承時,代碼的執行順序以下:
一、執行父類的靜態代碼塊,並初始化父類的靜態成員
二、執行子類的靜態代碼塊,並初始化子類的靜態成員
三、執行父類的構造代碼塊,執行父類的構造函數,並初始化父類的普通成員變量
四、執行子類的構造代碼塊,執行子類的構造函數,並初始化子類的普通成員變量
Java初始化流程圖:
class Parent { /* 靜態變量 */ public static String p_StaticField = "父類--靜態變量"; /* 變量 */ public String p_Field = "父類--變量"; protected int i = 9; protected int j = 0; /* 靜態初始化塊 */ static { System.out.println(p_StaticField); System.out.println("父類--靜態初始化塊"); } /* 初始化塊 */ { System.out.println(p_Field); System.out.println("父類--初始化塊"); } /* 構造器 */ public Parent() { System.out.println("父類--構造器"); System.out.println("i=" + i + ", j=" + j); j = 20; } } public class SubClass extends Parent { /* 靜態變量 */ public static String s_StaticField = "子類--靜態變量"; /* 變量 */ public String s_Field = "子類--變量"; /* 靜態初始化塊 */ static { System.out.println(s_StaticField); System.out.println("子類--靜態初始化塊"); } /* 初始化塊 */ { System.out.println(s_Field); System.out.println("子類--初始化塊"); } /* 構造器 */ public SubClass() { System.out.println("子類--構造器"); System.out.println("i=" + i + ",j=" + j); } /* 程序入口 */ public static void main(String[] args) { System.out.println("子類main方法"); new SubClass(); } }
結果:
父類--靜態變量 父類--靜態初始化塊 子類--靜態變量 子類--靜態初始化塊 子類main方法 父類--變量 父類--初始化塊 父類--構造器 i=9, j=0 子類--變量 子類--初始化塊 子類--構造器 i=9,j=20
(1)訪問SubClass.main(),(這是一個static方法),因而裝載器就會爲你尋找已經編譯的SubClass類的代碼(也就是SubClass.class文件)。在裝載的過程當中,裝載器注意到它有一個基類(也就是extends所要表示的意思),因而它再裝載基類。無論你創不建立基類對象,這個過程總會發生。若是基類還有基類,那麼第二個基類也會被裝載,依此類推。
(2)執行根基類的static初始化,而後是下一個派生類的static初始化,依此類推。這個順序很是重要,由於派生類的「static初始化」有可能要依賴基類成員的正確初始化。
(3)當全部必要的類都已經裝載結束,開始執行main()方法體,並用new SubClass()建立對象。
(4)類SubClass存在父類,則調用父類的構造函數,你可使用super來指定調用哪一個構造函數。基類的構造過程以及構造順序,同派生類的相同。首先基類中各個變量按照字面順序進行初始化,而後執行基類的構造函數的其他部分。
(5)對子類成員數據按照它們聲明的順序初始化,執行子類構造函數的其他部分。