JDK的3個(gè)bug
作者:孤獨(dú)的探索號(hào)
來源地址:my.oschina.net/tommylemon/blog/2967187
1.Annotation引用非空enum數(shù)組返回空數(shù)組
首次發(fā)現(xiàn)時(shí)的環(huán)境:JDK 1.8
首次發(fā)現(xiàn)所在項(xiàng)目:APIJSON
測試用例:
public enum RequestRole {
/**未登錄,不明身份的用戶
*/
UNKNOWN,
/**已登錄的用戶
*/
LOGIN,
/**聯(lián)系人,必須已登錄
*/
CONTACT,
/**圈子成員(CONTACT + OWNER),必須已登錄
*/
CIRCLE,
/**擁有者,必須已登錄
*/
OWNER,
/**管理員,必須已登錄
*/
ADMIN;
//似乎不管怎么做,外部引用后都是空值。并且如果在注解內(nèi)的位置不是最前的,還會(huì)導(dǎo)致被注解的類在其它類中import報(bào)錯(cuò)。
//雖然直接打印顯示正常,但被@MethodAccess內(nèi)RequestRole[] GET()等方法引用后獲取的是空值
public static final RequestRole[] ALL = {RequestRole.UNKNOWN};//values();//所有
public static final RequestRole[] HIGHS;//高級(jí)
static {
HIGHS = new RequestRole[] {OWNER, ADMIN};
}
public static final String[] NAMES = {
UNKNOWN.name(), LOGIN.name(), CONTACT.name(), CIRCLE.name(), OWNER.name(), ADMIN.name()
};
}
@MethodAccess(
GETS = RequestRole.ALL,
HEADS = RequestRole.HIGHS
)
public class Verify {
}
public class DemoVerifier {
// <TableName, <METHOD, allowRoles>>
// <User, <GET, [OWNER, ADMIN]>>
public static final Map<String, Map<RequestMethod, RequestRole[]>> ACCESS_MAP;
static { //注冊(cè)權(quán)限
ACCESS_MAP = new HashMap<String, Map<RequestMethod, RequestRole[]>>();
ACCESS_MAP.put(Verify.class.getSimpleName(), getAccessMap(Verify.class.getAnnotation(MethodAccess.class)));
}
public static HashMap<RequestMethod, RequestRole[]> getAccessMap(MethodAccess access) {
if (access == null) {
return null;
}
HashMap<RequestMethod, RequestRole[]> map = new HashMap<>();
map.put(GET, access.GET());
map.put(HEAD, access.HEAD());
map.put(GETS, access.GETS());
map.put(HEADS, access.HEADS());
map.put(POST, access.POST());
map.put(PUT, access.PUT());
map.put(DELETE, access.DELETE());
return map;
}
}解決方案:
不抽象數(shù)組常量ALL,HIGHTS等,而是在每個(gè)用到的地方硬編碼寫死具體的值。
2.ArrayList可通過構(gòu)造函數(shù)傳入非指定泛型的List并在get時(shí)出錯(cuò)
首次發(fā)現(xiàn)時(shí)的環(huán)境:JDK 1.7
首次發(fā)現(xiàn)所在項(xiàng)目:APIJSON
測試用例:
JSONArray arr = new JSONArray(); //com.alibaba.fastjson.JSONArray
arr.add("s");
List<Long> list = new ArrayList<>(arr);
list.get(0); //Exception cannot cast String to Long解決方案:
1.改用 Open JDK8
2.升級(jí) JDK
注:后面多次測試,已無法復(fù)現(xiàn)。
3.基本類型在三元表達(dá)式內(nèi)可賦值為null,編譯通過但運(yùn)行出錯(cuò)
首次發(fā)現(xiàn)時(shí)的環(huán)境:JDK 1.7
測試用例:
int i = true ? null : 0; //Exception in thread "main" java.lang.NullPointerException首次發(fā)現(xiàn)所在項(xiàng)目:ZBLibrary
解決方案:
在給基礎(chǔ)類型用3元表達(dá)式賦值時(shí),null 先轉(zhuǎn)為基礎(chǔ)類型的默認(rèn)值。
最后再提2個(gè)不是bug,但容易引發(fā)編程bug的問題:
1.局部變量和同名的全局變量能在一個(gè)方法內(nèi),編譯通過,運(yùn)行也正常。
public class Test {
int val;
@Override
public String toString() {
val = 1;
String val = "";
return super.toString();
}
}如果兩個(gè)變量中間隔了比較長的其它代碼,很可能會(huì)導(dǎo)致開發(fā)人員將兩者混淆,導(dǎo)致邏輯認(rèn)知錯(cuò)誤,從而寫出或改出有問題的代碼。
解決方案:
命名局部變量前先搜素,確保沒有已聲明的同名全局變量。
2. (非 JDK bug)Gson 通過 TypeToken 轉(zhuǎn)換 List<T> 能寫入不屬于 T 類型的數(shù)據(jù),get 出來賦值給 T 類型的變量/常量報(bào)錯(cuò)。
String json = "[1, '2', 'a']";
Type type = new TypeToken<Integer>(){}.getType();
Gson gson = new Gson();
List<Integer> list = gson.fromJson(json, type);
Integer i = list == null || list.isEmpty() ? null : list.get(1); //Exception cannot cast String to Integer解決方案:
1.手動(dòng)檢查列表內(nèi)數(shù)據(jù)都符合泛型 T
2.改用 fastjson 等其它能靜態(tài)檢查類型的庫。
