safety-eval-service/docs/JAVA规范.md

366 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# ***\*卓云科技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 和 valueConcurrentHashMap 不允许 null 的 key 和 valueTreeMap 不允许 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-resources 语句关闭资源。
正例:
```java
try (InputStream is = new FileInputStream(path)) {
// 业务逻辑
}
```
9. 【推荐】捕获异常与抛出异常必须是完全匹配,或者捕获异常是抛异常的父类。
说明:如果预期抛出的是 A 异常,实际捕获了 B 异常A 的父类),可能导致问题掩盖。
# ***\*八、并发/多线程\****
1. 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
2. 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明Executors 返回的线程池对象的弊端如下:
1FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE可能会堆积大量请求从而导致 OOM。
2CachedThreadPool 和 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. 【推荐】及时清理不再使用的代码段或配置信息