??今天要說的是 Java 中兩個非常重要的概念——類和對象。
??什么是類,什么又是對象呢?類是對特定集合的概括描述,比如,人,這個類,外在特征上,有名字,有年齡,能說話,能吃飯等等,這是我們作為人類的相同特征,那么對象呢?我們口口聲聲說要面向?qū)ο缶幊?,可是找了這么久也沒找到對象,這還怎么編程(滑稽)。此對象非彼對象,Java 中的對象是某個具體類的實例,就好比你和我都是人類這個大類的一個實例個體,也就是說,我們都是人類的一個具體對象,我們有各自的名字和年齡。
??那為什么要用類和對象這樣的概念呢?
??這是一個好問題,類是從面向過程編程向面向?qū)ο缶幊剔D(zhuǎn)變的產(chǎn)物。以前的程序,用 C 語言為例子,設計程序是算法 + 數(shù)據(jù)結構的集合,先設計算法,然后再選擇合適的數(shù)據(jù)結構去使用算法。而現(xiàn)在面向?qū)ο缶幊虅t剛好相反,先選擇合適的數(shù)據(jù)結構,再設計相應的算法來解決問題。簡單來說,面向過程注重考慮的是事情該怎么做,采用的是上帝視角來處理事情,而面向?qū)ο笞⒅氐氖鞘虑樵撜l來做,里面的主角是各鐘類型的對象。面向過程是由上而下的解決問題,而面向?qū)ο髣t是由下而上
??來舉一個生動形象的栗子,雙十一快到了,該準備剁手了,那具體的剁手步驟呢?
??面向過程是這樣的:先設置好預算 budget,然后選擇商品 A,B,C,D,一個個加入收藏,等待雙十一,付款,完成。一步一步有條不紊的進行。各個商品的名稱價格信息分別用兩個字符串數(shù)組進行存儲和處理。
??而面向?qū)ο髣t是這樣的:因為需要處理的商品數(shù)據(jù),因此可以構建一個商品類 Goods,商品類有名稱,鏈接,價格等屬性,此外還需要進行商品預算管理,因此可以構建一個購物車類 Cart,對商品進行預算進行統(tǒng)計管理,添加商品,刪除商品等方法,然后再設置一個 Money 類來對財務進行統(tǒng)一管理,有設置預算,支付等方法,構建好這幾個類之后,需要做的就是新建商品對象,往購物車里添加商品對象,然后等待雙十一,付款,完成。
??面向?qū)ο蟮乃枷胫校黧w是對象,通過對象與對象之間的交互來解決問題,就像上面那樣,關注的是商品等對象,而面向過程則關注的是如何解決問題,即如何在預算范圍內(nèi)買到合適的商品。
??當然,你也許會說,這樣一看,似乎面向?qū)ο蟾訌碗s也更加麻煩,對于簡單的問題,確實如此,因為面向?qū)ο蟮某霈F(xiàn)本身是為了解決那些復雜的項目,并提供更好的維護方法。所以往往越是復雜的問題,越能體現(xiàn)出面向?qū)ο蟮膬?yōu)越性。那問題來了,既然如此,我舉上面那個栗子來打臉干嘛呢???切莫著急,等說完后面的內(nèi)容,最后再來回過頭看看這個問題,就知道怎么回事了。
??那現(xiàn)在來看看 Java 中的類到底是什么樣的,按慣例先舉個小栗子:
class Goods{
String title;
double price;
}
??這里定義了一個最簡單的類,因為僅做示例用,它實際上并沒有什么卵用,只是為了說明類的一般定義方式,即 class + 類名后面再接大括號,在大括號里面寫上類的屬性及方法。這里的 title 跟 price 都是在類中定義的,也叫做類成員變量,一般在類的最前端定義我們需要關注的數(shù)據(jù)變量或者對象,這一部分也稱為類的實例域。類定義好了,我們需要使用的話怎么使用呢?這時候需要用到 new 關鍵字來創(chuàng)建類的實例,也就是對象。
public class Test{
public static void main(String[] args) {
Goods goodsA = new Goods();
goodsA.price=1.1;
goodsA.title="123";
System.out.println(goodsA.price);
}
}
class Goods{
String title;
double price;
}
??這里是在同一個文件下定義和使用類,而實際上,為了便于管理,通常把每個類放到單獨的文件中,并用類名來定義文件名,比如 Goods 類放到 Goods.java 文件中,而 Test 則放在 Test.java 文件中,那一個文件中引用另一個文件中定義的類,會不會報錯呢?答案是不會的,編譯器會自動幫我們尋找,只要按規(guī)范書寫類名及文件名即可。當然使用 IDE 的話,在開頭會聲明類所屬的包,關于包的概念在之前已有闡述,這里就不做過多介紹了。編譯器會自動在包中尋找相應的類。但是需要在 Goods 的定義前加上 public 關鍵字,表示可以被外部類調(diào)用。如果需要使用其他包中的類,則需要使用 import 關鍵字來導入類,如,import java.util.*;這里的 * 代表導入 java.util 下的所有類,導入之后就能像一般類一樣正常使用了。



