# ***\*卓云科技JAVA规范\*******

| 日期       | 修订版本  | 修订描述 | 编写人 |
| ---------- | --------- | -------- | ------ |
| 2024.05.23 | v01.00.00 |          |        |
|            |           |          |        |
|            |           |          |        |
|            |           |          |        |

# **一、** ***\*命名风格\****

1. 【强制】代码中的命名均不能以下划线或美元符号开始，也不能以下划线或美元符号结束。 

2. 【强制】代码中的命名严禁使用拼音与英文混合的方式，更不允许直接使用中文的方式。 

   ​	说明：正确的英文拼写和语法可以让阅读者易于理解，避免歧义。注意，即使纯拼音命名方式 也要避免采用。 	alibaba / taobao / youku / hangzhou 等国际通用的名称，可视同英文。

3. 【强制】类名使用 UpperCamelCase 风格，但以下情形例外：DO / BO / DTO / VO / AO / PO / UID 等。 

4. 【强制】方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格，必须遵从 驼峰形式。

5. 【强制】常量命名全部大写，单词间用下划线隔开，力求语义表达完整清楚，不要嫌名字长。

6. 【强制】抽象类命名使用 Abstract 或 Base 开头；异常类命名使用 Exception 结尾；测试类 命名以它要测试的类的名称开始，以 Test 结尾。

7. 【强制】包名统一使用小写。

8. 【强制】为了达到代码自解释的目标，任何自定义编程元素在命名时，使用尽量完整的单词 组合来表达其意。**禁止使用a, b, c这种随意的命名方式**

9. 【推荐】如果模块、接口、类、方法使用了设计模式，在命名时需体现出具体模式。

10.  【推荐】接口类中的方法和属性不要加任何修饰符号（public 也不要加），保持代码的简洁 性，并加上有效的 Javadoc 注释。尽量不要在接口里定义变量，如果一定要定义变量，肯定是 与接口方法相关，并且是整个应用的基础常量。

    ​	说明：JDK8 中接口允许有默认实现，那么这个 default 方法，是对所有实现类都有价值的默 认实现。

11. 接口和实现类的命名有两套规则：

    1. 【强制】对于 Service 和 DAO 类，基于 SOA 的理念，暴露出来的服务一定是接口，内部 的实现类用 Impl 的后缀与接口区别。 

       ​	正例：CacheServiceImpl 实现 CacheService 接口。 

    2. 【推荐】如果是形容能力的接口名称，取对应的形容词为接口名（通常是–able 的形式）。

       ​	正例：AbstractTranslator 实现 Translatable 接口。

12. 【参考】枚举类名建议带上 Enum 后缀，枚举成员名称需要全大写，单词间用下划线隔开
13. 

# ***\*二、常量定义 \****

1. 【强制】在 long 或者 Long 赋值时，数值后使用大写的 L，不能是小写的 l，小写容易跟数字 1 混淆，造成误解。
2. 【推荐】不要使用一个常量类维护所有常量，要按常量功能进行归类，分开维护。

# ***\*三、代码格式\****

1. 【强制】大括号的使用约定。如果是大括号内为空，则简洁地写成{}即可，不需要换行；如果 是非空代码块则： 1） 左大括号前不换行。 2） 左大括号后换行。 3） 右大括号前换行。 4） 右大括号后还有 else 等代码则不换行；表示终止的右大括号后必须换行。

2.  【强制】左小括号和字符之间不出现空格；同样，右小括号和字符之间也不出现空格；而左大 括号前需要空格。

   ​	反例：if (空格 a == b 空格)

3.  【强制】任何二目、三目运算符的左右两边都需要加一个空格。

   ​	 说明：运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。

4. 【强制】单行代码超出屏幕前必须换行，编辑器禁止出现左右滚动条

5. 【强制】方法参数在定义和传入时，多个参数逗号后边必须加空格。

6. 【强制】IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式， 不要使用 Windows 格式。

7. 【推荐】单个方法的总行数不要过长。

# ***\*四、卓云科技OOP规约\****

