看过很多思考不够深入的代码,因此写一下总结吧,让你代码更好的小建议。希望大家日常写代码多点思考,多点总结,加油!同时哪里有不对的,也望指出,感谢哈~
我们经常遇到类似的业务场景,如,判断某个用户userId是否是会员。
「(反例):」 一些小伙伴会这样实现,先查从用户信息表查出用户记录,然后再去判断是否是会员:
boolean isVip (String userId){ UserInfo userInfo = userInfoDAp.selectUserByUserId(userId); return UserInfo!=null && "Y".equals(userInfo.getVipFlag())}
「(正例):」 针对这种业务场景,其实更好的实现,是直接select count一下,或者select limit 1如下:
boolean isVip (String userId){ int vipNum = userInfoDAp.countVipUserByUserId(userId); return vipNum>0}
假设业务需求是这样:如果用户是会员,并且第一次登陆时,需要发一条通知的短信。假如没有经过思考,代码很可能直接这样写了。
if(isUserVip && isFirstLogin){ sendMsgNotify();}
假设总共有5个请求进来,isUserVip通过的有3个请求,isFirstLogin通过的有1个请求。那么以上代码,isUserVip执行的次数为5次,isFirstLogin执行的次数也是3次,如下:
如果调整一下isUserVip和isFirstLogin的顺序呢?
if(isFirstLogin && isUserVip ){ sendMsg();}
isFirstLogin执行的次数是5次,isUserVip执行的次数是1次,如下:
如果你的isFirstLogin,判断逻辑只是select count 一下数据库表,isUserVip也是select count 一下数据库表的话,显然,把isFirstLogin放在前面更高效。
「反例:」
select * from user_info where user_id =#{userId};
「正例:」
select user_id , vip_flag from user_info where user_id =#{userId};
「理由:」
如果你的变量,后面的逻辑判断,一定会被赋值;或者说,只是一个字符串变量,直接初始化字符串常量就可以了,没有必要愣是要new String().
反例:
String s = new String ("这是一句话");
正例:
String s= "这是一句话 ”;
阿里的开发手册,也明确提到这个点:
假设你的map要存储的元素个数是15个左右,最优写法如下
//initialCapacity = 15/0.75+1=21 Map map = new HashMap(21); 又因为hashMap的容量跟2的幂有关,所以可以取32的容量 Map map = new HashMap(32);
「反例:」
try{ // do something}catch(Exception e){ log.info("捡田螺的小男孩,你的程序有异常啦");}
「正例:」
try{ // do something}catch(Exception e){ log.info("捡田螺的小男孩,你的程序有异常啦:",e); //把exception打印出来}
「理由:」
我们在打印日志的时候,经常想看下一个请求参数对象request是什么。于是很容易有类似以下这些代码:
publick Response dealWithRequest(Request request){ log.info("请求参数是:".request.toString)}
打印结果如下:
请求参数是:local.Request@49476842
这是因为对象的toString方法,默认的实现是“类名@散列码的无符号十六进制”。所以你看吧,这样子打印日志就没啥意思啦,你都不知道打印的是什么内容。
所以一般对象(尤其作为传参的对象),「都覆盖重写toString()方法」:
class Request { private String age; private String name; @Override public String toString() { return "Request{" + "age='" + age + '\'' + ", name='" + name + '\'' + '}'; }}publick Response dealWithRequest(Request request){ log.info("请求参数是:".request.toString)}
打印结果如下:
请求参数是:Request{age='26', name='这是一句话'}
假设有这么一个公有方法,形参有四个。。。
public void getUserInfo(String name,String age,String sex,String mobile){ // do something ...}
如果现在需要多传一个version参数进来,并且你的公有方法是类似dubbo这种对外提供的接口的话,那么你的接口是不是需要兼容老版本啦?
public void getUserInfo(String name,String age,String sex,String mobile){ // do something ...}/** * 新接口调这里 */public void getNewUserInfo(String name,String age,String sex,String mobile,String version){ // do something ...}
所以呢,一般一个方法的参数,一般不宜过长。过长的参数列表,不仅看起来不优雅,并且接口升级时,可能还要考虑新老版本兼容。如果参数实在是多怎么办呢?可以用个DTO对象包装一下这些参数呢~如下:
public void getUserInfo(UserInfoParamDTO userInfoParamDTO){ // do something ...}class UserInfoParamDTO{ private String name; private String age; private String sex; private String mobile;}
用个DTO对象包装一下,即使后面有参数变动,也可以不用动对外接口了,好处杠杠的。
「反例:」
/** * * @desc: 复制一张图片文件 */public class MainTest { public static void main(String[] args) throws FileNotFoundException { long begin = System.currentTimeMillis(); try (FileInputStream input = new FileInputStream("C:/456.png"); FileOutputStream output = new FileOutputStream("C:/789.png")) { byte[] bytes = new byte[1024]; int i; while ((i = input.read(bytes)) != -1) { output.write(bytes,0,i); } } catch (IOException e) { log.error("复制文件发生异常",e); } log.info("常规流读写,总共耗时ms:"+(System.currentTimeMillis() - begin)); }}
运行结果:
常规流读写,总共耗时ms:52
使用FileInputStream、FileOutputStream实现文件读写功能,是没有什么问题的。但是呢,可以使用缓冲流BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream等,减少IO次数,提高读写效率。
❝
如果是不带缓冲的流,读取到一个字节或者字符的,就会直接输出数据了。而带缓冲的流,读取到一个字节或者字符时,先不输出,而是等达到缓冲区的最大容量,才一次性输出。
❞
「正例:」
/** * * @desc: 复制一张图片文件 */public class MainTest { public static void main(String[] args) throws FileNotFoundException { long begin = System.currentTimeMillis(); try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("C:/456.png")); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("C:/789.png"))) { byte[] bytes = new byte[1024]; int i; while ((i = input.read(bytes)) != -1) { output.write(bytes,0,i); } } catch (IOException e) { log.error("复制文件发生异常",e); } log.info("总共耗时ms"+(System.currentTimeMillis() - begin)); }}
运行结果:
缓冲流读写,总共耗时ms:12
「反例:」
public Response dealRequest(Request request){ UserInfo userInfo = userInfoDao.selectUserByUserId(request.getUserId); if(Objects.isNull(request)){ return ; } insertUserVip(request.getUserId); }private int insertUserVip(String userId){ //又查了一次 UserInfo userInfo = userInfoDao.selectUserByUserId(request.getUserId); //插入用户vip流水 insertUserVipFlow(userInfo); ....}
很显然,以上程序代码,已经查到 userInfo,然后又把userId传下去,又查多了一次。。。实际上,可以把userInfo传下去的,这样可以省去一次查表操作,程序更高效。
「正例:」
public Response dealRequest(Request request){ UserInfo userInfo = userInfoDao.selectUserByUserId(request.getUserId); if(Objects.isNull(request)){ return ; } insertUserVip(userInfo);}private int insertUserVip(UserInfo userInfo){ //插入用户vip流水 insertUserVipFlow(userInfo); ....}
留言与评论(共有 0 条评论) “” |