关系经济人类预测化学自然
投稿投诉
自然科学
知识物理
化学生物
地理解释
预测理解
本质社会
人类现象
行为研究
经济政治
心理结构
关系指导
人文遗产

99的Java程序员会踩的六个坑

5月17日 拭朱砂投稿
  前言
  作为Java程序员的你,不知道有没有踩过一些基础知识的坑。
  有时候,某个bug,你查了半天,最后发现竟然是一个非常低级的错误。
  有时候,某些代码,这一批数据功能正常,但换了一批数据就出现异常了。
  有时候,你可能会看着某行代码目瞪口呆,心里想:这行代码为什么会出错?
  今天跟大家一起聊聊99的Java程序员踩过,或者即将踩的6个坑。1、用号比较的坑
  不知道你在项目中有没有见过,有些同事对Integer类型的两个参数使用号比较是否相等?
  反正我见过的,那么这种用法对吗?
  我的回答是看具体场景,不能说一定对,或不对。
  有些状态字段,比如:orderStatus有:1(未下单),0(已下单),1(已支付),2(已完成),3(取消),5种状态。
  这时如果用判断是否相等:IntegerorderStatus1newInteger(1);IntegerorderStatus2newInteger(1);System。out。println(orderStatus1orderStatus2);
  返回结果会是true吗?
  答案:是false。
  有些同学可能会反驳,Integer中不是有范围是:128127的缓存吗?
  为什么是false?
  先看看Integer的构造方法:
  它其实并没有用到缓存。
  那么缓存是在哪里用的?
  答案在valueOf方法中:
  如果上面的判断改成这样:StringorderStatus1newString(1);StringorderStatus2newString(1);System。out。println(Integer。valueOf(orderStatus1)Integer。valueOf(orderStatus2));
  返回结果会是true吗?
  答案:还真是true。
  我们要养成良好编码习惯,尽量少用判断两个Integer类型数据是否相等,只有在上述非常特殊的场景下才相等。
  而应该改成使用equals方法判断:IntegerorderStatus1newInteger(1);IntegerorderStatus2newInteger(1);System。out。println(orderStatus1。equals(orderStatus2));
  运行结果为true。2、Objects。equals的坑
  假设现在有这样一个需求:判断当前登录的用户,如果是我们指定的系统管理员,则发送一封邮件。系统管理员没有特殊的字段标识,他的用户id888,在开发、测试、生产环境中该值都是一样的。
  这个需求真的太容易实现了:UserInfouserInfoCurrentUser。getUserInfo();if(Objects。isNull(userInfo)){log。info(请先登录);}if(Objects。equals(userInfo。getId(),888L)){sendEmail(userInfo):}
  从当前登录用户的上下文中获取用户信息,判断一下,如果用户信息为空,则直接返回。
  如果获取到的用户信息不为空,接下来判断用户id是否等于888。如果等于888,则发送邮件。如果不等于888,则啥事也不干。
  当我们用id888的系统管理员账号登录之后,做了相关操作,满怀期待的准备收邮件的时候,却发现收了个寂寞。
  后来,发现UserInfo类是这样定义的:DatapublicclassUserInfo{privateIprivateSprivateIprivateS}
  此时,有些小伙伴可能会说:没看出什么问题呀。
  但我要说的是这个代码确实有问题。
  什么问题呢?
  下面我们重点看看它的equals方法:publicstaticbooleanequals(Objecta,Objectb){return(ab)(a!nulla。equals(b));}
  equals方法的判断逻辑如下:该方法先判断对象a和b的引用是否相等,如果相等则直接返回true。如果引用不相等,则判断a是否为空,如果a为空则返回false。如果a不为空,调用对象的equals方法进一步判断值是否相等。
  这就要从Integer的equals方法说起来了。
  它的equals方法具体代码如下:publicbooleanequals(Objectobj){if(objinstanceofInteger){returnvalue((Integer)obj)。intValue();}}
  先判断参数obj是否是Integer类型,如果不是,则直接返回false。如果是Integer类型,再进一步判断int值是否相等。
  而上面这个例子中b是long类型,所以Integer的equals方法直接返回了false。
  也就是说,如果调用了Integer的equals方法,必须要求入参也是Integer类型,否则该方法会直接返回false。
  除此之外,还有Byte、Short、Double、Float、Boolean和Character也有类似的equals方法判断逻辑。
  常见的坑有:Long类型和Integer类型比较,比如:用户id的场景。Byte类型和Integer类型比较,比如:状态判断的场景。Double类型和Integer类型比较,比如:金额为0的判断场景。
  如果你想进一步了解Objects。equals方法的问题,可以看看我的另一篇文章《Objects。equals有坑》。3、BigDecimal的坑
  通常我们会把一些小数类型的字段(比如:金额),定义成BigDecimal,而不是Double,避免丢失精度问题。
  使用Double时可能会有这种场景:doubleamount10。02;doubleamount20。03;System。out。println(amount2amount1);
  正常情况下预计amount2amount1应该等于0。01
  但是执行结果,却为:0。009999999999999998
  实际结果小于预计结果。
  Double类型的两个参数相减会转换成二进制,因为Double有效位数为16位这就会出现存储小数位数不够的情况,这种情况下就会出现误差。
  常识告诉我们使用BigDecimal能避免丢失精度。
  但是使用BigDecimal能避免丢失精度吗?
  答案是否定的。
  为什么?BigDecimalamount1newBigDecimal(0。02);BigDecimalamount2newBigDecimal(0。03);System。out。println(amount2。subtract(amount1));
  这个例子中定义了两个BigDecimal类型参数,使用构造函数初始化数据,然后打印两个参数相减后的值。
  结果:0。0099999999999999984734433411404097569175064563751220703125
  不科学呀,为啥还是丢失精度了?
  Jdk中BigDecimal的构造方法上有这样一段描述:
  大致的意思是此构造函数的结果可能不可预测,可能会出现创建时为0。1,但实际是0。1000000000000000055511151231257827021181583404541015625的情况。
  由此可见,使用BigDecimal构造函数初始化对象,也会丢失精度。
  那么,如何才能不丢失精度呢?BigDecimalamount1newBigDecimal(Double。toString(0。02));BigDecimalamount2newBigDecimal(Double。toString(0。03));System。out。println(amount2。subtract(amount1));
  我们可以使用Double。toString方法,对double类型的小数进行转换,这样能保证精度不丢失。
  其实,还有更好的办法:BigDecimalamount1BigDecimal。valueOf(0。02);BigDecimalamount2BigDecimal。valueOf(0。03);System。out。println(amount2。subtract(amount1));
  使用BigDecimal。valueOf方法初始化BigDecimal类型参数,也能保证精度不丢失。在新版的阿里巴巴开发手册中,也推荐使用这种方式创建BigDecimal参数。4、Java8filter的坑
  对于Java8中的Stream用法,大家肯定再熟悉不过了。
  我们通过对集合的Stream操作,可以实现:遍历集合、过滤数据、排序、判断、转换集合等等,N多功能。
  这里重点说说数据的过滤。
  在没有Java8之前,我们过滤数据一般是这样做的:publicListUserfilterUser(ListUseruserList){if(CollectionUtils。isEmpty(userList)){returnCollections。emptyList();}ListUserresultListLists。newArrayList();for(Useruser:userList){if(user。getId()1000user。getAge()18){resultList。add(user);}}returnresultL}
  通常需要另一个集合辅助完成这个功能。
  但如果使用Java8的filter功能,代码会变得简洁很多,例如:publicListUserfilterUser(ListUseruserList){if(CollectionUtils。isEmpty(userList)){returnCollections。emptyList();}returnuserList。stream()。filter(useruser。getId()1000user。getAge()18)。collect(Collectors。toList());}
  代码简化了很多,完美。
  但如果你对过滤后的数据,做修改了:ListUseruserListqueryUser();ListUserfilterListfilterUser(userList);for(Useruser:filterList){user。setName(user。getName()测试);}for(Useruser:userList){System。out。println(user。getName());}
  你当时可能只是想修改过滤后的数据,但实际上,你会把元素数据一同修改了。
  意不意外,惊不惊喜?
  其根本原因是:过滤后的集合中,保存的是对象的引用,该引用只有一份数据。
  也就是说,只要有一个地方,把该引用对象的成员变量的值,做修改了,其他地方也会同步修改。
  如下图所示:
  5、自动拆箱的坑
  Java5之后,提供了自动装箱和自动拆箱的功能。
  自动装箱是指:JDK会把基本类型,自动变成包装类型。
  比如:Integerinteger1;
  等价于:IntegerintegernewInteger(1);
  而自动拆箱是指:JDK会把包装类型,自动转换成基本类型。
  例如:IntegerintegernewInteger(2);intsuminteger5;
  等价于:IntegerintegernewInteger(2);intsuminteger。intValue()5;
  但实际工作中,我们在使用自动拆箱时,往往忘记了判空,导致出现NullPointerException异常。(1)运算
  很多时候,我们需要对传入的数据进行计算,例如:publicclassTest2{publicstaticvoidmain(String〔〕args){System。out。println(add(newInteger(1),newInteger(2)));}privatestaticIntegeradd(Integera,Integerb){}}
  如果传入了null值:System。out。println(add(null,newInteger(2)));
  则会直接报错。(2)传参
  有时候,我们定义的某个方法是基本类型,但实际上传入了包装类,比如:publicstaticvoidmain(String〔〕args){IntegeranewInteger(1);ISystem。out。println(add(a,b));}privatestaticIntegeradd(inta,intb){}
  如果出现add方法报NullPointerException异常,你可能会懵逼,int类型怎么会出现空指针异常呢?
  其实,这个问题出在:Integer类型的参数,其实际传入值为null,JDK字段拆箱,调用了它的intValue方法导致的问题。6、replace的坑
  很多时候我们在使用字符串时,想把字符串比如:ATYSDFAY中的字符A替换成字符B,第一个想到的可能是使用replace方法。
  如果想把所有的A都替换成B,很显然可以用replaceAll方法,因为非常直观,光从方法名就能猜出它的用途。
  那么问题来了:replace方法会替换所有匹配字符吗?
  jdk的官方给出了答案。
  该方法会替换每一个匹配的字符串。
  既然replace和replaceAll都能替换所有匹配字符,那么他们有啥区别呢?
  replace有两个重载的方法。其中一个方法的参数:charoldChar和charnewChar,支持字符的替换。source。replace(A,B)另一个方法的参数是:CharSequencetarget和CharSequencereplacement,支持字符串的替换。source。replace(A,B)
  而replaceAll方法的参数是:Stringregex和Stringreplacement,即基于正则表达式的替换。
  例如对普通字符串进行替换:source。replaceAll(A,B)
  使用正则表达替换(将替换成C):source。replaceAll(,C)
  顺便说一下,将替换成C使用replace方法也可以实现:source。replace(,C)
  小伙们看到看到二者的区别了没?使用replace方法无需对特殊字符进行转义。
  不过,千万注意,切勿使用如下写法:source。replace(,C)
  这种写法会导致字符串无法替换。
  还有个小问题,如果我只想替换第一个匹配的字符串该怎么办?
  这时可以使用replaceFirst方法:source。replaceFirst(A,B)
  说实话,这里内容都很基础,但越基础的东西,越容易大意失荆州,更容易踩坑。
