首页 > 编程语言 >盘点最常见且坑爹的Java事故现场,你中招了几个?

盘点最常见且坑爹的Java事故现场,你中招了几个?

时间:2022-10-04 15:00:50浏览次数:47  
标签:Java String jdk 事故现场 list null 方法 中招 Bigdecimal

前言

jdk作为我们每天必备的调用类库,里面大量提供了基础类供我们使用.可以说离开jdk,我们的java代码寸步难行,jdk带给我们的便利可谓是不胜枚举,但同时这些方法在使用起来也存在一些坑,如果不注意就很容易掉入到陷阱里面,导致程序抛出错误。jdk中的很多方法都不会做非null判断,可能设计jdk的作者默认开发者已经处理好null值了.不过这个设计可能会造成很严重的后果,实在是暗藏杀机。比如今天早上我们查了一笔订单没有退款,查了一早上最终才发现是同事写的代码的BigDecimal的subtract方法的值没有做非null判断处理导致程序抛出了空指针异常,看似简单的异常却直接无法让很多订单退款,实在是小问题造成大事故。而要修补退款这个问题,要耗费很多时间去修补,出错的成本太高,本期我们就来看看jdk中那些坑你没商量的方法,这些方法很常见,相信你一定遇到过。

一:String.valueOf()方法的陷阱

案发现场:某个鸟语花香的早上,我们在开心的敲着代码,突然客户群有人投诉反映,我们发给用户的短信有部分是尊敬的"null"你好,xx等。开发第一时间看了代码,觉得没有问题啊,为什么短信内容会出现用户名为null呢,不是经过了非空判断的吗?String.valueOf()是String提供的一个类型转换的方法,我们来看一下(代码简化过后的):

// 调用用户服务根据用户id获取用户信息        
Map<String, Object> userInfo = userService.getUserInfoById(userId);
Object userNameObject = userInfo.get("name");
String userName = String.valueOf(userNameObject);
// 判空
if(userName!=null&&userName.length()>0) {
      String message = getMessage(userName);
      smsService.send(message);
}

这段代码是简化过的,主要作用就是通过用户服务根据id获取用户信息发送短信,后来经过定位发现了问题所在:首先用户的名字里有特殊的emoji符号,数据库写入的时候有部分写入失败,因为当时的

数据库字符格式并无法兼容emoji,而获取的时候因为这个问题值为null了,接下来是重点:

这里是重点,也是最大的坑人之处,注意这里返回了一个"null"的字符串,而不是null。这两个是有很大区别的,当进行非空判断的时候,返回的是ture。也就是这个"null"的字符串它是符合判空条件的!

正确的姿势是在String.valueOf方法前必须判空:

二:Integer.parseInt()方法很矫情

事故现场:一次业务场景为拉取订单,打出订单列表记录,财务人员需要拉出对账,结果总是发现很奇怪的一个现象,每次拉取少很多数据,。还好财务发现了,要不然和第三方财务对账就会亏很多钱...最终发现是订单的一个字段值转Integer出错了,那个订单下的字段值是120.0通过Integer.parseInt()直接报错了,恰好开发人员认为这段开发肯定没问题,因此就没有catch异常,最后找了很久才发现(因为涉及到第三方,还让别人查了半天...). 知道真相的我们都有点汗颜,这么丁点的错误排查了很久,实在是不应该啊。

Integer.parseInt()方法用于将字符串转化为Integer类型的方法,此方法的适用方向就显得比较窄,因为是String类型的参数,没有任何限定,当在传入一些比如50.0、20L、30d、40f这类数据的情况下,

我们来看一个栗子:

 会抛出异常NumberFormatException:

 事实上对于这样的数据,比如小数、浮点数据、long型数据它都可以自动转换,而不是给我们抛出烦人的报错信息,如果预先知道是整数或者小数,可以用Bigdecimal转换(注意此方法不适用于double和float、Long类型的数据,比如10d,20L)

对于浮点类型、long类型的数据可以用以下方法来处理:

推荐使用hutool的NumberUtil.parseInt()方法,充分考虑到了浮点、long、小数等类型数据可能带来的解析异常的问题,hutool是一个国人开源的工具类库,这里实名推荐,容错性和处理异常能力很强,可以自行百度搜索使用

三:Bigdecimal的除法坑你没商量

众所周知,BigDecimal是处理金额最有效的数据类型,一般进行财务报表计算的时候为了防止金额出现错误,一般情况下都会采用Bigdecimal,而double、float都会存在些许的误差。你开开心心的用Bigdecimal进行了计算,而最终的结果返回却有问题,我们来看一个栗子:

 常见的除法用起来没有任何丝毫的问题,妥妥的没毛病.但是一旦程序中的数据出现以下情况,如果用Bigdecimal来接受前端的参数,而前端的参数是用户输入不确定的,一旦出现如下的数据,我们来看看结果:

 执行结果一看,居然报错了哎

 这就是BidDecimal的坑,一旦返回的结果是无限循环小数,就会抛出ArithmeticException。因此在进行Bigdecimal除法的时候,需要进行保留小数的处理,正确的处理姿势:

四:Collections.emptyList()此list非彼list

我们先来看一个sample:

  public List<String> getUserNameList(String userId) {
        
        List<String> resultList = Collections.emptyList();
        try {
            resultList = userDao.getUserName(userId);
        } catch (Exception ex) {
            logger.info(ex);
        }
        return resultList;
    }

这样会抛出错误,主要问题在于Collections.emptyList()并非我们平时看到的List,此list不支持add、remove方法,否则会抛出operationNotSupportException:

 结果抛出异常:

 

 原因是:Collections.emptyList返回的并不是我们平时认识的那个list,它是一个内部常量类:

public static final List EMPTY_LIST = new EmptyList<>();

这个list并不具有add、remove元素的能力,我猜想是因为jdk设计之初的想法是将这个list作为一种只读的list,并不提供数据的写入能力,因此它仅可作为一种 空值返回,无法进行删除、添加操作。

五:list可以一边删除一边遍历吗?

答案是肯定可以的,要不然的话list怎么删除数据呢,不过要注意遍历的姿势,我们再来看一个简单的例子:

 很不幸,又双叒叕报错了:

 

 

仔细翻阅源码会发现,每次remove之前会检查元素的条数,如果发现预期的modCount和当前的modCount不一致就会抛出这个异常.modCount是list中用来记录修改次数的一个属性,当对元素进行统计的时候就会对该元素加1,而当对list边遍历边删除的话,就会造成

excepted与modCount不一致,从而抛出异常。

 正确的删除姿势就是使用Iterator.remove进行遍历删除,可以规避这个问题。

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
    Integer integer = iterator.next();
    if (integer == 2) {
        iterator.remove();
    }
}

六:其他

6.1 Bigdecimal在比较的时候,最好使用compareTo方法,不要使用equals方法,如下案例,虽然Bigdecimal重写了equals方法,但是使用会存在问题:

 

 

1和1.0在比较的时候返回了false,这是因为在equals的源码中进行了数据的scale(也就是精度)的比较,如果不一致就会返回false,如果使用compareTo方法就不存在这个问题

6.2:mysql的减法计算如果有null值结果就为null

 select 5-null 结果会返回null,所以在进行mysql计算的时候,对于有可能出现null值的列一定要进行ifnull(field,0)的转换,将null值转化为0,否则就会出现一些意想不到的数据错误和空指针问题

 正确的姿势:

 

6.3: String的split方法在进行||分割的时候需要进行转义,否则结果会有问题

 

 

七:总结

jdk的设计者有两个很大的特点:①大多不会做非null判断②**出现错误直接throw new Exception,容错性很差,**在实际开发中,面对jdk一定要谨慎使用,jdk提供了便利的同时,也有一些我们使用上的盲区,应该养成多看源码,多注意错误性处理,防止在小问题上栽大跟头。回到最开始说的那个subtract方法的问题,因为这个问题等需要我处理完之后用户才能收到退款,这直接造成了用户体验直线下降,而部分用户还直接打电话投诉。同事一个小小的不谨慎和马虎就给公司造成了很多负面影响,技术问题虽然不大但是带来的业务影响范围很严重。所以我们必须防微杜渐,小小的问题都得细细的打磨,才能避免很多问题的产生。

来源: cnblogs.com/wyq178/p/13520745.html

标签:Java,String,jdk,事故现场,list,null,方法,中招,Bigdecimal
From: https://www.cnblogs.com/jiutang001/p/16753766.html

相关文章

  • Java字段初始化的规律
    JAVA字段的初始化规律1.类的构造方法(1)“构造方法”,也称为“构造函数”,当创建一个对象时,它的构造方法会被自动调用。构造方法与类名相同,没有返回值。(2)如果类没有定......
  • java入门基础 static final 关键字 修饰符 解释(通俗易懂)
    final和static和finalstatic区别解释?static是用来修饰静态资源的(包括类、方法、变量等),final是用来保证当前变量为常量,finalstatic即保证为静态常量(意思就是不依......
  • Java 属性文件乱码问题
    属性文件乱码通常是中文乱码,在英文下通常没有这个问题。 ​​​​ 如上图显示的,中文字符在属性文件中读取后会显示为乱码.问题和解决导致这个问题的主要原因是属性文件如......
  • Java 属性文件乱码问题
    属性文件乱码通常是中文乱码,在英文下通常没有这个问题。  如上图显示的,中文字符在属性文件中读取后会显示为乱码.问题和解决导致这个问题的主要原因是属性文件如果......
  • 初学者了解的Java!
    简单看JavaJava的诞生和发展Java是由SunMicrosystems公司于1995年5月推出的Java面向对象程序设计语言和Java平台的总称。由JamesGosling和同事们共同研发......
  • Java方法(方法的调用,重载)
    方法调用:调用方法:对象名.方法名(实参列表)Java支持两种调用方法的方式,根据方法是否返回值来选择当方法返回一个值的时候,方法调用通常被当做一个值,例如intlarger......
  • java网络编程--5 URL 下载网络资源
    java网络编程--5URL下载网络资源1.8、URL统一资源定位符,定位互联网的某一个资源DNS域名解析www.baidu.com-->xxx.xxx.xxx.xxx//协议://IP地址:端口号/项目名/资源......
  • java----面向对象(1)概念,类与对象的创建,构造器,创建对象和内存分析,以及小总结
    面向对象编程1.java的核心思想就是OOP面向过程思想:步骤清晰,第一步做什么,第二步做什么,适合处理一些较为简单的问题2.面向对象思想:分类的斯文模式,思考问题首先会解决问题......
  • 多线程详解——Java
    多线程详解——Java.Thread1.1多任务​ 在计算中,多任务是一种多个任务(也称之为进程)共享处理资源(如CPU)的方法。在多任务操作系统上,例如WindowsXP,您可以同时运行多个应用......
  • 0579-5.15.1-Java 应用程序中修改Kerberos ticket_lifetime参数无效异常分析
    作者:辉少1文档编写目的在Kerberos环境中,我们的应用程序通过Java代码来提交任务需要先进行Kerberos凭证的初始化然后进行应用程序的提交,本文档主要讲述Java应用程序中读取krb......