成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java和C++在細(xì)節(jié)上的差異:程序設(shè)計(jì)結(jié)構(gòu)

開發(fā) 后端
本文主要從程序設(shè)計(jì)結(jié)構(gòu)、對(duì)象與類、繼承三個(gè)方面講解了Java和C++在細(xì)節(jié)上的差異。

一、基本程序設(shè)計(jì)結(jié)構(gòu):

Java的基本程序結(jié)構(gòu)、關(guān)鍵字、操作符都和C/C++非常相似,以下為主要的幾點(diǎn)區(qū)別:

1. Java的原始數(shù)值型數(shù)據(jù)類型中不包含無符號(hào)類型,如c中的unsigned int。

2. 在進(jìn)行移位運(yùn)算時(shí),當(dāng)向左邊移動(dòng)時(shí),如1 << 35, 對(duì)于int類型,由于其占有4個(gè)bytes(32bits), 因此在Java中,大于32的移位將對(duì)32取模,即1 << 35的結(jié)果等于1 << 3,以此類推,long將會(huì)對(duì)64取模。對(duì)于int類型而言,如果確實(shí)需要獲取32位以上的移位,需要將返回值的類型提升到long即可。

3. 在c語言中,可以通過判斷一個(gè)數(shù)值是否為0來代替布爾中的false,其他的數(shù)值均表示為true。該寫法可應(yīng)用于if和while等子句中,如 if (i) {....}, 當(dāng)i的值不為0時(shí),該條件可為真,或者是在判斷指針對(duì)象是否為NULL時(shí),也可作為if和while的條件,因此很容易出現(xiàn)將if (i == 9) {...}寫成if (i = 9) {...}的低級(jí)錯(cuò)誤,在Java中禁止了該類轉(zhuǎn)換,既if和while中條件必須是布爾類型,如果在Java中寫成 if (i = 9) {...}將會(huì)直接導(dǎo)致編譯錯(cuò)誤,從而更好的避免了該類問題的發(fā)生。

