JDK 的 3 个 Bug,看这篇就对了!

    点击上方 "程序员小乐"关注, 星标或置顶一起成长

每天凌晨00点00分, 第一时间与你相约

每日英文

The first person that you think of in the morning and the last person you think of in the night is either the cause of your happiness or the cause of your pain. 

清晨想到的第一个人和夜晚想到的最后一个人,不是让你幸福的人,就是让你痛苦的人。

每日掏心

彼岸的守望,是此岸的感动;千里的陪同,是心中的丰盈。最深沉的爱,总是风雨兼程;最浓厚的情,总是冷暖与共。

来自:孤独的探索号 | 责编:乐乐

链接:my.oschina.net/tommylemon/blog/2967187

程序员小乐(ID:study_tech)第 781 次推文   图片来自百度

往日回顾:小白都能看得懂的Java虚拟机内存模型

   正文   

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

解决方案:

  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 类型的变量/常量报错。

        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. 手动检查列表内数据都符合泛型 T。

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

欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,学习能力的提升上有新的认识,欢迎转发分享给更多人。

欢迎各位读者加入订阅号程序员小乐技术群,在后台回复“加群”或者“学习”即可。

猜你还想看

阿里、腾讯、百度、华为、京东最新面试题汇集

这一顿神操作!从把3000行代码重构成15行代码谈起!

Java8中一个极其强悍的新特性,很多人没用过(非常实用)

MySQL高性能优化规范建议

关注订阅号「程序员小乐」,收看更多精彩内容

嘿,你在看吗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值