詳解Java的構(gòu)造方法及類的初始化
一. 利用構(gòu)造方法給對(duì)象初始化
1. 構(gòu)造方法的概念
構(gòu)造方法(也稱為構(gòu)造器)是一個(gè)特殊的成員方法,其名字必須與類名相同,在創(chuàng)建對(duì)象時(shí),由編譯器自動(dòng)調(diào)用,并且在整個(gè)對(duì)象的生命周期內(nèi)只調(diào)用一次。
構(gòu)造方法的作用就是給對(duì)象中的成員進(jìn)行初始化,并不負(fù)責(zé)給對(duì)象開辟空間。
public class Date {
public int year;
public int month;
public int day;
// 構(gòu)造方法:
// 名字與類名相同,沒有返回值類型,設(shè)置為void也不行
// 一般情況下使用public修飾
// 在創(chuàng)建對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,并且在對(duì)象的生命周期內(nèi)只調(diào)用一次
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
System.out.println("Date(int,int,int)方法被調(diào)用了");
}
public void printDate() {
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
// 此處創(chuàng)建了一個(gè)Date類型的對(duì)象,并沒有顯式調(diào)用構(gòu)造方法
Date d = new Date(2021, 6, 9);
// 輸出Date(int,int,int)方法被調(diào)用了
d.printDate(); // 2021-6-9
}
}
2. 構(gòu)造方法的特性
1.名字必須與類名相同
2.沒有返回值類型,設(shè)置為void也不行
3.創(chuàng)建對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,并且在對(duì)象的生命周期內(nèi)只調(diào)用一次
4.絕大多數(shù)情況下使用public來修飾,特殊場(chǎng)景下會(huì)被private修飾
5.構(gòu)造方法可以重載(用戶根據(jù)自己的需求提供不同參數(shù)的構(gòu)造方法); 下面兩個(gè)構(gòu)造方法:名字相同,參數(shù)列表不同,因此構(gòu)成了方法重載
public class Date {
public int year;
public int month;
public int day;
// 無參構(gòu)造方法
public Date(){
this.year = 1900;
this.month = 1;
this.day = 1;
}
// 帶有三個(gè)參數(shù)的構(gòu)造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}6.如果用戶沒有顯式定義,編譯器會(huì)生成一份默認(rèn)的構(gòu)造方法,生成的默認(rèn)構(gòu)造方法一定是無參的; 一旦用戶定義,編譯器則不再生成;下面代碼中,沒有定義任何構(gòu)造方法,編譯器會(huì)默認(rèn)生成一個(gè)不帶參數(shù)的構(gòu)造方法。
public class Date {
public int year;
public int month;
public int day;
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
Date d = new Date();
d.printDate();
}
}
7.構(gòu)造方法中,可以通過this調(diào)用其他構(gòu)造方法來簡(jiǎn)化代碼
【注意事項(xiàng)】
- 構(gòu)造方法中,通過this(…)去調(diào)用其他構(gòu)造方法,這條語句必須是構(gòu)造方法中第一條語句
- 多個(gè)構(gòu)造方法不可以互相調(diào)用(不能形成環(huán)), 會(huì)形成構(gòu)造器的遞歸調(diào)用,但卻沒有調(diào)用的結(jié)束條件
public class Date {
public int year;
public int month;
public int day;
// 無參構(gòu)造方法--內(nèi)部給各個(gè)成員賦值初始值,該部分功能與三個(gè)參數(shù)的構(gòu)造方法重復(fù)
// 此處可以在無參構(gòu)造方法中通過this調(diào)用帶有三個(gè)參數(shù)的構(gòu)造方法
// 但是this(2022,8,16);必須是構(gòu)造方法中第一條語句
public Date(){
//System.out.println(year); 注釋取消掉,編譯會(huì)失敗
this(2022, 8, 16);
//this.year = 1900;
//this.month = 1;
//this.day = 1;
}
// 帶有三個(gè)參數(shù)的構(gòu)造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}3. 子類構(gòu)造方法
在繼承基礎(chǔ)上,子類對(duì)象構(gòu)造時(shí),需要先調(diào)用基類構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法。
在子類構(gòu)造方法中,并沒有寫任何關(guān)于基類構(gòu)造的代碼,但是在構(gòu)造子類對(duì)象時(shí),先執(zhí)行基類的構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法,
原因在于:子類對(duì)象中成員是有兩部分組成的,基類繼承下來的以及子類新增加的部分 。父類和子類, 肯定是先有父再有子,所以在構(gòu)造子類對(duì)象時(shí)候 ,子類構(gòu)造方法中先要調(diào)用基類的構(gòu)造方法,將從基類繼承下來的成員構(gòu)造完整 ,然后再完成子類自己的構(gòu)造,將子類自己新增加的成員初始化完整 。
【注意事項(xiàng)】
1.若父類顯式定義無參或者默認(rèn)的構(gòu)造方法,在子類構(gòu)造方法第一行默認(rèn)有隱含的super()調(diào)用,即調(diào)用基類構(gòu)造方法
public class Base {
public Base(){
System.out.println("Base()");
}
}
public class Derived extends Base{
public Derived(){
// super(); // 注意子類構(gòu)造方法中默認(rèn)會(huì)調(diào)用基類的無參構(gòu)造方法:super(),
// 用戶沒有寫時(shí),編譯器會(huì)自動(dòng)添加,而且super()必須是子類構(gòu)造方法中第一條語句,
// 并且只能出現(xiàn)一次
System.out.println("Derived()");
}
}
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
2.如果父類構(gòu)造方法是帶有參數(shù)的,此時(shí)需要用戶為子類顯式定義構(gòu)造方法,并在子類構(gòu)造方法中選擇合適的父類構(gòu)造方法調(diào)用,否則編譯失敗。
public class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Animal(String , int )");
}
}
public class Dog extends Animal{
//傻狗 是狗的屬性
public boolean silly;
public Dog(String name,int age,boolean silly) {
//1. 先幫助父類部分初始化 必須放到第一行
super(name,age);
this.silly = silly;
System.out.println("Dog(String ,int ,boolean )");
}
public static void main(String[] args) {
Animal animal2 = new Dog("金毛",6,false);
}
}
3.在子類構(gòu)造方法中,super(…)調(diào)用父類構(gòu)造時(shí),必須是子類構(gòu)造方法中第一條語句。
4.super(…)只能在子類構(gòu)造方法中出現(xiàn)一次,由與this(…)調(diào)用時(shí)也要在第一條語句,所以super(…)不能和this(…)同時(shí)出現(xiàn),也就是是說子類構(gòu)造方法中不能使用this(…)
4. 避免在構(gòu)造方法中調(diào)用重寫的方法
一段有坑的代碼. 我們創(chuàng)建兩個(gè)類, B 是父類, D 是子類. D 中重寫 func 方法. 并且在 B 的構(gòu)造方法中調(diào)用 func
class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Main {
public static void main(String[] args) {
D d = new D();
}
}執(zhí)行結(jié)果:

