发现 JDK 的 3 个 bug

2018-12-04 10:22:34 +08:00
 TommyLemon

1.Annotation 引用非空 enum 数组返回空数组

首次发现时的环境:JDK 1.8

首次发现所在项目:APIJSON

测试用例:

public enum RequestRole {

	/**未登录,不明身份的用户
	 */
	UNKNOWN,

	/**已登录的用户
	 */
	LOGIN,

	/**联系人,必须已登录
	 */
	CONTACT,

	/**圈子成员(CONTACT + OWNER),必须已登录
	 */
	CIRCLE,

	/**拥有者,必须已登录
	 */
	OWNER,

	/**管理员,必须已登录
	 */
	ADMIN;

	//似乎不管怎么做,外部引用后都是空值。并且如果在注解内的位置不是最前的,还会导致被注解的类在其它类中 import 报错。
	//虽然直接打印显示正常,但被 @MethodAccess 内 RequestRole[] GET()等方法引用后获取的是空值
	public static final RequestRole[] ALL = {RequestRole.UNKNOWN};//values();//所有
	public static final RequestRole[] HIGHS;//高级
	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 { //注册权限
        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;
	}

}

解决方案:

不抽象数组常量 ALL,HIGHTS 等,而是在每个用到的地方硬编码写死具体的值。

2.ArrayList 可通过构造函数传入非指定泛型的 List 并在 get 时出错

首次发现时的环境:JDK 1.7

首次发现所在项目:APIJSON

测试用例:

JSONArray arr = new JSONArray(); //com.alibaba.fastjson.JSONArray
arr.add("s");

List<Long> list = new ArrayList<>(arr); 
list.get(0); //throw new IllegalArgumentException

解决方案:

1.改用 Open JDK8

2.升级 JDK

注:后面多次测试,已无法复现。

3.基本类型在三元表达式内可赋值为 null,编译通过但运行出错

首次发现时的环境:JDK 1.7

测试用例:

int i = true ? null : 0; //Exception in thread "main" java.lang.NullPointerException

首次发现所在项目:ZBLibrary

解决方案:

在给基础类型用 3 元表达式赋值时,null 先转为基础类型的默认值。

最后再提 2 个不是 bug,但容易引发编程 bug 的问题:

1.局部变量和同名的全局变量能在一个方法内,编译通过,运行也正常。

    public class Test {
        
        int val;
        @Override
        public String toString() {
            val = 1;
            String val = "";
            return super.toString();
        }
    }

如果两个变量中间隔了比较长的其它代码,很可能会导致开发人员将两者混淆,导致逻辑认知错误,从而写出或改出有问题的代码。

解决方案:

命名局部变量前先搜素,确保没有已声明的同名全局变量。

2. (非 JDK bug ) Gson 通过 TypeToken 转换 List<t> 能写入不属于 T 类型的数据,get 出来赋值给 T 类型的变量 /常量报错。</t>


        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(0); //Exception cannot cast String to Integer

解决方案:

1.手动检查列表内数据都符合泛型 T

2.改用 fastjson 等其它能静态检查类型的库。

6492 次点击
所在节点    Java
22 条回复
Keyes
2018-12-05 10:05:40 +08:00
变着花样推广项目,不过开源辛苦,就蒙上眼睛支持一下吧
mritd
2018-12-05 19:05:23 +08:00
容我再说句实话,我目测楼主以前可能是一位前端开发者,是否优秀不敢说,我也没资格评价,但是你这么跨界写 java,java 的操作我倒是不敢恭维; 我突然有点理解左耳朵耗子喷一堆后端开发者去前端跨界装逼的处境了……还有就是,那个三木运算吧,int 为啥要赋值 null? 你是不是想写 nil 你....

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/514093

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX