一、Integer是int的包裝類,int則是java的一種基本數據類型 二、Integer變量必須實例化後才能使用,而int變量不須要 三、Integer實際是對象的引用,當new一個Integer時,其實是生成一個指針指向此對象;而int則是直接存儲數據值 四、Integer的默認值是null,int的默認值是0 延伸: 關於Integer和int的比較 一、因爲Integer變量其實是對一個Integer對象的引用,因此兩個經過new生成的Integer變量永遠是不相等的(由於new生成的是兩個對象,其內存地址不一樣)。 Integer i = new Integer(100); Integer j = new Integer(100); System.out.print(i == j); //false 二、Integer變量和int變量比較時,只要兩個變量的值是向等的,則結果爲true(由於包裝類Integer和基本數據類型int比較時,java會自動拆包裝爲int,而後進行比較,實際上就變爲兩個int變量的比較) Integer i = new Integer(100); int j = 100; System.out.print(i == j); //true 三、非new生成的Integer變量和new Integer()生成的變量比較時,結果爲false。(由於非new生成的Integer變量指向的是java常量池中的對象,而new Integer()生成的變量指向堆中新建的對象,二者在內存中的地址不一樣) Integer i = new Integer(100); Integer j = 100; System.out.print(i == j); //false 四、對於兩個非new生成的Integer對象,進行比較時,若是兩個變量的值在區間-128到127之間,則比較結果爲true,若是兩個變量的值不在此區間,則比較結果爲false Integer i = 100; Integer j = 100; System.out.print(i == j); //true Integer i = 128; Integer j = 128; System.out.print(i == j); //false 對於第4條的緣由: java在編譯Integer i = 100 ;時,會翻譯成爲Integer i = Integer.valueOf(100);,而java API中對Integer類型的valueOf的定義以下: 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); } java對於-128到127之間的數,會進行緩存,Integer i = 127時,會將127進行緩存,下次再寫Integer j = 127時,就會直接從緩存中取,就不會new了
在 Java 5 中,爲 Integer 的操做引入了一個新的特性,用來節省內存和提升性能。整型對象在內部實現中經過使用相同的對象引用實現了緩存和重用。java
這種 Integer 緩存策略僅在自動裝箱(autoboxing)的時候有用,使用構造器建立的 Integer 對象不能被緩存。編程
在建立新的 Integer 對象以前會先在 IntegerCache.cache (是個Integer類型的數組)中查找。有一個專門的 Java 類來負責 Integer 的緩存。數組
這個類是用來實現緩存支持,並支持 -128 到 127 之間的自動裝箱過程。最大值 127 能夠經過 JVM 的啓動參數 -XX:AutoBoxCacheMax=size 修改。 緩存經過一個 for 循環實現。從小到大的建立儘量多的整數並存儲在一個名爲 cache 的整數數組中。這個緩存會在 Integer 類第一次被使用的時候被初始化出來。之後,就可使用緩存中包含的實例對象,而不是建立一個新的實例(在自動裝箱的狀況下)。緩存
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } 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() {} }
這種緩存行爲不只適用於Integer對象。咱們針對全部整數類型的類都有相似的緩存機制。安全
裝箱就是 自動將基本數據類型轉換爲包裝器類型;拆箱就是 自動將包裝器類型轉換爲基本數據類型。數據結構
//拆箱 int yc = 5; //裝箱 Integer yc = 5; 3.2 裝箱和拆箱是如何實現的 以Interger類爲例,下面看一段代碼來了解裝箱和拆箱的實現 public class Main { public static void main(String[] args) { Integer y = 10; int c = i; } }
而後來編譯一下,看下所示:ide
建議避免無心中的裝箱、拆箱行爲,尤爲是在性能敏感的場合,建立 10 萬個 Java 對象和 10 萬個整數的開銷可不是一個數量級的,不論是內存使用仍是處理速度,光是對象頭的空間佔用就已是數量級的差距了。佈局
Java自帶的線程安全的基本類型包括: AtomicInteger, AtomicLong, AtomicBoolean, AtomicIntegerArray,AtomicLongArray等性能
200個線程,每一個線程對共享變量 count 進行 50 次 ++ 操做優化
int 做爲基本類型,直接存儲在內存棧,且對其進行+,-操做以及++,–操做都不是原子操做,都有可能被其餘線程搶斷,因此不是線程安全。int 用於單線程變量存取,開銷小,速度快
int count = 0; private void startThread() { for (int i = 0;i < 200; i++){ new Thread(new Runnable() { @Override public void run() { for (int k = 0; k < 50; k++){ count++; } } }).start(); } // 休眠10秒,以確保線程都已啓動 try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally { Log.e("打印日誌----",count+""); } } //指望輸出10000,最後輸出的是9818 //注意:打印日誌----: 9818
AtomicInteger類中有有一個變量valueOffset,用來描述AtomicInteger類中value的內存位置 。
當須要變量的值改變的時候,先經過get()獲得valueOffset位置的值,也即當前value的值.給該值進行增長,並賦給next
compareAndSet()比較以前取到的value的值當前有沒有改變,若沒有改變的話,就將next的值賦給value,假若和以前的值相比的話發生變化的話,則從新一次循環,直到存取成功,經過這樣的方式可以保證該變量是線程安全的
value使用了volatile關鍵字,使得多個線程能夠共享變量,使用volatile將使得VM優化失去做用,在線程數特別大時,效率會較低。
private static AtomicInteger atomicInteger = new AtomicInteger(1); static Integer count1 = Integer.valueOf(0); private void startThread1() { for (int i = 0;i < 200; i++){ new Thread(new Runnable() { @Override public void run() { for (int k = 0; k < 50; k++){ // getAndIncrement: 先得到值,再自增1,返回值爲自增前的值 count1 = atomicInteger.getAndIncrement(); } } }).start(); } // 休眠10秒,以確保線程都已啓動 try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); }finally { Log.e("打印日誌----",count1+""); } } //指望輸出10000,最後輸出的是10000 //注意:打印日誌----: 10000 //AtomicInteger使用了volatile關鍵字進行修飾,使得該類能夠知足線程安全。 private volatile int value; /** * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ public AtomicInteger(int initialValue) { value = initialValue; }
Java 的泛型某種程度上能夠算做僞泛型,它徹底是一種編譯期的技巧,Java 編譯期會自動將類型轉換爲對應的特定類型,這就決定了使用泛型,必須保證相應類型能夠轉換爲Object。
Java 的對象都是引用類型,若是是一個原始數據類型數組,它在內存裏是一段連續的內存,而對象數組則否則,數據存儲的是引用,對象每每是分散地存儲在堆的不一樣位置。這種設計雖然帶來了極大靈活性,可是也致使了數據操做的低效,尤爲是沒法充分利用現代 CPU 緩存機制。
Java 爲對象內建了各類多態、線程安全等方面的支持,但這不是全部場合的需求,尤爲是數據處理重要性日益提升,更加高密度的值類型是很是現實的需求。
對象在內存中存儲的佈局能夠分爲3塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。
HotSpot虛擬機的對象頭包括兩部分信息,第一部分用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等,這部分數據的長度在32位和64位的虛擬機(未開啓壓縮指針)中分別爲32bit和64bit,官方稱它爲"Mark Word"。
對象頭的另一部分是類型指針,即對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例。並非全部的虛擬機實現都必須在對象數據上保留類型指針,換句話說,查找對象的元數據信息並不必定要通過對象自己,這點將在2.3.3節討論。另外,若是對象是一個Java數組,那在對象頭中還必須有一塊用於記錄數組長度的數據,由於虛擬機能夠經過普通Java對象的元數據信息肯定Java對象的大小,可是從數組的元數據中卻沒法肯定數組的大小。
獲取一個JAVA對象的大小,能夠將一個對象進行序列化爲二進制的Byte,即可以查看大小
//獲取一個JAVA對象的大小,能夠將一個對象進行序列化爲二進制的Byte,即可以查看大小 Integer value = 10; ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos ; try { oos = new ObjectOutputStream(bos); oos.writeObject(value); oos.close(); } catch (IOException e) { e.printStackTrace(); } // 讀出當前對象的二進制流信息 Log.e("打印日誌----",bos.size()+""); //打印日誌----: 81