??現(xiàn)在定義的類,只有屬性,沒有方法,看起來就像是一個將兩個數(shù)據(jù)捆綁在一個類中而已,就像 C 語言中的 struct。接下來,我們要擴展這個類。
??首先,我們需要初始化我們的商品標題和價格,這里為了用做介紹,強行使用了初始化塊(滑稽)。
public class Goods{
String title;
double price;
{
title = "";
price = 0.0;
}
}
??初始化塊,顧名思義,就是專門用做初始化的代碼塊,會在類初始化的時候先于構造器運行,因為某些變量的初始化并不是賦值這么簡單,需要經(jīng)過一些騷操作才能實現(xiàn),而如果放到構造器中,會顯得臃腫,特別是有多個構造器的時候。所以這里的初始化塊是大材小用系列。完全可以寫成以下形式,這里只是為了介紹初始化塊而強行加上的內(nèi)容。
public class Goods{
String title=”“;
double price=0.0;
}
??接下來加上一個構造器,什么是構造器?就是構造這個類的一個特殊方法,每個類都至少有一個構造器。那上面的栗子不是沒有嗎?事實上,如果沒有顯式的添加構造器方法,系統(tǒng)會提供一個默認的無參構造器,但是這個構造器什么也不做,所以才會毫無存在感?,F(xiàn)在我們要賦予它神圣的使命,讓它變得有價值起來。
public class Goods{
String title="";
double price=0.0;
public Goods(String aTitle,double aPrice){
title = aTitle;
price = aPrice;
}
}
??構造器的名稱跟類名一致,前面加上 public 修飾符,小括號內(nèi)是參數(shù)列表,這里用了兩個參數(shù),分別用來指定類的 title 跟 price 信息。這樣,之前 Test 類就可以這樣寫了。
public class Test{
public static void main(String[] args) {
Goods goodsA = new Goods("123",1.1);
System.out.println(goodsA.price);
}
}
??這樣使用起來是不是更加簡單粗暴,一般的簡單初始化代碼也會放到構造器中進行。我們還可以定義多個構造器。
public class Goods{
String title="";
double price=0.0;
public Goods(String aTitle,double aPrice){
title = aTitle;
price = aPrice;
}
public Goods(double aPrice){
price = aPrice;
title = "Goods";
}
}
public class Test{
public static void main(String[] args) {
Goods goodsA = new Goods("notebook",1.1);
Goods goodsB = new Goods(2.2);
System.out.println("goodsA title:"+goodsA.title+" price:"+goodsA.price);
System.out.println("goodsB title:"+goodsB.title+" price:"+goodsB.price);
}
}
??這樣既可以使用兩個參數(shù)的構造器,也可以使用只有一個參數(shù)的構造器,會執(zhí)行不同的構造器方法。
??構造器有了,接下來加上兩個方法,用于讀取價格和標題,以及設置價格和標題。
public class Goods{
private String title="";
private double price=0.0;
public Goods(String aTitle,double aPrice){
title = aTitle;
price = aPrice;
}
public Goods(double aPrice){
price = aPrice;
title = "Goods";
}
public String getTitle(){
return title;
}
public double getPrice(){
return price;
}
public void setTitle(String aTitle){
title = aTitle;
}
public void setPrice(double aPrice){
price = aPrice;
}
}
??這樣我們的類就已經(jīng)很豐滿,呸,飽滿了。這里我們添加了四個方法,兩個方法用于讀取成員變量,兩個方法用于設置成員變量,此外,我們還將兩個成員變量設置成了 private,這樣這兩個成員變量就只能在類的內(nèi)部的方法中使用,在其他類中是禁止使用的。你可能會問,為什么要弄的這樣復雜呢,兩個數(shù)據(jù)直接操作不好嗎?這就是封裝的意義了,把數(shù)據(jù)完全封裝在類里,只開放接口進行訪問和修改,這樣類就像一個插座一樣,外部代碼不需要知道插座里面是什么東西,只需要知道這是三孔插座還是兩孔插座,知道怎樣使用就可以了,這樣的好處在于,可以很方便的進行維護,因為數(shù)據(jù)形式是容易改變的,但只要提供的接口不改變,其他代碼就不需要改變,降低代碼之間的依賴程度,這樣就能實現(xiàn)模塊化的效果。
??那現(xiàn)在 Test 類也需要做相應調(diào)整了,因為 Goods 類成員已經(jīng)聲明為 private 了,所以只能通過類方法來進行訪問。通常把用與訪問類成員的方法叫做訪問器,設置類成員的方法叫做更改器。
public class Test{
public static void main(String[] args) {
Goods goodsA = new Goods("notebook",1.1);
Goods goodsB = new Goods(2.2);
System.out.println("goodsA title:"+goodsA.getTitle()+" price:"+goodsA.getPrice());
System.out.println("goodsB title:"+goodsB.getTitle()+" price:"+goodsB.getPrice());
}
}
??好了,現(xiàn)在我們的類變得有些厲害了,那如果現(xiàn)在需要將商品鏈接也加進去,該怎么辦呢?
public class Goods{
private String title="";
private double price=0.0;
private String link = "";
public Goods(String aTitle,double aPrice,String aLink){
title = aTitle;
price = aPrice;
link = aLink;
}
public Goods(String aTitle,double aPrice){
title = aTitle;
price = aPrice;
link = "www.baidu.com";
}
public Goods(double aPrice){
price = aPrice;
title = "Goods";
link = "www.baidu.com";
}
public String getTitle(){
return title;
}
public double getPrice(){
return price;
}
public String getLink() {
return link;
}
public void setTitle(String aTitle){
title = aTitle;
}
public void setPrice(double aPrice){
price = aPrice;
}
public void setLink(String aLink){
link = aLink;
}
}
??加上一個成員變量,再加上相應的訪問器和更改器即可,當然,這里新增了一個構造器,這樣的話,不僅之前的代碼仍可以使用,還能使用新方法,騷出新高度。
public class Test{
public static void main(String[] args) {
Goods goodsA = new Goods("notebook",1.1);
Goods goodsB = new Goods(2.2);
Goods goodsC = new Goods("Java class",233,"www.cnblogs.com/mfrank/p/7747587.html");
System.out.println("goodsA title:"+goodsA.getTitle()+" price:"+goodsA.getPrice()+" link:"+goodsA.getLink());
System.out.println("goodsB title:"+goodsB.getTitle()+" price:"+goodsB.getPrice()+" link:"+goodsB.getLink());
System.out.println("goodsC title:"+goodsC.getTitle()+" price:"+goodsC.getPrice()+" link:"+goodsC.getLink());
}
}
??這樣就能輸出三個對象的所有信息了,等等,不覺得輸出的時候太麻煩了嗎,重復三次及以上的地方就需要考慮用一個函數(shù)來代替。嗯,來給我們的 Goods 類加上一個輸出方法。
public class Goods{
private String title="";
private double price=0.0;
private String link = "";
public Goods(String aTitle,double aPrice,String aLink){
title = aTitle;
price = aPrice;
link = aLink;
}
public Goods(String aTitle,double aPrice){
title = aTitle;
price = aPrice;
link = "www.baidu.com";
}
public Goods(double aPrice){
price = aPrice;
title = "Goods";
link = "www.baidu.com";
}
public String getTitle(){
return title;
}
public double getPrice(){
return price;
}
public String getLink() {
return link;
}
public void setTitle(String aTitle){
title = aTitle;
}
public void setPrice(double aPrice){
price = aPrice;
}
public void setLink(String aLink){
link = aLink;
}
public void print(){
System.out.println("title:"+title+" price:"+price+" link:"+link);
}
}
public class Test{
public static void main(String[] args) {
Goods goodsA = new Goods("notebook",1.1);
Goods goodsB = new Goods(2.2);
Goods goodsC = new Goods("Java class",233,"www.cnblogs.com/mfrank/p/7747587.html");
goodsA.print();
goodsB.print();
goodsC.print();
}
}
??你看,我們的類定義好之后,主函數(shù)里的代碼是不是就變得很簡單了。這就是封裝的好處,封裝好以后只需要知道怎樣使用就行了,不需要關注內(nèi)部是怎樣實現(xiàn)的。
??好了,關于類與對象的內(nèi)容就說到這了,總結一下,類是某一特定集合的特征描述,對象是類的具體實例,在使用的時候類的時候,需要用 new 關鍵字來 new 一個對象,然后才能使用類方法來操作這個對象。類可以看作是對象的模版,就像一個工廠一樣,可以生成衣服,但每件衣服的款式是可以不一樣的。
??至此,本篇講解結束,歡迎大家繼續(xù)關注。