国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java 中的參數(shù)傳遞和引用類型

gnehc / 3115人閱讀

摘要:強(qiáng)引用執(zhí)行結(jié)果如下,可知垃圾收集器寧愿拋出內(nèi)存溢出異常,也不會回收正在使用中的強(qiáng)引用軟引用此時,對于這個數(shù)組對象,有兩個引用路徑,一個是來自對象的軟引用,一個來自變量的強(qiáng)引用,所以這個數(shù)組對象是強(qiáng)可及對象。

本文主要分三部分介紹 Java 中的值、指針與引用的概念。
第一部分從編程語言的三種參數(shù)傳遞方式入手,闡釋“為什么 Java 中只有值傳遞”。
第二部分排除自動裝箱和自動拆箱的干擾,理解 Integer 等封裝類作為參數(shù)傳值的情形。
第三部分通過簡單的示例,展示強(qiáng)引用、軟引用、弱引用和虛引用之間的區(qū)別。
一、參數(shù)傳遞方式 1.1 值傳遞

形參是實(shí)參的拷貝,改變形參的值并不會影響外部實(shí)參的值。
從被調(diào)用函數(shù)的角度來說,值傳遞是單向的(實(shí)參->形參),參數(shù)的值只能傳入,不能傳出。

public class IntegerTest01 {

    private static void changeInt(int value) {
        ++value;
    }

    public static void main(String[] args) {
        int a = 1;
        changeInt(a);
        System.out.println("a = " + a);
    }
}

執(zhí)行結(jié)果為a = 1

1.2 指針傳遞

Java 中沒有指針,為了直觀展示指針傳遞,這里使用了 C++ 的例子。
指針從本質(zhì)上講是一個變量,變量的值是另一個變量的地址。因此可以說指針傳遞屬于值傳遞。

#include 
using namespace std;

void fun(int *x) {// 聲明指針
   *x += 5; // *x 是取得指針?biāo)赶虻膬?nèi)存單元,即指針解引用
   // x += 5; 則對實(shí)參沒有影響
}

int main() {
   int y = 0;
   fun(&y);// 取地址
   cout<< "y =  "<< y <

執(zhí)行結(jié)果為y = 5

Java 中的“指針”

《Head First Java》中關(guān)于 Java 參數(shù)傳遞的說明:

Java 中所傳遞的所有東西都是值,但此值是變量所攜帶的值。引用對象的變量所攜帶的是遠(yuǎn)程控制而不是對象本身,若你對方法傳入?yún)?shù),實(shí)際上傳入的是遠(yuǎn)程控制的拷貝。

《深入理解 JVM 虛擬機(jī)》中關(guān)于 Sun HotSpot 虛擬機(jī)進(jìn)行對象訪問的方式的說明:

如果使用直接指針,那么 Java 堆對象的布局中就必須考慮如何放置訪問對象類型數(shù)據(jù)的相關(guān)信息,而 reference 中存儲的直接就是對象地址。

在 Java 中聲明并初始化一個對象Object object = new Object(),在堆中存儲對象實(shí)例數(shù)據(jù),在棧中存儲對象地址,這里的變量 object 相當(dāng)于 C/C++ 中的指針。

因此,可以通過 Java 對象的引用,達(dá)到指針傳遞的效果。

public class IntegerTest02 {

    private static void changeInt(int[] value) {
        ++value[0];
    }

    public static void main(String[] args) {
        int[] a = {1};
        changeInt(a);
        System.out.println("a[0] = " + a[0]);
    }
}

執(zhí)行結(jié)果為a[0] = 2

1.3 引用傳遞

既然 Java 中沒有引用傳遞,那么到底什么是引用傳遞呢,看下 C++ 中的例子。

#include 
using namespace std;

void fun(int &x){// 聲明一個別名
   x += 5; // 修改的是 x 引用的對象值 &x = y;
}

