Java初始化對象的工具 - 構造器
本文關鍵字:對象、初始化、實例化、構造器、構造方法
[TOC]
一、構造器的作用
明確了類與對象的關係後,我們知道:類只是一個定義的結構,用來表述我們想要描述的事物,即具備哪些屬性(成員變量),可以產生哪些行爲(方法)。那麼具體行爲的發生,也就是方法的調用要靠對象來完成,同時屬性值也要附着在對象上纔有意義。創建對象的過程被叫做類的實例化,或者稱爲對象的初始化,在這個過程中需要使用的就是new關鍵字和類的構造器。
對於相關概念還不清楚的同學請進傳送門:Java中的基本操作單元 - 類和對象
二、構造器的定義
1. 構造器的別稱
沒錯,他們都是同一個意思。
- 構造器
- 構造方法
- 構造函數
2. 構造器定義格式
構造器本身更像一種方法,因此定義的格式與方法類似,可以區別着進行記憶。構造器同樣被定義在class的大括號內,構造器的定義格式如下:
public class className{
// 構造器定義開始
[權限修飾符] 類名(參數列表){
// 代碼部分
}
// 構造器定義結束
}
從以上的結構我們看到,構造器的聲明格式與方法類似:
- 以權限修飾符開頭
- 不能被static、final、abstract等修飾符修飾
- 無返回值類型
- 方法名稱與類名相同
- 一個類中可以出現多個構造方法,區別在於參數列表不同
那麼,在這個構造方法當中我們都應該寫些什麼呢?還是從構造器的作用入手,既然他的作用是初始化一個對象,那麼對象在初始化時最需要做的就是對屬性賦值,所以如果有需要我們會在調用時傳入某些屬性的初始值,或者在對象初始化時執行某些代碼,幫助我們判斷對象初始化的狀態。
public class Student{
public Student(){
System.out.println("學生對象初始化成功");
// 其他代碼
}
}
public class Test{
public static void main(String[] args){
Student student = new Student();
// 執行效果 -> 輸出:學生對象初始化成功
}
}
對於創建對象時爲屬性賦值的用法將在構造器的重載中演示。
3. 隱式構造器
在剛剛開始學習面向對象部分時,可能都會存在一個疑問,之前定義的class都沒有定義構造器呀,不是一樣可以通過new來創建實例嗎?這是因爲當一個類被定義後,如果沒有手動的創建任何的構造方法,會默認提供一個空的構造器,供初始化使用,這個過程是編譯時完成的。
public class Person{
}
我們對Person類進行編譯,得到Person.class文件,然後我們對class文件進行反編譯,就可以看到已經出現了一個空的構造器:
Java程序在執行時,加載的都是.class文件,並且所生成的.class文件與我們定義的.java文件一般都是存在差異的。所以這就能夠解釋,爲什麼明明我們在.java文件中沒有定義構造器,但是在創建對象時卻可以使用new調用到。
隱式構造器還有一個特點,就是如果我們已經手動創建了一個無參的構造器,或者一個有參的構造器,那麼在編譯時就不會生成無參構造器了。
public class Person{
public Person(String name){
System.out.println(name);
}
}
此時,由於我們已經手動指定了一個構造器了,所以在編譯時就不會再產生默認的無參構造器了,只會有自己手動定義的構造器:
那麼,大家應該也注意到了一個問題,既然用new創建對象時是調用的構造器,那麼現在我們自己定義了一個有參數的構造器,那麼就會使得我們最常使用的new Person()這種實例化的代碼報錯,因爲此時類中已經沒有無參構造器可供調用了,也可以認爲無參的構造器被覆蓋了,必須要傳入一個參數才能初始化對象。
public class Test{
public static void main(String[] args){
Person person = new Person();
// 編譯不通過,已經無法調用無參構造器來初始化對象
}
}
那麼如果我們還是想用這個無參構造器來創建對象該怎麼辦呢?沒錯,手動聲明一下就好了,裏面不需要寫任何內容:
public class Person{
// 無參構造器
public Person(){}
// 有參構造器,可以接收一個參數
public Person(String name){
System.out.println(name);
}
}
我們來看一下效果,很明顯,將會同時存在兩個構造器,我們在使用new進行對象初始化的時候可以根據需要來使用。
`
public class Test{
public static void main(String[] args){
Person person1 = new Person();
// 編譯通過,執行後person1被成功實例化,無輸出
Person person2 = new Person("小明");
// 編譯通過,執行後person2被成功實例化,輸出:小明
}
}
4. 構造器的重載
從上面的例子我們已經可以看到,一個類結構中可以存在多個構造器,用於在有不同需要時被調用。而且由於構造器本身的主要作用是用於爲類的屬性賦初始值,所以在構造器中我們會指定一些參數,用於被調用時傳入,爲當前類的屬性賦值。
public class Person{
// 無參構造器
public Person(){}
// 兩參構造器,可以給name和age屬性賦值
public Person(String name,int age){
this.name = name;
this.age = age;
}
// 三參構造器,可以給name、age和job屬性賦值
public Person(String name,int age,String job){
this.name = name;
this.age = age;
this.job = job;
}
public String name;
public int age;
public String job;
}
在上面的代碼中我們可以看到有三個構造器,名稱相同,只有參數列表不同,這種關係被稱爲重載,在方法中也有類似的概念。可以看到構造器中存在部分代碼,且都是賦值語句。
- this關鍵字的用法
this可以指代當前對象,<font color="red">使用this可以調用出直接在類下定義的成員(變量和方法)</font>,其中一個最主要的作用就是可以區分同名的變量。我們在進行變量命名時,一直強調見名知意,那麼問題就來了:在類中定義的成員變量名稱已經確定了,而構造器中傳入的參數就是爲了給這些屬性賦值的,那麼參數的名稱是不是應該和類成員變量一樣才更能表達意思呢?如果這樣的話就造成了參數列表中的變量名稱與類成員變量的名稱同名,這時就可以通過this來區分。
明確了this的用法,我們再來看構造器中的內容就很好理解了,將傳入的參數賦值給當前對象的類成員變量,具體的調用過程我們看下面的例子。
三、構造器的調用
src
└──edu
└──sandtower
└──bean
│ Person.java
└──test
│ Test.java
以上爲實體類與測試類所在的目錄結構,Person實體類所在包:edu.sandtower.bean,Test測試類所在包:edu.sandtower.test,則代碼如下:
package edu.sandtower.bean;
public class Person{
// 無參構造器
public Person(){}
// 三參構造器,可以給name、age和job屬性賦值
public Person(String name,int age,String job){
this.name = name;
this.age = age;
this.job = job;
}
public String name;
public int age;
public String job;
}
package edu.sandtower.test;
// 導包操作:指明需要使用的Person類的所在位置
import edu.sandtower.bean.Person;
public class Test{
public static void main(String[] args){
Person person1 = new Person();
// person1被成功實例化,各屬性無初始值,可以手動賦值
person1.name = "小張";
person1.age = 26;
person1.job = "Linux運維工程師";
Person person2 = new Person("小李",25,"Java開發工程師");
// person2被成功實例化,並具有如下初始值
// name:小李
// age:25
// job:Java開發工程師
// 輸出進行驗證
System.out.println("name:" + person2.name);
System.out.println("age:" + person2.age);
System.out.println("job:" + person2.job);
}
}
在進行對象的初始化時,可以根據需要取得一個空的對象(如:person1)後手動賦值,也可以通過有參構造器直接對屬性賦值(如:person2),避免逐一賦值的麻煩。