Java
Java開發(fā)中最基本的,在于對數(shù)據(jù)結(jié)構(gòu)、JDK常用類、方法,常用工具包的使用。
讓我們用優(yōu)雅的代碼寫出可靠的程序吧!
Integer
自動裝箱和拆箱。
Integer i = 100;
// 編譯后
Integer i = Integer.valueOf(100);
// JDK源碼:
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);
}
緩存。
// 緩存
Integer i = 100;
Integer j = 100;
System.out.println(i == j); // 執(zhí)行結(jié)果?
// JDK源碼
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// 初始化cache。
}
private IntegerCache() {}
}
Integer非線程安全,例如i + +,i - -,多線程訪問無法保證一致性。
Integer i = new Integer(0);
public void test() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
i++;
System.out.println(i);
try {
Thread.sleep(10 * j);
} catch (Exception e) {
}
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
i--;
System.out.println(i);
try {
Thread.sleep(10 * j);
} catch (Exception e) {
}
}
}
});
t2.start();
}
java.util.concurrent.atomic.AtomicInteger:一個提供原子操作的Integer的類。
內(nèi)部通過volatile實現(xiàn)多線程可見性。使用原生Unsafe類實現(xiàn)同步修改。unsafe.compareAndSwapInt:比較obj的offset處內(nèi)存位置中的值和期望的值,如果相同則更新,并返回TRUE。
// AtomicInteger
public final int get(); // 獲取當(dāng)前的值
public final int getAndSet(int newValue); // 取當(dāng)前的值,并設(shè)置新的值
public final int getAndIncrement(); // 獲取當(dāng)前的值,并自增
public final int getAndDecrement(); // 獲取當(dāng)前的值,并自減
public final int getAndAdd(int delta); // 獲取當(dāng)前的值,并加上預(yù)期的值
AtomicInteger i = new AtomicInteger(0);
public void test() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
i.getAndIncrement();
System.out.println(i);
try {
Thread.sleep(10 * j);
} catch (Exception e) {
}
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
i.getAndDecrement();
System.out.println(i);
try {
Thread.sleep(10 * j);
} catch (Exception e) {
}
}
}
});
t2.start();
}
當(dāng)我們使用VO或Form封裝前端傳入的參數(shù)時,數(shù)值型參數(shù)我們是定義成int還是Integer?
String
一個字符串常量只有一個拷貝,不可變性,內(nèi)容存放常量池。
String str0 = "123";
String str1 = "123";
String str2 = "12" + "3"; // 編譯后"123"
static final String str = "3";
String str3 = "12" + str; // 編譯后"123"
System.out.println(str0 == str1); //結(jié)果?
System.out.println(str1 == str2); //結(jié)果?
String str3 = new String("123");
String str4 = new String("123");
System.out.println(str3 == str4); //結(jié)果?
常用方法
public int compareToIgnoreCase(String str); // 比較(忽略大小寫)
public boolean startsWith(String prefix); // 傳入的是
public boolean matches(String regex); // 正則匹配。
public static String format(String format, Object... args); // 格式化。
public String[] split(String regex); // 注意是正則表達式。
StringBuffer、StringBuilder
StringBuilder線程不安全,StringBuffer線程安全(synchronized)。
定義常量時,使用String。速度快。
StringUtils(Commons, Spring)
>>> org.apache.commons.lang.StringUtils
public static boolean isEmpty(String str); // 判斷是否為空串。
public static boolean isBlank(String str); // 判斷是否為空白串。
public static boolean contains(String str, String searchStr); // 包含
public static String join(Object[] array, String separator); // 字符串拼接。
StringUtils.join(["a", "b", "c"], "-") = "a-b-c"
public static String rightPad(String str, int size, char padChar); // 右邊補齊
StringUtils.rightPad("bat", 5, 'z') = "batzz"
public static boolean isAlpha(String str); // 是否全字母。
public static boolean isAlphanumeric(String str); // 是否只包含字母和數(shù)字。
public static String abbreviate(String str, int maxWidth); // 過長省略。
StringUtils.abbreviate("abcdefg", 6) = "abc..."
>>> org.springframework.util.StringUtils
public static String getFilename(String path); // 獲取文件名,不包含路徑。
public static String getFilenameExtension(String path); // 獲取文件后綴名。
>>> org.apache.commons.lang.builder.ToStringBuilder
public static String reflectionToString(Object object, ToStringStyle style); // 打印對象屬性。
Array
數(shù)組初始化
數(shù)組相關(guān)常用類
// 初始化
String[] arr1 = new String[6];
String[] arr2 = {"a", "b", "c", "d", "e"};
String[] arr3 = new String[]{"a", "b", "c", "d", "e"};
>>> java.util.Arrays
public static void sort(Object[] a); // 排序
public static int binarySearch(Object[] a, Object key); // 二分法查找,前提數(shù)組是有序的。
public static <T> List<T> asList(T... a); // 轉(zhuǎn)成List。
public static String toString(Object[] a); // 拼裝元素,可用作打印。
>>> org.apache.commons.lang.ArrayUtils
public static void reverse(Object array[]); // 元素翻轉(zhuǎn)。
public static Object[] addAll(Object array1[], Object array2[]); // 兩個數(shù)組拼接。
public static boolean contains(Object array[], Object objectToFind); // 包含某個元素。
public static int indexOf(Object array[], Object objectToFind); // 查找元素位置。
public static Object[] remove(Object array[], int index); // 移除第幾個元素。
List
面向接口編程:List
// 調(diào)用方不需要知道List的實現(xiàn)。
List<Object> lst = new ArrayList<Object>();
ArrayList增長機制,源碼。
特性:插入慢(當(dāng)數(shù)據(jù)到一定量時),遍歷速度快。
// JDK源碼
private transient Object[] elementData; // 內(nèi)部維護一個數(shù)組。
public ArrayList() { // 初始化長度10
this(10);
}
public ArrayList(int initialCapacity) { // 指定初始化大小。
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
this.elementData = new Object[initialCapacity];
}
public boolean add(E e) {
ensureCapacity(size + 1); // 擴展長度。
elementData[size++] = e;
return true;
}
public void ensureCapacity(int minCapacity) { // 擴展長度。
modCount++;
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1; // 按1.5倍增長。
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity); // 數(shù)組拷貝,耗性能。
}
}
public E get(int index) { // 直接通過下標返回數(shù)組元素。
RangeCheck(index);
return (E) elementData[index];
}
LinkedList源碼。
特性:插入速度快,遍歷速度慢。

