從頭學Java — 基本資料型別和String
1.基本資料型別
在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 |
long | 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); // false
Integer c = Integer.valueOf(123);
Integer d = Integer.valueOf(123);
System.out.println(z == k); // true
編譯器會在自動裝箱過程呼叫 valueOf()
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); // false
String 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中String物件的雜湊碼被頻繁地使用, 比如在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
所修飾。