在Java中由8種基本資料類型,四個整數型別(byte,short,int,long),兩個小數型別(float,double),一個字元型別(char),一個布林型別(boolean)
型別 |
字節 | 取值範圍 |
byte |
#1 |
-2^7 ~ 2^7 - 1 |
short |
2 |
-2^15 ~ 2^15 - 1 |
##int | 4 | -2^31 ~ 2^31 - 1 |
長 | 8 | -2^63 ~ 2^63 - 1 |
(一個位元組表示一個8位元二進位)float佔32位,double佔64位,char佔16,boolean佔1位元
因為Java是物件導向的一門語言,所以這八個基本資料型別都有對應的包裝類別:Byte,Short,Integer,Long,Float,Double,Character,Boolean。這八個基本類型與其對應的包裝類型之間的賦值使用自動裝箱與拆箱完成。
Integer a = 1; // 基本数据类型int自动装箱为Integer包装类(实际上在编译时会调用Integer .valueOf方法来装箱)int b = a; // 自动拆箱(实际上会在编译调用intValue)
登入後複製
那麼new Integer(123)
與 Integer.valueOf(123)
有什麼不同?
new Integer(123)
每次都會建立一個對象,而Integer.valueOf(123)
使用到了快取對象,因此多次使用Integer .valueOf(123)
時,只會取得同一個物件的參考。
Integer a = new Integer(123);Integer b = new Integer(123);
System.out.println(x == y); // falseInteger c = Integer.valueOf(123);Integer d = Integer.valueOf(123);
System.out.println(z == k); // true
登入後複製
編譯器會在自動裝箱程序呼叫 valueOf()
方法,因此多個 Integer 實例使用自動裝箱來建立且值相同,那麼就會引用相同的物件。
Integer m = 123;Integer n = 123;
System.out.println(m == n); // true
登入後複製
根據查看Integer類別的原始碼發現,使用valueOf()
時,先判斷值是否在快取池中,如果在的話就直接傳回快取池的內容。
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);
}
登入後複製
但看下面的程式碼
Integer i1 = 128; Integer i2 = 128;
System.out.println(i1 == i2); //false
登入後複製
發現回傳的是false,這是因為在Integer中,快取池的範圍為: -128 ~ 127
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() {}
}
登入後複製
2 .String類型
除了上面的八個基本資料類型,String類別也是在寫入程式中最常用的一種類型。
String類別被宣告為final,所以它不可以被繼承。其內部使用 char 數組存儲數據,該數組被聲明為 final,這意味著 value 數組初始化之後就不能再引用其它數組,並且 String 內部沒有改變 value 數組的方法,因此可以保證 String 不可變。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
登入後複製
String.intern()
使用 String.intern() 可以保證相同內容的字串變數引用相同的記憶體物件。
下面範例中,s1 和s2 採用new String() 的方式新建了兩個不同對象,而s3 是透過s1.intern() 方法取得一個物件引用,這個方法先把s1 引用的對象放到String Pool(字串常數池)中,然後回傳這個物件參考。因此 s3 和 s1 所引用的是同一個字串常數池的物件。
String s1 = new String("aaa");String s2 = new String("aaa");
System.out.println(s1 == s2); // falseString s3 = s1.intern();
System.out.println(s1.intern() == s3); // true
登入後複製
如果是採用 “bbb” 這種使用雙引號的形式來建立字串實例,會自動地將新建的物件放入 String Pool 中。
String s4 = "bbb";String s5 = "bbb";
System.out.println(s4 == s5); // true
登入後複製
在 Java 7 之前,字串常數池被放在執行時間常數池中,它屬於永久代。而在 Java 7,字串常數池被放在堆中。這是因為永久代的空間有限,在大量使用字串的場景下會導致 OutOfMemoryError 錯誤。
那麼知道String不可變性,那這樣設計有什麼好處呢?
1.字串池的需求
字串常數池(String intern pool) 是Java堆記憶體中一個特殊的儲存區域, 當建立一個String對象時。假如此字串值已經存在於常數池中,則不會建立一個新的對象,而是引用已經存在的對象。
2.允許String物件快取HashCode
#Java物件的雜湊碼被頻繁使用, 例如在hashMap 等容器中。
字串不變性保證了hash碼的唯一性,因此可以放心地進行快取。這也是一種效能最佳化手段,意味著不必每次都去計算新的雜湊碼
3.安全性
String被許多的Java類別(函式庫)用來當做參數,例如網路連線位址URL,檔案路徑path,還有反射機制所需的String參數等,假若String不是固定不變的,將會造成各種安全隱患。
boolean connect(string s){
if (!isSecure(s)) {
throw new SecurityException();
}
// 如果在其他地方可以修改String,那么此处就会引起各种预料不到的问题/错误
causeProblem(s);
}
登入後複製
4.執行緒安全
String 不可變性天生具備執行緒安全,可以在多個執行緒中安全地使用。
這篇文章有詳細的介紹。
String是不可變,那麼有沒有可變的字串呢?
在Java中提供了StringBuffer 和 StringBuilder,是可變的。在String中,定義的是一個final字元數組,所以不可變,而StringBuffer和StringBuilder因為繼承了AbstractStringBuilder,根據來源碼可看出是一個可變的字元數組。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence{
登入後複製
AbstractStringBuilder(int capacity) {
value = new char[capacity];
} /**
* The value is used for character storage.
*/
char[] value;
登入後複製
根據原始碼還可以知道,StringBuffer是線程安全的,其中的方法都被synchronized
所修飾。
相關文章:
JavaScript基礎知識之資料型別_基礎知識
Java中基本資料型別與流
相關影片:
資料類型的概述和分類-JAVA 初級入門影片教學