// JDK源碼
private transient Entry<E> header = new Entry<E>(null, null, null); // 起始節(jié)點。
private static class Entry<E> { // 每個節(jié)點元素。
E element;
Entry<E> next;
Entry<E> previous;
...
}
public boolean add(E e) { // 增加元素。
addBefore(e, header);
return true;
}
private Entry<E> addBefore(E e, Entry<E> entry) { // 新增節(jié)點,修改引用即可。
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
public E get(int index) { // 查找元素。
return entry(index).element;
}
private Entry<E> entry(int index) { // 依次遍歷,性能差。
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
Entry<E> e = header;
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
兩者比較:鏈表插入數(shù)據(jù)速度快的說法是相對的,在數(shù)據(jù)量很小的時候,ArrayList的插入速度不僅不比LinkedList慢,而且還快很多。只有當(dāng)數(shù)據(jù)量達到一定量,這個特性才會體現(xiàn)出來,使用時根據(jù)場景選擇。
工具類
>>> java.util.Collections
public static <T extends Comparable<? super T>> void sort(List<T> list); // 排序。
public static <T> void sort(List<T> list, Comparator<? super T> c);
public static <T> boolean addAll(Collection<? super T> c, T... elements); // 添加多個元素。
>>> org.apache.commons.collections.CollectionUtils
public static boolean isEmpty(Collection coll); // 是否為空。
public static Collection removeAll(Collection collection, Collection remove); // 移除。
public static Collection union(Collection a, Collection b); // 并集。
public static Collection intersection(Collection a, Collection b); // 交集。
public static Collection disjunction(Collection a, Collection b); // 交集的補集。
public static Collection subtract(Collection a, Collection b); // 集合相減。
>>> com.google.common.collect.Lists
List<String> lst = new ArrayList<String>(); // 這樣初始化代碼多,麻煩。
lst.add("a");
lst.add("b");
lst.add("c");
Lists.newArrayList("a", "b", "c"); // 簡單明了。
Lists.newArrayListWithCapacity(3);
Lists.newLinkedList();
- CopyOnWriteArrayList??刹l(fā)的ArrayList,讀寫分離。
List<String> lst = new ArrayList<String>();
lst.add("a");
lst.add("b");
lst.add("c");
for (Iterator<String> itor = lst.iterator(); itor.hasNext(); ) { // 有問題,ConcurrentModificationException
if ("b".equals(itor.next())) {
lst.remove(itor.next());
}
}
for (Iterator<String> itor = lst.iterator(); itor.hasNext(); ) { // 使用Iterator.remove
if ("a".equals(itor.next())) {
// lst.remove(itor.next());
itor.remove();
}
}
List<String> lst = new CopyOnWriteArrayList<String>(); // 定義CopyOnWriteArrayList
Map
Map作為最基本的數(shù)據(jù)結(jié)果,在代碼中出現(xiàn)頻率非常大。Java JDK、C++ STL都對其有很好的支持。
HashMap、Hashtable內(nèi)部維護了一個Entry數(shù)組。不同的是Hashtable對操作的方法都使用了synchronized確保線程安全。
HashTable中hash數(shù)組默認大小是11,增加的方式是 old * 2 + 1。HashMap中hash數(shù)組的默認大小是16,而且一定是2的指數(shù)。
加載因子:Hsah表中元素的填滿的程度。加載因子越大,填滿的元素越多,空間利用率高,Hash沖突的機會大。加載因子越小,填滿的元素越少,沖突的機會減小,空間浪費多了。
沖突的機會越大,則查找的成本越高。反之,查找的成本越小。
JDK源碼:
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
public V put(K key, V value) { // 添加
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode()); // 計算Hash對應(yīng)的表索引。
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 替換相同的Key元素。
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i); //
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) { // 添加新元素到鏈表頭。
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length); // 擴容,會重新調(diào)整Hash數(shù)組,代價較高。
}
public V get(Object key) { // 查找。
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode()); // 計算Hash對應(yīng)的表索引。
for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
Map<String, String> map = new HashMap<String, String>(4); // 這樣寫會擴容嗎?
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
map.put("4", "4");
Map<String, String> map = new HashMap<String, String>(4, 1);
TreeMap內(nèi)部通過紅黑樹保證取出來的是排序后的鍵值對。插入、刪除需要維護平衡會犧牲一些效率。但如果要按自然順序或自定義順序遍歷鍵,那么TreeMap會更好。Comparable, Comparator. TreeMap詳解
TreeSet內(nèi)部通過TreeMap實現(xiàn)。TreeSet元素實現(xiàn)Comparable接口或自定義比較器。
HashMap效率高線程不安全,Hashtable線程安全但效率不高。ConcurrentHashMap折中辦法。
ConcurrentHashMap結(jié)構(gòu)
Guava增強的集合
Multiset:可重復(fù)的Set。HashMultiset,內(nèi)部HashMap實現(xiàn)。
List<Integer> lst = Lists.newArrayList(1, 2, 3, 4, 2, 3);
// 一般寫法
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer i : lst) {
Integer count = map.get(i);
if (count == null) {
map.put(i, 1);
} else {
map.put(i, count + 1);
}
}
System.out.println(map.get(2)); // 結(jié)果:2
// Guava寫法
Multiset<Integer> set = HashMultiset.create();
set.addAll(lst);
int count = set.count(2); // 結(jié)果:2
System.out.println(Arrays.toString(set.toArray())); // 結(jié)果為排序后的:[1, 2, 2, 3, 3, 4]
BiMap:一個雙向映射的Map,強制Value唯一性。
BiMap<String, String> map = HashBiMap.create();
map.put("M", "1");
map.put("F", "2");
map.put("F", "1"); // 報錯:value already present.
map.forcePut("F", "3"); // 忽略校驗Put
System.out.println(map.get("F")); // 3
System.out.println(map.inverse().get("1")); // M
Multimap:一個Key對應(yīng)多個Value,比Map<String, List<Integer>>更優(yōu)雅的寫法。
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>(); // 一般寫法。
Multimap<String, Integer> multimap = ArrayListMultimap.create(); // Guava寫法。
for (int i = 0; i < 10; i++) {
multimap.put((i % 2 == 0) ? "m" : "n", i);
}
System.out.println("size: " + multimap.size()); // size: 10
System.out.println("keys: " + multimap.keys()); // keys: [n x 5, m x 5]
System.out.println(multimap.get("m").toString()); // [0, 2, 4, 6, 8]
System.out.println(multimap.get("n").toString()); // [1, 3, 5, 7, 9]
System.out.println(multimap.containsValue(10)); // FALSE,是否存在指定的Value
反射
Java反射機制主要提供了以下功能:在運行時判斷任意一個對象所屬的類;在運行時構(gòu)造任意一個類的對象;在運行時判斷任意一個類所具有的成員變量和方法;在運行時調(diào)用任意一個對象的方法;生成動態(tài)代理。
class User {
private String name;
public Integer age;
private String getName() {
return name;
}
public Integer getAge() {
return age;
}
public static void Test() {
System.out.println("Test");
}
}
Class clazz = User.class;
User user = (User)clazz.newInstance();
user.age = 10;
clazz.getSimpleName(); // User
clazz.getName(); // 包名+User
clazz.getPackage();
Field[] fields = clazz.getDeclaredFields(); // 聲明的所有屬性。[name, age]
Field[] fields = clazz.getFields(); // 可訪問的。[age]
Object value = clazz.getField("age").get(user); // 10
Method[] methods = clazz.getDeclaredMethods(); // 聲明的所有方法。[getAge, getName]
Method[] methods = clazz.getMethods(); // 可訪問的。[getAge, equals, toString, hashCode...]
Object result = clazz.getMethod("getAge").invoke(user); // 10
clazz.getMethod("Test").invoke(null); // 調(diào)用靜態(tài)方法
PropertyDescriptor pd = new PropertyDescriptor("name", clazz);
pd.getReadMethod().getName();
Bean屬性復(fù)制
Commons BeanUtils PropertyUtils, Spring BeanUtils
Commons的BeanUtils.copyProperties()會對類型轉(zhuǎn)換,如:Integer默認0。Spring的BeanUtils.copyProperties()不會。
Commons的PropertyUtils.copyProperties()對屬性的類型強校驗。
類型轉(zhuǎn)換:org.apache.commons.beanutils.converters
public class User1 {
private Integer age;
private Integer name;
// getter setter ...
}
public class User2 {
private Integer age;
private String name;
// getter setter ...
}
User1 u1 = new User1();
User2 u2 = new User2();
org.apache.commons.beanutils.BeanUtils.copyProperties(u2, u1); // u2.getAge() == 0
org.springframework.beans.BeanUtils.copyProperties(u1, u2); // u2.getAge() == null
org.apache.commons.beanutils.PropertyUtils.copyProperties(u2, u1); // u2.getAge() == null
User1 u1 = new User1();
u1.setName(123);
User2 u2 = new User2();
org.apache.commons.beanutils.BeanUtils.copyProperties(u2, u1); // u2.getName() == "123"
// Converting 'Integer' value '123' to type 'String'
org.springframework.beans.BeanUtils.copyProperties(u1, u2); // u2.getName() == null
org.apache.commons.beanutils.PropertyUtils.copyProperties(u2, u1); // u2.getName() 報錯
