詳解Java的構(gòu)造方法及類的初始化
一. 利用構(gòu)造方法給對象初始化
1. 構(gòu)造方法的概念
構(gòu)造方法(也稱為構(gòu)造器)是一個(gè)特殊的成員方法,其名字必須與類名相同,在創(chuàng)建對象時(shí),由編譯器自動調(diào)用,并且在整個(gè)對象的生命周期內(nèi)只調(diào)用一次。
構(gòu)造方法的作用就是給對象中的成員進(jìn)行初始化,并不負(fù)責(zé)給對象開辟空間。
public class Date {
public int year;
public int month;
public int day;
// 構(gòu)造方法:
// 名字與類名相同,沒有返回值類型,設(shè)置為void也不行
// 一般情況下使用public修飾
// 在創(chuàng)建對象時(shí)由編譯器自動調(diào)用,并且在對象的生命周期內(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類型的對象,并沒有顯式調(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)建對象時(shí)由編譯器自動調(diào)用,并且在對象的生命周期內(nèi)只調(diào)用一次
4.絕大多數(shù)情況下使用public來修飾,特殊場景下會被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.如果用戶沒有顯式定義,編譯器會生成一份默認(rèn)的構(gòu)造方法,生成的默認(rèn)構(gòu)造方法一定是無參的; 一旦用戶定義,編譯器則不再生成;下面代碼中,沒有定義任何構(gòu)造方法,編譯器會默認(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)造方法來簡化代碼
【注意事項(xiàng)】
- 構(gòu)造方法中,通過this(…)去調(diào)用其他構(gòu)造方法,這條語句必須是構(gòu)造方法中第一條語句
- 多個(gè)構(gòu)造方法不可以互相調(diào)用(不能形成環(huán)), 會形成構(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); 注釋取消掉,編譯會失敗
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ǔ)上,子類對象構(gòu)造時(shí),需要先調(diào)用基類構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法。
在子類構(gòu)造方法中,并沒有寫任何關(guān)于基類構(gòu)造的代碼,但是在構(gòu)造子類對象時(shí),先執(zhí)行基類的構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法,
原因在于:子類對象中成員是有兩部分組成的,基類繼承下來的以及子類新增加的部分 。父類和子類, 肯定是先有父再有子,所以在構(gòu)造子類對象時(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)會調(diào)用基類的無參構(gòu)造方法:super(),
// 用戶沒有寫時(shí),編譯器會自動添加,而且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 對象的同時(shí), 會調(diào)用 B 的構(gòu)造方法.
- B 的構(gòu)造方法中調(diào)用了 func 方法, 此時(shí)會觸發(fā)動態(tài)綁定, 會調(diào)用到 D 中的 func
- 此時(shí) D 對象自身還沒有構(gòu)造, num 處在未初始化的狀態(tài), 值為 0;如果具備多態(tài)性,num的值應(yīng)該是1.
- 所以在構(gòu)造函數(shù)內(nèi),盡量避免使用實(shí)例方法,除了final和private方法。
【結(jié)論】:
“用盡量簡單的方式使對象進(jìn)入可工作狀態(tài)”, 盡量不要在構(gòu)造器中調(diào)用方法(如果這個(gè)方法被子類重寫, 就會觸發(fā)動態(tài)綁定, 但是此時(shí)子類對象還沒構(gòu)造完成), 可能會出現(xiàn)一些隱藏的但是又極難發(fā)現(xiàn)的問題.
二. 對象的默認(rèn)初始化
在Java方法內(nèi)部定義一個(gè)局部變量時(shí),用戶必須要將其賦值或者初始化,否則會編譯失??;
但對象中的字段(成員變量),用戶不需要將其初始化就可直接訪問使用,這里其原因在于new對象時(shí),jvm會給出字段的默認(rèn)初始化。
下面是new對象是時(shí),jvm層面執(zhí)行的概述:
1.檢測對象對應(yīng)的類是否加載了,如果沒有加載則加載
2.為對象分配內(nèi)存空間
3.處理并發(fā)安全問題
比如:多個(gè)線程同時(shí)申請對象,JVM要保證給對象分配的空間不沖突
4.初始化所分配的空間
即:對象空間被申請好之后,對象中包含的成員已經(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è)置對象頭信息(關(guān)于對象內(nèi)存模型后面會介紹)
6.調(diào)用構(gòu)造方法,給對象中各個(gè)成員賦值
三. 就地初始化對象
在聲明成員變量時(shí),就直接給出了初始值。
代碼編譯完成后,編譯器會將所有給成員初始化的這些語句添加到各個(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í),會按順序執(zhí)行
- 靜態(tài)代碼塊只會執(zhí)行一次,且靜態(tài)的變量、常量等只會創(chuàng)建一份
非靜態(tài)部分(實(shí)例變量、常量、實(shí)例代碼塊)
當(dāng)有對象創(chuàng)建時(shí)才會執(zhí)行,按順序執(zhí)行
最后執(zhí)行構(gòu)造方法,當(dāng)有對象創(chuàng)建時(shí)才會執(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í)例化子類對象時(shí),父類和子類的靜態(tài)部分會執(zhí)行; 之后再實(shí)例化子類對象時(shí),父類和子類的靜態(tài)部分都不會再執(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Myeclipse鏈接Oracle等數(shù)據(jù)庫時(shí)lo exception: The Network Adapter coul
今天小編就為大家分享一篇關(guān)于Myeclipse鏈接Oracle等數(shù)據(jù)庫時(shí)lo exception: The Network Adapter could not establish the connection,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03
Spring占位符Placeholder的實(shí)現(xiàn)原理解析
這篇文章主要介紹了Spring占位符Placeholder的實(shí)現(xiàn)原理,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03

