本文發(fā)表于KuTear's Blog,轉(zhuǎn)載請注明
最簡單的例子說起
數(shù)組
粗糙數(shù)組
數(shù)組中構(gòu)成矩陣的每個向量都可以具有任意的長度
int[][] array = new int[][]{
new int[]{1, 2, 3},
new int[]{1, 2}
};
System.out.println(Arrays.deepToString(array));//輸出 [[1, 2, 3], [1, 2]]
System.out.println(array[1][2]);// java.lang.ArrayIndexOutOfBoundsException: 2
基礎(chǔ)
- 新生成一個數(shù)組晤斩,其中所有引用被自動初始化為null漾唉,基本類型被初始化為0(boolean是false)
Arrays
方法 | 說明 |
---|---|
equals() | 比較兩個數(shù)字是否相等 |
deepEquals() | 用于多維數(shù)組比較 |
fill() | 填充數(shù)組 |
sort() | 數(shù)組排序 |
binarySearch() | 在已經(jīng)排序的數(shù)組中查找元素 |
toString | 產(chǎn)生數(shù)組的String表示 |
hashCode() | 產(chǎn)生數(shù)組的散列碼 |
asList() | 接受任意的序列或數(shù)組作為其參數(shù)父虑,并轉(zhuǎn)換為List容器 |
數(shù)組與泛型
通常娃循,數(shù)組與泛型不能很好結(jié)合丑蛤,你不能實例化具有參數(shù)化類型的數(shù)組诸迟。
Peel<Banana>[] peels = new Peel<>[10]
不合法壹粟,擦除會移除參數(shù)類型信息,而數(shù)組必須知道它們所持有的確切類型呻此,以強制保證類型安全轮纫。
類型擦除
Class c1 = new ArrayList<Integer>().getClass();
Class c2 = new ArrayList<String>().getClass();
System.out.println(c1 == c2); //Output: true
參數(shù)化方法
private <T> Class get(T data){
return data.getClass();
}
參數(shù)化類
public class Container<K, V> {
private K key;
private V value;
public Container(K k, V v) {
key = k;
value = v;
}
}
泛型自動打包拆包
HashMap<String,Integer> map = new HashMap<>();
map.put("one",1); // int ---> Integer
int value = map.get("one"); // Integer ---> int
通過反編譯查看實現(xiàn)
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/HashMap
3: dup
4: invokespecial #3 // Method java/util/HashMap."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String one
11: iconst_1
12: invokestatic #5 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;[打包]
15: invokevirtual #6 // Method java/util/HashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/
lang/Object;
18: pop
19: aload_1
20: ldc #4 // String one
22: invokevirtual #7 // Method java/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
25: checkcast #8 // class java/lang/Integer
28: invokevirtual #9 // Method java/lang/Integer.intValue:()I [拆包]
31: istore_2
32: return
}
字符串
String對象不可變
任意的包含改變String的方法其實都是重新生成對象
//String.java
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
"+"重載原理
String result1 = "AAA" + "BBB";
String result2 = "AAABBB";
上面的result1==result2
.
通過反編譯可得
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String AAABBB
2: astore_1
3: ldc #2 // String AAABBB
5: astore_2
6: return
java編譯器對它進(jìn)行了優(yōu)化.
public static void main(String[] args) {
String result1 = new String("AAA");
String result2 = result1+"BBB";
}
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String AAA
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: new #5 // class java/lang/StringBuilder
13: dup
14: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
17: aload_1
18: invokevirtual #7 // Method java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: ldc #8 // String BBB
23: invokevirtual #7 // Method java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_2
30: return
}
由上可以看出"+"的實現(xiàn)是利用StringBuilder
的append()
實現(xiàn)
for(int i = 0; i < fields.length; i++) {
result += fields[i];
}
根據(jù)上面的原理知道這里的+=
會生成臨時StringBuilder
N多個,會造成不必要的消耗,這時應(yīng)主動用StringBuilder
StringBuilder result = new StringBuilder();
for(int i = 0; i < fields.length; i++) {
result.append(fields[i]);
}
return result.toString();
StringBuilder & StringBuffer
StringBuffer是線程安全的,速度慢些焚鲜;StringBuilder是線程不安全的掌唾,但是速度快些;
性能: StringBuilder > StringBuffer > String
字符格式化
System.out.println("%d %f", x, y);
System.out.printf("%d %f", x, y);
java.util.Formatter
String.format("%d %f", x, y);
正則表達(dá)式
符號 | 說明 | 符號 | 說明 | 符號 | 說明 |
---|---|---|---|---|---|
^ | 一行的起始 | \W | 非字母數(shù)字 | \w | 字母數(shù)字 |
$ | 一行的末尾 | \D | 非數(shù)字 | \d | 數(shù)字 |
+ | >=1 | ? | 0或1次 | * | 任意次 |
. | 非換行任意字符 | [^x] | 非X字符的任意字符 | \S | 任意非空白符字符 |
補充
Java中的String內(nèi)存分析
棧(Stack) :存放基本類型的變量數(shù)據(jù)和對象的引用忿磅,但對象本身不存放在棧中糯彬,而是存放在堆(new 出來的對象)或者常量池中(字符串常量對象存放在常量池中)
堆(heap):存放所有new出來的對象和數(shù)組。
常量池(constant pool):在堆中分配出來的一塊存儲區(qū)域葱她,存放儲顯式的String常量和基本類型常量(float撩扒、int等)。另外吨些,可以存儲不經(jīng)常改變的東西(public static final)搓谆。常量池中的數(shù)據(jù)可以共享。
靜態(tài)存儲:存放靜態(tài)成員(static定義的)锤灿。
Java中字符串對象創(chuàng)建有兩種形式挽拔,一種為字面量形式,如String str = "droid"
;但校,另一種就是使用new這種標(biāo)準(zhǔn)的構(gòu)造對象的方法螃诅,如String str = new String("droid")
.
當(dāng)代碼中出現(xiàn)字面量形式創(chuàng)建字符串對象時,JVM首先會對這個字面量進(jìn)行檢查,如果字符串常量池中存在相同內(nèi)容的字符串對象的引用术裸,則將這個引用返回倘是,否則新的字符串對象被創(chuàng)建,然后將這個引用放入字符串常量池袭艺,并返回該引用搀崭。
當(dāng)我們使用了new來構(gòu)造字符串對象的時候,不管字符串常量池中有沒有相同內(nèi)容的對象的引用猾编,新的字符串對象都會創(chuàng)建瘤睹。
String str1 = "droid";
String str2 = "droid";
String str3 = new String("droid");
System.out.println(str1 == str2); //true
System.out.println(str1 == str3);//false
使用new
創(chuàng)建對象,會在堆中創(chuàng)建對象(真正的內(nèi)存占用),同時會在棧中創(chuàng)建指向堆中該對象的首地址的一個引用(相當(dāng)于指針,同樣占一小部分內(nèi)存),當(dāng)程序運行到該變量的作用域之后,棧中的引用會被置為空,但是此時堆中的內(nèi)存并沒有被即可釋放,會根據(jù)程序所占用的內(nèi)存Java GC在合適的時候回收堆中的內(nèi)存.
對于String
的比較問題,我們需要記住
String a = new String("A");
String b = new String("A");
對于new
出來的對象,==
操作是不相等的.
String a = "A";
String b = a + "B";
String c = "A" + "B";
通過反編譯,發(fā)現(xiàn)b
這里其實利用StringBuilder
,最后用StringBuilder#toString()
方法生成String
,是經(jīng)過new
出來的,而前面說過,new
出來的對象是棧中的引用指向堆中的地址,而字面量形式是棧中的引用指向常量區(qū)的地址.當(dāng)然是不相等的.
而c
經(jīng)編譯器優(yōu)化等同于String c = "AB"
.
//StringBuilder.java
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}