1. 程式人生 > >Java基礎【面向物件 構造方法 this super關鍵字】

Java基礎【面向物件 構造方法 this super關鍵字】

第12天面向物件

今日內容介紹
 構造方法
 this
 super

第1章 構造方法

我們對封裝已經有了基本的瞭解,接下來我們來看一個新的問題,依然以Person為例,由於Person中的屬性都被private了,外界無法直接訪問屬性,必須對外提供相應的set和get方法。當建立人物件的時候,人物件一建立就要明確其姓名和年齡,那該怎麼做呢?

1.1 構造方法介紹

在開發中經常需要在建立物件的同時明確物件的屬性值,比如員工入職公司就要明確他的姓名、年齡等屬性資訊。
那麼,建立物件就要明確屬性值,那怎麼解決呢?也就是在建立物件的時候就要做的事情,當使用new關鍵字建立物件時,怎麼給物件的屬性初始化值呢?這就要學習Java另外一門小技術,構造方法。
那什麼是構造方法呢?從字面上理解即為構建創造時用的方法,即就是物件建立時要執行的方法。既然是物件建立時要執行的方法,那麼只要在new物件時,知道其執行的構造方法是什麼,就可以在執行這個方法的時候給物件進行屬性賦值。
 構造方法的格式:

修飾符 構造方法名(引數列表)
{
}

 構造方法的體現:

	構造方法沒有返回值型別。也不需要寫返回值。因為它是為構建物件的,物件建立完,方法就執行結束。
	構造方法名稱必須和型別保持一致。
	構造方法沒有具體的返回值。

 構造方法的程式碼體現:

class Person {
	// Person的成員屬性age和name
	private int age;
	private String name;
// Person的構造方法,擁有引數列表
Person(int a, String nm) {
	// 接受到建立物件時傳遞進來的值,將值賦給成員屬性
	age = a;
	name = nm;
}

}

1.2 構造方法呼叫和記憶體圖解

理解構造方法的格式和基本功能之後,現在就要研究構造方法是怎麼執行的呢?在建立物件的時候是如何初始化的呢?
構造方法是專門用來建立物件的,也就是在new物件時要呼叫構造方法。現在來看看如何呼叫構造方法。

class Person {
	// Person的成員屬性age和name
	private int age;
	private String name;
// Person的構造方法,擁有引數列表
Person(int a, String nm) {
	// 接受到建立物件時傳遞進來的值,將值賦給成員屬性
	age = a;
	name = nm;
}

public void speak() {
	System.out.println("name=" + name + ",age=" + age);
}
}
class PersonDemo {
	public static void main(String[] args) {
		// 建立Person物件,並明確物件的年齡和姓名
		Person p2 = new Person(23, "張三");
		p2.speak();
	}
}

上述程式碼演示了建立物件時構造方法的呼叫。即在建立物件時,會呼叫與引數列表對應的構造方法
上述程式碼的圖解:

在這裡插入圖片描述
 圖解說明:
1、首先會將main方法壓入棧中,執行main方法中的 new Person(23,“張三”);
2、在堆記憶體中分配一片區域,用來存放建立的Person物件,這片記憶體區域會有屬於自己的記憶體地址(0x88)。然後給成員變數進行預設初始化(name=null,age=0)。
3、執行構造方法中的程式碼(age = a ; name = nm;),將變數a對應的23賦值給age,將變數nm對應的”張三賦值給name,這段程式碼執行結束後,成員變數age和name的值已經改變。執行結束之後構造方法彈棧,Person物件建立完成。將Person物件的記憶體地址0x88賦值給p2。

1.3 預設構造方法和細節

在沒有學習構造方法之前,我們也可以通過new關鍵字建立物件,並呼叫相應的方法,同時在描述事物時也沒有寫構造方法。這是為什麼呢?
在之前學習的過程中,描述事物時,並沒有顯示指定構造方法,當在編譯Java檔案時,編譯器會自動給class檔案中新增預設的構造方法。如果在描述類時,我們顯示指定了構造方法,那麼,當在編譯Java原始檔時,編譯器就不會再給class檔案中新增預設構造方法。

