0 基礎準備
0-1 基本類型
基本類型的值就是一個數字,一個字符或一個布爾值,八種基本數據類型boolean,char,byte,short,int,long,float,double都是基本類型。
0-2 引用類型
引用類型是一個對象類型,它的值是指向內存空間的引用,就是地址,所指向的地址保存着變量的值,主要有:類,接口,數組。
1 Java String
1-1 Java中String不是基本數據類型,而是一種特殊的類,String類內部成員有一個被final修飾的char[],數組類型是引用類型,被final修飾後指的的value指向的字符數組首地址不可變,但是字符數組的內容是可以更改的。
private final char value[];
因爲value數組是被修飾成private final,所以我們在用String類的時候,是無法對其進行修改的。從String類的設計角度來講,由於String類中沒有提供對value本身的修改,這樣來講,String的內容是不可變的。但是我們可以反射出String對象的value屬性,設置爲可以訪問,進而改變value所指向的數組的內容進行修改,但是一般不這樣做,這樣做就破壞了jdk的封裝。
public class StringTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
String str1 = "hzw";
String str2 = "hzw";
String strObj1 = new String("hzw");
String strObj2 = new String("hzw");
System.out.println("****** Testing Object ******");
System.out.println("str1 == str2 ?" + (str1==str2));
System.out.println("strObj1 == strObj2 ?" + (strObj1 == strObj2));
System.out.println("str1 == strObj1 ?" + (str1 == strObj1));
String str3 = "hello world!";
System.out.println("str3 = " + str3);
Field valueFieldOfString = String.class.getDeclaredField("value");
//更改value屬性的訪問權限
valueFieldOfString.setAccessible(true);
//獲取str3對象的value屬性的值
char[] value = (char[])valueFieldOfString.get(str3);
value[5] = '_';
System.out.println("str3 reflect == " + str3);
}
}
上面代碼塊執行結果如下:
分析執行結果:
String類型對象直接賦值和new操作符產生的結果在JVM內存分配過程中是不同的:
String str1 = "hzw";//首先在JVM棧內存中創建一個String類的變量str1,然後通過引用去字符串常量池裏去找有沒有"hzw",如果有"hzw",則直接將引用str1指向"hello",沒有"hello",則在常量池中創建"hello",並將引用指向該"hello" 產生一個引用和一個對象
String str2 = "hzw";//由於上面已經字符串常量池中創建了"hzw"對象,這裏只在棧內存中創建引用str2,指向常量池的字符串對象
String strObj1 = new String("hzw");//首先會在棧內存中創建一個String的類型引用變量strObj1,new()操作會在jvm的heap內存中創建一個新的"hzw"對象,並將strObj1引用指向堆內存的對象,同時檢查String pool常量池中是否有"hzw",如果沒有也產生一個"hzw"對象,如果有則不產生。 只需要產生1個對象及1個引用
String strObj2 = new String("hzw");//因爲new每次都會保證在heap堆內存中產生新的對象,並將棧中的引用指向對應的堆中的地址.
1-2 String的intern的方法
因此a.intern(),b.intern(),newA.intern(),newB.intern()隱含的各自在棧中分配了各自的內存區域,同時都將棧中的引用用全部指向了String pool常量池中的同一塊區域"hello"
intern方法,如果池中包含等於(equal)此對象的字符串,會返回常量池中的字符串,如果不存在,則返回String對象的引用
1-3 length
public int length() {
return value.length;
}
返回字符數組的length
1-4 isEmpty
public boolean isEmpty() {
return value.length == 0;
}
判斷字符數組的長度是否爲0,爲0方法返回true1.5 charAt
/**
* 判斷字符串長度是否爲0或位序是否大於等於字符串長度,如果是,就會發生字符數組越界的異常
* @param index 位序
* @return 返回指定位序的字符
*/
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
1.6 codePointAt
/**
* codePointAt 返回指定位序字符的unicode code
* @param index 位序
* @return 指定位序上的字符的unicode碼
*/
public int codePointAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return Character.codePointAtImpl(value, index, value.length);
}
/**
* 執行字符數組拷貝,從dst數組的dstBegin位置,將當前字符串拷貝到詞,且不執行範圍檢查,底層調用的是System.arraycopy方法
* @param dst
* @param dstBegin
*/
void getChars(char dst[], int dstBegin) {
System.arraycopy(value,0, dst, dstBegin, value.length);
}
1.8 getBytes
/**
* 獲得String對象的指定編碼的字節數組,底層使用的是StringCoding.encode方法
* @param charsetName java支持的字符編碼
* @return 以charsetName指定的編碼,編碼字符串返回的字節數組
* @throws UnsupportedEncodingException 不是Java支持的編碼格式拋出此異常
*/
public byte[] getBytes(String charsetName)
throws UnsupportedEncodingException {
if (charsetName == null) throw new NullPointerException();
return StringCoding.encode(charsetName, value, 0, value.length);
}
1.9 equals,equalsIgnoreCase是忽略大小寫的String內容比較
/**
* equals方法用於比較兩個字符串對象的值是否相同,底層是用的字符等於來實現
* @param anObject 比較對象
* @return 相同返回true
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
1.8 contentEquals
/**
* String對象和StringBuffer對象的內容比較
* @param sb
* @return 相同返回true
*/
public boolean contentEquals(StringBuffer sb) {
return contentEquals((CharSequence)sb);
}
1.9 caseInsensitiveComparator(忽略大小寫的字符串比較器)
private static class CaseInsensitiveComparator
implements Comparator<String>, java.io.Serializable {
// use serialVersionUID from JDK 1.2.2 for interoperability
private static final long serialVersionUID = 8575799808933029326L;
public int compare(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
}
return n1 - n2;
}
/** Replaces the de-serialized object. */
private Object readResolve() { return CASE_INSENSITIVE_ORDER; }
}
1.10 startWith
/**
* 判斷toffset位序開始的子字符串是否以prefix爲前綴,startwith不傳偏移量的實現是startWith(prefix,0);
* @param prefix 前綴
* @param toffset 偏移
* @return 是否以前綴開頭
*/
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
1.11 endsWith
/**
* endsWith的實現是startsWith
* @param suffix 後綴
* @return 從指定的位序判斷是否已suffix結尾
*/
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}