Java中的值傳遞和引用傳遞

理解Java中的值傳遞和引用傳遞,形參和實參

1. 兩大數據類型以及特殊的String

  • 8種基本數據類型
    int char byte boolean long short float double
  • 對象類型
    1、8種基本數據類型的包裝類型
    2、數組等
    3、定義的對象
  • 特殊: String

2. 兩大引用類型

  • 基本類型
  • 對象的引用
    這兩者是有區別的
基本類型 對象的引用
聲明定義的時候就爲實際的數據分配了空間 聲明時只是分配了一個引用的空間,一般是4個字節,實際對象的空間未分配

特殊的: String 類型只是定義的話,也只是分配了4個字節的引用空間

int a; // 已經爲存放a分配了內存
a=10;//正確,因爲聲明a時就分配了空間

Date a; //在內存開闢一個引用空間, 即實際才使用了4個字節
a = new Date();//開闢存儲Date對象的數據空間,並把該空間的首地址賦給a

String  a; //在內存開闢一個引用空間

3. 值傳遞和引用傳遞

  • 1.形參:
    形式參數,用來接收調用該方法時傳遞的參數, 相當於一個實際參數的副本,只有在被調用的時候才分配內存空間,一旦調用結束,就釋放內存空間。因此僅僅在方法內有效
  • 2.實參:
    傳遞給被調用方法的值,預先創建並賦予確定值。

值傳遞

方法調用時,實際參數把它的值傳遞給對應的形式參數,函數接收的是 原始值的一個copy,此時內存中存在兩個相等的基本類型,即實際參數和形式參數,後面方法中的操作都是 對形參這個值的修改,不影響實際參數的值。

引用傳遞

也稱爲傳地址。方法調用時,實際參數的引用(地址,而不是參數的值)被傳遞給方法中相對應的形式參數,函數接收的是原始值的內存地址;
在方法執行中,形參和實參內容相同,指向同一塊內存地址,方法執行中對引用的操作將會影響到實際對象

具體例子說明一下:

public class A {

    public static void main(String[] args) { 
        A t = new A(); 
        int a = 99; 
        t.test1(a);//這裏傳遞的參數a就是按值傳遞 
        System.out.println(a);

        MyObj obj=new MyObj(); 
        obj.b = 10;
        t.test2(obj);//這裏傳遞的參數obj就是引用傳遞
        System.out.println(obj.b);
    } 

    public void test1(int a){ 
        a = a + 1;
        System.out.println(a);
        } 

    public void test2(MyObj obj){ 
        obj.b = 100;
        System.out.println(obj.b);
        }
}

//  輸出結果
100
99 
100 
100

特殊的:對於String 類型, 以及8中基本類型的包裝類型等immutable不可變的Java類型,其傳遞方式爲值傳遞

4. 總結

  • 1.java的基本數據類型是值傳遞,對象引用類型是引用傳遞,但是特殊的:String, Integer, Double等immutable的不可變類型可以理解爲值傳遞。
  • 2.當傳值調用時,改變的是形參的值,並沒有改變實參的值,實參的值可以傳遞給形參,但是,這個傳遞是單向的,形參不能傳遞迴實參。
  • 3.當引用調用時,傳遞的是地址,指向的是同一個對象,方法中的操作會改變實參對象的內容。

本文參考了博客: http://www.cnblogs.com/binyue/p/3862276.html
https://www.cnblogs.com/jaylon/p/5721571.html

這裏面牽扯到了 不可變類,簡單介紹一下:

不可變類
所謂的不可變類是指這個類的實例一旦創建完成後,就不能改變其成員變量值。如JDK內部自帶的很多不可變類(String+ 8種基本數據類型):Interger、Long 和 String等。

String類型如何設計成不可變類
- 1、類使用final修飾符,設計成不可被繼承的
- 2、成員變量,使用final修飾符,設計成不可以被修改的
- 3、成員變量設計成私有的private,而且不設置setter方法
- 4、通過構造器初始化所有成員,進行深拷貝(deep copy) 在getter方法中,不要直接返回對象本身,而是克隆對象,並返回對象的拷貝, String(char value[])
- 5、獲取,在getter方法中,不要直接返回對象本身,而是克隆對象,並返回對象的拷貝,toCharArray()方法

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[]; //存儲字符串

    /** Cache the hash code for the string */
    private int hash; // Default to 0 字符串的hash值

    // 構造函數,使用深克隆
    public String(char value[]) {
      this.value = Arrays.copyOf(value, value.length);
    }

    // 獲取時,不直接返回對象的引用,而是返回複製後的副本,防止外界進行修改
    public char[] toCharArray() {
       // Cannot use Arrays.copyOf because of class initialization order issues
       char result[] = new char[value.length];
       System.arraycopy(value, 0, result, 0, value.length);
       return result;
    }
  }

String 設計成不可變類的好處
- 1、設計成不可變的,方便 線程安全,可以被多個線程共享
- 2、字符串常量池 可以將一些字符常量放在常量池中重複使用,避免每次都重新創建相同的對象、節省存儲空間。但如果字符串是可變的,此時相同內容的String還指向常量池的同一個內存空間,當某個變量改變了該內存的值時,其他遍歷的值也會發生改變。所以不符合常量池設計的初衷
- 3、類加載器要用到字符串,不可變性提供了安全性,以便正確的類被加載。譬如你想加載java.sql.Connection類,而這個值被改成了myhacked.Connection,那麼會對你的數據庫造成不可知的破壞。
- 4、支持hash映射和緩存
因爲字符串是不可變的,所以在它創建的時候hashcode就被緩存了,不需要重新計算。這就使得字符串很適合作爲Map中的鍵,字符串的處理速度要快過其它的鍵對象。這就是HashMap中的鍵往往都使用字符串

String 的intern方法的作用:
如果常量池中存在字符,則返回常量池中字符的引用,
如果常量池中不存在字符,則將字符串添加到常量池中(在新的jdk1.7以上,在常量池中創建的字符串存儲的是堆中字符串的引用),返回該對象的引用。
更加詳細的請查看這篇博客
http://blog.csdn.net/seu_calvin/article/details/52291082
特殊的是,可以通過反射機制改變這一不可變的特性,通過反射,我們可以改變String類型的值的內容

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