int main()
{
   int y = 0;
   fun(y);
   cout<< "y =  "<< y <

執(zhí)行結(jié)果y = 5

C++ 中的引用就是某一變量(目標(biāo))的一個別名,對引用的操作與對變量直接操作完全一樣。
聲明一個引用,不是新定義了一個變量,它只表示該引用名是目標(biāo)變量名的一個別名,它本身不是一種數(shù)據(jù)類型,因此引用本身不占存儲單元,系統(tǒng)也不給引用分配存儲單元

Java 中的引用

Java 中的引用是 reference 類型,類似于 C/C++ 中指針的概念,而跟 C/C++ 中引用的概念完全不同。

在 JDK 1.2 以前,Java 中的引用的定義:如果 reference 類型的數(shù)據(jù)中存儲的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱這塊內(nèi)存代表著一個引用。

在JDK 1.2之后,Java對引用的概念進(jìn)行了擴(kuò)充,將引用分為強(qiáng)引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference)4種,這4種引用強(qiáng)度依次逐漸減弱。

進(jìn)一步的介紹見 Java 中的 Reference 類型

二、Integer 參數(shù)傳遞問題

回到開篇值傳遞的例子:

public class IntegerTest01 {

    private static void changeInt(int value) {
        ++value;
    }

    public static void main(String[] args) {
        int a = 1;
        changeInt(a);
        System.out.println("a = " + a);
    }
}

如果把代碼中的 int 類型換成 Integer 對象,結(jié)果會怎么樣?

public class IntegerTest02 {

    private static void changeInteger(Integer value) {
        ++value;
    }

    public static void main(String[] args) {
        Integer a = 1;
        changeInteger(a);
        System.out.println("a = " + a);
    }
}

首先需要排除自動裝箱和自動拆箱的干擾。

2.1 自動裝箱和自動拆箱
package com.sumkor.jdk7.integer02;

public class IntegerTest {
    public static void main(String[] args) {
        Integer a = 1;
        int b = a;
    }
}

使用命令javap -c IntegerTest.class進(jìn)行反編譯:

Compiled from "IntegerTest.java"
public class com.sumkor.jdk7.integer02.IntegerTest {
  public com.sumkor.jdk7.integer02.IntegerTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: astore_1
       5: aload_1
       6: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
       9: istore_2
      10: return
}

由此可知:
自動裝箱實(shí)際調(diào)用的是Integer.valueOf
自動拆箱實(shí)際調(diào)用的是Integer.intValue

因此,排除自動裝箱、自動拆箱,例子 IntegerTest02 等價于以下寫法:

public class IntegerTest03 {

    private static void changeInteger(Integer value) {
        value = Integer.valueOf(value.intValue() + 1);
    }

    public static void main(String[] args) {
        Integer a = Integer.valueOf(1);
        changeInteger(a);
    }
}

查看 Integer 源碼,可知valueOf()會將形參指向不同的 Integer 對象實(shí)例。

    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
   /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the -XX:AutoBoxCacheMax= option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }
2.2 關(guān)于 IntegerCache

IntegerCache 在首次使用時被初始化,最小值為 -128,最大值默認(rèn)為 127,也可以通過 VM 參數(shù)-XX:AutoBoxCacheMax=設(shè)置最大值。

    @Test
    public void test01() {
        Integer a = 1;
        Integer b = 1;
        System.out.println(a == b);

        Integer aa = 128;
        Integer bb = 128;
        System.out.println(aa == bb);
    }

變量ab指向的是同一個IntegerCache.cache,因此比較結(jié)果為true.
變量aabb指向的是不同的 Integer 實(shí)例,因此比較結(jié)果為false.

三、Java 中的 Reference 類型

《深入理解 JVM 虛擬機(jī)》中對此的介紹為:

強(qiáng)引用就是指在程序代碼之中普遍存在的,類似Object object = new Object()這類的引用,只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會回收掉被引用的對象。

