亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JDK源碼分析(2)之 Array 相關

發布時間:2020-06-14 10:09:59 來源:網絡 閱讀:240 作者:沙漏半杯 欄目:編程語言

一、Array 是一個是對象嗎?

首先可以肯定的是,數組是一個對象;但是在推導的過程中還是有些難以理解的問題,比如對于任意一個引用對象A,

  • 數組是協變的,所以Object[]A[]的父類,即Object[] o = A[];

  • 數組是一個對象,所以數組的父類是Object,即Object oo = o;

  • 那么A[]Object[]Object是什么關系呢?

是這樣嗎?
JDK源碼分析(2)之 Array 相關

我們可以通過反射來觀察一下:

private?static?void?test05()?{
??Object[]?o?=?new?String[2];
??System.out.println(o.getClass().getName());
??System.out.println(o.getClass().getSuperclass().getName());
??String[]?s?=?(String[])?o;
??System.out.println(s.getClass().getSuperclass().getName());
??Object?oo?=?s;?
}

打印:
[Ljava.lang.String;
java.lang.Object
java.lang.Object

可以看見A[]Object[]的直接父類都是Object,所以他們之間的關系也一定不是上圖中的多繼承關系,那么數組協變產生的關系一定不同于extends關鍵字產生的關系;

extends關鍵字產生的繼承關系是怎么定義呢?
這里我們可以從《Virtual Machine Specifications》中找到答案:

//?ClassFile?結構ClassFile?{
??u4????????????????magic;
??u2????????????????minor_version;
??u2????????????????major_version;
??u2????????????????constant_pool_count;
??cp_info???????????constant_pool[constant_pool_count-1];
??u2????????????????access_flags;
??u2????????????????this_class;
??u2????????????????super_class;
??u2????????????????interfaces_count;
??u2????????????????interfaces[interfaces_count];
??u2????????????????fields_count;
??field_info????????fields[fields_count];
??u2????????????????methods_count;
??method_info???????methods[methods_count];
??u2????????????????attribute_count;
??attributes_info;??attributes[attributes_count];?
}

可以看到extends關鍵字產生的繼承關系是記錄在class文件中的super_class里面的。我這里還沒有在 JDK 源碼里面找到數組協變關系的產生,但是可以猜想這個應該是后來加的類似語法關系的結構。這里先留著以后看源碼的時候確認吧。

二、Array 的 length 域相關

在準備看Array源碼的時候,我直接就點開了java.lang.reflect.Array,后來才知道這根本不是Array的源碼,看包名就知道,這是使用反射操作數組的一些方法。Array的class是在運行過程中動態生成的。
那么在Array的class中到底包含了什么呢?在很多的資料中都寫了,Array中有類似public final int length的成員變量。但是在《Java Language Specifications》10.1. Array Types中明確寫了,length不是類型的一部分;

  • An array's length is not part of its type.

private?static?void?test06()?{
??String[]?s?=?new?String[2];
??System.out.println(s.length);
??System.out.println(s.getClass().getDeclaredFields().length);??try?{
??System.out.println(s.getClass().getDeclaredField("length"));
??}?catch?(NoSuchFieldException?e)?{
??System.out.println(e.toString());
??}?
}

打印:20java.lang.NoSuchFieldException:?length

可以看到length并不是Array的成員變量,那么length是從哪里來的呢?
同樣我們可以從ClassFile結構中找打答案;

JDK源碼分析(2)之 Array 相關

可以看到Arraylength信息是記錄在對象頭中的,而讀取length信息的時候,是使用的arraylength字節碼指令來讀取的。

三、Array 的創建流程

//?數組創建的幾種形式String[]?s?=?{"a",?"b",?"c"};??//?初始化器String[]?s1?=?new?String[3];???//?有維度表達式String[]?s2?=?(String[])?Array.newInstance(String.class,?3);?//?有維度表達式

數組創建流程

是否有維度表達式:
??無:
????創建的時候每個元素遞歸深入初始化,失敗則退出
????變量類型檢查?->?與數組類型不兼容?->?編譯錯誤
????不是可具化類型(如:null)?->?編譯錯誤?
????空間不足?->?OutOfMemoryError
??有:
????創建的時候,從左向右地計算,任意維度表達式計算失敗則退出
????檢查所有維度值,有小于0?->?NegativeArraySizeException?
????分配空間,若空間不足?->?OutOfMemoryError
????只有一個維度表達式,創建一維數組,每個元素初始化化為初始值
????有n個維度表達式,執行深度為n-1的循環

四、協變數組

1. 逆變與協變

逆變與協變用來描述類型轉換(type transformation)后的繼承關系
如果A、B表示類型,f(?)表示類型轉換,≤表示繼承關系(比如,A≤B表示A是由B派生出來的子類)

  • f(?)是逆變的,當A≤B時有f(B)≤f(A)成立;

  • f(?)是協變的,當A≤B時有f(A)≤f(B)成立;

  • f(?)是不變的,當A≤B時上述兩個式子均不成立,即f(A)與f(B)相互之間沒有繼承關系。

