java 是一個完全面向對象程式設計語言,但是為了程式設計的友善還是引入了基本資料類型,為了能夠将這些基本資料類型當成對象操作,Java 為每一個基本資料類型都引入了對應的包裝類型(wrapper class),int 的包裝類就是Integer,從 Java 5 開始引入了自動裝箱/拆箱機制,使得二者可以互相轉換。
java 為每個原始類型提供了包裝類型:
● 基本資料類型: boolean,char,byte,short,int,long,float,double
● 包裝類型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
由于Integer變量實際上是對一個Integer對象的引用,是以兩個通過new生成的Integer變量永遠是不相等的(因為new生成的是兩個對象,其記憶體位址不同)
Integer變量和int變量比較時,隻要兩個變量的值相等,則結果為true(因為包裝類Integer和基本資料類型int比較時,java會自動拆包裝為int,然後進行比較,實際上就變為兩個int變量的比較)
非new生成的Integer變量和new Integer()生成的變量比較時,結果為false。
原因:
(1)當變量值在-128~127之間時,非new生成的Integer變量指向的是java常量池中的對象,而new Integer()生成的變量指向堆中建立的對象,兩者在記憶體中的位址不同;
(2)當變量值在-128~127之間時,非new生成Integer變量時,java API中最終會按照new Integer(i)進行處理,最終兩個Interger的記憶體位址同樣是不相同的
對于兩個非new生成的Integer對象,進行比較時,如果兩個變量的值在區間-128到127之間,則比較結果為true,如果兩個變量的值不在此區間,則比較結果為false
自動裝箱與拆箱實際上算是一種“文法糖”。所謂文法糖,可簡單了解為Java平台為我們自動進行了一些轉換,保證不同的寫法在運作時等價。是以它們是發生在編譯階段的,也就是說生成的位元組碼是一緻的。
對于整數,javac替我們自動把裝箱轉換為Integer.valueOf(),把拆箱替換為Integer.intValue()。可以通過将代碼編譯後,再反編譯加以證明。
原則上,建議避免無意中的裝箱、拆箱行為,尤其是在性能敏感的場合,建立10萬個Java對象和10萬個整數的開銷可不是一個數量級的。當然請注意,隻有确定你現在所處的場合是性能敏感的,才需要考慮上述問題。畢竟大多數的代碼還是以開發效率為優先的。
順帶說一下,在32位環境下,Integer對象占用記憶體16位元組;在64位環境下則更大。
就像上一講談到的String,Java也為Integer提供了值緩存。
上述代碼中第一行與第二行的寫法取值使用了值緩存,而第三行的寫法則沒有利用值緩存。結合剛剛講到的自動裝箱、拆箱的知識,第一行代碼用到的自動裝箱,等價于調用了Integer.valueOf()。
不僅僅是Integer,Java也為其它包裝類提供了值緩存機制,包括Boolean、Byte、Short和Character等。但與String不同的是,預設都隻會将絕對值較小的值放入緩存。以Integer為例,預設情況下隻會緩存-128到127之間的值。當然如果你願意也可以通過以下JVM參數進行設定:
-XX:AutoBoxCacheMax=N
這個問題的正确答案是“線程不安全”,是否有些出乎你的意料?
原始資料類型的變量,需要使用并發相關手段才能保證線程安全。特别的是,部分比較寬的資料類型,比如long、float、double,甚至不能保證更新操作的原子性,可能出現程式讀取到隻更新了一半資料位的數值!關于這個話題會在這個專欄後面的并發主題詳細介紹。如果有線程安全的計算需要,建議考慮使用類似AtomicInteger、AtomicLong這樣線程安全的類。