軟引用是用來描述一些還有用但并非必需的對象。對于軟引用關(guān)聯(lián)著的對象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會把這些對象列進(jìn)回收范圍之中進(jìn)行第二次回收。如果這次回收還沒有足夠的內(nèi)存,才會拋出內(nèi)存溢出異常。在JDK 1.2之后,提供了 SoftReference 類來實(shí)現(xiàn)軟引用。

弱引用也是用來描述非必需對象的,但是它的強(qiáng)度比軟引用更弱一些,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時,無論當(dāng)前內(nèi)存是否足夠,都會回收掉只被弱引用關(guān)聯(lián)的對象。在JDK 1.2之后,提供了 WeakReference 類來實(shí)現(xiàn)弱引用。

虛引用也稱為幽靈引用或者幻影引用,它是最弱的一種引用關(guān)系。一個對象是否有虛引用的存在,完全不會對其生存時間構(gòu)成影響,也無法通過虛引用來取得一個對象實(shí)例。為一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個對象被收集器回收時收到一個系統(tǒng)通知。在JDK 1.2之后,提供了 PhantomReference 類來實(shí)現(xiàn)虛引用。

Reference 類型的強(qiáng)度跟 JVM 垃圾回收有關(guān),可惜書上沒有給出實(shí)例,本文對此進(jìn)行補(bǔ)充。

注意,以下例子中,使用 JDK 1.8,且均設(shè)置 JVM 參數(shù)為-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
即堆大小為 20 m,其中新生代大小為 10 m,按照 1:8 比例分配,Eden 區(qū)大小為 8 m。

3.1 強(qiáng)引用
/**
 * Created by Sumkor on 2018/9/10.
 */
public class StrongReferenceTest {

    public static void main(String[] args) {
        byte[] allocation01 = new byte[1024 * 1024 * 9];
        byte[] allocation02 = new byte[1024 * 1024 * 9];
    }
}

執(zhí)行結(jié)果如下,可知垃圾收集器寧愿拋出內(nèi)存溢出異常,也不會回收正在使用中的強(qiáng)引用:

 [GC (Allocation Failure)  11197K->10032K(19456K), 0.0014301 secs]
 [Full GC (Ergonomics)  10032K->9851K(19456K), 0.0072375 secs]
 [GC (Allocation Failure)  9851K->9851K(19456K), 0.0004413 secs]
 [Full GC (Allocation Failure)  9851K->9833K(19456K), 0.0093839 secs]
 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
 at com.sumkor.reference.StrongReferenceTest.main(StrongReferenceTest.java:18)
3.2 軟引用
@Test
public void test01() {

    byte[] allocation01 = new byte[1024 * 1024 * 8];
    SoftReference softReference = new SoftReference(allocation01);
    // 此時,對于這個byte數(shù)組對象,有兩個引用路徑,一個是來自SoftReference對象的軟引用,一個來自變量allocation01的強(qiáng)引用,所以這個數(shù)組對象是強(qiáng)可及對象。

    System.out.println("softReference.get() = " + softReference.get());
    allocation01 = null;
    // 結(jié)束變量allocation01對這個byte數(shù)組實(shí)例的強(qiáng)引用,此后該byte數(shù)組對象變成一個軟可及對象,可以通過softReference進(jìn)行訪問
    System.out.println("softReference.get() = " + softReference.get());

    System.gc();
    System.out.println("softReference.get() = " + softReference.get());
}

