1. 程式人生 > >Java之路:引用傳遞

Java之路:引用傳遞

1、引用傳遞

引用傳遞也稱為傳地址,指的是在方法呼叫時,傳遞的引數是按引用進行傳遞,其實傳遞的是引用的地址,也就是變數所對應的記憶體空間的地址。

方法呼叫時,實際引數的引用——地址被傳遞給方法中相對應的形式引數,即形式引數和實際引數擁有相同的儲存單元。在方法執行過程中,對形式引數的操作實際上就是對實際引數的操作,因此形式引數值的改變將會影響實際引數的值。

2、堆記憶體與棧記憶體

(1)堆記憶體:堆記憶體可以理解為一個物件的具體資訊,每一個物件儲存的只是屬性資訊,每一塊堆記憶體的開闢都要通過關鍵字new來完成。

(2)棧記憶體:可以理解為一個整型變數(只能夠儲存一個數值),其中儲存的是一塊(只能儲存一塊)堆記憶體空間的記憶體地址數值,但是為了方便理解,現在可以假設其儲存的是物件的名字。

class Book {
	String title;
	double price;
	public void printInfo() {
		System.out.println("title:" + this.title);
		System.out.println("price:" + this.price);
	}
}
public class Reference {
	public static void main(String[] args) {
		Book book = null;   // 宣告物件
		book = new Book();	// 例項化一個物件
		book.
title = "Java程式設計"; book.price = 39.8; book.printInfo(); // 通過物件呼叫,不是直接呼叫 } }

【結果】
在這裡插入圖片描述
下面看一下該程式的內部執行過程:
在這裡插入圖片描述
如果把“book = new Book() ;”註釋掉,此時會出現如下的錯誤提示:
在這裡插入圖片描述

“NullPointerException”表示空指向異常,指的是使用了一個未例項化的物件(未開闢堆記憶體空間的物件)進行了類中屬性或方法的呼叫的時候就會出現本異常資訊。 所以物件使用之前一定要開闢堆記憶體空間。

在程式中一個關鍵字new產生一個物件,就開闢一個新空間,如果有兩個關鍵字new,就表示要開闢兩個新記憶體空間,此處的兩個物件都佔有各自的記憶體空間,彼此的操作應該是獨立的。也即,只要用了new,不管在何種情況下,都表示要開闢新的記憶體空間。

3、引用資料型別的傳遞

在Java中,類本身就是引用資料型別,而對於引用資料型別實際上就相當於其他語言之中的指標概念。
在Java中對於方法引數的傳遞,物件是傳遞引用,基本資料型別是傳遞值。

下面看一個傳遞基本資料型別的例子:

public class ReferenceDemo2 {
	public static void main(String[] args) {
		int i =3,
			j = 4;
		swap(i, j);
		System.out.println("i = " + i);
		System.out.println("j = " + j);
	}
	
	public static void swap(int i, int j) {
		int temp = i;
		i = j;
		j = temp;
	}
}

【結果】
在這裡插入圖片描述
從本範例中可以看到,引用資料型別的傳遞並沒有改變資料本身的值。因為引數中傳遞的是基本型別 i 和 j 的備份,在函式中交換的也是那份備份的值而不是資料本身。

再看下面這個例子:引用傳遞

public class ReferenceDemo2 {
	public static void main(String[] args) {
		int[] count = {1,2,3,4,5}; 
		System.out.println("方法執行前 : count[0] = " + count[0]);
		swap(count);
		System.out.println("方法執行後 : count[0] = " + count[0]);
	}
	public static void swap(int[] count) {
		count[0] = 0;
		System.out.println("在方內部 : count[0] = " + count[0]);
	}
}

【結果】
在這裡插入圖片描述
從本範例中可以看到,在方法中傳遞引用資料型別int陣列,實際上傳遞的是其引用count的備份,它們都指向陣列物件,在方法中可以改變陣列物件的內容。即:對複製的引用所呼叫的方法更改的是同一個物件。

再來看一個關於物件引用傳遞的例子:

class Person {
	String name;
	int age;
}
public class ReferenceDemo3 {
	public static void main(String[] args) {
		Person p1 = null;	// 宣告p1,此物件值為null,尚未例項化
		Person p2 = null;
		p1 = new Person();	// 例項化物件p1
		p1.age = 20;
		p1.name = "小光";
		p2 = p1;	// 將p1的引用賦給p2
		System.out.println("姓名 : " + p2.name);
		System.out.println("年齡 : " + p2.age);
		p1 = null;
	}
}

【結果】
在這裡插入圖片描述
下而是具體的引用過程:
在這裡插入圖片描述

所謂的引用傳遞,指的是一塊堆記憶體空間,同時被多個棧記憶體所指向。引用傳遞的核心認識:不同的棧記憶體如果指向了同一塊堆記憶體之中,所做的修改將影響所有的棧記憶體。

可以發現,每一塊棧記憶體只能夠儲存一塊堆記憶體的地址,但是反過來,一塊堆記憶體可以同時被多個棧記憶體所指向,在這種情況下,如果要改變某一個棧記憶體的儲存地址內容,則必須先斷開已有的堆記憶體地址連線,才可以指向新的堆記憶體空間,而如果一塊堆記憶體空間沒有任何的棧記憶體所指向的話,那麼這塊空間就將成為垃圾,所有的垃圾將等待被JVM中的GC(Garbage Collector)進行不定期的收集,同時進行記憶體空間的釋放。