4. Java中去除了goto字句,但是仍然視為保留字。然而Java中的break字句,新增了帶標(biāo)簽的break [label],可以使break語句直接跳出指定的循環(huán),而不僅僅是缺省的最內(nèi)層循環(huán)。注:標(biāo)簽必須放在希望跳出的最外層循環(huán)之前,并且緊跟一個(gè)冒號(hào)。如:

  1. public void test() { 
  2.     int n; 
  3.     read_data: 
  4.     while (...) { 
  5.         for (...) { 
  6.             System.out.print("Enter a number >= 0: "); 
  7.             n = in.nextInt(); 
  8.             if (n < 0
  9.                 break read_data; 
  10.         } 
  11.     } 
  12.     //下面的代碼將會(huì)被立即執(zhí)行,當(dāng)break跳出最外層的循環(huán)之后。 
  13.     if (n < 0) { 
  14.         ... 
  15.     } else { 
  16.         ... 
  17.     } 

5. Java中支持0長(zhǎng)度的數(shù)組定義,如int et = new int[0]; 在C/C++中,該寫法將會(huì)導(dǎo)致編譯錯(cuò)誤。

6. 多維數(shù)組的兩種常用訪問方式。

  1. public static void main(String[] args) { 
  2.     int[][] magicSquare =  
  3.     { 
  4.         {16,3,2,13}, 
  5.         {5,10,11,8}, 
  6.         {9,6,7,12}, 
  7.         {4,15,14,1
  8.     }; 
  9.     // 通過普通的for循環(huán)訪問 
  10.     for (int i = 0; i < magicSquare.length; ++i) { 
  11.         for (int j = 0; j < magicSquare[i].length; ++j) { 
  12.             System.out.printf("%s ",magicSquare[i][j]); 
  13.         } 
  14.         System.out.println(); 
  15.     } 
  16.     // 通過普通的for each循環(huán)訪問 
  17.     for (int[] row : magicSquare) { 
  18.         for (int col : row) { 
  19.             System.out.printf("%s ",col); 
  20.         } 
  21.         System.out.println(); 
  22.     } 
  23. /*兩次輸出結(jié)果均為:         
  24.   16 3 2 13  
  25.   5 10 11 8  
  26.   9 6 7 12  
  27.   4 15 14 1 */ 

7. Java中的不規(guī)則二維數(shù)組。

  1. public void foo() { 
  2.     int[][] odds = new int[NMAX+1][]; 
  3.     for (int n = 0; n <= NMAX; ++n) 
  4.         odds[n] = new int[n + 1]; 
  5.      
  6.     for (int n = 0; n < odds.length; ++n) { 
  7.         for (int k = 0; k < odds[n].length; ++k) 
  8.             odds[n][k] = n * k; 
  9.     } 

 C/C++中對(duì)應(yīng)于Java的不規(guī)則二維數(shù)組的表示方式。

  1. void foo() { 
  2.     int** odds = new int*[10]; 
  3.     for (int n = 0; n < 10; ++n) { 
  4.         if (n == 0
  5.             odds[n] = new int
  6.         else 
  7.             odds[n] = new int[n + 1]; 
  8.     } 
  9.          
  10.     for (int n = 0; n < 10; ++n) { 
  11.         for (int k = 0; k < n + 1; ++k) 
  12.             odds[n][k] = n * k; 
  13.     } 
  14.     //注:C/C++代碼部分需要自行釋放分配的內(nèi)存。             
  15.     for (int n = 0; n < 10; ++n) { 
  16.         if (n == 0
  17.             delete odds[n]; 
  18.         else 
  19.             delete [] odds[n]; 
  20.     } 
  21.     delete [] odds; 

 二、對(duì)象與類:

1. Java對(duì)象實(shí)例的存儲(chǔ)方式:

所有的Java對(duì)象實(shí)例都是通過new的方式創(chuàng)建的,如Employee employee = new Employee()。而此時(shí)創(chuàng)建的employee對(duì)象實(shí)例,實(shí)際是指向Employee對(duì)象的一個(gè)實(shí)例的引用,主要體現(xiàn)為實(shí)例之間基于等號(hào)的賦值,如:employee = employee2; 賦值后兩個(gè)變量將指向同一個(gè)Employee對(duì)象實(shí)例。Java處理對(duì)象變量的方式和C++中的引用比較類似,但是還是存在一定的差異,首先C++不存在空引用,既引用變量定義時(shí)也必須被同時(shí)聲明其所引用的對(duì)象實(shí)例,再者就是引用一旦定義時(shí)初始化后就不能再被重新賦值了。因此這里可以將Java的對(duì)象變量看做C++中的對(duì)象指針,如:BirthdayDate d; /*Java*/ 等同于 BirthdayDate* d; /*C++*/。

與Java對(duì)象實(shí)例聲明的方式相同,C++中的對(duì)象指針也是通過new的方式進(jìn)行初始化的,如BirthdayDate* d = new BirthdayDate. 同樣可以將C++中的對(duì)象指針賦值為NULL,也可以將其重新賦值指向另外一個(gè)對(duì)象實(shí)例。與Java相同,通過new操作符創(chuàng)建的對(duì)象實(shí)例是存儲(chǔ)在堆中的,不同的是,Java的對(duì)象在創(chuàng)建后,無需開發(fā)人員在去關(guān)注該對(duì)象實(shí)例需要合適被釋放,所有的操作均有Java虛擬機(jī)中提供的垃圾回收機(jī)制自動(dòng)完成。而C++中的該類對(duì)象,則需要開發(fā)人員通過調(diào)用delete操作符來自行完成釋放,如果忘記釋放將會(huì)產(chǎn)生內(nèi)存泄露。在C++中,不僅可以將對(duì)象存儲(chǔ)在堆上,同樣也可以定義并存儲(chǔ)的棧上,如BrithdayDate d; 該對(duì)象實(shí)例不需要手工釋放,在棧退出時(shí)將自動(dòng)釋放該對(duì)象的存儲(chǔ)空間,同時(shí)也會(huì)調(diào)用該對(duì)象的析構(gòu)函數(shù)。

2. Java對(duì)象方法的顯式參數(shù)和隱式參數(shù):

  1. public class Employee { 
  2.     public void raiseSalary(double byPercent) { 
  3.         double raise = salary + byPercent / 100
  4.         salary += raise; 
  5.     } 
  6.     private double salary; 

raiseSalary是Employee類的一個(gè)成員方法,該方法是由兩個(gè)參數(shù)構(gòu)成,一個(gè)是顯式參數(shù)byPercent,另一個(gè)則是隱式參數(shù)this,既raiseSalary方法是實(shí)現(xiàn)體可以改為:

  1. public void raiseSalary(double byPercent) { 
  2.     double raise = this.salary + byPercent / 100
  3.     this.salary += raise; 

這里的隱式參數(shù)this表示當(dāng)前調(diào)用raiseSalary方法的對(duì)象實(shí)例的自身,該機(jī)制和C++基本相同。

注:靜態(tài)方法中不存在該特征。

3. Java對(duì)象中定義的final實(shí)例域,如:public class Employee { ... private final String name; }, 該類型的field必須在對(duì)象構(gòu)造函數(shù)中進(jìn)行初始化,之后該變量將不能再被重新賦值。和final字段相似,C++對(duì)象中的const成員變量也必須在對(duì)象構(gòu)造函數(shù)的初始化列表中完成賦值任務(wù),在之后的使用中該字段將不會(huì)再被修改,否則會(huì)產(chǎn)生編譯錯(cuò)誤。對(duì)于Java的final域而言,以便應(yīng)用于基本數(shù)據(jù)類型,如int,double等,或者不可變類型,如String。對(duì)于可變類型而言,final修飾符可能會(huì)造成某些預(yù)料之外的混亂,如 private final Date hiredate; 當(dāng)該field作為某個(gè)get方法的返回值返回給調(diào)用者之后,final的修飾作用只能保證返回后的date對(duì)象不能再被重新賦值并指向新的對(duì)象實(shí)例引用,但是可以通過直接修改返回值對(duì)象的自身數(shù)據(jù)來破壞對(duì)象的封裝性,從而可能造成數(shù)據(jù)的非法性,或者狀態(tài)的不一致性。

4. 函數(shù)參數(shù)傳遞的方式:傳值和傳引用。

在Java中調(diào)用函數(shù)是,參數(shù)都是通過傳值的方式傳遞到函數(shù)內(nèi)部,然而根據(jù)參數(shù)類型的不同,其表現(xiàn)仍然存在一定的差異。主要總結(jié)為以下3點(diǎn):

1)被調(diào)用方法不能修改一個(gè)基本數(shù)據(jù)類型的參數(shù),如:int,double,boolean等,見如下代碼:

  1. private static void tripleValue(double x) { 
  2.     x *= 3
  3.     System.out.println("End of method: x = " + x); 
  4.  
  5. public static void testTripleValue() { 
  6.     System.out.println("Test tripleValue"); 
  7.     double percent = 10
  8.     System.out.println("Before: percent = " + percent); 
  9.     tripleValue(percent); 
  10.     System.out.println("After: percent = " + percent); 
  11. /*    結(jié)果如下: 
  12.     Test tripleValue 
  13.     Before: percent = 10.0 
  14.     End of method: x = 30.0 
  15.     After: percent = 10.0  */ 

2)被調(diào)用方法可以改變一個(gè)對(duì)象參數(shù)的狀態(tài),見如下代碼:

  1. private static void tripleSalary(Employee x) { 
  2.     x.raiseSalary(200); 
  3.     System.out.println("End of method: salary = " + x.getSalary()); 
  4.  
  5. public static void testTripleSalary() { 
  6.     System.out.println("Test tripleSalary"); 
  7.     Employee harry = new Employee("Harry",50000); 
  8.     System.out.println("Before: salary = " + harry.getSalary()); 
  9.     tripleSalary(harry); 
  10.     System.out.println("After: salary = " + harry.getSalary()); 
  11. /*    結(jié)果如下: 
  12.     Test tripleSalary 
  13.     Before: salary = 50000.0 
  14.     End of method: x = 150000.0 
  15.     After: salary = 150000.0  */ 

3)被調(diào)用方法不能實(shí)現(xiàn)讓對(duì)象參數(shù)引用一個(gè)新的對(duì)象,見如下代碼:

  1. private static void swap(Employee a,Employee b) { 
  2.     Employee temp = x; 
  3.     x = y; 
  4.     y = temp; 
  5.     System.out.println("End of method: x = " + x.getName()); 
  6.     System.out.println("End of method: y = " + y.getName()); 
  7. }  
  8. public static void testSwap() { 
  9.     System.out.println("Test Swap"); 
  10.     Employee a = new Employee("Alice",70000); 
  11.     Employee b = new Employee("Bob",60000); 
  12.     System.out.println("Before: a = " + a.getName()); 
  13.     System.out.println("Before: b = " + b.getName()); 
  14.     swap(a,b); 
  15.     System.out.println("After: a = " + a.getName()); 
  16.     System.out.println("After: b = " + b.getName()); 
  17. /*    結(jié)果如下: 
  18.     Test swap 
  19.     Before: a = Alice 
  20.     Before: b = Bob 
  21.     End of method: x = Bob 
  22.     End of method: y = Alice 
  23.     After: a = Alice 
  24.     After: b = Bob     */ 

C++有值調(diào)用和引用調(diào)用,引用參數(shù)標(biāo)有&符號(hào)。如:void tripleValue(double& x)或void swap(Employee& x,Employee& y)方法實(shí)現(xiàn)修改他們引用參數(shù)的目的,既該方法執(zhí)行完成后,調(diào)用函數(shù)的參數(shù)變量的值將發(fā)生改變。

5. 對(duì)象的構(gòu)造和構(gòu)造函數(shù):

在Java中如果一個(gè)class沒有定義任何構(gòu)造函數(shù),Java編譯器將自動(dòng)生成一個(gè)缺省的構(gòu)造函數(shù),沒有任何參數(shù),其行為只是按照J(rèn)ava默認(rèn)的方式初始化該類的所有域變量,如數(shù)值型為0,布爾為false,對(duì)象則為null。但是如果該class定義了自己的構(gòu)造函數(shù),那么缺省構(gòu)造函數(shù)將不會(huì)被自動(dòng)生成,再試圖調(diào)用自動(dòng)生成的缺省構(gòu)造函數(shù)將會(huì)導(dǎo)致編譯錯(cuò)誤。該行為和C++完全一致。但是Java提供了另外一種域變量初始化方式,如下:

  1. public class Employee { 
  2.     ... 
  3.     private String name = "";    //直接賦值 
  4.     private int id = assignId();//通過調(diào)用域方法完成初始化。 

在C++中不能直接在類的定義中以任何形式直接初始化成員變量。但是C++提供了在構(gòu)造函數(shù)中以初始化列表的方式完成成員變量對(duì)象的初始化,特別是const成員,必須在這里賦值。

通過一個(gè)構(gòu)造器調(diào)用另一個(gè)構(gòu)造器從而完成域變量的初始化和部分代碼復(fù)用。通過this關(guān)鍵字(或稱隱式參數(shù))作為函數(shù)名,然后傳入?yún)?shù)調(diào)用你期望的另一個(gè)構(gòu)造函數(shù),注:this被調(diào)用之前不能執(zhí)行任何其他的code。

  1. public Employee(double s) { 
  2.     //calls Employee(String,double) 
  3.     this("Employee #" + nextId,s); 
  4.     ++nextId; 

在C++中如果打算完成此功能,必須將構(gòu)造函數(shù)的部分邏輯抽取出來,以便讓多個(gè)構(gòu)造函數(shù)去調(diào)用,然后不同的構(gòu)造函數(shù)之間不能直接調(diào)用。

在Java定義的子類中,如果子類的構(gòu)造函數(shù)不是調(diào)用父類的缺省構(gòu)造函數(shù),則需要在子類構(gòu)造函數(shù)的***行代碼中指定欲調(diào)用的父類構(gòu)造函數(shù),該調(diào)用需要通過super關(guān)鍵字來完成。見如下代碼:

  1. public class MyFirst { 
  2.     public static void main(String[] args) { 
  3.         BaseClass bc1 = new SonClass(); 
  4.         BaseClass bc2 = new SonClass(5); 
  5.     } 
  6.  
  7. class BaseClass { 
  8.     public BaseClass() { 
  9.         System.out.println("This is BaseClass"); 
  10.     } 
  11.      
  12.     public BaseClass(int i) { 
  13.         System.out.println("This is BaseClass with i."); 
  14.     } 
  15.  
  16. class SonClass extends BaseClass { 
  17.     public SonClass() { 
  18.         System.out.println("This is SonClass"); 
  19.     } 
  20.      
  21.     public SonClass(int i) { 
  22.         super(5); 
  23.         System.out.println("This is SonClass with i"); 
  24.     } 
  25. /*    結(jié)果如下: 
  26.     This is BaseClass 
  27.     This is SonClass 
  28.     This is BaseClass with i. 
  29.     This is SonClass with i */ 

在C++中也可以完成該種類型的指定,但是必須在子類構(gòu)造函數(shù)的初始化列表中完成對(duì)父類指定構(gòu)造函數(shù)的調(diào)用。

  1. class BaseClass { 
  2. public
  3.     BaseClass() { 
  4.         printf("This is BaseClass\n"); 
  5.     } 
  6.  
  7.     BaseClass(int i) { 
  8.         printf("This is BaseClass with i\n"); 
  9.     } 
  10. }; 
  11.  
  12. class SonClass : public BaseClass { 
  13. public
  14.     SonClass() { 
  15.         printf("This is SonClass\n"); 
  16.     } 
  17.  
  18.     SonClass(int i) : BaseClass(i) { 
  19.         printf("This is SonClass with i\n"); 
  20.     } 
  21. }; 
  22.  
  23. int main() 
  24.     BaseClass* bc1 = new SonClass; 
  25.     BaseClass* bc2 = new SonClass(5); 
  26.     delete bc1; 
  27.     delete bc2; 
  28.     return 0
  29. /*    結(jié)果如下: 
  30.     This is BaseClass 
  31.     This is SonClass 
  32.     This is BaseClass with i. 
  33.     This is SonClass with i */ 

在Java的域變量初始化方法中存在初始化塊的方式,既除聲明即初始化、構(gòu)造函數(shù)初始化之外的第三種域變量初始化方式。在一個(gè)類的聲明中可以存在多個(gè)代碼塊,只要構(gòu)造類的對(duì)象,這些塊就會(huì)被執(zhí)行,然后再運(yùn)行類的構(gòu)造函數(shù)。靜態(tài)域變量可以在靜態(tài)初始化塊中完成初始化的工作,但是該初始化塊只是在類***次加載時(shí)被執(zhí)行一次,之后都將不再被執(zhí)行。見如下代碼:

  1. class Employee { 
  2.          public Employee(String n,double s) { 
  3.              name = n; 
  4.              salary = s; 
  5.          } 
  6.           
  7.          ... 
  8.           
  9.          private static int nextId; 
  10.          private int id; 
  11.          private String name; 
  12.          private double salary; 
  13.           
  14.          //object initialization block. 
  15.          { 
  16.              id = nextId; 
  17.              nextId++; 
  18.          } 
  19.           
  20.          //static initialization block. 
  21.          static 
  22.          { 
  23.              Random generator = new Random(); 
  24.              nextId = generator.nextInt(); 
  25.          } 
  26.      } 

6. C++的對(duì)象析構(gòu)和Java對(duì)象的finalize方法:

C++是有顯式的析構(gòu)方法,其中放置一些當(dāng)對(duì)象不再使用時(shí)需要執(zhí)行的清理代碼。在析構(gòu)函數(shù)中,最常見的操作時(shí)回收分配給對(duì)象的存儲(chǔ)空間,系統(tǒng)資源等。有Java有自動(dòng)的垃圾回收器,不需要人工回收內(nèi)存,所以Java并不支持析構(gòu)函數(shù)。如果打算在Java的代碼中完成類似的工作,可以通過為該類添加finalize方法,該方法將會(huì)在垃圾收集器清除對(duì)象之前調(diào)用,在實(shí)際應(yīng)用中,不要依賴于使用finalize方法回收任何短缺的資源,這是因?yàn)楹茈y知道這個(gè)方法什么時(shí)候才能調(diào)用。如果某個(gè)資源確實(shí)需要在使用完畢后立刻關(guān)閉,那么就需要由人工來管理。可以應(yīng)用一個(gè)類似dispose或close的方法完成相應(yīng)的清理操作。特別需要說明,如果一個(gè)類使用了這樣的方法,當(dāng)對(duì)象不再被使用時(shí)一定要調(diào)用它。

7. Java的包 vs C++的名字空間

他們具有極為相同的只能,即防止名字污染。當(dāng)一個(gè)應(yīng)用程序中存在多個(gè)第三方組件,那么不同組件中命名了相同名稱的類將是極為可能發(fā)生的,如Java中的Date類,在java.util和java.sql中均存在該名稱的類的聲明。為了有效的防止名字污染,C++中采用了namespace和using namespace的指令來明確定義某個(gè)類具體所位于的具體位置,Java中則采用了package和import語句。
Java在Java SE5.0 開始,import語句不僅可以導(dǎo)入類,還增加了導(dǎo)入靜態(tài)方法和靜態(tài)域的功能。如import static java.lang.System.*。在完成該靜態(tài)導(dǎo)入之后,就可以在剩下的代碼中直接使用System類的靜態(tài)方法和靜態(tài)域了,如out.println();exit(0)。該技巧主要用于帶有較長(zhǎng)名稱的常量,如if (d.get(DAY_OF_WEEK) == MONDAY) ...,看起來比if (d.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY) ...要容易的多。

#p#

三、繼承:

1. Java和C++在對(duì)象繼承方面的主要差異:

對(duì)象的繼承性是所有面向?qū)ο笳Z言都支持的面向?qū)ο筇匦灾唬琂ava和C++作為兩個(gè)重要的面向?qū)ο箝_發(fā)語言在此方面有著較多的相似性,但是在有些概念的表示方式上還是存在著一定的差異,先列舉如下:

1) 對(duì)象繼承的關(guān)鍵字,Java中采用extents關(guān)鍵字,如class DeriveClass extends BaseClass, 在C++中則使用(:)冒號(hào)表示類之間的繼承,如class DeriveClass : public BaseClass。

2) Java的繼承方式中不存在public,protected和private,其表現(xiàn)行為和C++中的public繼承完全一致。

3) 在有些情況下,子類中的方法需要顯式的調(diào)用超類中的方法實(shí)現(xiàn),特別是當(dāng)子類中也存在同樣方法簽名的實(shí)現(xiàn)時(shí),如果沒有明確的指出需要調(diào)用超類的方法,Java的編譯器會(huì)將子類當(dāng)前的方法列為本次調(diào)用的候選方法,見如下代碼:

  1. class DeriveClass extends BaseClass { 
  2.     public double getSalary() { 
  3.         double baseSalary = getSalary(); 
  4.         return baseSalary + bonus; 
  5.     } 

以上代碼中的getSalary()方法將會(huì)遞歸的調(diào)用其自身,而開發(fā)者的實(shí)際用意是調(diào)用超類中的getSalary方法,由于超類和子類中具有相同簽名的該方法,因此編譯器在此時(shí)選擇了子類中的getSalary。其修改方式如下:

  1. class DeriveClass extends BaseClass { 
  2.     public double getSalary() { 
  3.         double baseSalary = super.getSalary(); 
  4.         return baseSalary + bonus; 
  5.     } 

加上關(guān)鍵字super明確的指出要調(diào)用超類中的getSalary方法。在C++中的實(shí)現(xiàn)方式為BaseClass::getSalary(),既在方法簽名的前面加上父類的名字和兩個(gè)連在一起的冒號(hào)(::)。

  1. class DeriveClass : public BaseClass { 
  2. public
  3.     double getSalary() { 
  4.         double baseSalary = BaseClass::getSalary(); 
  5.         return baseSalary + bonus; 
  6.     } 

4) Java中所有未聲明為final的方法都視為可以繼承的虛方法。在C++中,盡管沒有此類限制,但是在實(shí)際的應(yīng)用中還是存在一些潛在的技巧以達(dá)到此效果。對(duì)于C++類中聲明的公有成員方法,如果該方法未聲明為virtual,既虛函數(shù),則暗示該類的子類實(shí)現(xiàn)者不要在子類中覆蓋(override)該方法。

5) Java中不支持多重繼承,不僅有效的避免了C++因多重繼承而帶來的一些負(fù)面影響,與此同時(shí),在Java中可以通過繼承(extends)單個(gè)父類和實(shí)現(xiàn)(implements)多個(gè)接口的方式更好表達(dá)該類設(shè)計(jì)意愿。

6) Java中如果子類和超類同時(shí)包含具有相同簽名的公有域方法,那么在子類中將覆蓋超類中的域方法。這其中的方法簽名只是包括方法名和參數(shù)列表,既參數(shù)的個(gè)數(shù)和類型,函數(shù)的返回值不包含在方法簽名中,但是在Java中針對(duì)該種方法覆蓋的返回值還是存在一定的限制,既子類中的返回值的類型,或者與超類中該方法的返回值類型相同,或者為其返回類型的子類。C++中沒有此類返回值類型的限制。但是Java的此類限制也會(huì)帶來一些潛在的迷惑和危險(xiǎn),見如下代碼:

  1. class Employee { 
  2.     public Employee[] getBuddies() { ... } 
  3.  
  4. class Manager extends Employee { 
  5.     public Manager[] getBuddies() { ... } 
  6.  
  7. public static void main(String[] args) { 
  8.     Employee[] m = new Manager().getBuddies(); 
  9.     //在Java中子類的數(shù)組在復(fù)制給超類的數(shù)組時(shí)不需要顯式的轉(zhuǎn)換,就像 
  10.     //子類的實(shí)例賦值給超類的實(shí)例一樣,也不需要任何顯式的轉(zhuǎn)換。 
  11.     //賦值之后e和m指向相同的內(nèi)存地址,同樣e[0]和m[0]也指向相同的實(shí)例。 
  12.     Employee[] e = m; 
  13.     //本次賦值合法也不會(huì)引發(fā)任何異常,但是會(huì)導(dǎo)致一個(gè)潛在的問題,既 
  14.     //m[0]的對(duì)象已經(jīng)被悄悄的改變了,指向了Employee的另外一個(gè)子類。 
  15.     e[0] = new OtherEmployee(); 
  16.     //此時(shí)再調(diào)用m[0]中Manager定義的域方法時(shí)將會(huì)引發(fā)Java的運(yùn)行時(shí)異常。 
  17.     m[0].setBonus(1000); 

7) Java中的final類,如果某個(gè)自定義類型被加入final關(guān)鍵字,則表示該類將不能被繼承,否則會(huì)直接產(chǎn)生編譯錯(cuò)誤。在C++中沒有特殊的關(guān)鍵字類完成此類限制,然而在實(shí)際的應(yīng)用中也同樣存在一些潛在的技巧協(xié)助開發(fā)者來進(jìn)行此類限制的甄別。如將父類中的析構(gòu)函數(shù)不設(shè)置為虛函數(shù),此方法則間接的暗示子類的實(shí)現(xiàn)者要留意,如果仍然繼承該父類,那么在實(shí)現(xiàn)多態(tài)時(shí),如BaseClass* c = new DeriveClass,如果之后需要釋放c變量的內(nèi)存資源時(shí) delete c, 此時(shí)由于父類中的析構(gòu)函數(shù)并不是虛函數(shù),因此此次調(diào)用將只會(huì)執(zhí)行父類的析構(gòu)函數(shù),而不會(huì)調(diào)用子類的析構(gòu)函數(shù),最終導(dǎo)致類分割所帶來的一些潛在錯(cuò)誤或資源泄漏。

8) 內(nèi)聯(lián)方法,在C++中有特殊的關(guān)鍵字inline用于幫助編譯器來推斷是否需要將該方法編譯成內(nèi)聯(lián)方法,以提高運(yùn)行時(shí)的效率。在Java中沒有此類關(guān)鍵字,而是通過編譯器的一連串推演,最終決定該域方法是否可以編譯成內(nèi)聯(lián)方法,主要候選方法為簡(jiǎn)短、被頻繁調(diào)用且沒有真正被子類覆蓋的域方法。

9) 超類到子類的強(qiáng)制類型轉(zhuǎn)換。在Java中可以通過直接強(qiáng)轉(zhuǎn)的方式來轉(zhuǎn)換,如Manager m = (Manager)e。如果裝換失敗將會(huì)引發(fā)運(yùn)行時(shí)異常ClassCastException,因此很多情況下為了避免此類異常的發(fā)生,需要在強(qiáng)轉(zhuǎn)之前先進(jìn)行判斷,如if (e instanceof Manager) { ... }, 如果條件為真,裝換將順利完成。在C++中也可以采用這樣的直接強(qiáng)轉(zhuǎn)方法,但是即使類型不匹配程序也不會(huì)在強(qiáng)轉(zhuǎn)是引發(fā)任何異常,而是在后面針對(duì)該變量的使用時(shí)才會(huì)導(dǎo)致錯(cuò)誤的發(fā)生。在C++中存在dynamic_cast關(guān)鍵字,如dynamic_cast<Manager*>和dynamic_cast<Manager&>,前者為基于指針的轉(zhuǎn)換,如果轉(zhuǎn)換失敗返回變量為NULL,而后者則會(huì)引發(fā)異常。

10) 抽象類:在Java中如果class被定義為abstract class,該類將不能被實(shí)例化,如果子類未能完全實(shí)現(xiàn)超類中所有的抽象方法,那么子類也將會(huì)被視為抽象類。C++中沒有特殊的關(guān)鍵字來表示抽象類,而且通過將類中的一個(gè)或多個(gè)方法定義為純虛方法來間接實(shí)現(xiàn)的,見如下C++代碼,其中的first和second均為純虛方法,既在方法的尾部添加" = 0 "。

  1. class AbstractClass { 
  2. public
  3.     virtual void first() = 0
  4.     virtual void second() = 0
  5.     virtual void third(); 

11) protected關(guān)鍵字在Java和C++中針對(duì)域方法和域字段的訪問方式存在著不同的限制級(jí)別,相同之處是protected的方法和字段都可以被子類直接訪問,不同之處是Java中相同包中的類也可以直接他們。C++自身并不存在包的概念,然而即便是相同名字空間內(nèi)的對(duì)象也不能直接訪問。

2. Object:

Java是單根結(jié)構(gòu)的框架,所有的對(duì)象都是Object的子類,即使在對(duì)象聲明時(shí)沒有進(jìn)行直接的指定,Java的編譯器將會(huì)自行搞定這些。C++中沒有適當(dāng)?shù)念愖鳛樗袑?duì)象的根類,然而在有些類庫中可以自行定義,如MFC的CObject等。Java的Object中有3個(gè)非常重要的方法equals、hashCode和toString。如果子類中重載了他們中的任意一個(gè)方法,同時(shí)也建議重載另外兩個(gè)域方法。

1) equals: 主要用于判定兩個(gè)對(duì)象是否相等。類的實(shí)現(xiàn)者可以根據(jù)自己的真實(shí)邏輯來重新實(shí)現(xiàn)該方法,通用實(shí)現(xiàn)規(guī)則見下例:

  1. public class Employee { 
  2.     //1. 顯式參數(shù)命名為otherObject,稍后需要將它轉(zhuǎn)換成另一個(gè)叫做other的變量。 
  3.     public boolean equals(Object otherObject) { 
  4.         //2. 檢測(cè)this與otherObject是否引用同一個(gè)對(duì)象(一種優(yōu)化) 
  5.         if (this == otherObject) 
  6.             return true
  7.         //3. 檢測(cè)otherObject是否為null,如果null,則返回false。 
  8.         if (otherObject == null
  9.             return false
  10.         //4. 比較this與otherObject是否屬于同一個(gè)類。 
  11.         //如果子類中的equals語義各不相同,使用下面的getClass方式,精確定義類類型。 
  12.         if (getClass() != otherObject.getClass()) 
  13.             return false
  14.         //如果子類中的equal語義和超類完全相同,可以使用instanceof檢測(cè)即可。 
  15.         //5. 將otherObject轉(zhuǎn)換為相應(yīng)的類類型變量 
  16.         Employee other = (Employee)otherObject; 
  17.         //6. 現(xiàn)在開始對(duì)所有需要比較的域進(jìn)行比較了。其中使用==比較基本類型, 
  18.     //使用equals比較對(duì)象類型。 
  19.         return name.equals(other.name) && salary == other.salary; 
  20.     } 

注:數(shù)組元素的比較可以調(diào)用Arrays.equals方法檢測(cè)。如果子類中重新定義了equals方法,就要在其中包含調(diào)用super.equals(other).

Java在語言規(guī)范中給出了自定義equals方法需要遵守的規(guī)則:

◆  自反性: 對(duì)于任何非空引用x,x.equals(x)應(yīng)該返回true。

◆  對(duì)稱性: 對(duì)于任何引用x和y,當(dāng)且僅當(dāng)y.equals(x)返回true,x.equals(y)也應(yīng)該返回true。

◆  傳遞性: 對(duì)于任何引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也應(yīng)該返回true。

◆  一致性: 如果x和y引用的對(duì)象沒有發(fā)生變化,反復(fù)調(diào)用x.equals(y)應(yīng)該返回同樣的結(jié)果。

對(duì)于任意非空引用x,x.equals(null)應(yīng)該返回false。

2) hashCode: 導(dǎo)出一個(gè)經(jīng)過哈希計(jì)算后的整型值,Java對(duì)hashCode的缺省實(shí)現(xiàn)是返回當(dāng)前對(duì)象的存儲(chǔ)地址。一下列出String的hashCode實(shí)現(xiàn)方式:

  1. public int hashCode() { 
  2.     int hash = 0
  3.     for (int i = 0; i < length(); ++i) 
  4.         hash = 31 * hash + charAt(i); 
  5.     return hash; 

注:自定義類型的equals和hashCode定義必須一致,如果x.equals(y)返回true,那么x.hashCode()就必須與y.hashCode()具有相同的值。如果打算實(shí)現(xiàn)自定義的hashCode方法,推薦使用在對(duì)象構(gòu)造初始化后就不會(huì)再改變的域字段作為hashCode的計(jì)算因子。否則一旦使用可變域資源作為hashCode計(jì)算因子的一部分,將會(huì)導(dǎo)致一些隱藏的問題。比如當(dāng)Employee對(duì)象實(shí)例存入HashMap中,但是使用者在存入集合之后,修改了某個(gè)參數(shù)hashCode計(jì)算的域字段的值,此后再在HashMap中查找原有對(duì)象時(shí)由于hashCode已經(jīng)改變,因此即使該對(duì)象已經(jīng)存入HashMap中,結(jié)果是仍然無法找到最初存入的對(duì)象了。數(shù)組類型的hashCode,可以通過Arrays.hashCode方法計(jì)算得出。

3) toString: Java中比較推薦的實(shí)現(xiàn)方式為:

  1. public String toString() { 
  2.     return getClass().getName() +  
  3.         "field1 = " + field1 + 
  4.         "field2 = " + field2; 

注:C#的Framework中也存在一個(gè)類似的Object對(duì)象,作為C#所有對(duì)象(包括自定義對(duì)象)的唯一根類,其中也有對(duì)應(yīng)的3個(gè)方法equals、hashCode和toString。Effective C#中針對(duì)這3個(gè)方法提供了一個(gè)很好的建議,既如果自定義類重載了這3個(gè)方法中任何一個(gè),那么強(qiáng)烈建議該類也重載另外兩個(gè)域方法。如對(duì)equals和toString而言,如果x.equals(y)返回true,那么x.toString.equals(y.toString)也將返回true,反之亦然。針對(duì)equals和hashCode域方法還有一種推薦的實(shí)現(xiàn)方式,如下:

  1. public bool equals(Object other) { 
  2.     return toString().equals(other.toString()); 
  3.  
  4. public int hashCode() { 
  5.     return toString().hashCode(); 

3. 包裝類和自動(dòng)打包:

1) 包裝器對(duì)象均為不可變對(duì)象,如String,既一旦初始化之后其值將不會(huì)再被改變。包裝器類是final類,不能為繼承。

2) 自動(dòng)拆包和打包:Integer n = 3; n++; 在執(zhí)行n++時(shí),Java編譯器將自動(dòng)插入一條拆包指令,然后進(jìn)行自增計(jì)算,***再將結(jié)果打入對(duì)象包內(nèi)。

3) 自動(dòng)打包的規(guī)范要求boolean, byte, char <= 127, 和介于-128--127之間的short和int被包裝到固定的對(duì)象中,見如下代碼:

  1. public void test() { 
  2.     Integer a1 = 1000
  3.     Ingeger a2 = 1000
  4.     if (a1 == a2) 
  5.         System.out.println( 
  6.             "This won't be printed out because they are greater than 127."); 
  7.  
  8.     Integer a3 = 100
  9.     Ingeger a4 = 100
  10.     if (a3 == a4) 
  11.         System.out.println( 
  12.             "This will be printed out because they are less then 127."); 

4) 打包和拆包過程是編譯器行為,不是虛擬機(jī)行為,是編譯器在生成字節(jié)碼的時(shí)候自動(dòng)插入的指令。

5) 包裝類在容器中的應(yīng)用。對(duì)于Java提供的泛型容器類其類型參數(shù)不能是primitive type,如int、float等,如果確實(shí)需要添加類似的數(shù)據(jù),需要將相應(yīng)的包裝類作為容器類型參數(shù),之后在插入原始類型數(shù)據(jù),但是在插入過程中Java的編譯器將自動(dòng)插入打包指令,因此實(shí)際插入到容器中的仍然是包裝類對(duì)象,見如下代碼:

  1. public static void main(String args[]) { 
  2.     ArrayList<Integer> l = new ArrayList<Integer>(); 
  3.     for (int i = 0; i < 10; ++i) 
  4.         l.add(i); 
  5.      
  6.     for (int i = 0; i < l.size(); ++i) { 
  7.         System.out.printf("The value is %d.\t",l.get(i)); 
  8.         System.out.printf("The class name is %s.\n" 
  9.             , l.get(i).getClass().getName()); 
  10.     } 
  11. /*    結(jié)果如下: 
  12.     The value is 0.    The class name is java.lang.Integer. 
  13.     The value is 1.    The class name is java.lang.Integer. 
  14.     The value is 2.    The class name is java.lang.Integer. 
  15.     The value is 3.    The class name is java.lang.Integer. 
  16.     The value is 4.    The class name is java.lang.Integer. 
  17.     The value is 5.    The class name is java.lang.Integer. 
  18.     The value is 6.    The class name is java.lang.Integer. 
  19.     The value is 7.    The class name is java.lang.Integer. 
  20.     The value is 8.    The class name is java.lang.Integer. 
  21.     The value is 9.    The class name is java.lang.Integer. 
  22. */ 

4. Java函數(shù)的變參表示方式:

PrintStream printf(String fmt,Object...args),其效果相當(dāng)于 PrintStream printf(String fmt,Object[] args)。在C++中變參的表示方式為int printf(const char* fmt, ...); 其后的缺省參數(shù)需要通過C語言中提供的宏VA_LIST來協(xié)助完成。

原文鏈接:http://www.cnblogs.com/stephen-liu74/archive/2011/07/27/2118660.html

【系列文章】

  1. Java和C++在細(xì)節(jié)上的差異:枚舉與反射
  2. Java和C++在細(xì)節(jié)上的差異:接口與內(nèi)部類
  3. Java和C++在細(xì)節(jié)上的差異:泛型程序設(shè)計(jì)
責(zé)任編輯:林師授 來源: Stephen_Liu的博客
相關(guān)推薦

2011-12-06 12:16:58

Java

2011-12-06 10:48:32

Java

2011-12-06 11:12:59

Java

2010-01-28 09:54:27

C++程序設(shè)計(jì)

2010-01-13 18:30:18

CC++程序設(shè)計(jì)

2012-11-08 09:49:30

C++Java程序員

2010-01-27 14:24:15

C++程序設(shè)計(jì)

2009-06-01 08:48:19

作用域變量作用域對(duì)象作用域

2011-07-22 13:41:57

java

2011-07-10 15:36:54

C++

2010-01-11 17:43:23

C++程序設(shè)計(jì)

2010-01-11 10:34:22

C++程序

2010-01-08 16:10:59

C++語言

2011-04-11 10:44:53

對(duì)象編程C++

2010-01-11 17:22:02

2009-10-12 13:10:58

馬爾可夫鏈

2011-08-05 15:46:32

Objective-C 程序設(shè)計(jì)

2010-01-21 16:45:02

C++設(shè)計(jì)目標(biāo)

2017-10-27 13:30:59

大數(shù)據(jù)MongoDBeBay

2009-09-02 13:22:23

C#組件化程序設(shè)計(jì)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩精品在线免费观看视频 | 欧洲亚洲精品久久久久 | 日韩中文一区二区三区 | 动漫www.被爆羞羞av44 | 麻豆久久久9性大片 | 一区二区三区四区电影视频在线观看 | 一a一片一级一片啪啪 | 午夜一区 | 99热这里都是精品 | 精品一区二区三区在线观看 | 97超碰在线播放 | 一区二区三区视频在线 | 国产久| 精品成人在线观看 | 天天操天天怕 | 亚洲精品大片 | 视频一区二区在线观看 | 毛片免费看的 | www.国产精 | 天天射天天干 | 精品美女 | 欧美一级在线视频 | 国产丝袜一区二区三区免费视频 | 69福利影院 | 少妇一级淫片免费播放 | 北条麻妃一区二区三区在线观看 | 国产成人一区二区 | 国产高清视频在线 | 91久久精品国产91久久 | 中文字幕一二三 | 国产精品免费一区二区三区四区 | 久久国产电影 | 日韩欧美高清dvd碟片 | 97国产一区二区 | 国内精品久久久久久久 | 欧区一欧区二欧区三免费 | 日韩视频在线一区二区 | 欧美中文字幕一区二区三区亚洲 | 超碰男人天堂 | 在线国产一区二区 | 久久丝袜视频 |