正因為數組是協變的,所以Object[] o = new A[];

2. 為什么要設計為協變數組

有種看法認為這是在泛型產生之前的妥協產物,比如在 JDK5 之前還沒有泛型,但是很多地方需要用泛型來解決,比如:

//?java.util.Arrayspublic?static?boolean?equals(Object[]?a,?Object[]?a2)?{??if?(a==a2)????return?true;????
??if?(a==null?||?a2==null)????return?false;????
??int?length?=?a.length;??if?(a2.length?!=?length)????return?false;????
??for?(int?i=0;?i<length;?i++)?{
????Object?o1?=?a[i];
????Object?o2?=?a2[i];????if?(!(o1==null???o2==null?:?o1.equals(o2)))??????return?false;
??}??return?true;?
}

最后調用的是Object.equals()方法,但是不想全部都重寫equals,這里最簡單的就是讓數組實現協變的特性;

3. 為什么不能使用泛型數組

這里簡單的講是因為泛型是不變的,而數組是協變的,所以不能使用泛型數組;

//?如果泛型也是協變的private?static?void?test07()?{
??List<Object>?list?=?new?ArrayList<String>();??//?原本會編譯出錯
??list.add(123);
??List<String>?list1?=?list;
??String?s?=?list1.get(0);????//?類型錯誤}

可以看到如果泛型也是協變的,那么Collection?在存取數據的時候,就會產生類型轉換錯誤;

4. 為什么數組可以是協變的

private?static?void?test07()?{
??Object[]?o?=?new?String[2];
??o[0]?=?123;
}

運行時:
Exception?in?thread?"main"?java.lang.ArrayStoreException:?java.lang.Integer

可以看到數組,在存數據的時候,還會檢查數據類型是否兼容,所以數組可以是協變的。

五、數組在 java 和 c++ 中的區別

  • C++ 中的數組只是一個指針,java 中的數組是一個對象

  • java 中訪問數組會有額外的范圍檢查

  • java 中會確保數組被初始化

六、Array 和 ArrayList的效率對比

private?static?final?int?SIZE?=?50000;private?static?final?Random?RANDOM?=?new?Random();private?static?void?test_array()?{
??System.out.println("Array:");??long?start?=?System.currentTimeMillis();
??String[]?s?=?new?String[SIZE];??for?(int?i?=?0;?i?<?SIZE;?i++)?{
????s[i]?=?i?+?"";
??}
??
??System.out.println("insert:"?+?(System.currentTimeMillis()?-?start));??
??start?=?System.currentTimeMillis();??for?(int?i?=?0,?len?=?SIZE?*?10;?i?<?len;?i++)?{
????String?ss?=?s[RANDOM.nextInt(SIZE)];
??}
??
??System.out.println("get:"?+?(System.currentTimeMillis()?-?start));
}??
private?static?void?test_list()?{
??System.out.println("ArrayList:");??long?start?=?System.currentTimeMillis();
??List<String>?list?=?new?ArrayList<>(SIZE);??for?(int?i?=?0;?i?<?SIZE;?i++)?{
????list.add(i?+?"");
??}
??
??System.out.println("insert:"?+?(System.currentTimeMillis()?-?start));
??start?=?System.currentTimeMillis();??for?(int?i?=?0,?len?=?SIZE?*?10;?i?<?len;?i++)?{
????String?s?=?list.get(RANDOM.nextInt(SIZE));
??}
??System.out.println("get:"?+?(System.currentTimeMillis()?-?start));
}

打印:
Array:
insert:13get:10ArrayList:
insert:7get:22

對比可以看到,數組的插入和隨機訪問效率都要比ArrayList高,但是一般建議優先使用列表,只有在優先考慮效率的時候才考慮使用數組,因為

  • 數組是協變的不能使用泛型

  • 數組是具體化的,只有在運行時才知道元素的類型

七、總結

在看數組的時候,因為class是動態創建的,所以看了很久,但是根據數組的特性,基本可以認為數組的域和方法,類似于:

class?A<T>?implements?Cloneable,?java.io.Serializable?{??public?final?int?length?=?X;??
??public?T[]?clone()?{??try?{????return?(T[])?super.clone();
??}?catch?(CloneNotSupportedException?e)?{????throw?new?InternalError(e.getMessage());
??}
?}
}
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

定结县| 府谷县| 灌阳县| 香格里拉县| 故城县| 施甸县| 武功县| 邳州市| 建德市| 龙井市| 彭阳县| 温州市| 柳林县| 永胜县| 金溪县| 吐鲁番市| 龙山县| 行唐县| 绥阳县| 清徐县| 岑溪市| 清苑县| 抚州市| 察隅县| 英超| 衡水市| 同江市| 百色市| 城步| 鄂托克前旗| 隆林| 托里县| 且末县| 玛沁县| 名山县| 万宁市| 图们市| 芒康县| 遂溪县| 容城县| 沂水县|