執(zhí)行結(jié)果如下,可見在觸發(fā) gc 時,內(nèi)存空間充足,并不會回收軟引用:

 softReference.get() = [B@5d6f64b1
 softReference.get() = [B@5d6f64b1
 [GC (System.gc())  14584K->9644K(19456K), 0.0040375 secs]
 [Full GC (System.gc())  9644K->9508K(19456K), 0.0115994 secs]
 softReference.get() = [B@5d6f64b1

再來看內(nèi)存不足的例子:

@Test
public void test02() {
    byte[] allocation01 = new byte[1024 * 1024 * 8];
    SoftReference softReference = new SoftReference(allocation01);
    // 此時,對于這個byte數(shù)組對象,有兩個引用路徑,一個是來自SoftReference對象的軟引用,一個來自變量allocation01的強(qiáng)引用,所以這個數(shù)組對象是強(qiáng)可及對象。

    System.out.println("softReference.get() = " + softReference.get());
    allocation01 = null;
    // 結(jié)束變量allocation01對這個byte數(shù)組實(shí)例的強(qiáng)引用,此后該byte數(shù)組對象變成一個軟可及對象,可以通過softReference進(jìn)行訪問
    System.out.println("softReference.get() = " + softReference.get());

    byte[] allocation02 = new byte[1024 * 1024 * 8];
    System.out.println("softReference.get() = " + softReference.get());
}

可見在觸發(fā) gc 時,內(nèi)存空間不足,回收軟引用:

 softReference.get() = [B@5d6f64b1
 softReference.get() = [B@5d6f64b1
 [GC (Allocation Failure)  14749K->9636K(19456K), 0.0056237 secs]
 [GC (Allocation Failure)  9636K->9684K(19456K), 0.0014787 secs]
 [Full GC (Allocation Failure)  9684K->9508K(19456K), 0.0128735 secs]
 [GC (Allocation Failure)  9508K->9508K(19456K), 0.0006353 secs]
 [Full GC (Allocation Failure)  9508K->1261K(19456K), 0.0107362 secs]
 softReference.get() = null
3.3 弱引用
package com.sumkor.reference;

import java.lang.ref.WeakReference;

/**
 * Created by Sumkor on 2018/9/10.
 */
public class WeakReferenceTest {

    public static void main(String[] args) {

        byte[] allocation01 = new byte[1024 * 1024 * 8];
        WeakReference weakReference = new WeakReference(allocation01);

        System.out.println("weakReference.get() = " + weakReference.get());// [B@154ebadd
        allocation01 = null;
        System.out.println("weakReference.get() = " + weakReference.get());// [B@154ebadd

        System.gc();
        System.out.println("weakReference.get() = " + weakReference.get());// null
    }
}

執(zhí)行結(jié)果如下,可見盡管內(nèi)存空間充足,垃圾回收器工作時回收掉只被弱引用關(guān)聯(lián)的對象:

 weakReference.get() = [B@14ae5a5
 weakReference.get() = [B@14ae5a5
 [GC (System.gc())  10177K->9008K(19456K), 0.0011390 secs]
 [Full GC (System.gc())  9008K->643K(19456K), 0.0069800 secs]
 weakReference.get() = null
3.4 虛引用
package com.sumkor.reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;

/**
 * Created by Sumkor on 2018/9/10.
 */
public class PhantomReferenceTest {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue referenceQueue = new ReferenceQueue<>();
        byte[] allocation01 = new byte[1024 * 1024 * 8];
        PhantomReference phantom = new PhantomReference<>(allocation01, referenceQueue);
        allocation01 = null;

        Thread.currentThread().sleep(3000);
        System.gc();
        Thread.currentThread().sleep(3000);

        Reference poll = referenceQueue.poll();
        System.out.println("poll = " + poll);// java.lang.ref.PhantomReference@5d6f64b1
        System.out.println("phantom.get() = " + phantom.get());
    }
}

執(zhí)行結(jié)果如下,phantom.get()總是為 null,當(dāng) byte 數(shù)組對象被垃圾回收器回收時,垃圾收集器會把要回收的對象添加到引用隊列ReferenceQueue,即得到一個“通知”:

 [GC (System.gc())  14742K->9608K(19456K), 0.0025841 secs]
 [Full GC (System.gc())  9608K->9510K(19456K), 0.0117227 secs]
 poll = java.lang.ref.PhantomReference@5d6f64b1
 phantom.get() = null

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/77076.html

相關(guān)文章

  • Java的方法參數(shù)傳遞——值傳遞or引用傳遞

    摘要:有種流行的觀點(diǎn)說的另外一個特殊之處在于,在方法調(diào)用傳參數(shù)時,是按值傳遞的,其他普通對象是引用傳遞。然而這種說法是大大錯誤的,至少是完全誤解了值傳遞和引用傳遞的概念。方法調(diào)用傳參只有一種傳遞就是值傳遞。 上篇文章說到Java的String是比較特殊的對象,它是不可變的。 有種流行的觀點(diǎn)說String的另外一個特殊之處在于,在方法調(diào)用傳參數(shù)時,String是按值傳遞的,其他普通對象是引用傳...

    Berwin 評論0 收藏0
  • 辨析Java方法參數(shù)中的傳遞引用傳遞

    摘要:引用數(shù)據(jù)類型指針存放在局部變量表中,調(diào)用方法的時候,副本引用壓棧,賦值僅改變副本的引用。方法執(zhí)行完畢,不再局部變量不再被使用到,等待被回收。 小方法大門道 小瓜瓜作為一個Java初學(xué)者,今天跟我說她想通過一個Java方法,將外部變量通過參數(shù)傳遞到方法中去,進(jìn)行邏輯處理,方法執(zhí)行完畢之后,再對修改過的變量進(jìn)行判斷處理,代碼如下所示。 public class MethodParamsPa...

    Aomine 評論0 收藏0
  • Java基礎(chǔ)知識儲備一:Java的值傳遞引用傳遞

    摘要:每個棧幀中包括局部變量表用來存儲方法中的局部變量非靜態(tài)變量函數(shù)形參。操作數(shù)棧虛擬機(jī)的解釋執(zhí)行引擎被稱為基于棧的執(zhí)行引擎,其中所指的棧就是指操作數(shù)棧。指向運(yùn)行時常量池的引用存儲程序執(zhí)行時可能用到常量的引用。 本篇文章轉(zhuǎn)自微信公眾號:Java后端技術(shù) 學(xué)過Java基礎(chǔ)的人都知道:值傳遞和引用傳遞是初次接觸Java時的一個難點(diǎn),有時候記得了語法卻記不得怎么實(shí)際運(yùn)用,有時候會的了運(yùn)用卻解釋不出...

    frontoldman 評論0 收藏0
  • 這一次,徹底解決Java的值傳遞引用傳遞

    摘要:操作數(shù)棧虛擬機(jī)的解釋執(zhí)行引擎被稱為基于棧的執(zhí)行引擎,其中所指的棧就是指操作數(shù)棧。基本數(shù)據(jù)類型的靜態(tài)變量前面提到方法區(qū)用來存儲一些共享數(shù)據(jù),因此基本數(shù)據(jù)類型的靜態(tài)變量名以及值存儲于方法區(qū)的運(yùn)行時常 本文旨在用最通俗的語言講述最枯燥的基本知識 學(xué)過Java基礎(chǔ)的人都知道:值傳遞和引用傳遞是初次接觸Java時的一個難點(diǎn),有時候記得了語法卻記不得怎么實(shí)際運(yùn)用,有時候會的了運(yùn)用卻解釋不出原理,而...

    Lavender 評論0 收藏0
  • java中傳值方式的個人理解

    摘要:接下了,我們調(diào)用方法,來嘗試改變的值以此驗證中的傳值方式。我們將作為實(shí)參傳給方法,形參來接受這個實(shí)參,在這里就體現(xiàn)出了兩種傳參方式的不同。中只有值傳遞這一種方式,只不過對于引用類型來說,傳遞的參數(shù)是對象的引用罷了。 前言 這幾天在整理java基礎(chǔ)知識方面的內(nèi)容,對于值傳遞還不是特別理解,于是查閱了一些資料和網(wǎng)上相關(guān)博客,自己進(jìn)行了歸納總結(jié),最后將其整理成了一篇博客。 值傳遞 值傳遞是指...

    vvpvvp 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<