1. 程式人生 > >Java基礎【面向物件 封裝】

Java基礎【面向物件 封裝】

第9天 面向物件

今日內容介紹
 面向物件
 封裝

第1章 面向物件

1.1 理解什麼是面向過程、面向物件

面向過程與面向物件都是我們程式設計中,編寫程式的一種思維方式。
 面向過程的程式設計方式,是遇到一件事時,思考“我該怎麼做”,然後一步步實現的過程。
例如:公司打掃衛生(擦玻璃、掃地、拖地、倒垃圾等),按照面向過程的程式設計方式會思考“打掃衛生我該怎麼做,然後一件件的完成”,最後把公司衛生打掃乾淨了。
 面向物件的程式設計方式,是遇到一件事時,思考“我該讓誰來做”,然後那個“誰”就是物件,他要怎麼做這件事是他自己的事,反正最後一群物件合力能把事就好就行了。
例如,公司打掃衛生(擦玻璃、掃地、拖地、倒垃圾等),按照面向物件的程式設計方式會思考“我該讓誰來做,如小明擦玻璃、讓小麗掃地、讓小郭拖地、讓小強倒垃圾等”,這裡的“小明、小麗、小郭、小強”就是物件,他們要打掃衛生,怎麼打掃是他們自己的事,反正最後一群物件合力把公司衛生打掃乾淨了。

1.2 面向物件舉例

 買電腦(組裝機)
先使用面向過程說明買電腦這件事:假如我們需要買組裝電腦,這時首先會在網上查詢具體每一個硬體的引數和報價。然後會去電腦城進行多家詢價,接著詢價結束後回家根據具體的結果分析出自己比較滿意的哪家報價,接著會到這家店裡進行組裝,組裝時還需要進行現場監督,組裝完成安裝相應的系統,然後電腦抱回家。
分析上述整個過程大體分一下幾步:上網查詢引數和報價、電腦城詢價、現場安裝和監督、抱電腦回家。在整個過程中我們參與了每一個細節,並且會感覺相當累。
使用面向物件說明買電腦這件事:假如我們需要買組裝機,這時應該找一個懂電腦硬體的人,讓他幫我們檢視引數和報價,並進行詢價和殺價,以及現場組裝監督。而我們自己並不需要親歷親為具體怎麼做,只要告訴這個人我們想要的具體需求即可。
分析上述整個過程,發現瞬間變的十分輕鬆,只要找到懂電腦硬體的這個人,我們的問題都可以解決。並且在這個過程中我們不用那麼辛苦。

1.3 面向物件思維方式的好處

通過生活中的真實場景使用面向物件分析完之後,我們開始分析面向過程和麵向物件的差異做出總結:
 面向物件思維方式是一種更符合人們思考習慣的思想
 面向過程思維方式中更多的體現的是執行者(自己做事情),面向物件中更多的體現是指揮者(指揮物件做事情)。
 面向物件思維方式將複雜的問題簡單化。


第2章 類與物件

相信到這你已經對JAVA裡的物件有了屬於你自己的認知

2.1 物件在需求中的使用

對面向物件有了瞭解之後,我們來說說在具體問題中如何使用面向物件去分析問題,和如何使用面向物件。
我們把大象裝冰箱為例進行分析。
在針對具體的需求,可以使用名詞提煉的辦法進行分析,尋找具體的物件。

需求:把大象裝冰箱裡
物件:大象、冰箱
分三步:
1、開啟冰箱門
2、將大象裝進去
3、關閉冰箱門
分析發現開啟、裝、關閉都是冰箱的功能。即冰箱物件具備如下功能:
冰箱開啟
冰箱儲存
冰箱關閉

用虛擬碼描述,上述需求中有兩個具體的事物 大象 和  冰箱
描述大象:

class 大象
{
}
描述冰箱
class冰箱
{
void 開啟(){}
void 儲存(大象){}
void 關閉(){}
}

當把具體的事物描述清楚之後,需要使用這些具體的事物,Java使用具體的事物,需要通過new關鍵字來建立這個事物的具體例項。
使用物件:

1、建立冰箱的物件
冰箱 bx = new 冰箱();
2、呼叫冰箱的功能
物件.功能();
bx.開啟();
bx.儲存(new 大象());
bx.關閉();

 總結:
1、先按照名詞提煉問題領域中的物件
2、對物件進行描述,其實就是在明確物件中應該具備的屬性和功能
3、通過new的方式就可以建立該事物的具體物件
4、通過該物件呼叫它以後的功能。

2.2 物件在程式碼中的體現

在分析現實生活中的事物時發現,這些事物都有其具體的特點和功能,這些特點和功能就組成了這個特殊的事物。

描述小汽車。
分析:
事物的特點(屬性):
顏色。
輪胎個數。
事物的(功能):
執行。

發現:事物其實就是由特點(屬性)和行為(功能)組成的
可以簡單理解:屬性就是數值,其實就是變數;行為就是功能,就是方法。

小汽車 {
顏色;
輪胎個數;
執行() { }
}
通過
計算機語言Java來描述這個事物。
 定義類的格式

public class 類名 {
//可編寫0至n個屬性
資料型別 變數名1;
資料型別 變數名2;

//可編寫0至n個方法
修飾符 返回值型別 方法名(引數){
	執行語句;

}
}

 汽車類

public class Car {
String color;
int number;

void run() {
	System.out.println(color + ":" + number);
}
}

通過程式碼的描述,知道類的真正意義就是在描述事物。屬性和功能統稱為事物中的成員。
事物的成員分為兩種:成員屬性和成員功能。
成員屬性在程式碼中的體現就是成員變數
成員功能在程式碼中的體現就是成員方法
把寫好的程式碼測試一下。需要一個可以獨立執行類。

建立物件的格式:
類名 物件名 = new 類名();
測試類

public class CarDemo {
	public static void main(String[] args) { 
		/*
		 *  測試:Car類中的run方法。
		 */
		// 1,建立Car的物件。給物件起個名字。
		Car c = new Car();// c是類型別的變數。c指向了一個具體的Car型別的物件。
		// 2,通過已有的物件呼叫該物件的功能。格式:物件.物件成員;
		// 3,可以該物件的屬性賦值。
		c.color = "red";
		c.number = 4;
		c.run();
	}
}

2.3 物件的記憶體圖解

經過上面對小汽車的描述,和Java程式碼測試,我們雖然可以將生活中的事物使用Java程式碼描述出來,但是這些程式碼在記憶體中是如何執行的,接下來我們需要研究下物件在記憶體的圖解。
接下來就是分析物件在記憶體中的分配情況。這裡需要畫圖一步一步演示,嚴格按照畫圖流程講解記憶體物件建立過程。
在這裡插入圖片描述

2.4 類和物件的區別

面向物件的程式設計思想力圖在程式中對事物的描述與該事物在現實中的形態保持一致。為了做到這一點,面向物件的思想中提出兩個概念,即類和物件。其中,類是對某一類事物的抽象描述,而物件用於表示現實中該類事物的個體。接下來通過一個圖例來抽象描述類與物件的關係,如下圖所示。

在這裡插入圖片描述
圖1-1 類與物件
在上圖中,可以將玩具模型看作是一個類,將一個個玩具看作物件,從玩具模型和玩具之間的關係便可以看出類與物件之間的關係。類用於描述多個物件的共同特徵,它是物件的模板。物件用於描述現實中的個體,它是類的例項。從上圖中可以明顯看出物件是根據類建立的,並且一個類可以對應多個物件,接下來分別講解什麼是類和物件。

經過前面幾個知識點的學習,基本上掌握了類是用於描述事物的,類中可以定義事物的屬性和行為。而物件是通過描述的這個類,使用new關鍵字創建出來通過物件就可以呼叫該物件具體的屬性和功能了

2.5 區域性變數和成員變數區別

理解清楚了類和物件之後,結合前5天的學習知識,發現在描述類的屬性和前面學習定義變數差別不大,唯一區別就是位置發生了改變,那麼類中定義的變數,和在方法定義的變數有啥差別呢?
回憶以前學習時變數的定義方式,和位置,以及現在定義類中屬性的特點。總結下面幾點異同

區別一:定義的位置不同
定義在類中的變數是成員變數
定義在方法中或者{}語句裡面的變數是區域性變數
區別二:在記憶體中的位置不同
成員變數儲存在對記憶體的物件中
區域性變數儲存在棧記憶體的方法中
區別三:宣告週期不同
成員變數隨著物件的出現而出現在堆中,隨著物件的消失而從堆中消失
區域性變數隨著方法的執行而出現在棧中,隨著方法的彈棧而消失
區別四:初始化不同
成員變數因為在堆記憶體中,所有預設的初始化值
區域性變數沒有預設的初始化值,必須手動的給其賦值才可以使用。