- 構(gòu)造 D 對(duì)象的同時(shí), 會(huì)調(diào)用 B 的構(gòu)造方法.
- B 的構(gòu)造方法中調(diào)用了 func 方法, 此時(shí)會(huì)觸發(fā)動(dòng)態(tài)綁定, 會(huì)調(diào)用到 D 中的 func
- 此時(shí) D 對(duì)象自身還沒有構(gòu)造, num 處在未初始化的狀態(tài), 值為 0;如果具備多態(tài)性,num的值應(yīng)該是1.
- 所以在構(gòu)造函數(shù)內(nèi),盡量避免使用實(shí)例方法,除了final和private方法。
【結(jié)論】:
“用盡量簡(jiǎn)單的方式使對(duì)象進(jìn)入可工作狀態(tài)”, 盡量不要在構(gòu)造器中調(diào)用方法(如果這個(gè)方法被子類重寫, 就會(huì)觸發(fā)動(dòng)態(tài)綁定, 但是此時(shí)子類對(duì)象還沒構(gòu)造完成), 可能會(huì)出現(xiàn)一些隱藏的但是又極難發(fā)現(xiàn)的問題.
二. 對(duì)象的默認(rèn)初始化
在Java方法內(nèi)部定義一個(gè)局部變量時(shí),用戶必須要將其賦值或者初始化,否則會(huì)編譯失?。?/p>
但對(duì)象中的字段(成員變量),用戶不需要將其初始化就可直接訪問使用,這里其原因在于new對(duì)象時(shí),jvm會(huì)給出字段的默認(rèn)初始化。
下面是new對(duì)象是時(shí),jvm層面執(zhí)行的概述:
1.檢測(cè)對(duì)象對(duì)應(yīng)的類是否加載了,如果沒有加載則加載
2.為對(duì)象分配內(nèi)存空間
3.處理并發(fā)安全問題
比如:多個(gè)線程同時(shí)申請(qǐng)對(duì)象,JVM要保證給對(duì)象分配的空間不沖突
4.初始化所分配的空間
即:對(duì)象空間被申請(qǐng)好之后,對(duì)象中包含的成員已經(jīng)設(shè)置好了初始值
| 數(shù)據(jù)類型 | 默認(rèn)值 |
|---|---|
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0 |
| float | 0.0f |
| double | 0.0 |
| char | /u0000 |
| boolean | false |
| reference (引用類型) | null |
5.設(shè)置對(duì)象頭信息(關(guān)于對(duì)象內(nèi)存模型后面會(huì)介紹)
6.調(diào)用構(gòu)造方法,給對(duì)象中各個(gè)成員賦值
三. 就地初始化對(duì)象
在聲明成員變量時(shí),就直接給出了初始值。
代碼編譯完成后,編譯器會(huì)將所有給成員初始化的這些語句添加到各個(gè)構(gòu)造方法中
public class Date {
public int year = 1900;
public int month = 1;
public int day = 1;
public Date(){
}
public Date(int year, int month, int day) {
}
public static void main(String[] args) {
Date d1 = new Date(2022,8,16);
Date d2 = new Date();
}
}四. 類的初始化順序
1. 普通類(沒有繼承關(guān)系)
靜態(tài)部分(靜態(tài)變量、常量,靜態(tài)代碼塊)
- 在類加載階段執(zhí)行,類中存在多個(gè)靜態(tài)部分時(shí),會(huì)按順序執(zhí)行
- 靜態(tài)代碼塊只會(huì)執(zhí)行一次,且靜態(tài)的變量、常量等只會(huì)創(chuàng)建一份
非靜態(tài)部分(實(shí)例變量、常量、實(shí)例代碼塊)
當(dāng)有對(duì)象創(chuàng)建時(shí)才會(huì)執(zhí)行,按順序執(zhí)行
最后執(zhí)行構(gòu)造方法,當(dāng)有對(duì)象創(chuàng)建時(shí)才會(huì)執(zhí)行
代碼演示:
class Person {
public String name;
public int age;
public Organ organ = new Organ();
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("構(gòu)造方法執(zhí)行");
}
{
System.out.println("實(shí)例代碼塊執(zhí)行");
}
static {
System.out.println("靜態(tài)代碼塊執(zhí)行");
}
}
class Organ {
//...
public Organ() {
System.out.println("實(shí)例變量::organ");
}
}
public class TestDemo {
public static void main(String[] args) {
Person person1 = new Person("xin",21);
System.out.println("==============");
Person person2 = new Person("xinxin",20);
}
}執(zhí)行結(jié)果:

2. 派生類( 有繼承關(guān)系)
靜態(tài)部分(靜態(tài)變量、常量,靜態(tài)代碼塊)
- 父類靜態(tài)代碼塊優(yōu)先于子類靜態(tài)代碼塊執(zhí)行,且是最早執(zhí)行
- 只有第一次實(shí)例化子類對(duì)象時(shí),父類和子類的靜態(tài)部分會(huì)執(zhí)行; 之后再實(shí)例化子類對(duì)象時(shí),父類和子類的靜態(tài)部分都不會(huì)再執(zhí)行
父類非靜態(tài)部分(實(shí)例變量、常量、實(shí)例代碼塊)和父類構(gòu)造方法
子類非靜態(tài)部分(實(shí)例變量、常量、實(shí)例代碼塊)和子類構(gòu)造方法
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:構(gòu)造方法執(zhí)行");
}
{
System.out.println("Person:實(shí)例代碼塊執(zhí)行");
}
static {
System.out.println("Person:靜態(tài)代碼塊執(zhí)行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:構(gòu)造方法執(zhí)行");
}
{
System.out.println("Student:實(shí)例代碼塊執(zhí)行");
}
static {
System.out.println("Student:靜態(tài)代碼塊執(zhí)行");
}
}
public class TestDemo4 {
public static void main(String[] args) {
Student student1 = new Student("張三",19);
System.out.println("===========================");
Student student2 = new Student("gaobo",20);
}
public static void main1(String[] args) {
Person person1 = new Person("bit",10);
System.out.println("============================");
Person person2 = new Person("gaobo",20);
}
}
執(zhí)行結(jié)果:

到此這篇關(guān)于詳解Java的構(gòu)造方法及類的初始化的文章就介紹到這了,更多相關(guān)Java初始化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
RocketMQ獲取指定消息的實(shí)現(xiàn)方法(源碼)
這篇文章主要給大家介紹了關(guān)于RocketMQ獲取指定消息的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用RocketMQ具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
學(xué)習(xí)Java之如何對(duì)時(shí)間進(jìn)行格式化
當(dāng)我們?cè)谀J(rèn)情況下構(gòu)造出來的時(shí)間對(duì)象,它的時(shí)間格式并不適合我們閱讀,并且在開發(fā)時(shí),pc端、Android端、iOS端等展示的時(shí)間格式可能也并不完全一樣,本文就從這幾個(gè)問題給大家介紹如何對(duì)時(shí)間進(jìn)行格式化,感興趣的同學(xué)可以借鑒一下2023-05-05
Springboot+MybatisPlus實(shí)現(xiàn)帶驗(yàn)證碼的登錄
本文主要介紹了Springboot+MybatisPlus實(shí)現(xiàn)帶驗(yàn)證碼的登錄,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05
springboot整合websocket后啟動(dòng)報(bào)錯(cuò)(javax.websocket.server.ServerCont
這篇文章主要介紹了springboot整合websocket后啟動(dòng)報(bào)錯(cuò)(javax.websocket.server.ServerContainer not available),通過分析錯(cuò)誤信息、排查代碼和配置,找出問題的根源,并給出相應(yīng)的解決方案,感興趣的可以了解一下2024-01-01
openEuler?搭建java開發(fā)環(huán)境的詳細(xì)過程
這篇文章主要介紹了openEuler?搭建java開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
SpringBoot項(xiàng)目讀取外置logback配置文件的問題及解決
SpringBoot項(xiàng)目讀取外置logback配置文件的問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
springboot創(chuàng)建多module項(xiàng)目的實(shí)例
這篇文章主要介紹了springboot創(chuàng)建多module項(xiàng)目的實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
最新jsonwebtoken-jwt 0.12.3 基本使用小結(jié)
這篇文章主要介紹了最新jsonwebtoken-jwt 0.12.3 基本使用小結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-12-12

