Java的引用和C/C++指針的關係

先給出簡答的結論:

Java的引用實際上是指針,本質上和C/C++的指針是一樣的東西,只是在使用方法上有不一樣的限制,所以起了一個不一樣的名字,防止和指針混淆。

當明白了這個個時候,再回頭看Object類equal函數清晰明瞭,看 System.arraycopy 就很清楚了,其實就是把他們當指針使用。

 

 

作爲一名程序員,我們應該對新知識和新技術刨根問底,而不應泛泛而談。我未曾接觸到Java的時候,我想聽得最多的東西還是關於Java中不存在指針的問題。此時,我會不斷地想:如果Java不存在指針的話,那麼是如何實現複雜的數據結構?這樣的語言與VB有什麼差別?如果一個靜態過程式或面嚮對象語言,如果不存在指針的話,那它如何會得到程序員的喜愛呢?功能是何其的有限?幸好有機會和一位師弟在交流C#,當時我也沒有接觸過C#,不過從他的觀點令我看清楚了一些問題。我還是很清楚地記得他說了一句:C#中同樣有指針,不過是有限制的指針,因此在C#中把它稱爲引用(references)。經過對Java的學習後,我非常贊同他的那句話。其實Java中也存在指針,不過是限制的指針,因此在Java語言的規範說明裏把它稱爲引用。下在從C++中的對象類型爲依據,談談Java語言中的引用到底與C++中的哪種對象類型更類似。

Java的對象類型


在這裏,我不泛談程序語言原理方面的知識,如何爲引用,何爲指針。只以C++的對象類型爲藍本,討論C++中對象類型與Java對象類型的異同。

C++的對象類型分爲三種:對象變量,對象指針和對象引用(這裏特指是C++的引用)。對象變量,與基本數據類型變量一樣,分配在棧中,對象在棧的生命空間結束後,系統會自動釋放對象所佔用的空間;對象指針,與C語言中的指針一樣,都是一個地址,它指向棧中或堆中的一個對象。對象引用是C++中與C不同之外,形象地說,引用就是一個別名,定義引用的時候必須一起初始化,不能引用不存在的對象。下面是C++中定義三種對象類型的代碼:

String a(“string a”);

String *pA = &a;

String *pB = new String(“string b”);

String &c = a;

語句1中定義一個String變量a,它的內容是”string a”;語句2中定義一個String對象指針,它指向對象a(棧對象);語句3中定義一個String對象指針,不過它指向一個分配在堆中的對象。最後一個語句是定義一個String對象的引用c,它引用a,也即是說c是a的別名,即同一個變量,兩個不同的名字而已。只要改變a或c,該變量內容都會發生改變的

Java中的對象類型只有一種,那就是引用(注意是Java的引用,而非C++的引用)。下面是定義一個引用的代碼。

String s = new String(“string”);

在執行此代碼時,在內存空間生成的結構如下面所示:

+--------+              +-------------------------+

       |引用s |--------------------à|對象內容爲“string”|

   +-------- +              +-------------------------+

   上面的語句中其實做了兩件事情,在堆中創建了一個String對象,內容爲”string”,在棧中創建了一個引用s,它指向堆中剛創建好的String對象。並且引用s值的改變不影響它所指的對象,只有通過它調用對象的方法對可能改變對象的內容。請看如下語句:

s = new String(“abc”);

在上面這個語句中,只改變s的值,因此不會對內容爲”string”對象造成影響(不考慮垃圾回收情況)。只不過是s指向堆中的新對象而已,從指針上來說,就是s的值改變了而已。

從上面來看,Java的引用,並不與C++的引用相同,因此它不是一個別名;與對象變量也不同,它只是表示一個內存位置,該位置就是存在一個對象的位置,而不是真實的對象變量。並且從指針的意義角度來說,C/C++的指針與Java的引用卻是不謀而合。它們都表是一個內存地址,都可以通過這個內存地址來操縱它所對應的對象。因此Java的引用更像C++中的指針,在下文中把它稱爲Java中的指針,同樣也可稱爲Java中的引用。

Java中的指針與C++中的指針

本文開始提到Java中的指針是限制的指針,那麼在這裏分析Java中的指針可以執行什麼運算符。

在C++的對象指針裏面,出現的指針運算符主要有以下幾個:*,->。

運算符*是返回指針所指向的對象,而->是返回指針所指向對象的數據成員或方法成員。由於不存在對象變量,而是通過指針來訪問對象的,因此Java中不需要提供*運算符,這是Java優化了C++的一個指針問題。對於->運行符,Java的指針是提供的,不過是採用.運算符的方式提供,看起來與C++中對象變量的.運算符一樣,其實意義是有不一樣的地方。

如String s = new String(“abc”);

s.indexOf(“a”);

       與下面C++代碼等價的

       String *s = new String(“abc”);

       s->indexOf(“a”);

  

       對於C++中對象變量出現的複製構造函數,“=”運算符以及“==”運算符問題同樣在Java中出現。

   在C++中,如果類沒有定義它的“=”運算符,那麼有可能會出現淺度複製,而非深度複製,Java也有類似的問題。兩者要程序員採用深度複製的策略編寫構造函數;如果C++中的對象沒有定義“==”運算法,那麼它會依次對兩個變量比較它的數據成員,看是否都相等。如果定義則按用戶的比較方式進行比較。在Java,Object對象定義了equals方法,這個方法是用來比較兩個對象如果相等的,Object中的equals方法只是比較兩個指針的值是否相等而已,要根據所指向的對象內容進行比較,那應重寫該類的equals方法。

       正因爲Java中存在指針,所以使用Java同樣能寫出豐富的數據結構,java中的集合框架就是最好的例子。

Java 引用和指針的差別

       上面只要談到Java中指針與C++中指針相同或類似的部分,我覺得兩者之間是有差別的,差別主要有三個。

       C++中的指針是可以參與和整數的加減運算的,當一個指對指向一個對象數組時,可以通過自增操作符訪問該數組的所有元素;並且兩個指針能進行減運算,表示兩個指表所指向內存的“距離”。而Java的指針是不能參與整數運算和減法運算的。

       C++中的指針是通過new運算或對象變量取地址再進行賦值而初始化的,可以指向堆中或棧中的內存空間。Java同樣是類似的,通new運算得到初始化。Java中指針只能是指向堆中的對象,對象只能生存在堆中。C語言是可以通過遠指針來指向任意內存的地址,因而可以該問任意內存地址(可能會造成非法訪存);Java中的指針向的內存是由JVM來分配的中,由於new運算來實現,不能隨所欲爲地指向任意內存。

       C++中通過delete和delete[] 運算符進行釋放堆中創建的對象。如果對象生存週期完結束,但沒有進行內存釋放,會出現內存泄露現象。在Java中,程序員不用顯式地釋放對象,垃圾回收器會管理對象的釋放問題,不用程序員擔心。Java使用了垃圾回收機制使得程序不用再管理複雜的內存機制,使軟件出現內存泄露的情況減少到最低。

      

小結


       通過上面的分析,我們可以得到這樣的結論:Java中的引用與C++中的引用是不同的,並且Java中的引用更像C++中的指針。因此,可以認爲Java中的引用就是指針,是一種限制的指針,不能參與整數運行和指向任意位置的內存,並且不用顯示回收對象。除了Java外,就本文開頭提到的C#以及VB.NET中出現的引用,都類似於C++中的指針。Java中的採用引用的說法,其實是想程序員忘記指針所帶來的痛苦;Java的引用比C++中的指針好用得多了,也容易管理,同時提供內存管理機制,讓大家用得安心,寫得放心而已。

手動放上原文鏈接,對原創尊重:https://blog.csdn.net/linyt/article/details/1573864

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章