2.6 基本型別和引用型別作為引數傳遞

引用型別資料和基本型別資料作為引數傳遞有沒有差別呢?我們用如下程式碼進行說明,並配合圖解讓大家更加清晰

在這裡插入圖片描述
基本型別作為引數傳遞時,其實就是將基本型別變數x空間中的值複製了一份傳遞給呼叫的方法show(),當在show()方法中x接受到了複製的值,再在show()方法中對x變數進行操作,這時只會影響到show中的x。當show方法執行完成,彈棧後,程式又回到main方法執行,main方法中的x值還是原來的值

在這裡插入圖片描述
當引用變數作為引數傳遞時,這時其實是將引用變數空間中的==記憶體地址(引用)==複製了一份傳遞給了show方法的d引用變數。這時會有兩個引用同時指向堆中的同一個物件。當執行show方法中的d.x=6時,會根據d所持有的引用找到堆中的物件,並將其x屬性的值改為6.show方法彈棧。
由於是兩個引用指向同一個物件,不管是哪一個引用改變了引用的所指向的物件的中的值,其他引用再次使用都是改變後的值


第3章 封裝

3.1 封裝概述
提起封裝,大家並不陌生。前面我們學習方法時,就提起過,將具體功能封裝到方法中,學習物件時,也提過將方法封裝在類中,其實這些都是封裝。
封裝,它也是面向物件思想的特徵之一。面向物件共有三個特徵:封裝,繼承,多型。接下來我們具體學習封裝。
 封裝表現:

1、方法就是一個最基本封裝體。
2、類其實也是一個封裝體。
 從以上兩點得出結論,封裝的好處:
1、提高了程式碼的複用性。
 2、隱藏了實現細節,還要對外提供可以訪問的方式。便於呼叫者的使用。這是核心之一,也可以理解為就是封裝的概念。
 3、提高了安全性。

3.2 封裝舉例

機箱:
一臺電腦,它是由CPU、主機板、顯示卡、記憶體、硬碟、電源等部件組長,其實我們將這些部件組裝在一起就可以使用電腦了,但是發現這些部件都散落在外面,很容造成不安全因素,於是,使用機箱殼子,把這些部件都裝在裡面,並在機箱殼上留下一些插口等,若不留插口,大家想想會是什麼情況。
總結機箱其實就是隱藏了辦卡裝置的細節,對外提供了插口以及開關等訪問內部細節的方式

3.3 私有private

瞭解到封裝在生活的體現之後,又要回到Java中,細說封裝的在Java程式碼中的體現,先從描述Person說起。
描述人。Person
屬性:年齡。
行為:說話:說出自己的年齡。

class Person {
int age;
String name;

public void show() {
	System.out.println("age=" + age + ",name" + name);
}
}
public class PersonDemo {
public static void main(String[] args) {
	// 建立Person物件
	Person p = new Person();
	p.age = -20; // 給Person物件賦值
	p.name = "人妖";
	p.show(); // 呼叫Person的show方法
}
}

通過上述程式碼發現,雖然我們用Java程式碼把Person描述清楚了,但有個“嚴重的問題”,就是Person中的屬性的行為可以任意訪問和使用。這明顯不符合實際需求。
可是怎麼才能不讓訪問呢?需要使用一個Java中的關鍵字也是一個修飾符 private(私有,許可權修飾符)。只要將Person的屬性和行為私有起來,這樣就無法直接訪問

class Person {
private int age;
private String name;

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

年齡已被私有,錯誤的值無法賦值,可是正確的值也賦值不了,這樣還是不行,那腫麼辦呢?按照之前所學習的封裝的原理,隱藏後,還需要提供訪問方式。只要對外提供可以訪問的方法,讓其他程式訪問這些方法。同時在方法中可以對資料進行驗證。
一般對成員屬性的訪問動作賦值(設定 set),取值(獲取 get),因此對私有的變數訪問的方式可以提供對應的 setXxx或者getXxx的方法

class Person {
// 私有成員變數
private int age;
private String name;

// 對外提供設定成員變數的方法
public void setAge(int a) {
	// 由於是設定成員變數的值,這裡可以加入資料的驗證
	if (a < 0 || a > 130) {
		System.out.println(a + "不符合年齡的資料範圍");
		return;
	}
	age = a; 
}

// 對外提供訪問成員變數的方法
public void getAge() {
	return age;
}
}

總結:
類中不需要對外提供的內容都私有化,包括屬性和方法。
以後再描述事物,屬性都私有化,並提供setXxx getXxx方法對其進行訪問。
 注意:私有僅僅是封裝的體現形式而已。

3.4 this關鍵字

3.4.1 成員變數和區域性變數同名問題
當在方法中出現了局部變數和成員變數同名的時候,那麼在方法中怎麼區別區域性變數成員變數呢?可以在成員變數名前面加上this.來區別成員變數和區域性變數

class Person {
private int age;
private String name;

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

3.4.2 物件的記憶體解釋

我們已經學習瞭如何把生活中的事物使用Java程式碼描述,接下來我們分析物件在記憶體中的分配情況。這裡需要畫圖一步一步演示,嚴格按照畫圖流程講解記憶體物件建立使用過程。

class Person {
	private int age;
	public int getAge() {
		return this.age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
public class PersonDemo {
	public static void main(String[] args) {
		Person p = new Person();
		p.setAge(30);
		System.out.println("大家好,今年我" + p.getAge() + "歲");
	}
}
下圖為程式中記憶體物件的建立使用過程。

在這裡插入圖片描述
圖1-2 記憶體物件建立使用過程

程式執行流程說明:
1、 先執行main方法(壓棧),執行其中的 Person p = new Person();
2、 在堆記憶體中開闢空間,併為其分配記憶體地址0x1234,緊接著成員變數預設初始化(age = 0);將記憶體地址0x1234賦值給棧內中的Person p 變數
3、 繼續執行p.setAge(30)語句,這時會呼叫setAge(int age)方法,將30賦值為setAge方法中的“age”變數;執行this.age = age語句,將age變數值30 賦值給成員變數this.age為30;
4、 setAge()方法執行完畢後(彈棧),回到main()方法,執行輸出語句System.out.println(),控制檯列印p物件中的age年齡值。

 注意:
this到底代表什麼呢?this代表的是物件,具體代表哪個物件呢?哪個物件呼叫了this所在的方法,this就代表哪個物件
 上述程式碼中的 p.setAge(30)語句中,setAge(int age)方法中的this代表的就是p物件。

3.4.3 this的應用

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

class Person {
private int age;
private String name;

public int getAge() {
	return age;
}

public void setAge(int age) {
	this.age = age;
}

public String getName() {
	return name;
}

public void setName(String name) {
	this.name = name;
}

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;
}

}


第4章

綜合案例---隨機點名案例重構

4.1 案例介紹

隨機點名器,即在全班同學中隨機的找出一名同學,列印這名同學的個人資訊。
此案例在我們前幾天課程學習中,已經介紹,現在我們要做的是對原有的案例進行升級,使用新的方式來實現。
我們來完成隨機點名器,它具備以下3個內容:

儲存所有同學姓名
總覽全班同學姓名
隨機點名其中一人,列印到控制檯

4.2 案例分析

全班同學中隨機的找出一名同學,列印這名同學的個人資訊。
我們對本案例進行分析,得出如下分析結果:

1.儲存全班同學資訊(姓名、年齡)
2.列印全班同學每一個人的資訊(姓名、年齡)
3.在班級總人數範圍內,隨機產生一個隨機數,查詢該隨機數所對應的同學資訊(姓名、年齡)並列印

隨機點名器明確地分為了三個功能。如果將多個獨立功能的程式碼寫到一起,則程式碼相對冗長,我們可以針對不同的功能可以將其封裝到一個方法中,將完整獨立的功能分離出來。
而在儲存同學姓名時,如果對每一個同學都定義一個變數進行姓名儲存,則會出現過多孤立的變數,很難一次性將全部資料持有。此時,我們採用前面講過的ArrayList集合來解決多個學生資訊的儲存問題。

4.3 重構內容分析

將原來使用的簡單Student類,封裝為包裝屬性和方法的相對完整的Student類,並將所有訪問屬性的地方改為通過get/set方法訪問。

4.4 實現程式碼步驟

每名學生都擁有多項個人資訊,為了方便管理每個人的資訊,我們對學生資訊進行封裝,編寫Student.java檔案

/**
 * 學生資訊類
 */
public class Student {
	private String name; // 姓名
	private int 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;
	}
}

上述程式碼中,對學生資訊(姓名、年齡)進行了封裝。這樣做的好處在於,以後只要找到這名學生,就能夠知道他的每項個人資訊了。
接下來我們編寫CallName.java檔案,完成程式的編寫。

main方法中呼叫三個獨立方法

public static void main(String[] args) {
	ArrayList<Student> list = new ArrayList<Student>(); //1.1建立一個可以儲存多個同學名字的容器
	/*
	 * 1.儲存全班同學資訊
	 */
	addStudent(list);
	/*
	 * 2.列印全班同學每一個人的資訊(姓名、年齡)
	 */
	printStudent(list);
	/*
	 * 3.隨機對學生點名,列印學生資訊
	 */
	randomStudent(list);
}

儲存所有學生的個人資訊

/**
 * 1.儲存全班同學名字
 */
public static void addStudent(ArrayList<Student> list) {
	//鍵盤輸入多個同學名字儲存到容器中
	Scanner sc = new Scanner(System.in);
	for (int i = 0; i < 3; i++) {
		//建立學生
		Student s = new Student();
		System.out.println("儲存第"+i+"個學生姓名:");
		String name = sc.next();
		s.setName(name);
		System.out.println("儲存第"+i+"個學生年齡:");
		int age = sc.nextInt();
		s.setAge(age);
		//新增學生到集合
		list.add(s);
	}
}
上述方法中,方法引數list中用來表示已儲存所有學生。通過Scanner,完成新學生資訊(姓名,年齡)的錄入,並將學生新增到集合中。

列印全班同學每一個人的資訊

/**
 * 2.列印全班同學每一個人的資訊(姓名、年齡)
 */
public static void printStudent (ArrayList<Student> list) {
	for (int i = 0; i < list.size(); i++) {
		Student s = list.get(i);
		System.out.println("姓名:"+s.getName() +",年齡:"+s.getAge());
	}
}
上述方法中,方法引數list中用來表示已儲存所有學生。通過遍歷集合中的每個元素,得到每個同學資訊,並輸出列印。

隨機對學生點名,列印學生資訊

/**
 * 3.隨機對學生點名,列印學生資訊
 */
public static void randomStudent (ArrayList<Student> list) {
	//在班級總人數範圍內,隨機產生一個隨機數
	int index = new Random().nextInt(list.size());
	//在容器(ArrayList集合)中,查詢該隨機數所對應的同學資訊(姓名、年齡)
	Student s = list.get(index);
	System.out.println("被隨機點名的同學:"+s.getName() + ",年齡:" + s.getAge());
}
上述方法中,通過隨機數類Random產生一個從0到集合長度的隨機索引。使用該索引獲取ArrayList集合中對應的值,便得到了全班同學的隨機學生資訊並列印。

第5章 總結

5.1 知識點總結
類與物件
用於描述多個物件的共同特徵,它是物件的模板
物件用於描述現實中的個體,它是類的例項
類的定義:使用關鍵字class來定義java中的類

格式:

		class 類名 {
			//屬性
			資料型別 變數名;
			…
			//方法
			修飾符 返回值型別 方法名(引數){   }
			…
		}

建立物件:

格式:

類名 物件名 = new 類名();

封裝(private關鍵字)
 封裝,把物件的屬性與方法的實現細節隱藏,僅對外提供一些公共的訪問方式
 封裝的體現:
 變數:使用 private 修飾,這就是變數的封裝
方法:也是一種封裝,封裝了多條程式碼
 類: 也是一種封裝,封裝了多個方法
 private關鍵字,私有的意思
 它可以用來修飾類中的成員(成員變數,成員方法)

private的特點:
 private修飾的成員只能在當前類中訪問,其他類中無法直接訪問
this關鍵字
 this關鍵字,本類物件的引用
 this是在方法中使用的,哪個物件呼叫了該方法,那麼,this就代表呼叫該方法的物件引用
 this什麼時候存在的?

當建立物件的時候

 this的作用?

用來區別同名的成員變數與區域性變數(this.成員變數)
public void setName(String name) {
this.name = name;
}