分享一些你代码更好的小建议,流畅编码提搞效率

29 篇文章 1 订阅

看过很多思考不够深入的代码,因此写一下总结吧,让你代码更好的小建议。希望大家日常写代码多点思考,多点总结,加油!同时哪里有不对的,也望指出,感谢哈~

1. 仅仅判断是否存在时,select count 比 select 具体的列,更好。

我们经常遇到类似的业务场景,如,判断某个用户userId是否是会员。

「(反例):」  一些小伙伴会这样实现,先查从用户信息表查出用户记录,然后再去判断是否是会员:

<select id="selectUserByUserId" resultMap="BaseResultMap">
     selct user_id , vip_flag from  user_info where user_id =#{userId};
 </select>

boolean isVip (String userId){
 UserInfo userInfo = userInfoDAp.selectUserByUserId(userId);
 return UserInfo!=null && "Y".equals(userInfo.getVipFlag())
}

「(正例):」 针对这种业务场景,其实更好的实现,是直接select count一下,或者select limit 1如下:

<select id="countVipUserByUserId" resultType="java.lang.Integer">
     selct count(1) from  user_info where user_id =#{userId} and vip_flag ='Y';
 </select>
 
 boolean isVip (String userId){
  int vipNum = userInfoDAp.countVipUserByUserId(userId);
  return vipNum>0
}

2. 复杂的if逻辑条件,可以调整顺序,让程序更高效

假设业务需求是这样:如果用户是会员,并且第一次登陆时,需要发一条通知的短信。假如没有经过思考,代码很可能直接这样写了。

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放在前面更高效。

3. 写查询Sql的时候,只查你需要用到的字段,还有通用的字段,拒绝反手的select *

「反例:」

select * from user_info where user_id =#{userId};

「正例:」

 select user_id , vip_flag from  user_info where user_id =#{userId};

「理由:」

  • 节省资源、减少网络开销。

  • 可能用到覆盖索引,减少回表,提高查询效率。

4. 优化你的程序,拒绝创建不必要的对象

如果你的变量,后面的逻辑判断,一定会被赋值;或者说,只是一个字符串变量,直接初始化字符串常量就可以了,没有必要愣是要new String().

反例:

String s = new String ("捡田螺的小男孩");

正例:

String s=  "捡田螺的小男孩 ”;

5. 初始化集合时,指定容量

阿里的开发手册,也明确提到这个点:

假设你的map要存储的元素个数是15个左右,最优写法如下

 //initialCapacity = 15/0.75+1=21
 Map map = new HashMap(21);
 
 又因为hashMap的容量跟2的幂有关,所以可以取32的容量
 Map map = new HashMap(32);

6.catch了异常,需要打印出具体的exception,方便更好定位问题

「反例:」

try{
  // do something
}catch(Exception e){
  log.info("捡田螺的小男孩,你的程序有异常啦");
}

「正例:」

try{
  // do something
}catch(Exception e){
  log.info("捡田螺的小男孩,你的程序有异常啦:",e); //把exception打印出来
}

「理由:」

  • 反例中,并没有把exception出来,到时候排查问题就不好查了啦,到底是SQl写错的异常还是IO异常,还是其他呢?所以应该把exception打印到日志中哦~

7. 打印日志的时候,对象没有覆盖Object的toString的方法,直接把类名打印出来了。

我们在打印日志的时候,经常想看下一个请求参数对象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='捡田螺的小男孩'}

8. 一个方法,拒绝过长的参数列表。

假设有这么一个公有方法,形参有四个。。。

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对象包装一下,即使后面有参数变动,也可以不用动对外接口了,好处杠杠的。

9. 使用缓冲流,减少IO操作

「反例:」

/**
 *  
 *  @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

使用FileInputStreamFileOutputStream实现文件读写功能,是没有什么问题的。但是呢,可以使用缓冲流BufferedReaderBufferedWriterBufferedInputStreamBufferedOutputStream等,减少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

10. 优化你的程序逻辑,比如前面已经查到的数据,在后面的方法也用到的话,是可以把往下传参的,减少方法调用/查表

「反例:」

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在写代码方面,Mac 和 Windows 都是很好的选择。这两种操作系统都供了众多的代码编辑器和集成开发环境 (IDE),可以满足不同的开发需求。 两者的区别通常在于用户偏好和生产力方面。每个人都有自己的喜好和使用习惯,所以哪一个更好取决于个人喜好。例如,有些人喜欢 Mac 的界面风格和触摸板,而有些人则更喜欢 Windows 的界面和快捷键。 就生产力而言,也并没有明确的优劣之分。如果你已经熟悉某一种操作系统,或者你所使用的软件只能在某一种操作系统上运行,那么这种操作系统可能会更适合你。 总的来说,决定使用 Mac 还是 Windows 并不是决定代码质量的关键因素。重要的是要选择一款适合自己的编辑器或 IDE,并且要坚持不懈地学习和练习。 ### 回答2: 用Mac写代码相比于Windows更好的原因有以下几点: 首先,Mac操作系统(macOS)基于UNIX,具有良好的稳定性和安全性。与Windows相比,Mac系统更加稳定,不容易崩溃或出现各种问题,可以提高代码编写的效率。 其次,Mac系统供了一个优秀的开发者工具集合,如Xcode和Terminal。Xcode是一个功能强大且易于使用的开发环境,它支持多种编程语言和框架,包括Swift和Objective-C。Terminal则供了一个可定制的命令行界面,方便开发者进行各种操作和调试。 此外,Mac系统还内置了一些很有用的开发工具,如Git和Homebrew。Git是一个版本控制系统,可以方便地追踪和管理代码的变更。Homebrew是一个软件包管理器,可以方便地安装和更新各种开发工具和库。 另外,对于移动开发者来说,Mac是首选平台之一。因为iOS开发只能使用Xcode,而Xcode只能在Mac上运行。如果你想开发iOS应用或者移动端的游戏,使用Mac是必要的。 最后,Mac系统的用户体验也是值得一的。Mac的界面设计简洁美观,操作流畅,给人一种愉悦的编程环境。此外,Mac上的应用程序往往更加注重用户体验,这有助于提高开发者的效率和工作体验。 总结而言,虽然开发者可以使用Windows进行编程,但是考虑到Mac系统的稳定性、开发者工具集合、移动开发支持以及用户体验,用Mac写代码通常会更好。 ### 回答3: 用Mac写代码相对于Windows来说有以下几个优点。 首先,Mac操作系统(OS X)是基于UNIX的,相比Windows更加稳定和可靠。它具有更好的性能和更高的效率,大大减少了系统崩溃和软件错误的风险,提高了开发的稳定性和质量。 其次,Mac拥有更好的开发工具和环境。Xcode是专为Mac开发者设计的集成开发环境(IDE),供了丰富强大的工具和调试功能,使得开发者可以更快速、高效地编写和调试代码。此外,Mac还支持众多的开发平台和编程语言,如iOS开发、Swift语言等,有助于开发者扩展技术能力。 第三,Mac具有漂亮的用户界面和出色的用户体验。Mac操作系统注重于用户界面的设计和用户体验的升,使开发者可以更加舒适和愉快地编写代码,减少了开发过程中的疲劳感,提高了工作效率。 第四,Mac具有更好的兼容性和稳定性。由于Mac的硬件和软件都是由苹果公司直接控制和设计的,所以在硬件和软件的匹配上更加紧密,具有更好的兼容性和稳定性,能够更好的开发环境和用户体验。 综上所述,相对于Windows,使用Mac进行编码更好的稳定性、更强大的开发工具和环境、更优秀的用户界面和用户体验,更好的兼容性和稳定性等优势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值