投诉 评论 转载

99的Java程序员会踩的六个坑前言作为Java程序员的你,不知道有没有踩过一些基础知识的坑。有时候,某个bug,你查了半天,最后发现竟然是一个非常低级的错误。有时候,某些代码,这一批数据功……从2023年一季度小麦市场价格变动情况看新麦上市走势进入2023年,我国第二大口粮小麦的市场价格可谓是越来越疲弱。元月上旬小麦(国标三等,下同)市场价格延续2022年下跌行情,上、中、下旬市场价格分别为3202元吨、320……血瘀体痛,血燥体痒,血虚体倦4个中成药补血养血,活血化瘀大家好,我是赵医生,血瘀则体痛,血燥则体痒,血虚则体倦,血寒则体寒,今天我就来跟大家解释清楚这四句话的意思,并分享4个中成药,补血养血,活血化瘀。第一个血瘀则痛,中医认为……互金理财社区社区布局内容建设用户体系和运营策略研本文分别从背景说明(市场规模和用户画像)、理财社区的布局,以及理财类app社区的内容建设、用户体系等四大内容,为你分析互金理财社区的情况,并给出社区建设的运营策略。201……考古新发现年前的大禹治水只是传说考古新发现:4000年前的大禹治水只是传说。据说4000多年前黄河流域爆发大洪水,给住在下游的民众带来灭顶之灾。大禹挺身而出开展治水工作,救民众于水火之中,他也成为中国历史上第……妈妈如何与胎儿进行语言交流妈妈对胎儿语言胎教的要求就是让父母用亲切、生动、形象的语言与胎儿对话,维系父母和孩子的亲情,时刻牢记胎儿的存在,并经常与之对话。那么如何与胎儿进行语言交流呢?安满给中国妈妈提出……10月19日湖人客战勇士打响202223赛季揭幕战TheAthletic记者ShamsCharania报道,202223赛季揭幕战将在北京时间10月19日开打。湖人将在揭幕日客场对阵勇士,打响新赛季第一枪!勇士会在当晚领取总冠……我与数学的一场战争哎!这个双休日又得让这一大堆试卷来充实我的生活了!看关眼前堆积如山的作业,我不禁又一次叹息。看在现在脑子还清醒,先做数学吧!不得不说,数学还真是一门让我头疼的科目,尤其是……世界十大凄美之花双生花寓意悲凉残酷的爱情在世界上有各种各样的花,它们不仅长的十分美丽,而且有的花会散发出迷人的香气,每一种花朵,都有它们不同的传说故事,有的美好,有的悲惨,那么世界十大凄美之花分别有哪些呢,它们都有各……一路走来第三十五章邻家天上飞横祸腰伤致残痛苦多挖着挖着,突然作业面的上方塌方了,忽地一下落下,幸好只有一个人在里面,这人叫江福业,我东邻家老二,由于他哥和我同岁,他和我挺好,加之我有时去家聊一聊,所以处的不错,江福业身高1……2022楚商年会将于本月7日在十堰举办12月2日,记者从湖北十堰市人民政府、湖北省工商业联合会在武汉召开新闻发布会上获悉,2022楚商年会将于2022年12月7日至9日在十堰举办。本次楚商年会以天下楚商荟聚武……借车给人出事故哪种情况保险不赔车子借给了朋友,结果出了车祸,那么借车给人出事故哪种情况保险不赔?借车给别人出了事故,两种情况保险不赔:1、未经车主同意二次转借他人的不赔,保险免责条款中规定,未经……
技术在建筑工程施工管理中的应用探索宝藏中的宝藏山西小瑞士,惊喜不止万年冰洞和绝美高山草原小鱼历险记莫兰特3585竟无缘今日最佳?约基奇30三双90命中率历史唯前任的炮到底该不该约口服避孕药真的能减肥吗李小璐为什么看上皮几万和贾乃亮有对比性吗紫气东来迎新日突然宣布11月1日起,恢复近40年每赛季失误最多球员!哈登6次登顶!2022年会是谁呢爱在秋天,我在等风,也等你eStar和狼队即战力分析,大概率24拿下比赛,易铮是个潜在
最新研究超级人工智能,从理论上就无法控制一次深刻的教训长白山国庆虎豹疑案是什么猛兽捕杀了天桥岭林区农家的黄牛自给率只有5,中国真的缺汽车芯片?剪辑视频怎样找背景音乐,免费分享给大家,。。。女人须知男人不想听哪些话郭姚两家的恩怨情仇如何搭配衣服显瘦视觉上瘦一圈疾病肆虐末日袭来!游戏往日不再将改编电影普拉提是什么?普拉提和瑜伽的区别老师,我恨你有手机号码可以查询对方的位置吗(不收费的定位找人软件)

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找菏泽德阳山西湖州宝鸡上海茂名内江三亚信阳长春北海西安安徽黄石烟台沧州湛江肇庆鹤壁六安韶关成都钦州