打開《Java 核心技術》第一卷的第4章:對象和類,第5章:繼承,第6章:接口和內部類。洋洋灑灑近150頁,幾乎集中了Java中面向對象語法的全部。不過憑藉我在C++中浸淫這麼多年的深厚功力,看起來還是相當輕鬆的:)這句話無疑是吹牛了,學習C++久了,我想人應該變得越來越謙虛纔對。不過我還是提倡在枯燥的學習中多給自己一點自吹自擂的驕傲吧:)
簡單的封裝:
面向對象最基本的單元就是類,類在本質上僅僅是把數據和方法結合在一起的產物,這種結合帶有一定的必然性,說白了這就是局部化或者叫分治思想在軟件設計中的具體應用,把一個過於複雜的東西拆成多個彼此獨立的簡單的東西。局部化是降低複雜性的最好武器。於是軟件設計就從面向過程的數據結構+算法進化到了面向對象的類的劃分和類的關係,類的劃分和類的關係做得多了纔有了設計模式。甚至更後來的面向組件思想(com),面向服務思想(SOA)又何嘗不是另一種意義上的分治。廢話多了些,直接寫個最簡單的類吧,就是數據+方法,在C++中它們的術語叫做成員變量和成員函數,在Java中它們的術語變成了域和方法,在Java中不用區別成員和非成員,因爲它只有成員的概念。
class Person
{
public int getId()
{
return id;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
private String name; //姓名
private int age; //年紀
private int id; //標識
}與之相應的C++代碼是:
class Person
{
public:
int getId()
{
return id;
}
string getName()
{
return name;
}
int getAge()
{
return age;
}
void setAge(int age)
{
this->age = age;
}
private:
string name; //姓名
int age; //年紀
int id; //標識
};可見,除了訪問權限聲明上Java是一對一的,而C++是分塊的之外,兩者幾乎沒有區別(類似結尾;之類的小問題不在考慮範圍之內),而在使用上:
Java的代碼:
Person p = new Person();
p.setAge(33);C++的代碼:
Person* p = new Person();
p->setAge(33);差別也非常小,Java中去掉了指針的語法,但是指針的語義還是隨處可見的,不過是少了個*和delete而已
再囉嗦一下類和對象的概念,對象是類的實例,類和對象之間是1對多的關係,類和類之間只需要類名就可以標識了(嚴格的說要加上所屬的名稱空間名字),對象和對象之間除了類型的區別以外,同樣類型的對象之間還需要用標識來區別,標識的概念也是面向對象中的重要概念。那麼如何實現從類到對象,說白了就是多了一個this指針,這在Java中應該叫this引用吧
加上了繼承:
類的繼承是面向對象的重要概念,這是面向對象可以提高軟件複用度的重要方法,可以構造一個從父類到子類不限層數的大大的派生體系樹,特別是象Java這樣的單基類的派生體系,基本上就是一棵樹。在C++中是不限制基類的,所以可能是多棵樹。
Java的代碼:
class Employee extends Person
{
}
C++的代碼:
class Employee : public Person
{
};
又僅僅是關鍵字的不同,在Java中也沒有對基類可見性的限制,總的來說Java中的可見性控制比C++中的要寬鬆的多。而且Java中根本就不支持多派生,這也是比C++簡單的重要方面。
如何實現繼承,在C++中,基本上就是把基類原樣複製到子類的內存佈局中,在Java中的實現就不是很清楚了
加上了多態:
方法的多態性是面向對象的重要概念,這是面向對象可以提高軟件可擴展性的重要方法。語法上其實太簡單了,同樣標識的方法在基類和子類中就構成了重寫,只不過在C++還需要在基類的方法前面加個virtual關鍵字而已。
在C++中,多態的實現要靠虛函數表,而在Java中就根本不需要這麼麻煩了,因爲它有完善的類型標識體系,做個反射都玩似的。不過話說回來了,既然虛函數表都可以做,也做個完善的類型標識體系很難嗎?但是C++註定要對內存和效率斤斤計較的,它把問題留給了程序員,要做可以,自己來。於是MFC搞了一堆表格,又是宏又是虛函數的,只爲了實現isA和isKindOf函數。於是Qt搞出了元數據,我自己解析,自己動態生成一個新文件。ATL更是搞出來一個thunk技術。真是八仙過海,各顯神通。就是暈你沒商量,如果還不暈,我們還有其它的各種奇招,怪招。。。
方法的重載:
方法是可以重載的,道理其實非常簡單,因爲方法的標識是函數名+參數類型,所以同名的方法可以重載。這在C++中也是很普通的概念,而且C++甚至支持運算符的重載,而Java就不支持,導致Java在做一些數學運算的時候,那代碼寫得真是慘不忍睹。
需要特別注意的是,Java中其實沒有C++中常用的覆蓋概念:
class Person
{
void testOverload()
{
System.out.println("testOverload()");
}
}
class Employee extends Person
{
public void testOverload(int i)
{
System.out.println("testOverload(int)");
}
public void testOverload(double r)
{
System.out.println("testOverload(double)");
}
}
Employee e = new Employee();
e.testOverload();
e.testOverload(1);
e.testOverload(1.0);同樣的代碼在C++中會因爲子類的testOverload函數覆蓋了基類中同名的函數,而導致e.testOverload()根本無法通過編譯。在C++中重載是不能跨越作用域的,C++這樣設計的目的是爲了保證重載規則的簡單,只搜索到子類這個層次就足夠了,不必要再去父類中搜索,因爲C++的重載匹配規則是非常複雜的。而這條基本的原則在Java中形同廢紙,它就是要繼續到父類中去搜索,它或許也有資格這樣做,因爲C++還要支持用戶自定義的類型轉換,而Java全然沒有那麼多的負擔,因爲它的基本原則就是足夠的簡單。
再多說一句,在C++中要想實現跨作用域的重載可以用using聲明,而Java中就好像默認已經全部這樣聲明瞭。
特殊的方法:
爲了使每個類都有點必要的人樣,一些特殊的方法是必須考慮的:
Java中的構造方法,和C++中的構造函數類似,不過Java中的構造函數之間可以相互調用,這其實更象個噱頭,在C++中搞個公共的初始化函數就ok了
Java中的finalize方法,和C++中的析構函數類型,不過Java中因爲有垃圾回收機制,到底這個函數什麼時候執行是不可知的,這可能是最令C++程序員不習慣的地方,那麼多資源管理的奇技淫巧統統不能用了,真是失落啊,不過這到也是代碼新手的最大福音了
Java中的equals方法,和C++中operater==類似,就是如何比較兩個對象是否具有相同的內容
Java中的clone方法,和C++中的operater=類似,不過在Java中對象基本上都是引用的語義,並不存在C++中operater=的語義,所以在C++中非常重要的拷貝構造函數和operater=基本上就沒有了,Java中只能通過clone方法生成一個對象了,總算是保留了一個可以繼續爭論深克隆和淺克隆的地方
嵌套類和局部類:
面向對象中的類真是無處不在,類可以另一個類裏面,這就是嵌套類,類也可以在方法裏面,這就是局部類。使用嵌套類和局部類的主要目的是爲了名稱控制和權限控制。在C++也有同樣的概念。
在Java中已經沒有提前聲明的語法了,於是它可以很自然的寫:
class Nest
{
class AA
{
BB b;
}
class BB
{
}
}而在C++中,這段代碼錯誤百出,你必須這樣來寫:
class Nest
{
public:
class BB;
class AA
{
BB* b;
};
class BB
{
};
};C++受制它的名稱解析機制和內存佈局機制,必須提前聲明class BB,而且在class AA也必須只能有BB的指針或引用。Java中的b根本就是引用,當然沒有那麼麻煩了。
上面的還是小問題,對於嵌套類和局部類,Java表現的真是超乎尋常的激進啊
this指針的傳遞:
class Nest
{
private void funNest()
{
}
class AA
{
private void funAA()
{
}
class BB
{
private void funBB()
{
funNest();
funAA();
}
}
}
public void test()
{
AA.BB b = new AA().new BB();
b.funBB();
}
}注意代碼AA.BB b = new AA().new BB();這裏Java的語義是把Nest.this,和AA.this指針自動傳遞給BB了,所以BB中可以自由的引用外層類的域和方法。C++可從來不允許編譯器這麼自以爲是,如果要實現相同的功能,還是讓程序員自己老老實實的把父類的指針通過構造函數傳遞進去吧。但是Java的這種簡化也是沒有任何問題:
class CC extends Nest.AA.BB
{
}在Java中居然無法編譯,因爲當用new CC執行CC的默認構造函數時,基類BB同時也是嵌套類,它需要自動傳遞的Nest.this,和AA.this指針找不到了,在Java中必須把CC的構造函數這樣寫:
class CC extends Nest.AA.BB
{
public CC(Nest.AA o)
{
o.super();
}
}
// 創建對象
CC c = new CC(new Nest().new AA());說實話,這樣的代碼確實有些醜陋,或許以後看慣了能好點吧:)
匿名類:
把內部類的名稱省略了就是一個匿名類,因爲沒有了名稱,也就無從談起構造函數了,這又是一個爲了少寫點代碼的語法:
Employee d = new Employee()
{
public void setAge(int age)
{
}
};
d.setAge(10);這裏其實構造了一個匿名的Employee的派生類,並且重寫了Employee中的setAge方法
不是結尾的結尾:
就先寫到這裏吧,本來想繼續討論Java的權限控制,static,final關鍵字和接口概念的,但是忽然發現,還是最好吧這些東西留在下一般文章吧
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/oowgsoo/archive/2008/01/16/2047335.aspx