前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何优雅的编写代码(持续更新......)

如何优雅的编写代码(持续更新......)

作者头像
botkenni
发布2022-03-24 11:44:35
7830
发布2022-03-24 11:44:35
举报
文章被收录于专栏:IT码农

《编写可读代码的艺术》

《代码整洁之道》

1、如何写出优雅的代码

命名规范

  1. 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
  2. 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式,尽量使用完整的英文名,不要嫌弃名字长。
  3. 抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以它要测试的类的名称开始,以Test结尾。
  4. POJO类中布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误。
  5. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
  6. 如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式,如:public class ResourceObserver。
  7. 接口类中的方法和属性不要加任何修饰符号(public也不要加),保持代码的简洁性,并加上有效的Javadoc注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。
  8. 接口和实现类的命名有两套规则: 1)对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别。 2)如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able的形式)
  9. 枚举类名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。
  10. 领域模型命名规约: 1)数据对象:xxxDO,xxx即为数据表名。 2)数据传输对象:xxxDTO,xxx为业务领域相关的名称。 3)展示对象:xxxVO,xxx一般为网页名称。 4)POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO
  11. 不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。
  12. 在long或者Long赋值时,数值后使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。
  13. 不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。

代码格式

  1. 左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格;而左大括号前需要空格。
  2. if/for/while/switch/do等保留字与括号之间都必须加空格。
  3. 任何二目、三目运算符的左右两边都需要加一个空格。
  4. 采用4个空格缩进,禁止使用tab字符。
  5. 注释的双斜线与注释内容之间有且仅有一个空格。
  6. 单行字符数限制不超过120个,超出需要换行,换行时遵循如下原则: 1)运算符与下文一起换行。 2)方法调用的点符号与下文一起换行。 3)方法调用中的多个参数需要换行时,在逗号后进行。 4)在括号前不要换行
  7. 单个方法的总行数不超过80行。
  8. 类、类属性、类方法的注释必须使用Javadoc规范,使用/**内容*/格式,不得使用//xxx方式。
  9. 所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
  10. 所有的类都必须添加创建者和创建日期。
  11. 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
  12. 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
  13. 谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///)来说明注释掉代码的理由。

OOP编程规范

  1. 所有的覆写方法,必须加@Override注解。
  2. 相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object。
  3. Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
  4. 所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。
  5. 关于基本数据类型与包装数据类型的使用标准如下: 1)所有的POJO类属性必须使用包装数据类型。 2)RPC方法的返回值和参数必须使用包装数据类型。 3)所有的局部变量使用基本数据类型。
  6. 定义DO/DTO/VO等POJO类时,不要设定任何属性默认值。
  7. 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。
  8. 禁止在POJO类中,同时存在对应属性xxx的isXxx()和getXxx()方法。
  9. 使用索引访问用String的split方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛IndexOutOfBoundsException的风险。
  10. setter方法中,参数名称与类成员变量名称一致,this.成员名=参数名。在getter/setter方法中,不要增加业务逻辑,增加排查问题的难度。
  11. 循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。
  12. 只要重写equals,就必须重写hashCode,如果自定义对象作为Map的键,那么必须重写hashCode和equals。
  13. ArrayList的subList结果不可强转成ArrayList,因为subList返回的是ArrayList的内部类SubList,并不是ArrayList,而是ArrayList的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。
  14. 使用集合转数组的方法,必须使用集合的toArray(T[]array),传入的是类型完全一样的数组,大小就是list.size()。
  15. 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法。asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
  16. 不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
  17. 集合初始化时,指定集合初始值大小。initialCapacity=(需要存储的元素个数/负载因子) + 1。注意负载因子(即loader factor)默认为0.75,如果暂时无法确定初始值大小,请设置为16(即默认值).
  18. 使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历。

并发处理

  1. 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
  2. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。否者系统可能创建大量同类线程,导致大量资源消耗在线程切换上,甚至产生OOM。
  3. 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
  4. SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。
  5. 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
  6. 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
  7. HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中可以使用其它数据结构或加锁来规避此风险。

控制流

  1. 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使空代码。
  2. 在高并发场景中,避免使用”等于”判断作为中断或退出的条件。
  3. 表达异常的分支时,少用if-else方式,这种方式可以改写成,可以使用卫语句、策略模式、状态模式等来代替。
  4. 除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
  5. 尽量避免采用取反逻辑运算符。
  6. 下列情形,不需要进行参数校验: 1)极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。 2)底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底层才会暴露问题。一般DAO层与Service层都在同一个应用中,部署在同一台服务器中,所以DAO的参数校验,可以省略。 3)被声明成private只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题,此时可以不校验参数。

参考资料

阿里巴巴Java开发手册

具体案例:

代码语言:javascript
复制
public static void main(String[] args) {
 
     Map<String, String> map = new HashMap<String, String>();
     map.put("1", "value1");
     map.put("2", "value2");
     map.put("3", "value3");
 
     //第一种:普遍使用,二次取值
     System.out.println("通过Map.keySet遍历key和value:");
     for (String key : map.keySet()) {
         System.out.println("key= "+ key + " and value= " + map.get(key));
     }
 
     //第二种
     System.out.println("通过Map.entrySet使用iterator遍历key和value:");
     Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
     while (it.hasNext()) {
         Map.Entry<String, String> entry = it.next();
         System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
     }
 
     //第三种:推荐,尤其是容量大时
     System.out.println("通过Map.entrySet遍历key和value");
     for (Map.Entry<String, String> entry : map.entrySet()) {
         System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
     }
 
     //第四种
     System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
     for (String v : map.values()) {
         System.out.println("value= " + v);
     }
  }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/01/16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 《编写可读代码的艺术》
  • 《代码整洁之道》
    • 命名规范
      • 代码格式
        • OOP编程规范
          • 并发处理
            • 控制流
              • 参考资料
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档