溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java包裝類型的詳細(xì)介紹

發(fā)布時(shí)間:2021-08-24 15:18:33 來(lái)源:億速云 閱讀:136 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“Java包裝類型的詳細(xì)介紹”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Java包裝類型的詳細(xì)介紹”吧!

目錄
  • 一、預(yù)備知識(shí)

    • 1.1 Java內(nèi)存管理

    • 1.2 基本數(shù)據(jù)類型的包裝類

    • 1.3 包裝類的構(gòu)造方法

    • 1.4 包裝類的優(yōu)缺點(diǎn)

    • 1.5 包裝類易錯(cuò)點(diǎn)

  • 二、自動(dòng)拆/裝箱

    • 三、整形池

      • 四、優(yōu)先選擇基本數(shù)據(jù)類型

        一、預(yù)備知識(shí)

        1、Java把內(nèi)存劃分成兩種:一種是棧內(nèi)存,另一種是堆內(nèi)存。

        2、int是基本類型,直接存數(shù)值;而 Integer是類,產(chǎn)生對(duì)象時(shí)用一個(gè)引用指向這個(gè)對(duì)象。

        3、包裝器(wrapper)——這是《JAVA核心技術(shù)》一書中對(duì)Integer這類對(duì)象的稱呼。

        4、包裝器位于java.lang包中。

        5、包裝類是引用傳遞而基本類型是值傳遞(下面的內(nèi)存管理給予解釋)

        6、基本類型的變量和對(duì)象的引用變量都是在函數(shù)的棧內(nèi)存中分配 ,而實(shí)際的對(duì)象是在存儲(chǔ)堆內(nèi)存中

        int i = 5;//直接在棧中分配空間
        Integer i = new Integr(5);//對(duì)象是在堆內(nèi)存中,而i(引用變量)是在棧內(nèi)存中

        1.1 Java內(nèi)存管理

        在函數(shù)中定義的一些基本類型的變量和對(duì)象的引用變量都是在函數(shù)的棧內(nèi)存中分配。當(dāng)在一段代碼塊中定義一個(gè)變量時(shí),Java就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)超過(guò)變量的作用域后,Java會(huì)自動(dòng)釋放掉為該變量分配的內(nèi)存空間,該內(nèi)存空間可以立刻被另作他用。

        堆內(nèi)存用于存放由new創(chuàng)建的對(duì)象和數(shù)組。在堆中分配的內(nèi)存,由Java虛擬機(jī)自動(dòng)垃圾回收器來(lái)管理。在堆中產(chǎn)生了一個(gè)數(shù)組或者對(duì)象后,還可以在棧中定義一個(gè)特殊的變量,這個(gè)變量的取值等于數(shù)組或者對(duì)象在堆內(nèi)存中的首地址,在棧中的這個(gè)特殊的變量就變成了數(shù)組或者對(duì)象的引用變量,以后就可以在程序中使用棧內(nèi)存中的引用變量來(lái)訪問(wèn)堆中的數(shù)組或者對(duì)象,引用變量相當(dāng)于為數(shù)組或者對(duì)象起的一個(gè)別名,或者代號(hào)。

        引用變量是普通變量,定義時(shí)在棧中分配內(nèi)存,引用變量在程序運(yùn)行到作用域外釋放。而數(shù)組&對(duì)象本身在堆中分配,即使程序運(yùn)行到使用new產(chǎn)生數(shù)組和對(duì)象的語(yǔ)句所在地代碼塊之外,數(shù)組和對(duì)象本身占用的堆內(nèi)存也不會(huì)被釋放,數(shù)組和對(duì)象在沒(méi)有引用變量指向它的時(shí)候,才變成垃圾,不能再被使用,但是仍然占著內(nèi)存,在隨后的一個(gè)不確定的時(shí)間被垃圾回收器釋放掉。這也就是Java比較占內(nèi)存的主要原因,實(shí)際上棧中的變量指向堆內(nèi)存中的變量,這就是 Java 中的指針!

        1.2 基本數(shù)據(jù)類型的包裝類

        Java包裝類型的詳細(xì)介紹

        基本類型包裝類
        booleanBoolean
        charCharacter
        byteByte
        intinteger
        longLong
        floatFloat
        doubleDouble
        shortShort

        1.3 包裝類的構(gòu)造方法

        1、所有包裝類都可將與之對(duì)應(yīng)的基本數(shù)據(jù)類型作為參數(shù),來(lái)構(gòu)造它們的實(shí)例

        2、除Character類外,其他包裝類可將一個(gè)字符串作為參數(shù)構(gòu)造它們的實(shí)例

        注意事項(xiàng):

        • Boolean類構(gòu)造方法參數(shù)為String類型時(shí),若該字符串內(nèi)容為true(不考慮大小寫),則該Boolean對(duì)象表示true,否則表示false。

        • 當(dāng)Number包裝類構(gòu)造方法參數(shù)為String類型時(shí),字符串不能為null,且該字符串必須可解析為相應(yīng)的基本數(shù)據(jù)類型的數(shù)據(jù),否則編譯通過(guò),運(yùn)行時(shí)報(bào)NumberFormatException異常。

        1.4 包裝類的優(yōu)缺點(diǎn)

        包裝類優(yōu)點(diǎn):

        1、提供了一系列實(shí)用的方法

        2、集合不允許存放基本數(shù)據(jù)類型數(shù)據(jù),存放數(shù)字時(shí),要用包裝類型

        包裝類缺點(diǎn):

        • 由于每個(gè)值分別包裝在對(duì)象中,所以ArrayList<Integer>的效率遠(yuǎn)遠(yuǎn)低于int[]數(shù)組。(應(yīng)該用其構(gòu)造小型集合,其原因是程序員操作的方便性要比執(zhí)行效率更加重要)

        1.5 包裝類易錯(cuò)點(diǎn)

        • 對(duì)象包裝器類是不可變的,即一旦構(gòu)造了包裝器,就不允許更改包裝在其中的值。

        • 對(duì)象包裝器類是不可變的,因此不能定義他們的子類。

        Integer i = new Integer(20);
        i = 50;
        System.out.println(i); // 50

        疑問(wèn):為什么變了,前面說(shuō)是不可變的咋變了,前后不是矛盾嗎?

        想想前面介紹的Java內(nèi)存管理方式,也許你已經(jīng)明白了,如果還不明白就看看我的解釋:

        Integer i 中的 i 只是棧中指向?qū)ο蟮囊粋€(gè)引用,后來(lái) i = 50 又將i指向了50(此處運(yùn)用到了自動(dòng)裝箱技術(shù)),這就是變化的原因,但是原來(lái)堆中創(chuàng)建的對(duì)象還是不變的。

        除了包裝器類型:Integer、Long、Short、Byte、Character、Boolean、Float和Double之外,還有BigInteger(java.math包)實(shí)例是不可變的,String、BigDecimal也是如此,不能修改它的值。不能修改現(xiàn)有實(shí)例的值,對(duì)這些類型的操作將返回新的實(shí)例。起先,不可變類型看起來(lái)可能很不自然,但是它具有很多勝過(guò)與其向?qū)?yīng)的可變類型的優(yōu)勢(shì)。不可變類型更容易設(shè)計(jì)、實(shí)現(xiàn)和使用;它出錯(cuò)的可能性更小,并且更加安全

        為了在一個(gè)包含對(duì)不可變對(duì)象引用的變量上執(zhí)行計(jì)算,需要將計(jì)算的結(jié)果賦值給該變量。如下面的示例:

        BigInteger fiveThousand = new BigInteger("5000");
        BigInteger fiftyThousand = new BigInteger("50000");
        BigInteger fiveHundredThousand = new BigInteger("500000");
        BigInteger total = BigInteger.ZERO;
        total = total.add(fiveThousand);
        total = total.add(fiftyThousand);
        total = total.add(fiveHundredThousand);
        System.out.println(total);

        二、自動(dòng)拆/裝箱

        基本數(shù)據(jù)(Primitive)類型的自動(dòng)裝箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0開始提供的功能。

        Java語(yǔ)言規(guī)范中說(shuō)道:在許多情況下包裝與解包裝是由編譯器自行完成的(在這種情況下包裝稱為裝箱,解包裝稱為拆箱)。

        //聲明一個(gè)Integer對(duì)象
        Integer num = 10;
        /*以上的聲明就是用到了自動(dòng)的裝箱,解析為:
        Integer num = new Integer(10);
        以上就是一個(gè)很好的體現(xiàn),因?yàn)?0是屬于基本數(shù)據(jù)類型的,原則上它是不能直接賦值給一個(gè)對(duì)象Integer的,但jdk1.5后你就可以進(jìn)行這樣的聲明,這就是自動(dòng)裝箱的魅力,自動(dòng)將基本數(shù)據(jù)類型轉(zhuǎn)化為對(duì)應(yīng)的封裝類型。成為一個(gè)對(duì)象以后就可以調(diào)用對(duì)象所聲明的所有的方法
        自動(dòng)拆箱:故名思議就是將對(duì)象重新轉(zhuǎn)化為基本數(shù)據(jù)類型:*/
         
        //裝箱
        Integer num_1 = 10;
        //拆箱
        int num_2 = num_1;
        /*自動(dòng)拆箱有個(gè)很典型的用法就是在進(jìn)行運(yùn)算的時(shí)候:因?yàn)閷?duì)象時(shí)不能直接進(jìn)行運(yùn)算的,而是要轉(zhuǎn)化為基本數(shù)據(jù)類型后才能進(jìn)行加減乘除*/
         
        Integer num_3 = 10;
        //進(jìn)行計(jì)算時(shí)隱含的有自動(dòng)拆箱
        System.out.print(num_3--);
        /*哈哈 應(yīng)該感覺(jué)很簡(jiǎn)單吧,下面我再來(lái)講點(diǎn)稍微難點(diǎn)的.*/
         
        //在-128~127 之外的數(shù)
        Integer num_4 = 297; Integer num_5 = 297;
        System.out.println("num_4==num_5: "+(num_4==num_5));
        // 在-128~127 之內(nèi)的數(shù)
        Integer num_6 = 97; Integer num_7 = 97;
        System.out.println("num_6==num_7: "+(num_6==num_7));
        /*打印的結(jié)果是:
            num_4==num_5: false 
            num_6==num_7: true 
        */
        //此處的解釋在下方

        注意事項(xiàng):

        1、裝箱和拆箱是編譯器認(rèn)可的,而不是虛擬機(jī)。編譯器在生成類的字節(jié)碼時(shí),插入必要的方法調(diào)用。虛擬機(jī)只是執(zhí)行字節(jié)碼。

        2、包裝對(duì)象和拆箱對(duì)象可以自由轉(zhuǎn)換,但是要剔除NULL值,因?yàn)閚ull值并不能轉(zhuǎn)化為基本類型。

        import java.util.ArrayList;
        import java.util.List;
        public class Ceshi {
            // 計(jì)算list中所有元素之和
            public static int f(List<Integer> list) {
                int count = 0;
                for (int i : list) {
                    count += i;
                }
                return count;
            }
         
            public static void main(String[] args) {
                List<Integer> list = new ArrayList<Integer>();
                list.add(1);
                list.add(2);
                list.add(null);
                System.out.println(f(list));
            }
        }

        運(yùn)行結(jié)果:Exception in thread "main" java.lang.NullPointerException

        運(yùn)行失敗,報(bào)空指針異常,我們稍稍思考一下很快就知道原因了:在程序的for循環(huán)中,隱含了一個(gè)拆箱過(guò)程,再此過(guò)程中包裝類型轉(zhuǎn)換為了基本類型。我們知道拆箱過(guò)程是通過(guò)調(diào)用包裝對(duì)象的intValue方法來(lái)實(shí)現(xiàn)的,由于包裝對(duì)象是null值,訪問(wèn)其intValue方法報(bào)空指針異常也就在所難免了。問(wèn)題找到了,那就解決。(即加入null值檢查即可)

        // 計(jì)算list中所有元素之和
        public static int f(List<Integer> list) {
            int count = 0;
            for (Integer i : list) {
                count += (null != i) ? i : 0;
            }
            return count;
        }

        針對(duì)此類問(wèn)題:謹(jǐn)記包裝類型參與運(yùn)算時(shí),要做null值校驗(yàn)。

        三、整形池

        @SuppressWarnings("resource")
        Scanner input = new Scanner(System.in);
        while (input.hasNextInt()) {
            int i = input.nextInt();
            System.out.println("\n*********" + i + "的相等判斷**********");
            // 兩個(gè)通過(guò)new產(chǎn)生的integer對(duì)象
            Integer temp1 = new Integer(i);
            Integer temp2 = new Integer(i);
            System.out.println("new產(chǎn)生的對(duì)象:" + (temp1 == temp2));
         
            // 基本類型轉(zhuǎn)為包裝類型后比較
            temp1 = i;
            temp2 = i;
            System.out.println("基本類型轉(zhuǎn)換的對(duì)象:" + (temp1 == temp2));
         
            // 通過(guò)靜態(tài)方法生成一個(gè)實(shí)例
            temp1 = Integer.valueOf(i);
            temp2 = Integer.valueOf(i);
            System.out.println("valueOf產(chǎn)生的對(duì)象:" + (temp1 == temp2));
        }

        運(yùn)行結(jié)果:

        127 128 258

        *********127的相等判斷**********
        new產(chǎn)生的對(duì)象:false
        基本類型轉(zhuǎn)換的對(duì)象:true
        valueOf產(chǎn)生的對(duì)象:true
        *********128的相等判斷**********
        new產(chǎn)生的對(duì)象:false
        基本類型轉(zhuǎn)換的對(duì)象:false
        valueOf產(chǎn)生的對(duì)象:false
        *********258的相等判斷**********
        new產(chǎn)生的對(duì)象:false
        基本類型轉(zhuǎn)換的對(duì)象:false
        valueOf產(chǎn)生的對(duì)象:false

        很不可思議,數(shù)字127的比較結(jié)果與另外兩個(gè)竟然不一樣,原因在哪里?

        • new產(chǎn)生的Integer對(duì)象:new聲明的就是要生成一個(gè)新的對(duì)象,因?yàn)槭莾蓚€(gè)對(duì)象,地址肯定不一樣,所以比較結(jié)果為false毫無(wú)疑問(wèn)。

        • 裝箱生成的對(duì)象:首先說(shuō)明一點(diǎn),自動(dòng)裝箱的動(dòng)作是通過(guò)valueOf方法實(shí)現(xiàn)的,也就是說(shuō)后兩個(gè)算法是相同的,所以他們的結(jié)果一樣,產(chǎn)生上面現(xiàn)象的原因是什么呢?

        我們來(lái)看一下valueOf的源代碼:

        /***
         * 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{
         * 
         * @code -XX:AutoBoxCacheMax=<size>}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) {
        			try {
        				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);
        			} catch (NumberFormatException nfe) {
        // If the property cannot be parsed into an int, ignore it.
        			}
        		}
        		high = h;
         
        		cache = new Integer[(high - low) + 1];
        		int j = low;
        		for (int k = 0; k < cache.length; k++)
        			cache[k] = new Integer(j++);
         
        // range [-128, 127] must be interned (JLS7 5.1.7)
        		assert IntegerCache.high >= 127;
        	}
         
        	private IntegerCache() {
        	}
         
        }
         
        /**
        * 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) {
        	if (i >= IntegerCache.low && i <= IntegerCache.high)
        		return IntegerCache.cache[i + (-IntegerCache.low)];
        	return new Integer(i);
        }

        如果不是-128到127之間的 int 類型轉(zhuǎn)換為 Integer 對(duì)象,則直接返回一個(gè)新的對(duì)象。否則直接從cache數(shù)組中獲得。

        cache是IntegerCache內(nèi)部類的一個(gè)靜態(tài)數(shù)組,容納的是-128到127之間的Integer對(duì)象。通過(guò)valueOf產(chǎn)生包裝對(duì)象時(shí),如果int參數(shù)在-128到127之間,則直接從整型池中獲得對(duì)象,不在該范圍的int類型則通過(guò)new生成包裝對(duì)象。

        這就是整形池,其存在不僅提高了系統(tǒng)性能,同時(shí)節(jié)約了內(nèi)存空間。

        所以在聲明包裝對(duì)象的時(shí)候使用valueOf生成,而不是通過(guò)構(gòu)造函數(shù)來(lái)生成的原因,在判斷對(duì)象是否相等的時(shí)候,最好是用equals方法,避免使用==產(chǎn)生非預(yù)期結(jié)果。

        注意:通過(guò)包裝類的valueOf生成包裝實(shí)例可以顯著提高空間和時(shí)間性能。

        四、優(yōu)先選擇基本數(shù)據(jù)類型

        包裝類型是一個(gè)類,它提供了諸如構(gòu)造方法、類型轉(zhuǎn)換、比較等非常實(shí)用的功能,而且自動(dòng)裝箱(拆箱)更是如虎添翼,但是無(wú)論是從安全性、性能方面來(lái)說(shuō),還是從穩(wěn)定性方面來(lái)說(shuō),基本類型是首選方案。

        public class Ceshi {
            public static void main(String[] args) {
                Ceshi temp=new Ceshi();
                int a=500;
                //分別傳遞int類型和Integer類型
                temp.f(a);
                temp.f(Integer.valueOf(a));
            }
            public void f(long i){
                System.out.println("基本類型參數(shù)的方法被調(diào)用");
            }
            public void f(Long i){
                System.out.println("包裝類型參數(shù)的方法被調(diào)用");
            }
        }

        上面程序的運(yùn)行結(jié)果是:

        基本類型參數(shù)的方法被調(diào)用
        基本類型參數(shù)的方法被調(diào)用

        很詫異是吧!感覺(jué)應(yīng)該輸出不一樣的,第一個(gè)輸出毫無(wú)疑問(wèn),系統(tǒng)進(jìn)行了自動(dòng)的類型轉(zhuǎn)換,這種轉(zhuǎn)換只能往高提升。第二個(gè)為什么沒(méi)有調(diào)用包裝類參數(shù)的函數(shù)呢?

        原因是自動(dòng)裝箱有一個(gè)重要的原則:基本類型可以先加寬,再轉(zhuǎn)成寬類型的包裝類型,但不能直接轉(zhuǎn)變成寬類型的包裝類型。換句話說(shuō)int可以加寬轉(zhuǎn)變成long,然后在轉(zhuǎn)變成Long對(duì)象,但不能直接轉(zhuǎn)變成包裝類型,注意這里指的都是自動(dòng)轉(zhuǎn)換,不是通過(guò)構(gòu)造函數(shù)生成。

        temp.f(Integer.valueOf(a));

        這段代碼的執(zhí)行過(guò)程為

        1、a 通過(guò)valueOf方法包裝成一個(gè)Integer對(duì)象。

        2、由于沒(méi)有f(Integer i)方法,編譯器“聰明”地把 Integer 對(duì)象又轉(zhuǎn)換成 int。

        3、int 自動(dòng)拓寬為 long,編譯結(jié)束。

        注意:基本數(shù)據(jù)類型優(yōu)先考慮。

        public class Ceshi {
            public static void main(String[] args) {
                Ceshi temp=new Ceshi();
                int a=500;
                //分別傳遞int類型和Long類型
                temp.f(a);
                temp.f(Long.valueOf(a));
            }
            public void f(long i){
                System.out.println("基本類型參數(shù)的方法被調(diào)用");
            }
            public void f(Long i){
                System.out.println("包裝類型參數(shù)的方法被調(diào)用");
            }
        }

        這段程序的輸出結(jié)果為:

        基本類型參數(shù)的方法被調(diào)用
        包裝類型參數(shù)的方法被調(diào)用

        到此,相信大家對(duì)“Java包裝類型的詳細(xì)介紹”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

        向AI問(wèn)一下細(xì)節(jié)

        免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

        AI