1. 【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法，无谓增加编译器解析成 本，直接用类名来访问即可。
2. 【强制】所有的覆写方法，必须加@Override 注解
3. 【强制】Object 的 equals 方法容易抛空指针异常，应使用常量或确定有值的对象来调用 equals。
4. 【强制】所有的相同类型的包装类对象之间值的比较，全部使用 equals 方法比较。
5. 【强制】所有的 POJO 类属性必须使用包装数据类型。
6. 【强制】构造方法里面禁止加入任何业务逻辑，如果有初始化逻辑，请放在 init 方法中。
7. 【推荐】当一个类有多个构造方法，或者多个同名方法，这些方法应该按顺序放置在一起， 便于阅读
8. 【推荐】循环体内，字符串的连接方式，使用 StringBuilder 的 append 方法进行扩展

#  ***\*五、注释规约\**** 

1. 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范，使用/**内容*/格式，不得使用 // xxx 方式。
2. 【强制】所有的抽象方法（包括接口中的方法）必须要用 Javadoc 注释、除了返回值、参数、 异常说明外，还必须指出该方法做什么事情，实现什么功能。
3. 【强制】方法内部单行注释，在被注释语句上方另起一行，使用//注释。方法内部多行注释 使用/* */注释，注意与代码对齐。
4. 【强制】所有的枚举类型字段必须要有注释，说明每个数据项的用途
5. 【推荐】代码修改的同时，注释也要进行相应的修改，尤其是参数、返回值、异常、核心逻辑 等的修改。
6. 【推荐】谨慎注释掉代码。在上方详细说明，而不是简单地注释掉。如果无用，则删除。

# ***\*六、集合处理\****

1. 【强制】初始化集合时，指定集合初始值大小。
   ​	说明：HashMap 使用如下构造方法，初始值大小为 (需要存储的元素个数 / 负载因子) + 1，负载因子默认为 0.75。

2. 【强制】使用 entrySet 遍历 Map 类集合 KV，而不是 keySet 方式进行遍历。
   ​	说明：keySet 其实是遍历了 2 次，一次是转为 Iterator 对象，另一次是从 HashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中，性能更优。

3. 【强制】使用 Collections.isEmpty() 检测空，而非 size() == 0。
   ​	说明：某些集合类可能存在 size() 方法时间复杂度高的情况。

4. 【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式，如果并发操作，需要对 Iterator 对象加锁。
   ​	正例：
   ```java
   Iterator<String> iterator = list.iterator();
   while (iterator.hasNext()) {
       String item = iterator.next();
       if (需要删除) {
           iterator.remove();
       }
   }
   ```

5. 【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时，必须注意当 value 为 null 时会抛 NPE 异常。

6. 【推荐】集合使用时，尽量指明适用场景，区分 ArrayList、LinkedList 的适用场景。
   ​	说明：ArrayList 查询快，增删慢；LinkedList 增删快，查询慢。

7. 【推荐】高度注意 Map 类集合 K/V 能不能存储 null 值的情况。
   ​	说明：HashMap 可以存储 null 的 key 和 value；ConcurrentHashMap 不允许 null 的 key 和 value；TreeMap 不允许 null 的 key。

# ***\*七、异常处理\****

1. 【强制】不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类，如：IndexOutOfBoundsException / NullPointerException，这类异常由程序员预检查来规避，保证程序健壮性。

2. 【强制】不要使用 try-catch 捕获 Throwable， Throwable 是所有错误和异常的父类，捕获它会包含 Error 等严重问题。

3. 【强制】捕获异常后，必须记录异常堆栈信息，不要只输出异常名称。
   ​	反例：e.printStackTrace()
   ​	正例：logger.error("处理失败", e);

4. 【强制】不要在 finally 块中使用 return。
   ​	说明：finally 块中的 return 返回后，try 块中的 return 失效。

5. 【强制】捕获后的异常，若需要重新抛出，必须保留原有异常信息。
   ​	正例：throw new BusinessException("系统异常", e);

6. 【强制】在调用 RPC、二方包、或动态生成类的相关方法时，必须要捕获异常。

7. 【强制】NPE 是程序员最常遇到的异常，必须进行预防性检查。
   ​	说明：当方法返回值为集合或对象时，先判空再操作。