class  Person {
	//如果沒有顯示指定構造方法,編譯會在編譯時自動新增預設的構造方法
	//Person(){}  //空引數的預設構造方法
}

當在描述事物時,要不要在類中寫構造方法呢?這時要根據描述事物的特點來確定,當描述的事物在建立其物件時就要明確屬性的值,這時就需要在定義類的時候書寫帶引數的構造方法。若建立物件時不需要明確具體的資料,這時可以不用書寫構造方法(不書寫也有預設的構造方法)。

 構造方法的細節:

1、一個類中可以有多個構造方法,多個構造方法是以過載的形式存在的
2、構造方法是可以被private修飾的,作用:其他程式無法建立該類的物件。

class Person {
	private int age;
	private String name;
// 私有無引數的構造方法,即外界不能通過new Person();語句建立本類物件
private Person() {
}

// 多個構造方法是以過載的形式存在
Person(int a) {
	age = a;
}

Person(String nm, int a) {
	name = nm;
	age = a;
}

}

1.4 構造方法和一般方法區別

到目前為止,學習兩種方法,分別為構造方法一般方法,那麼他們之間有什麼異同呢?
構造方法在物件建立時就執行了而且只執行一次
一般方法是在物件建立後,需要使用時才被物件呼叫並可以被多次呼叫
 問題:
有了構造方法之後可以對物件的屬性進行初始化,那麼還需要對應的set和get方法嗎?

需要相應的set和get方法,因為物件在建立之後需要修改和訪問相應的屬性值時,在這時只能通過set或者get方法來操作。

思考,如下程式碼有問題嗎?(認真思考~)

class Person {
	void Person() {
	}
}

class PersonDemo {
	public static void main(String[] args) {
		Person p = new Person();
	}
}

第2章 this關鍵字

在之前學習方法時,我們知道方法之間是可以相互呼叫的,那麼構造方法之間能不能相互呼叫呢?若可以,怎麼呼叫呢?

2.1 this呼叫構造方法

在之前學習方法之間呼叫時,可以通過方法名進行呼叫。可是針對構造方法,無法通過構造方法名來相互呼叫。
構造方法之間的呼叫,可以通過this關鍵字來完成

 構造方法呼叫格式:

this(引數列表);

 構造方法的呼叫

class Person {
	// Person的成員屬性
	private int age;
	private String name;
// 無引數的構造方法
Person() {
}

// 給姓名初始化的構造方法
Person(String nm) {
	name = nm;
}

// 給姓名和年齡初始化的構造方法
Person(String nm, int a) {
	// 由於已經存在給姓名進行初始化的構造方法 name = nm;因此只需要呼叫即可
	// 呼叫其他構造方法,需要通過this關鍵字來呼叫
	this(nm);
	// 給年齡初始化
	age = a;
}
}

2.2 this的原理圖解

瞭解了構造方法之間是可以相互呼叫,那為什麼他們之間通過this就可以呼叫呢?
通過上面的學習,簡單知道使用this可以實現構造方法之間的呼叫,但是為什麼就會知道this呼叫哪一個構造方法呢?接下來需要圖解完成。

class Person {
	private int age;
	private String name;
Person() {
}
Person(String nm) {
	name = nm;
}
Person(String nm, int a) {
	this(nm);
	age = a;
}
}
class PersonDemo {
	public static void main(String[] args) {
		Person p = new Person("張三", 23);
	}
}

在這裡插入圖片描述

 圖列說明:
1、先執行main方法,main方法壓棧,執行其中的new Person(“張三”,23);
2、堆記憶體中開闢空間,併為其分配記憶體地址0x33,,緊接著成員變數預設初始化(name=null age = 0);
3、擁有兩個引數的構造方法(Person(String nm , int a))壓棧,在這個構造方法中有一個隱式的this,因為構造方法是給物件初始化的,那個物件呼叫到這個構造方法,this就指向堆中的那個物件。
4、由於Person(String nm , int a)構造方法中使用了this(nm);構造方法Person(String nm)就會壓棧,並將“張三”傳遞給nm。在Person(String nm , int a)構造方法中同樣也有隱式的this,this的值同樣也為0x33,這時會執行其中name = nm,即把“張三”賦值給成員的name。當賦值結束後Person(String nm , int a)構造方法彈棧。
5、程式繼續執行構造方法(Person(String nm , int a)中的age = a;這時會將23賦值給成員屬性age。賦值結束構造方法(Person(String nm , int a)彈棧。
6、當構造方法(Person(String nm , int a)彈棧結束後,Person物件在記憶體中建立完成,並將0x33賦值給main方法中的p引用變數。
注意:

this到底代表什麼呢?this代表的是物件,具體代表哪個物件呢?哪個物件呼叫了this所在的方法,this就代表哪個物件。
呼叫其他構造方法的語句必須定義在構造方法的第一行,原因是初始化動作要最先執行。

2.3 成員變數和區域性變數同名問題

通過上面學習,基本明確了物件初始化過程中的細節,也知道了構造方法之間的呼叫是通過this關鍵字完成的。但this也有另外一個用途,接下來我們就學習下。
當在方法中出現了局部變數和成員變數同名的時候,那麼在方法中怎麼區別區域性變數成員變數呢?可以在成員變數名前面加上this.來區別成員變數和區域性變數

class Person {
	private int age;
	private String name;
// 給姓名和年齡初始化的構造方法
Person(String name, int age) {
	// 當需要訪問成員變數是,只需要在成員變數前面加上this.即可
	this.name = name;
	this.age = age;
}

public void speak() {
	System.out.println("name=" + this.name + ",age=" + this.age);
}
}
class PersonDemo {
	public static void main(String[] args) {
		Person p = new Person("張三", 23);
		p.speak();
	}
}

2.4 this的應用

學習完了構造方法、this的用法之後,現在做個小小的練習。
需求:在Person類中定義功能,判斷兩個人是否是同齡人

class Person {
	private int age;
	private String name;
// 給姓名和年齡初始化的構造方法
Person(String name, int age) {
	// 當需要訪問成員變數是,只需要在成員變數前面加上this.即可
	this.name = name;
	this.age = age;
}

public void speak() {
	System.out.println("name=" + this.name + ",age=" + this.age);
}

// 判斷是否為同齡人
public boolean equalsAge(Person p) {
	// 使用當前呼叫該equalsAge方法物件的age和傳遞進來p的age進行比較
	// 由於無法確定具體是哪一個物件呼叫equalsAge方法,這裡就可以使用this來代替
	/*
	 * if(this.age == p.age) { return true; } return false;
	 */
	return this.age = p.age;
}
}

第3章 super關鍵字

3.1 子父類中構造方法的呼叫

在建立子類物件時,父類的構造方法會先執行,因為子類中所有構造方法的第一行有預設的隱式super();語句
格式:

呼叫本類中的構造方法
this(實參列表);
呼叫父類中的空引數構造方法
super();
呼叫父類中的有引數構造方法
super(實參列表);

為什麼子類物件建立都要訪問父類中的構造方法?因為子類繼承了父類的內容,所以建立物件時,必須要先看父類是如何對其內容進行初始化的,看如下程式:

public class Test {
	public static void main(String[] args) {
		new Zi();
	}
	
}
class Fu{
	int num ;
	Fu(){
		System.out.println("Fu構造方法"+num);
		num = 4;
	}
}
class Zi extends Fu{
	Zi(){
         //super(); 呼叫父類空引數構造方法
		System.out.println("Zi構造方法"+num);
	}
}

執行結果:
   Fu構造方法0
   Zi構造方法4

通過結果發現,子類構造方法執行時中,呼叫了父類構造方法,這說明,子類構造方法中有一句super()。
那麼,子類中的構造方法為什麼會有一句隱式的super()呢?
原因:子類會繼承父類中的內容,所以子類在初始化時,必須先到父類中去執行父類的初始化動作。這樣,才可以使用父類中的內容。
當父類中沒有空引數構造方法時,子類的構造方法必須有顯示的super語句,指定要訪問的父類有引數構造方法。

3.2 子類物件建立過程的細節

如果子類的構造方法第一行寫了this呼叫了本類其他構造方法,那麼super呼叫父類的語句還有嗎?
這時是沒有的,因為this()或者super(),只能定義在構造方法的第一行,因為初始化動作要先執行。
父類構造方法中是否有隱式的super呢?
也是有的。記住:只要是構造方法預設第一行都是super();
父類的父類是誰呢?super呼叫的到底是誰的構造方法呢?
Java體系在設計,定義了一個所有物件的父類Object

 注意

類中的構造方法預設第一行都有隱式的super()語句,在訪問父類中的空引數構造方法。所以父類的構造方法既可以給自己的物件初始化,也可以給自己的子類物件初始化。
如果預設的隱式super()語句在父類中沒有對應的構造方法,那麼必須在構造方法中通過this或者super的形式明確要呼叫的構造方法。

3.3 super應用

練習:描述學生和工人這兩個類,將他們的共性name和age抽取出來存放在父類中,並提供相應的get和set方法,同時需要在建立學生和工人物件就必須明確姓名和年齡

//定義Person類,將Student和Worker共性抽取出來
class Person {
	private String name;
	private int age;
	public Person(String name, int age) {
		// super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
class Student extends Person {
	// Student類的構造方法
	Student(String name, int age) {
		// 使用super關鍵字呼叫父類構造方法,進行相應的初始化動作
		super(name, age);
	}
	public void study() {// Studnet中特有的方法
		System.out.println(this.getName() + "同學在學習");
	}
}
class Worker extends Person {
	Worker(String name, int age) {
		// 使用super關鍵字呼叫父類構造方法,進行相應的初始化動作
		super(name, age);
	}
	public void work() {// Worker 中特有的方法
		System.out.println(this.getName() + "工人在工作");
	}
}
public class Test {
	public static void main(String[] args) {
		Student stu = new Student("小明",23);
stu.study();
		
Worker w = new Worker("小李",45);
w.work();
	}
}

第4章 綜合案例—完整的員工類

4.1 案例介紹

某IT公司有多名員工,按照員工負責的工作不同,進行了部門的劃分(研發部員工、維護部員工)。研發部根據所需研發的內容不同,又分為JavaEE工程師、Android工程師;維護部根據所需維護的內容不同,又分為網路維護工程師、硬體維護工程師。
公司的每名員工都有他們自己的員工編號、姓名,並要做它們所負責的工作。
 工作內容

 JavaEE工程師:員工號為xxx的 xxx員工,正在研發淘寶網站
 Android工程師:員工號為xxx的 xxx員工,正在研發淘寶手機客戶端軟體
 網路維護工程師:員工號為xxx的 xxx員工,正在檢查網路是否暢通
 硬體維護工程師:員工號為xxx的 xxx員工,正在修復印表機

請根據描述,完成員工體系中所有類的定義,並指定類之間的繼承關係。進行XX工程師類的物件建立,完成工作方法的呼叫

4.2 案例分析

 根據上述部門的描述,得出如下的員工體系圖
在這裡插入圖片描述
 根據員工資訊的描述,確定每個員工都有員工編號、姓名、要進行工作。則,把這些共同的屬性與功能抽取到父類中(員工類),關於工作的內容由具體的工程師來進行指定。

 工作內容

 JavaEE工程師:員工號為xxx的 xxx員工,正在研發淘寶網站
 Android工程師:員工號為xxx的 xxx員工,正在研發淘寶手機客戶端軟體
 網路維護工程師:員工號為xxx的 xxx員工,正在檢查網路是否暢通
 硬體維護工程師:員工號為xxx的 xxx員工,正在修復印表機

 建立JavaEE工程師物件,完成工作方法的呼叫

4.3 案例程式碼實現

 根據員工體系圖,完成類的定義
定義員工類(抽象類)

public abstract class Employee {
	private String id;// 員工編號
	private String name; // 員工姓名
//空引數構造方法
public Employee() {
	super();
}
//有引數構造方法
public Employee(String id, String name) {
	super();
	this.id = id;
	this.name = name;
}
public String getId() {
	return id;
}
public void setId(String id) {
	this.id = id;
}
public String getName() {
	return name;
}
public void setName(String name) {
	this.name = name;
}
//工作方法(抽象方法)
public abstract void work(); 
}

 定義研發部員工類Developer 繼承 員工類Employee

public abstract class Developer extends Employee {
	//空引數構造方法
	public Developer() {
		super();
	}
	//有引數構造方法
	public Developer(String id, String name) {
		super(id, name);
	}
}

 定義維護部員工類Maintainer 繼承 員工類Employee

public abstract class Maintainer extends Employee {
	//空引數構造方法
	public Maintainer() {
		super();
	}
	//有引數構造方法
	public Maintainer(String id, String name) {
		super(id, name);
	}
}

 定義JavaEE工程師 繼承 研發部員工類,重寫工作方法

public class JavaEE extends Developer {
	//空引數構造方法
	public JavaEE() {
		super();
	}
	//有引數構造方法
	public JavaEE(String id, String name) {
		super(id, name);
	}
@Override
public void work() {
	System.out.println("員工號為 " + getId() + " 的 " + getName() + " 員工,正在研發淘寶網站");
}
}

 定義Android工程師 繼承 研發部員工類,重寫工作方法

public class Android extends Developer {
	//空引數構造方法
	public Android() {
		super();
	}
	//有引數構造方法
	public Android(String id, String name) {
		super(id, name);
	}
@Override
public void work() {
	System.out.println("員工號為 " + getId() + " 的 " + getName() + " 員工,正在研發淘寶手機客戶端軟體");
}
}

 定義Network網路維護工程師 繼承 維護部員工類,重寫工作方法

public class Network extends Maintainer {
	//空引數構造方法
	public Network() {
		super();
	}
	//有引數構造方法
	public Network(String id, String name) {
		super(id, name);
	}
@Override
public void work() {
	System.out.println("員工號為 " + getId() + " 的 " + getName() + " 員工,正在檢查網路是否暢通");
}
}

 定義Hardware硬體維護工程師 繼承 維護部員工類,重寫工作方法

public class Hardware extends Maintainer {
	//空引數構造方法
	public Hardware() {
		super();
	}
	//有引數構造方法
	public Hardware(String id, String name) {
		super(id, name);
	}
@Override
public void work() {
	System.out.println("員工號為 " + getId() + " 的 " + getName() + " 員工,正在修復印表機");
}

}

 在測試類中,建立JavaEE工程師物件,完成工作方法的呼叫

public class Test {
	public static void main(String[] args) {
		//建立JavaEE工程師員工物件,該員工的編號000015,員工的姓名 小明
		JavaEE ee = new JavaEE("000015", "小明");
		//呼叫該員工的工作方法
		ee.work();
	}
}

第5章 總結

5.1 知識點總結

 this關鍵字

 this關鍵字,本類物件的引用
 this是在方法中使用的,哪個物件呼叫了該方法,那麼,this就代表呼叫該方法的物件引用
 this什麼時候存在的?當建立物件的時候,this存在的
 this的作用:用來區別同名的成員變數與區域性變數(this.成員變數)
public void setName(String name) {
this.name = name;
}

 構造方法: 用來給類的成員進行初始化操作

 格式:
修飾符 類名 (引數列表) {

}
 構造方法的特點:
 1, 方法名與類名相同
 2,沒有返回值,也沒有返回值型別,連void也沒有
 構造方法什麼時候會被呼叫執行?
只有在建立物件的時候才可以被呼叫

 super: 指的是父類的儲存空間(理解為父類的引用)

呼叫父類的成員變數:
super.成員變數;
呼叫父類的構造方法:
super(引數);
呼叫方法的成員方法:
super.成員方法();

 繼承中的構造方法注意事項:

1,如果我們手動給出了構造方法,編譯器不會在給我們提供預設的空引數構造方法
	   如果我們沒寫任何的構造方法,編譯器提供給我們一個空引數構造方法
2, 在構造方法中,預設的第一條語句為 super();
   它是用來訪問父類中的空引數構造方法,進行父類成員的初始化操作
	3, 當父類中沒有空引數構造方法的時候,怎麼辦?
		a: 通過 super(引數) 訪問父類有引數的構造方法
		b: 通過 this(引數) 訪問本類中其他構造方法
		   注意:[本類中的其他構造方法已經能夠正常訪問父類構造方法]
	4, super(引數) 與 this(引數) 不能同時在構造方法中存在