你好呀,我是歪歪。
前几天看到“Qunar技能沙龙”公众号推送了一篇关于他们举办了一场“Code Review大赛”的文章。
看到 Code Review 我很感兴趣啊,“啪”的一下就点进去了啊,很快啊。
难能可贵的是,“Qunar技能沙龙”本着开放共享准则,把他们的 CR 大赛真题进行了免费开放。
我其时“啪”的一下就先把真题拿到手了,然后对着真题“刷刷刷”便是一顿输出。
不得不说,这套题出的是真的很不错,所以写篇文章带大家感受一波。
可是我觉得为了让你有更好的体验,我强烈建议你先在拿到这套真题,自己做一遍,然后在来和我对一下答案。
那么问题就来了:怎么拿到真题呢?
在公众号”why技能”后台回复关键字“CR”即可拿到。
假如你仍是觉得麻烦,那么你也能够点开下面这张图片,然后往右边滑动,这样你就能够先只看到标题,而不看我的解析,(可是有或许忽然看到一些奇怪的图片),能够自己先在心里做一遍。
还得提早阐明一下的是,我写文章的时分,官方并没有给出每道题的标准答案是什么,官方题解也仅仅出了七道,后边还会慢慢做出来,所以我给出的解析也纷歧定是悉数正确的。
还有,我能提示你的是,一个标题里边或许有多个问题点,你需求尽量多的识别出来,宁可错杀,不行放过。
由于官方还未彻底公布正确答案,那这样就更加有意思了,由于当你发现你的答案和我的答案纷歧样的时分,便是咱们相互学习的时分。我特别希望你能纠正我的答案,也特别感谢你能在谈论区分享你的、和我纷歧样的答案。
咱们碰撞一下,共同进步。
别的,官方题解在这儿:
《看ChatGpt解去哪儿网第四届CR大赛标题》
目前只回答了 7 道标题,后边的回答还在路上。可是官方的题解中整了个活儿,便是把整段代码喂给 ChatGPT 让它帮助诊断有什么问题,能够去看看。
我只能用一个字来形容它:
第 1 题
首先,拿到这题榜首眼,我就看到一个十分别扭的当地,办法入参:cazeList?
不该该是 caseList 吗?
其实我理解 core review 不仅仅应该关注业务逻辑,也应该关注到这些命名标准、变量拼写的“小当地”。
所以,秉着宁可错杀,不行放过的准则,这算是一个需求修正的点。
然后第二个点:入参不判别非空的吗?
你看第 9 行,拿着 caseList 这个入参就在开端 get(0) 了。caseList 是传入进来的,假如传了一个 null 怎么办?
不要给我说外面或许判别了,不会传递 null。不要依据假定去剖析问题,再次强调:宁可错杀,不行放过。
在这儿加上一个非空判别,代码的健壮性更强。
接着看代码,在第 9 行做了一个批量刺进的动作。被 try-catch 代码块包裹了起来。
在 catch 代码块里边履行了表不存在则创立,然后再刺进的动作。这个代码能用,可是我个人觉得:不要在 catch 代码块里边写过多的逻辑,也便是尽量不要依据反常去做程序逻辑控制。
在刺进之前先履行创立表的动作,然后再履行批量刺进,这样的正向逻辑它不香吗?
总结一下榜首题:
- 入参姓名拼写错误,cazeList 应该是 caseList。
- caseList 没有做非空检验。
- 应该把 catch 代码块里创立表的动作,放到 try 逻辑里边去。先尝试创立表,再批量刺进。
第 2 题
这题没啥好说的,直接秒杀。
五秒之内没有看出问题,阐明“陈腔滥调文”背的不牢。
我问你:Spring 业务注解收效的条件是什么?
是不是业务办法的调用方得是经过 Spring 署理后的目标?
那么上面代码中的业务会收效吗?
必定不会的。
经典的 this 调用导致业务注解失效的场景,一眼秒杀之。
第 3 题
这题也应该归于能够“秒杀”的标题。
你看一下 getInstance 办法的完成,这是什么老伙计?
这不便是咱们可爱的依据两层锁检查完成的单例形式吗?
当面试官问你两层锁检查的时分,考点是什么?
榜首个看锁目标,这儿是 class 目标,大局一把锁,没缺点。
第二个问你为什么要判别两次 instance == null。
由于有或许 A,B 两个线程都履行到了 16 行去抢锁。这个时分 A 先抢到了,B 就被堵塞。A 线程创立完一个实例之后释放了锁,这个时分 B 继续履行,假如不再次判别 instance == null,那么就会再次履行初始化的动作。
第三个调查点 instance 为什么要加 volatile?
由于一个目标的创立是分为三步的:
- 分配目标的内存空间
- 初始化目标
- 设置instance指向刚分配的内存地址
假如 2 和 3 步被重排序了,那么就会呈现一个有内存地址,可是还没有初始化完成的目标。
假如这个目标被其他线程拜访到了,那么便是有问题的。所以咱们加一个 volatile 来就禁止指令重排序,来躲避这个问题。
这些都是老陈腔滥调了,就不细说了。
拿着刚刚说的三个点,去和标题里边的代码比照,就会发现标题里边的代码是没有加 volatile,所以这便是要修正的当地。
别的,多说一句。加 volatile 这个方案,得 JDK5 版本以上运用,由于在 JDK5 之后,运用的新的 JSR-133 内存模型标准,在这个标准里边增强了 volatile 的语义。
该死,这句话是怎么呈现在我的脑子里边的?
它就像是忽然蹦出的相同,这便是传说中的刻在基因里边的“陈腔滥调文”吧。
第 4 题
这题榜首个十分显着的优化点在第 20 行。
要是看到这句话之后你还没反响过来是咋回事,那完犊子了,多半是要回去等通知了。
关于“流”的操作,封闭流的代码有必要放在 finally 代码块里边啊,或许用 try-with-resource 语法糖,这是前期学 JavaSE 的时分就要要点掌握的东西了。
第二个要一眼秒杀的优化点是 22 行。
谁教你这样打印日志的?这样打印能够说彻底没有任何卵用啊,由于丢掉了“故事现场”。
应该这样写:
log.error(“呈现反常啦”,e);
保留整个反常仓库,便利后续定位问题,
第三个问题是 fileName,文件称号用的是时刻戳,这样是有并发危险的。虽然看描绘,这是一个给运营人员运用的功能,没啥并发量。在这个场景下,归于“错杀”。
可是,“并发”这根弦仍是应该绷紧的。假如是外部客户导出功能呢?假如外部客户许多呢?假如许多外部客户便是在同一时刻发起了统计呢?
第四个问题是我没搞懂第 11 行里边在干啥,由于标题中没有给出这部分的代码。
我理解应该是一个从某个数据源依据指定参数查询数据的功能。
为什么要把数据查出来之后再和 TOP_CAPCITY 比较并截取 list 呢?
假如运营商许多,比方有 10w 个运营商,可是运营只想看前 10 的运营商,也便是 TOP_CAPCITY=10 的状况。
这样把 10w 个运营商查询出来彻底没有含义,并且还简略产生大目标,对 GC 也不友好啊。
所以,为什么不在查询数据源的时分,把 TOP_CAPCITY 作为参数传进去呢?
我以为这是第四个优化点。
总结一下第四题:
- 输出流封闭未放到 finally 代码块,或许用 try-with-resource。
- 日志打印不标准。
- 文件称号用的是时刻戳,有并发危险。
- 查询数据的时分应该把 TOP_CAPCITY 作为条件,而不是悉数查询出来再截取。
第 5 题
针对 future 进行反常捕获的时分,只捕获了 ExecutionException 反常。我觉得至少还得捕获一个 TimeoutException 吧。
由于这个 future 是 dubbo 接口异步获取的,所以 get 操作获取超时的场景应该仍是比较常见吧。
别的一个便是 19 行和 29 行的日子打印,我不知道为什么没有采用 {} 占位符的办法去做,而是采用了 + 拼接。这个点由于 TITLE_Arg1 参数到底长啥样的,代码里边没有表现出来,所以仅仅提上一嘴。
可是相同是日志打印,34 行的日志打印和前面的相同,丢掉了“故事现场”,没有把 e 打出来。
第 6 题
这一题,我首先想到的榜首个是没有对 redPlanIdList 目标进行非空判别。
然后一看 Safes.of 这是什么写法,没见过啊。
所以用关键字查找了一把:
相关的成果很少很少,只要看到这个靠谱一点:
这是他人自己封装的一个办法,并不是什么组件里边的。
好吧,就权且当这个办法是对 null 目标进行了处理吧,把 null 转化为了空调集,避免了空指针反常。
然后咱们接着往下看,在第 7 行运用了 parallelStream,并行流来对 redPlanIdList 目标进行操作。
可是你看一下第五行,咱们知道 redPlanIdList 这个目标是一个 List,可是它详细是什么 List 呢,是 ArrayList 仍是 CopyOnWriteArrayList 呢?
不得而知。
那咱们假定对错线程安全的 ArrayList 呢?
有的同学看到这儿的时分,是不是要开端抢答了:这题我会,在多线程里边运用了 ArrayList,有线程安全问题…
不对啊,抢答的不对啊。
即便这个当地的 redPlanIdList 对错线程安全的 ArrayList 也没有关系。由于在并行流里边,仅仅遍历了它,并没有对它进行新增操作。
所以 redPlanIdList 并没有线程安全问题。
有问题的是 15 行,result 是 ArrayList,调用了 addAll 办法,有线程安全问题。需求运用线程安全的调集类。
或许,把 parallelStream 修正为单线程的 stream,对吧?
对吗?
从程序逻辑上讲是对的,运用单线程没有任何缺点。
可是从需求布景上讲,这个当地完彻底全就能够、也应该运用多线程的办法去提高响应速度嘛。
所以,总结一下这题:在多线程里边调用了 arrayList 的 addAll 办法,有线程安全问题。需求运用线程安全的调集类。
别的,追问一个陈腔滥调文:请问 ArrayList 的线程不安全详细表现在什么当地?
针对这个问题,早年间,我还写过相关的文章,假如你不清楚的话,能够去翻翻:《ArrayList 的线程不安全》
这儿就直接说答案了。
ArrayList 的线程不安全表现在多线程调用 add 办法的时分。详细有两个表现:
- 当在需求进行数组扩容的临界点时,假如有两个线程同时来进行刺进,或许会导致数组下标越界反常。
- 由于往数组中增加元素不是原子操作,所以或许会导致元素掩盖的状况产生。
第 7 题
这题,我靠,榜首眼,不得秒了它?
遍历 ArrayList 的同时,还在调用它的 remove(int index) 办法。这写法,江湖大忌啊。
在这个标题里边,由于调用了 remove(int index) 办法,所以导致行列的长度在变化,因而第 7 行会抛出数组下标越界的反常:
此外,假如把标题中的 for 循环修正为这样:
for(Strings:testStrArray){
testStrArray.remove(s);
}
也是要完犊子的写法,会抛出 ConcurrentModificationException,老陈腔滥调了,不细说了。
紧记一句话:针对 ArrayList 的删除,用迭代器来完成,稳得一笔。
除了这个显而易见的问题之外,在上面的标题中还有一处能够优化的当地。
便是 emp、empany、q、t 这几个变量姓名取的真丑。你取名的时分,取做 isAllMatch、isAnyMatch、findFirstOfStr 它不香吗?
第 8 题
问题很好发现,精度丢掉。
改正嘛…
为什么不问问奇特的 ChatGPT 呢?
第 9 题
这个 B…
我是说这个类称号,B,是不是有点太随意了?
当然我猜想这或许是主办方为了脱敏,随便取了一个 B,不过这是不是有点太随意了?
然后看看第 10 行,我理解不该该把反常抛给前端吧,除非你项目里边有大局反常处理器,可是标题中没表现,所以也提一嘴,保平安。
接着 15 行,又是流没有封闭,前面现已呈现过一次了,不赘述。
再看看 16 行,HashSet 这玩意也不是线程安全的啊。所以在线程池里边,多线程调用其 add 办法,得改。
别的,你这个 HastSet 目标的称号叫做 visted。
我知道你是想表达这个 Set 里边放的是被拜访过的 url。
可是 visted,什么鬼?不该该叫做 visited 吗,老铁。
已然都说到拼写错误了,那么你在看看这个办法名:proceessLog。
处理日志,应该是 processLog 吧。
多了一个字母,有一点代码洁癖的人,看得难受。
最终,最严重的一个问题,你看看第 10 行的这个线程池:newCachedThreadPool。
这玩意的调用的线程池的构造办法是这样的:
这是一个能够不断开新线程的线程池啊,然后你再读题:在文件中有许多的 URL。
“许多”,完犊子,线程过多,性能反而拉胯。
别的,线程池提交用的是 execute 办法,应该用 try-catch 代码块把线程池里边的逻辑给包起来,在 catch 里边记载日志,以免后边出问题了,日志都找不到。
第 10 题
官方给的标题中,第 9 题和第 10 题重复了。
我大略是病了,反正都睡不着,坐动身来点起了一支烟,这悲伤没有由来,黯然看着面前的两套考卷,一套是我的,另一套也是我的。
我翻开考卷一查,这标题没有答案,歪歪斜斜的每页上都写着“找出BUG”四个字。我反正睡不着,细心看了半夜,才从字缝里看出字来,满本都写着两个字是:卷起来。
第 11 题
这题,读完题就得秒啊,得秒之。
都现已说的很理解了:
- is 最初的 boolean 类型的特点,用的是 FastJson 序列化。
- 反序列化的东西有或许是 FastJson,也有或许是 gson。
首先榜首点,即便你之前不知道 FastJson 关于 is 最初的 boolean 类型特点的处理办法,可是你从标题上也能读出来,标题上特意写了这句话,阐明里边是有故事的。
第二个,序列化东西是 FastJson,可是反序列化或许是 FastJson 也或许是 gson。
这不就离了大谱了吗?
上面的代码我也拿出来给你跑一下,就很清楚了:
FastJson 序列化的时分,把 isWmsSend 变成了 wmsSend。然后用 Gson 反序列化的时分,发现没有 isWmsSend 这个特点呀,再一看,是根本类型的 boolean,那就给默认值,false 吧。
所以 isWmsSend 应该用包装类 Boolean,尽量不运用根本类型 boolean。由于包装类的默认值是 null,根本类型的默认值是 false。
在你的业务代码中,null 和 false 代表的可彻底不是一个东西。
由于反序列化的问题导致把 true 变成了 false,这一听便是一个大锅啊,可不敢乱背。
第 12 题
看到这个题的榜首眼,我的留意力是放在注释上的。
你看第 13 行到第 15 行的注释是放在代码后边的。可是第 23 行到 28 行的注释又是放在代码上面的。
这是两种不同的风格,我个人喜爱的是第二种。可是不论你喜爱哪一种,你至少统一一下吧。
还有,这儿又呈现了 proccess 这个单词。前面第 9 题的时分,写的是 proceess。
我能说什么呢?
我只能单打一个:6。
我承认,这两个点的确有点吹毛求疵了,可是作为有点代码洁癖的我来说,有必要要说出来才爽。
然后,再说说这个代码真实有问题的当地。
榜首个其实很显着,一千万的数据量,一页读取一千条数据,用传统的分页,到后边的深度分页,你会发现越来越慢,由于这个是有性能问题的。
应该采取游标的办法,比方返回本次查询的 max(id),然后下一次分页的时分带着 id 去查询。
这个也算是一个陈腔滥调文吧,假如你不了解的话,去找材料背一背。
第二个问题是没有对 objectList 进行非空判别,防止空指针。
第三个问题,应该在 26 行的 for 循环里边搞个线程池,来提高数据处理速度。你这 1000w 的数据量了,单线程得搞到什么时分啊。
或许营销活动都要完毕了,用户还没收到推送。到时分活动作用不好,又是程序员背锅。
第四个问题是 35 行这个办法。虽然我不明白 UserDao 这个写法是什么意思,应该是公司内部封装的耐久化结构。
可是我看到这儿是把 tableName 作为参数传递到了办法里边,这个时分就得提高警惕了:同志们,谨防 SQL 注入啊。安全的那帮哥们就喜爱扫这样的代码,拿出来公示。
第 13 题
这个题很有意思啊,由于它的考点比较多,也算比较“偏一点”。
别的,总算把 process 写对了,舒服了。
首先榜首个,第 7 行加锁,第 8 行假如抛出反常了怎么办?
所以这个锁弄的有大问题啊,依照标准来说,加锁操作是要放在 try 代码块外的榜首行。这之间不该该有任何其他的代码,以免加锁成功了,可是未解锁,呈现死锁的现象。
第二个问题,很显着,你看第 15 行,processWorkB 办法有两个入参,可是第 10 行调用的时分只给了一个?
这个应该是题出的失误了,不做追究。要是有人真写出了这样的代码并没有发现问题,阐明他是个奇才。关键是这玩意,代码都编译不过啊。
第三个问题,仍是 processWorkB 办法,没有对 param 参数进行非空判别。
来我问你一个问题:假如 param 是 null 会呈现什么状况?
必定是空指针反常了啊。
可是,你有没有想过一个问题:switch/case 里边为什么不做成支撑 null 的形式呢?
假如表达式为 null ,咱们就拿着 null 去 case 里边匹配,这样理论上做也是能够做的。
关于这个问题,我再这篇文章里边做过探讨,有兴趣能够去看看:《被阿里一道根底面试题给干懵了,一气之下写出万字长文。》
第四个问题:在 processWorkA 里边,隐藏的比较深,可是的确是一个我在实践编码的过程中踩到过的一个坑。
简略来说便是三目表达式里边的拆箱问题。
巧了,这个问题,我之前也提到过一次:《三目表达式的主动拆箱问题》
网上的相关解析也许多,假如不了解的话能够定向攻破一下。
详细到这个代码中的问题,便是中间的 params1*params2 之后的成果,类型是 int 型。由于三目表达式类型要对齐的特性,所以 params3 会被拆箱为 int 类型。
成果,params3 参数又是一个 null。
哦豁,空指针就来了。
第 14 题
标题太长,我实在是看的费劲儿。
就……不解析了吧。
你发现了问题,能够再谈论区教教我。
第 15 题
这一题,说实话,我榜首眼没看出来问题。
隔了一会,第二眼看,也没看出来啥问题。
要不是这是一道标题,代表它的确有问题,否则在实践 review 的时分,我应该就放过这个部分了。
当我带着它一定是有问题的心态去看问题的时分,总算仍是发现了端倪。
第 6 行,多线程操作,一般命题人会在多线程里边埋坑,只要找出多线程里边线程不安全的操作即可。
而多线程里边的中心逻辑是给 Status 的 reason 字段赋值。再定眼一看:Status 是个枚举啊。
枚举是单例啊。
单例目标的一个字段,在多线程里边被疯狂操作…
你理解我意思吧?
我给你搞段代码验证一下:
首先,把 List 里边的目标搞得多多的。
然后,closeStatus 办法里边打印 log 的时分,入参的 id 必定要和 Status 枚举中的 reason 这个字符串里边拼接的 id 相同,对吧?
所以,我在 log 里边打印日志的时分,判别 reason 不包含当时的 id。
假如有输出,则阐明有问题,能反响过来吧?
程序跑起来,的确有输出:那就阐明的确有问题:
id:4548当时的状况是:封闭改变原由于:封闭当时状况3370
id:796当时的状况是:封闭改变原由于:封闭当时状况3300
id:8621当时的状况是:封闭改变原由于:封闭当时状况8000
id:9791当时的状况是:封闭改变原由于:封闭当时状况7528
id:8283当时的状况是:封闭改变原由于:封闭当时状况7842
不要在多线程里边对单例目标进行修正操作,你掌握不住。
第 16 题
这题,拿到手榜首眼,十分扎眼的一个问题便是 SearchCategory 目标里边的特点没有被 private 修饰,不满足 Java 关于目标进行封装的思维。
第二个调查点其实也不难,第 22 行,两头都是 Integer 目标,针对封装目标的比照,应该运用 !equals 而不是 !=。
别问为什么,问便是 Integer 缓存了 -128~127 之间的数字,在这个范围内用 == 和这个范围外用 ==,运行成果纷歧样。
老陈腔滥调了,不细说了。
第三个调查点,稍微稍微荫蔽一点:SearchCategory 没有重写 hashcode 和 equals 办法。
categorySet 是一个 Set 调集目标。第 25 行里边,在把 SearchCategory 往 categorySet 里边放。
自定义目标往 Set 调集里边放,假如不重写 hashcode 和 equals 办法,那么会有重复元素滴。
假如这个你一时刻没反响过来的话,那我问你一个老陈腔滥调:HashMap 的 key 是自定义目标的时分,应该留意什么?
这样一听,是不是就了解多了。
你在结合我之前写过的这篇文章:《悄然给你说几个HashCode的破事。》
轻松拿下。
第 17 题
这题我没搞懂第 6 行和第 9 行,调用了 queryUserStock 办法,可是这个办法又在同一个类里边。
为什么要经过自己注入自己的办法去调用呢?
又不涉及到比方业务、缓存这些切面相关的东西。所以我觉得这两行代码没用。
然后是第 4 行里边的入参 userId 是根本类型 long,而到了 14 行里边就变成了包装类型 Long。
所以,从这个代码片段来说,15 行的判别永远为 true,由于传递进来的 userId 是根本类型,默认值为 0,不或许为 null。
应该保持类型一致,都用包装类型。
还有一个点是 11 行,关于返回的调集没有做非空判别,假如空指针了呢,对吧?
对个毛线,我在这儿虚晃一枪,看看你有没有带着自己的思考看文章。
14 行的办法是不会返回空指针的,假如没查到数据,MyBatis 会主动帮咱们封装为空调集,而不是 null。
所以外面拿到空调集也没有任何缺点。
别的,再这儿我再次重申一下:官方也没有给出悉数的标题解析,所以我给的答案纷歧定是正确的,你要带着批评的眼光来看。
我等着你发现错误,纠正我呢。
第 18 题
标题做到这儿后,一看到多线程,就开端条件反射式的想到了线程安全问题。
定眼一看,33 行,CopyOnWriteArrayList 是线程安全的,看来这次的调查点纷歧样了。
从上往下看,首先是自定义线程池,我一眼就看出了问题:
(r, executor) -> log.error(“…”)
自定义回绝战略中,仅仅打印了一行日志,没有做其他任何操作,有坑,简略喜提生产事件:
概况见连接:《虽然是我遇到的一个扎手的生产问题,可是我写出来之后,便是你的了。》
所以这个回绝战略需求重新写一个。
第二个问题:21 行调用了 filter 办法,在这个办法里边用到了线程池,可是你细心剖析一下 34 行的这个 for 循环,要循环多少次?
是不是要循环“一页的巨细”,也便是第 19 行的 500 次。可是这个线程池的容量才多少?
这个线程池同时刻做多也只能包容 120 个使命,所以这个当地参数不合理,需求调整线程池参数。
别的,34 行这个循环目标也没有判空啊。这是一个 JSONObject 目标,一不留神,取出一个 null 怎么办?
第三个问题,35 行,用了 submit 提交使命,这个办法是有返回值的,可是代码里边又没有运用这个返回值,那么为什么不用 execute 办法呢?
第四个问题,47 行,await 办法没有指定等候时刻,简略死等。
比方就这个代码,线程池满了之后,并不会履行 countDown 办法,喜提一个永远在堵塞的线程。
这题靠多线程,算是撞我枪口上了,秒了,再会。
第 19 题
这题没啥特别好说的,第 18 行 repaceFirst 的榜首个入参是正则表达式。
所以,假如供应商的姓名里边有一些什么 *?.| 这些玩意,就会呈现问题。
其实 Sting 的一些关于内容替换的办法里边很多都是支撑正则的,所以假如你不清楚这个“坑”的话,那后边需求留意一下了。
这个题就不多说了,写个案例,一目了然:
别问我为什么一眼就看到了 repaceFirst,由于我在它身上吃过亏。还有 split,这个“坑爹”的办法,也是相同,有正则坑。
第 20 题
这题…
恕我学艺不精,的确没发现有什么问题啊?
莫非是 13 行这个 false?它代表用 JDK 的动态署理,要完成接口,可是这个代码也的确完成了接口呀?
肉眼没看出来,我把代码放到自己的 idea 里边去跑也是能正常跑的:
假如这题在 AOP 上有考点的话,并且我觉得必定是有考点的,我的确拿不到这个分。
假如考的是不要把 http 接口写在项目启动类上的这种编码结构上的标准的话。好吧,只能说考查视点刁钻。
不知道你有没有看出啥问题,能够在谈论区教教我。
第 21 题
只要几行代码,秒之。
第 7、8 行,用 redis 加锁,set 办法和给值设置过期时刻的办法非原子性,假如 set 完成之后,expire 无法履行, 会呈现死锁。
第 11 行,捕获反常后没有经过日志打印反常详细的信息,不便利追溯问题。
第 22 题
其实标题做到这儿的时分,都现已有点掉入思维圈套里边了。
啥意思呢。
比方这个题,我仅仅瞟了一眼,我就知道 34 行的这个办法里边必定是有问题的。
由于假如没有问题,依照命题人的习惯,这个办法的完成会省略。
所以,当我带着这个想法去看这部分代码的时分,getCerts 之后直接调用了 get(0),不用判别调集目标是否为空吗?
可是,你留意啊,其实在 23 行的时分判别了。
所以这个当地其实不是空指针的问题。
我觉得应该是需求结合到业务场景来看的,certs 是一个调集,里边放的是乘机人的证件,一个人或许有多个证件,比方身份证、护照、工牌、打工人认证、一叠好人卡等等这些玩意,所以用调集来装,和合理。
可是,get(0) 你怎么给我确保获取出来的不是好人卡,而是身份证呢?
代码中都没有表现这个逻辑,所以我怀疑命题人在这儿是埋坑了。
第二个问题就很显着了,你细心看看标题中的三次 stream 操作。前两次操作的都是同一个调集,然后用成果去掩盖了 passengers 参数。
看代码,是会一点点流式操作的,可是不多,否则也不会分隔写了,三次操作彻底能够合并为一个。
第 23 题
这题一看…
代码太多,看得我眼睛充血,脑壳疼。
再一看题,依照“充血形式”开发,补全一些代码。
这玩意感觉得结合着业务来才行啊,算了,我抛弃,好吧。
这题我不做了。
你来。
第 24 题
感觉问题在 21 行的这个 Lists.transform 办法上,可是我没有用过这个办法,不清楚是干啥的。
所以在网上查了一圈:
简略来说便是第 24 行这个 for 循环是没有用的。你能够理解未返回的这个 addresses 仅仅是一个视图,对视图进行修正,没有任何卵用。
然后是相同没有看到关于反常的处理机制,假如没有大局反常处理机制的话,有或许会直接抛给用户不友好的错误提示。
第 25 题
第 29 行,又看到业务了,又是业务不收效,就不多说了。这个业务不收效这一点的确是一大考点,一定要把这个玩意搞的滚瓜烂熟才行。
然后是 41 行到44 行,这是个什么奇特的写法,链式 set? 没见过。
所以我感觉这个当地也是有问题的,应该是想用 Builder 形式来构建目标吧。
别的,saveData 办法里边有一些数据库操作,难免会呈现一点反常状况,在这个办法里边我没有看到关于反常的相关捕获和处理。
最终一个点,38 行,除非你用了 MDC 链路追踪,否则这行日志没有任何含义啊,至少再加上一个订单号吧。才干一眼看出来是哪个订单号被成功更新了。
附加题
官方的标题只公开了 25 题,可是歪歪歪师傅也想给你出几道附加题,让你来体验一下。
我丑话说在前头:做不出来的,都是假粉丝。
再多说一句,我都说了是附加题了,这些题必定就很偏了,看着图个乐就行,真实的代码 review 留意点,仍是得看前面的标题。
附加题一
请找出下面代码的问题:
《附加题一的答案》
附加题二
请找出下面代码的问题:
《附加题二的答案》
附加题三
请解说下面代码不能正常完毕的原因:
《附加题三的答案》
附加题四
asyncResult 是一个 Future 目标,下面框起来的代码能够以为是无限期等候,请指出其和 future.get() 的区别:
《附加题四的答案》
附加题五
请找出下面代码的问题:
《附加题五的答案》
附加题六
请解说下面代码不能正常完毕的原因:
《附加题六的答案》
附加题七
以下程序是否会抛出空指针反常,并解说为什么:
《附加题七的答案》
附加题八
请找出下面代码的问题:
《附加题八的答案》
再说一次,附加题,考点很偏,看着图个乐就行。别把自己给 PUA 了。
最终,求个赞,不过火吧?
“本文正在参加「金石方案」”