8. 【推荐】尽量使用 try-with-fileStorages 语句关闭资源。
   ​	正例：
   ```java
   try (InputStream is = new FileInputStream(path)) {
       // 业务逻辑
   }
   ```

9. 【推荐】捕获异常与抛出异常必须是完全匹配，或者捕获异常是抛异常的父类。
   ​	说明：如果预期抛出的是 A 异常，实际捕获了 B 异常（A 的父类），可能导致问题掩盖。

# ***\*八、并发/多线程\****

1. 【强制】创建线程或线程池时请指定有意义的线程名称，方便出错时回溯。

2. 【强制】线程池不允许使用 Executors 去创建，而是通过 ThreadPoolExecutor 的方式，这样的处理方式让写的同学更加明确线程池的运行规则，规避资源耗尽的风险。
   ​	说明：Executors 返回的线程池对象的弊端如下：
   1）FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE，可能会堆积大量请求，从而导致 OOM。
   2）CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE，可能会创建大量线程，从而导致 OOM。

3. 【强制】SimpleDateFormat 是线程不安全的类，一般不要定义为 static 变量，如果定义为 static，必须加锁，或者使用 DateUtils 工具类。
   ​	正例：注意线程安全，使用 DateUtils。亦推荐如下处理：
   ```java
   private static final ThreadLocal<SimpleDateFormat> formatter =
       ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
   ```

4. 【强制】高并发时，同步调用应该去考量锁的性能损耗。能用无锁数据结构，就不要用锁；能锁区块，就不要锁整个方法体；能用对象锁，就不要用类锁。

5. 【强制】对多个资源、数据库表、对象同时加锁时，需要保持一致的加锁顺序，否则可能会造成死锁。

6. 【强制】并发修改同一记录时，避免更新丢失，需要加锁。要么在应用层加锁，要么在缓存加锁，要么在数据库使用乐观锁，使用 version 作为更新依据。
   ​	说明：如果每次访问冲突概率小于 20%，推荐使用乐观锁，否则使用悲观锁。乐观锁的重试次数不得小于 3 次。

7. 【强制】避免 Random 实例被多线程使用，虽然共享该实例是线程安全的，但会因竞争同一 seed 导致的性能下降。
   ​	说明：在 JDK7 之后，可以直接使用 ThreadLocalRandom。

8. 【推荐】volatile 解决多线程内存不可见问题。对于一写多读，是可以解决变量同步问题的，但是如果多写，同样无法解决线程安全问题。
   ​	说明：如果是 count++ 操作，使用如下类实现：AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8，推荐使用 LongAdder。

9. 【推荐】HashMap 在容量不够进行扩容时，rehash 操作非常耗费性能，所以在能够预估容量大小的情况下，使用初始化容量可以减少扩容次数。

# ***\*九、日志规范\****

1. 【强制】应用中不可直接使用日志系统（Log4j、Logback）中的 API，而应依赖使用日志框架（SLF4J、JCL）中的 API。
   ​	说明：使用门面模式的日志框架，有利于维护和各个类的日志处理方式统一。

2. 【强制】所有日志文件至少保存 15 天，因为有些异常具备以"周"为频次发生的特点。

3. 【强制】应用中的扩展日志（如打点、临时监控、访问日志等）命名方式：appName_logType_logName.log。
   ​	说明：推荐使用工具类统一生成日志名称，避免手动命名造成的混乱。

4. 【强制】在日志输出时，字符串变量之间的拼接使用占位符的方式。
   ​	说明：因为 String 字符串的拼接会使用 StringBuilder 的 append() 方式，有一定的性能损耗。使用占位符仅是替换动作，可以有效提升性能。
   ​	正例：logger.debug("处理结果：{}", result);

5. 【强制】对于 trace/debug/info 级别的日志输出，必须进行日志级别的开关判断。
   ​	说明：虽然在 debug(parameters) 方法的内部会判断日志级别，但参数的字符串拼接仍然会执行，影响性能。
   ​	正例：
   ```java
   if (logger.isDebugEnabled()) {
       logger.debug("处理结果：{}", result);
   }
   ```

6. 【强制】避免重复打印日志，浪费磁盘空间，务必在 log4j.xml 中设置 additivity=false。
   ​	正例：<logger name="com.taobao.dubbo" additivity="false">

