溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java和C++的差異有哪些

發(fā)布時間:2021-11-30 15:57:31 來源:億速云 閱讀:141 作者:iii 欄目:編程語言

這篇文章主要介紹“Java和C++的差異有哪些”,在日常操作中,相信很多人在Java和C++的差異有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java和C++的差異有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

一、基本程序設計結構:

Java的基本程序結構、關鍵字、操作符都和C/C++非常相似,以下為主要的幾點區(qū)別:

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

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

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

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

public void test() {     int n;     read_data:     while (...) {         for (...) {             System.out.print("Enter a number >= 0: ");             n = in.nextInt();             if (n < 0)                 break read_data;         }     }     //下面的代碼將會被立即執(zhí)行,當break跳出最外層的循環(huán)之后。     if (n < 0) {         ...     } else {         ...     } }

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

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

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

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

public void foo() {     int[][] odds = new int[NMAX+1][];     for (int n = 0; n <= NMAX; ++n)         odds[n] = new int[n + 1];          for (int n = 0; n < odds.length; ++n) {         for (int k = 0; k < odds[n].length; ++k)             odds[n][k] = n * k;     } }

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

void foo() {     int** odds = new int*[10];     for (int n = 0; n < 10; ++n) {         if (n == 0)             odds[n] = new int;         else             odds[n] = new int[n + 1];     }              for (int n = 0; n < 10; ++n) {         for (int k = 0; k < n + 1; ++k)             odds[n][k] = n * k;     }     //注:C/C++代碼部分需要自行釋放分配的內存。                 for (int n = 0; n < 10; ++n) {         if (n == 0)             delete odds[n];         else             delete [] odds[n];     }     delete [] odds; }

 二、對象與類:

1.    Java對象實例的存儲方式:

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

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

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

public class Employee {     public void raiseSalary(double byPercent) {         double raise = salary + byPercent / 100;         salary += raise;     }     private double salary; }

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

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

這里的隱式參數(shù)this表示當前調用raiseSalary方法的對象實例的自身,該機制和C++基本相同。

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

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

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

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

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

private static void tripleValue(double x) {     x *= 3;     System.out.println("End of method: x = " + x); }  public static void testTripleValue() {     System.out.println("Test tripleValue");     double percent = 10;     System.out.println("Before: percent = " + percent);     tripleValue(percent);     System.out.println("After: percent = " + percent); } /*    結果如下:     Test tripleValue     Before: percent = 10.0     End of method: x = 30.0     After: percent = 10.0  */

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

private static void tripleSalary(Employee x) {     x.raiseSalary(200);     System.out.println("End of method: salary = " + x.getSalary()); }  public static void testTripleSalary() {     System.out.println("Test tripleSalary");     Employee harry = new Employee("Harry",50000);     System.out.println("Before: salary = " + harry.getSalary());     tripleSalary(harry);     System.out.println("After: salary = " + harry.getSalary()); } /*    結果如下:     Test tripleSalary     Before: salary = 50000.0     End of method: x = 150000.0     After: salary = 150000.0  */

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

private static void swap(Employee a,Employee b) {     Employee temp = x;     x = y;     y = temp;     System.out.println("End of method: x = " + x.getName());     System.out.println("End of method: y = " + y.getName()); }  public static void testSwap() {     System.out.println("Test Swap");     Employee a = new Employee("Alice",70000);     Employee b = new Employee("Bob",60000);     System.out.println("Before: a = " + a.getName());     System.out.println("Before: b = " + b.getName());     swap(a,b);     System.out.println("After: a = " + a.getName());     System.out.println("After: b = " + b.getName()); } /*    結果如下:     Test swap     Before: a = Alice     Before: b = Bob     End of method: x = Bob     End of method: y = Alice     After: a = Alice     After: b = Bob     */

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

5.    對象的構造和構造函數(shù):

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

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

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

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

public Employee(double s) {     //calls Employee(String,double)     this("Employee #" + nextId,s);     ++nextId; }

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

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

public class MyFirst {     public static void main(String[] args) {         BaseClass bc1 = new SonClass();         BaseClass bc2 = new SonClass(5);     } }  class BaseClass {     public BaseClass() {         System.out.println("This is BaseClass");     }          public BaseClass(int i) {         System.out.println("This is BaseClass with i.");     } }  class SonClass extends BaseClass {     public SonClass() {         System.out.println("This is SonClass");     }          public SonClass(int i) {         super(5);         System.out.println("This is SonClass with i");     } } /*    結果如下:     This is BaseClass     This is SonClass     This is BaseClass with i.     This is SonClass with i */

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

class BaseClass { public:     BaseClass() {         printf("This is BaseClass\n");     }      BaseClass(int i) {         printf("This is BaseClass with i\n");     } };  class SonClass : public BaseClass { public:     SonClass() {         printf("This is SonClass\n");     }      SonClass(int i) : BaseClass(i) {         printf("This is SonClass with i\n");     } };  int main() {     BaseClass* bc1 = new SonClass;     BaseClass* bc2 = new SonClass(5);     delete bc1;     delete bc2;     return 0; } /*    結果如下:     This is BaseClass     This is SonClass     This is BaseClass with i.     This is SonClass with i */

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

class Employee {          public Employee(String n,double s) {              name = n;              salary = s;          }                    ...                    private static int nextId;          private int id;          private String name;          private double salary;                    //object initialization block.          {              id = nextId;              nextId++;          }                    //static initialization block.          static          {              Random generator = new Random();              nextId = generator.nextInt();          }      }

6.    C++的對象析構和Java對象的finalize方法:

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

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

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

三、繼承:

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

對象的繼承性是所有面向對象語言都支持的面向對象特性之一,Java和C++作為兩個重要的面向對象開發(fā)語言在此方面有著較多的相似性,但是在有些概念的表示方式上還是存在著一定的差異,先列舉如下:

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

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

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

class DeriveClass extends BaseClass {     public double getSalary() {         double baseSalary = getSalary();         return baseSalary + bonus;     } }

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

class DeriveClass extends BaseClass {     public double getSalary() {         double baseSalary = super.getSalary();         return baseSalary + bonus;     } }

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

class DeriveClass : public BaseClass { public:     double getSalary() {         double baseSalary = BaseClass::getSalary();         return baseSalary + bonus;     } }

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

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

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

class Employee {     public Employee[] getBuddies() { ... } }  class Manager extends Employee {     public Manager[] getBuddies() { ... } }  public static void main(String[] args) {     Employee[] m = new Manager().getBuddies();     //在Java中子類的數(shù)組在復制給超類的數(shù)組時不需要顯式的轉換,就像     //子類的實例賦值給超類的實例一樣,也不需要任何顯式的轉換。     //賦值之后e和m指向相同的內存地址,同樣e[0]和m[0]也指向相同的實例。     Employee[] e = m;     //本次賦值合法也不會引發(fā)任何異常,但是會導致一個潛在的問題,既     //m[0]的對象已經(jīng)被悄悄的改變了,指向了Employee的另外一個子類。     e[0] = new OtherEmployee();     //此時再調用m[0]中Manager定義的域方法時將會引發(fā)Java的運行時異常。     m[0].setBonus(1000); }

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

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

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

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

class AbstractClass { public:     virtual void first() = 0;     virtual void second() = 0;     virtual void third(); }

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

2.    Object:

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

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

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

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

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

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

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

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

◆  一致性: 如果x和y引用的對象沒有發(fā)生變化,反復調用x.equals(y)應該返回同樣的結果。

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

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

public int hashCode() {     int hash = 0;     for (int i = 0; i < length(); ++i)         hash = 31 * hash + charAt(i);     return hash; }

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

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

public String toString() {     return getClass().getName() +          "field1 = " + field1 +         "field2 = " + field2; }

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

public bool equals(Object other) {     return toString().equals(other.toString()); }  public int hashCode() {     return toString().hashCode(); }

3.    包裝類和自動打包:

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

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

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

public void test() {     Integer a1 = 1000;     Ingeger a2 = 1000;     if (a1 == a2)         System.out.println(             "This won't be printed out because they are greater than 127.");      Integer a3 = 100;     Ingeger a4 = 100;     if (a3 == a4)         System.out.println(             "This will be printed out because they are less then 127."); }

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

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

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

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

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

到此,關于“Java和C++的差異有哪些”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI