1. 程式人生 > >java引數傳遞底層實現原理解析

java引數傳遞底層實現原理解析

1.如下程式碼最終輸出為:???

這裡就要說到java 傳遞引數的兩種方式:值傳遞與引用傳遞.

1.值傳遞:方法呼叫時,實際引數把它的值傳遞給對應的形式引數,方法執行中形式引數值的改變不影響實際參 數的值。

2.引用傳遞:也稱為傳地址。方法呼叫時,實際引數的引用(地址,而不是引數的值)被傳遞給方法中相對應的形式引數,在方法執行中,對形式引數的操作實際上就是對實際引數的操作,方法執行中形式引數值的改變將會影響實際引數的值。

a.值傳遞的資料型別:八種基本資料型別(int, short, long, byte, float, double, boolean, char)和String(事實上String也是傳遞的地址,只是string物件和其他物件是不同的,string物件是不能被改變的,內容改變就會產生新物件,如下例子:)

1 String str="abc"; 2 System.out.println(str); 3 str=str+"de"; 4 System.out.println(str);

如果執行這段程式碼會發現先輸出“abc”,然後又輸出“abcde”,好像是str這個物件被更改了,其實,這只是一種假象罷了,JVM對於這幾行程式碼是這樣處理的,首先建立一個String物件str,並把“abc”賦值給str,然後在第三行中,其實JVM又建立了一個新的物件也名為str,然後再把原來的str的值和“de”加起來再賦值給新的str,而原來的str就會被JVM的垃圾回收機制(GC)給回收掉了,所以,str實際上並沒有被更改,String物件一旦建立之後就不可更改了。所以,Java中對String物件進行的操作實際上是一個不斷建立新的物件並且將舊的物件回收的一個過程,所以執行速度很慢。

順便提一下String與StringBuffer的區別:

String為字串常量,而StringBuilder和StringBuffer均為字串變數,即String物件一旦建立之後該物件是不可更改的,但後兩者的物件是變數,是可以更改的.

b.傳遞地址值的資料型別:除String及八種基礎資料型別以外的所有複合資料型別,包括陣列、類和介面 

所以,答案就很明顯了,上兩例分別輸出:5和s  

那麼問題來了,下面這兩個例子該怎麼輸出呢?

分別輸出(ss,s)和(2,1)

輸出(ss,s)是因為s="ss"這句程式碼新建了物件s,JVM回收了原s字串物件,而新建的x字串物件在賦值時,賦的是原s字串物件的值,所以會這樣輸出.

輸出(2,1)是因為int 屬於java基礎資料型別,這裡就要引入java裡面堆疊的概念:

棧(stack)與堆(heap)都是Java用來在Ram中存放資料的地方。與C++不同,Java自動管理棧和堆,程式設計師不能直接地設定棧或堆。 

1.堆(heap)。一種通用性的記憶體池(存在於RAM中),用於存放所有的JAVA物件。堆不同於棧的地方是:編譯器不需要知道要從堆裡分配多少儲存區 域,也不必知道儲存的資料在堆裡存活多長時間。因此,在堆裡分配儲存有很大的靈活性。當你需要建立一個物件的時候,只需要new寫一行簡單的程式碼,當執行 這行程式碼時,會自動在堆裡進行儲存分配。當然,為這種靈活性必須要付出相應的程式碼。用堆進行儲存分配比用棧進行儲存儲存需要更多的時間。 在java中,所有使用new xxx()構造出來的物件都在堆中儲存,當垃圾回收器檢測到某物件未被引用,則自動銷燬該物件.所以,理論上說java中物件的生存空間是沒有限制的,只要有引用型別指向它,則它就可以在任意地方被使用. 

2.棧(stack)。位於通用RAM中,但通過它的“棧指標”可以從處理器那裡獲得支援。棧指標若向下移動,則分配新的記憶體;若向上移動,則釋放那些 記憶體。這是一種快速有效的分配儲存方法,僅次於暫存器。建立程式時候,JAVA編譯器必須知道儲存在棧內所有資料的確切大小和生命週期,因為它必須生成 相應的程式碼,以便上下移動堆疊指標, 這一約束限制了程式的靈活性; 棧 是一個先進後出的資料結構,通常用於儲存方法(函式)中的引數,區域性變數. 在java中,所有基本資料型別和引用型別都在棧中儲存.棧中資料的生存空間一般在當前scopes內(就是由{...}括起來的區域). 

知道了這兩個概念,我們再來分析上例的底層實現:

編譯器先處理int s = 1;首先它會在棧中建立一個變數為s的引用,然後查詢有沒有字面值為1的地址,沒找到,就開闢一個存放1這個字面值的地址,然後將s指向1的地址。接著處 理int x = s,此時s=1,於是編譯器會在棧中建立一個變數為x的引用,然後將x指向1的地址; 再接著處理s=2,編譯器會查詢有沒有字面值為2的地址,如果有直接指過去,如果沒有就開闢一個存放2這個字面值的地址,然後將s指向2的地址,此時,s的值已經變了,但x的指向不會變,依然指向1這個地址, 通過字面值的引用來修改其值,不會導致另一個指向此字面值的引用的值也跟著改變.

所以結果就是s="1"  x="1" 了. 參照博文:https://blog.csdn.net/maoyeqiu/article/details/49250339