7. 【强制】谨慎地记录日志。生产环境禁止输出 debug 日志；有选择地输出 info 日志；如果使用 warn 来记录刚上线时的业务行为信息，一定要注意日志输出量的问题，避免把服务器磁盘撑爆，并记得及时删除这些观察日志。

8. 【强制】生产环境禁止使用 System.out 或 System.err 输出日志。

9. 【强制】异常信息应该包括两类信息：案发现场信息和异常堆栈信息。如果不处理，那么通过关键字往上找。

10. 【强制】日志打印时禁止使用行号来记录日志位置信息。

11. 【推荐】谨慎地记录日志。生产环境禁止输出 debug 日志；有选择地输出 info 日志。

12. 【推荐】可以使用 warn 日志级别来记录用户输入参数错误的情况，避免用户投诉时，无所适从。注意日志输出的级别，error 级别只记录系统逻辑出错、异常等重要的错误信息。

# ***\*十、控制语句\****

1. 【强制】switch 块必须包含 default 分支，且 default 分支应该是最后一个分支。

2. 【强制】switch 语句中的每个 case 分支必须包含 break、return 或 throw 语句，防止 case 穿透。
   ​	说明：如果确实需要 case 穿透，必须显式注释说明。

3. 【强制】在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码，避免采用 单行的编码方式：if (condition) statements;

4. 【推荐】表达异常的分支时，少用 if-else 方式，推荐使用卫语句。
   ​	说明：if-else 方式会嵌套过深，卫语句可以使代码更清晰。
   ​	反例：
   ```java
   if (condition) {
       // 业务逻辑
   } else {
       return;
   }
   ```
   ​	正例：
   ```java
   if (!condition) {
       return;
   }
   // 业务逻辑
   ```

5. 【推荐】不要在条件判断中执行复杂逻辑或复杂表达式，以提高代码可读性。

# ***\*十一、NPE 防护\****

1. 【强制】禁止使用对象直接调用 Object 类中的 equals 方法，而应该使用常量或确定有值的对象来调用 equals。
   ​	正例："test".equals(object);
   ​	反例：object.equals("test");

2. 【强制】所有相同类型的包装类对象之间值的比较，全部使用 equals 方法比较。
   ​	说明：对于 Integer var = ? 在-128 至 127 之间的赋值，Integer 对象是在 IntegerCache.cache 产生，会复用已有对象，这个区间内的 Integer 值可以进行 == 判断，但是这个区间之外的所有数据，都会在堆上产生，并不会复用已有对象，推荐使用 equals 方法。

3. 【强制】任何数据的金额、金额计算，必须使用 BigDecimal，严禁使用 float、double。
   ​	说明：float 和 double 存在精度丢失问题。

4. 【强制】BigDecimal 的等值比较应使用 compareTo() 方法，而不是 equals() 方法。
   ​	说明：equals() 会比较精度，如 new BigDecimal("1.0").equals(new BigDecimal("1.00")) 为 false。

5. 【强制】基本数据类型与包装数据类型的使用标准：
   1） 所有的 POJO 类属性必须使用包装数据类型。
   2） RPC 方法的返回值和参数必须使用包装数据类型。
   3） 所有的局部变量推荐使用基本数据类型。
   ​	说明：POJO 类属性没有初值是提醒使用者在需要使用时，必须自己显式赋值，任何 NPE 问题，或者入库检查，都由使用者来保证。

6. 【推荐】当方法返回值为集合或对象时，若可能为 null，建议使用空集合或空对象返回，避免 NPE。
   ​	说明：可以使用 Collections.emptyList() 返回空列表。

7. 【推荐】使用 Optional 类来包装可能为 null 的值。
   ​	正例：
   ```java
   Optional<String> name = Optional.ofNullable(getName());
   name.ifPresent(n -> System.out.println(n));
   ```

8. 【推荐】在进行数据库查询时，若结果可能为空，必须判空后再使用。

# ***\*十二、序列化规范\****

1. 【强制】序列化类新增属性时，请不要修改 serialVersionUID 字段，避免反序列化失败；如果完全不兼容升级，避免反序列化混乱，那么请修改 serialVersionUID 值。
   ​	说明：注意 serialVersionUID 不一致会抛出序列化运行时异常。

2. 【强制】禁止在序列化类中，使用未声明为 static final 的 serialVersionUID 字段。
   ​	说明：private static final long serialVersionUID = 1L;

3. 【强制】序列化类中的属性必须实现 Serializable 接口。

4. 【强制】序列化类中不建议使用非静态的内部类。
   ​	说明：内部类会持有外部类的引用，可能导致序列化问题。

5. 【推荐】当序列化类中有自定义的序列化逻辑时，请使用 readObject、writeObject 方法，并声明为 private。

6. 【推荐】序列化类中的敏感字段（如密码、密钥）建议标记为 transient，避免序列化到外部存储中。

# ***\*十三、安全规范\****

1. 【强制】用户请求的参数必须进行校验，包括：非空校验、长度校验、格式校验、范围校验等。
   ​	说明：避免恶意参数导致系统异常或被攻击。

2. 【强制】禁止直接将用户输入拼接到 SQL 语句中，必须使用参数化查询（PreparedStatement）。
   ​	说明：防止 SQL 注入攻击。

3. 【强制】用户输入的 HTML 标签必须进行转义或过滤，防止 XSS 攻击。
   ​	说明：可以使用 OWASP ESAPI 或框架自带的转义工具。

4. 【强制】禁止在日志中输出敏感信息，包括但不限于：密码、身份证号、银行卡号、手机号、Token、密钥等。
   ​	说明：敏感信息需要脱敏处理，如：138****1234。

5. 【强制】密码必须使用不可逆加密算法存储，推荐使用 BCrypt、PBKDF2、Argon2、国密 等算法。
   	反例：使用 MD5、SHA1 等弱哈希算法存储密码。
   	正例：使用 BCrypt.encode(password) 加密存储。

6. 【强制】密钥、密码等敏感信息禁止硬编码在代码中，必须配置在配置文件或密钥管理系统中。
   ​	说明：代码可能会被泄露，配置文件可以单独管理和加密。

7. 【强制】文件上传必须进行严格校验：
   1） 校验文件类型（通过文件头校验，而不仅仅是文件后缀）。
   2） 校验文件大小。
   3） 重命名上传的文件，防止路径遍历攻击。
   4） 上传文件必须存储在非 Web 根目录下。
   5） 禁止上传可执行文件（如 .jsp、.exe、.sh 等）。

8. 【强制】对外提供的接口必须进行权限校验，防止越权访问。
   ​	说明：包括接口鉴权、数据权限校验、操作权限校验等。

9. 【强制】涉及资金、重要数据的操作，必须进行二次验证或审批，防止误操作或恶意操作。

10. 【强制】使用 HTTPS 协议传输敏感数据，禁止使用 HTTP 传输明文敏感信息。

11. 【强制】敏感数据的加密密钥必须定期更换，并建立密钥管理机制。

12. 【强制】禁止在 URL、Cookie 中传输敏感信息，如密码、Token 等。

13. 【强制】对于重定向操作，必须校验目标 URL，防止开放重定向攻击。
    ​	说明：禁止直接使用用户输入的 URL 进行重定向。

14. 【强制】定时任务、批处理任务等后台任务，必须进行权限控制和操作日志记录。

15. 【推荐】使用成熟的权限框架，如 Spring Security、Apache Shiro 等。

16. 【推荐】对于重要的接口，推荐添加防重放攻击机制，如：Nonce、时间戳、签名等。

17. 【推荐】对于第三方接口调用，必须添加超时控制和重试机制，防止资源耗尽。

18. 【推荐】定期进行安全扫描和漏洞检测，及时修复已知漏洞。

19. 【推荐】建立安全事件响应机制，发现安全问题时能及时处理和追溯。

# ***\*十四、其他\****

1. 【推荐】循环体中的语句要考量性能，以下操作尽量移至循环体外处理，如定义对象、变量、获取数据库连接，进行不必要的 try-catch 操作
2. 【推荐】避免采用取反逻辑运算符。
3. 【推荐】对外提供的开放接口，尽量进行参数校验
4. 【推荐】及时清理不再使用的代码段或配置信息

