Java后端规范
下面是我在公司规范中摘写出来的一部分内容,这部分对绝大部分的团队都是使用的,希望能对大家有帮助!
命名规范
1. 在 if/else/for/while/do 语句中必须使用大括号。
说明:即使只有一行代码,也禁止不采用大括号的编码方式:if (condition) statements;
反例:
public void initInventoryAdjustmentReportJob() throws Exception {
final JobNameEnum jobNameEnum = JobNameEnum.INIT_INVENTORY_ADJUSTMENT;
// 需要执行的站点
final List<MarketInfo> marketInfos = jobHelper.initExecuteMarketMergeEuNaIncludeUk(jobNameEnum);
if (CollectionUtil.isEmpty(marketInfos)) return;
// CenterId和国家映射关系
final Map<String, String> centerIdMap = cowService.centerIdMap();
正例:
if (flag) {
System.out.println("hello world");
}
2. 不允许任何魔法值(即未经定义的常量)直接出现在代码中。
说明:对于一些影响性能的参数值,后续可以考虑集成到Apollo配置中心
反例:
if (records.size() <= 5000) {
inventoryAdjustmentsService.saveBatchSomeColumn(records);
} else {
Lists.partition(records, 4000).forEach(v -> inventoryAdjustmentsService.saveBatchSomeColumn(v));
}
正例:
String KEY_PRE = "Id#taobao_1";
if (KEY_PRE.equals(key)) {
//...
}
3. Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
反例:
if (marketList.contains(marketId) || MarketHelper.SHARE_MARKET_ID.equals(marketId)){
List<String> countryList = new ArrayList<>();
if (warehouseById.getCountry().equals("EU")){
countryList.addAll(Country.getEuCountryListNew());
}else if (warehouseById.getCountry().equals("NA")){
countryList.addAll(Country.getNaCountryListNew());
}else{
countryList.add(warehouseById.getCountry());
}
if (countryList.contains(ss.getCountry())){
smartRepStorageVo.setOverseasShippedQuantity(nullTo0(smartRepStorageVo.getOverseasShippedQuantity()) + quantity);
}
}
正例:
public void f(String str){
String inner = "hi";
if (inner.equals(str)) {
System.out.println("hello world");
}
}
4. 所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生, 会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都 会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
反例:
public static boolean isShareMarket(Integer marketId) {
if (Objects.isNull(marketId)) return false;
return StorageSerial.SHARE_MARKET_ID == marketId;
}
正例:
Integer a = 235;
Integer b = 235;
if (a.equals(b)) {
// code
}
5. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词
反例:com.gerpgo.x.dao.KingdeeData
正例:com.gerpgo.x.dao.kingdee.data
6.类名使用 UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等,方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格, 例如: localValue / getHttpMessage() / inputUserId
反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion
正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion 7. 枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开,所有的枚举类型字段必须要有Javadoc注释,说明每个数据项的用途
说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
反例:
public enum MarketOtherParams {
MainWarehouseId,
DefaultWarehouseId,
TicketAssignPerson, //Email回复
TicketCallPerson, //Call回复
CharEncoding,
NumberCountType, //数值转换格式
LoginMsg //预警消息
}
正例:
/**
* 子账号生成状态枚举,请使用name比对
* @Author: balen
* @Date: 2021/6/22 11:31
*/
public enum GenerateStatusEnum {
/**
* 待执行
*/
NEW,
/**
* 执行成功
*/
SUCCESS,
/**
* 执行失败
*/
FAIL
}
8. 将所有XxxConstant类名修改为XxxConstants,常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护,说明:大而全的常量类,杂乱无章,使用查找功能才能定位到修改的常量,不利于理解,也不利于维护。例如:全局参数相关常量放在类GlobalConstants下;国家相关常量放在类 CountryConstants 下;通用常量放在类CommonConstants下;
反例:
public interface RocketmqConstant {
String XXL_JOB_MQ_TRIGGER_TOPIC = "XXL_JOB_MQ_TRIGGER_TOPIC";
String XXL_JOB_MQ_TRIGGER_TAG_LOGISTICS = "XXL_JOB_MQ_TRIGGER_TAG_LOGISTICS";
String XXL_JOB_MQ_TRIGGER_PRODUCER_GROUP = "XXL_JOB_MQ_TRIGGER_PRODUCER_ERP_GROUP";
}
public class AdsStrategyConstant {
/**
* 默认时间格式化
*/
public static final String FORMAT = DateUtil.YYYY_MM_DD;
/**
* 每小时订单量统计起止时间Key
*/
public static final String LISTING_START_DATE = "listingStartDate";
public static final String LISTING_END_DATE = "listingEndDate";
/**
* Acos起止时间Key
*/
public static final String ACOS_START_DATE = "acosStartDate";
public static final String ACOS_END_DATE = "acosEndDate";
}
正例:
/**
* 全局参数
* @author admin
*/
public class GlobalConstants {
/**
* 财务软件对接API密钥
*/
public static final String FINANCE_API_KEY = "FINANCE_API_KEY";
}
9. 所有的类都必须添加类的描述信息、创建者、创建日期,类属性、方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用// xxx 方式。 方法除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。
说明: 对于注释的要求:第一、能够准确反映设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看的,使其能够快速接替自己的工作。
正例:
/**
* 类的描述信息
* @author Tom
* @date 2021/08/27
*/
/**
* 查询SP广告 对应站点在时间段内对应的销售额
* @param marketId 站点ID
* @param dateBegin 开始时间
* @param dateEnd 结束时间
* @return 站点中msku时间段内对应的总销售额
*/
List<AllocationFeeThirdDataBase> getSPAdvertisementFee(Integer marketId, String dateBegin, String dateEnd);
10. 所有的时间类使用LocalDateTime或者LocalDate不再使用Date
集合规范
1. 判断集合为空用cn.hutool.core.collection.CollectionUtil.isEmpty()判断
正例:
// 判断集合为空
CollectionUtil.isEmpty(list);
// 判断集合不为空
CollectionUtil.isNotEmpty(list);
反例
list != null && list.size() > 0
数据库规范
1. 简单的查询应该使用Mybatis Plus框架自带的查询方法实现,不应该去xml在写这些简单通用的查询
正例:
LbqWrapper<Warehouse> wrapper = Wraps.<Warehouse>lbQ().eq(Warehouse::getType, Warehouse.Type.SELF.name());
List<Warehouse> warehouseList = warehouseService.list(wrappber);
反例:
@ResultMap(value = "BaseResultMap")
@Select("select * from e_warehouse where type = #{type}")
List<Warehouse> listByType(@Param("type") String type);
2. 禁止使用*查询,需要用到什么字段只查对应的字段即可(主要就是防止全表扫描,减少回表操作)
正例:
// 例如只要查id和name字段
final LbqWrapper<Warehouse> wrapper = Wraps.<Warehouse>lbQ()
.eq(Warehouse::getType, Warehouse.Type.SELF.name())
.select(Warehouse::getId, Warehouse::getName);
反例:
// 例如只要查id和name字段,该方法查出了所有字段,其他字段查出来是没意义的
@ResultMap(value = "BaseResultMap")
@Select("select * from e_warehouse where type = #{type}")
List<Warehouse> listByType(@Param("type")String type);
3. 联表查询需要写在对应的xml中,不能写在Mapper.java文件
正例:
SELECT
t1.id,
t1.warehouse_id AS warehouseId,
t1.product AS sku,
t1.seller_sku AS msku,
t1.fulfillable_quantity AS availableQuantity,
t1.unsellable_quantity AS defectiveQuantity,
t1.reserved_untreated AS reservedUntreated,
t1.reserved_processed AS reservedProcessed,
t3.small_image_url AS productImageUrl,
t3.`name` AS productName,
t3.product_type AS productType,
t3.unit,
t3.state AS productState
FROM e_storage t1
JOIN e_warehouse t2 ON t2.id = t1.warehouse_id
JOIN e_product t3 ON t3.sku = t1.product
反例:
// 在Mapper.java文件写联表查询阅读性非常差
@Select("<script>" +
"SELECT " +
"t1.product AS product , " +
"sum(" +
"t1.purchase_cost * " +
"IF( t2.type = 'FBA', t1.fulfillable_quantity + t1.unsellable_quantity + t1.reserved_quantity, " +
"t1.fulfillable_quantity + t1.unsellable_quantity )) / " +
"sum(IF( t2.type = 'FBA', t1.fulfillable_quantity + t1.unsellable_quantity + t1.reserved_quantity, " +
" t1.fulfillable_quantity + t1.unsellable_quantity)) AS purchaseCost " +
"FROM `e_storage` t1 " +
"LEFT JOIN e_warehouse t2 ON t1.warehouse_id = t2.id " +
"where t1.product in " +
"<foreach item='sku' index='index' collection='skus' open='(' separator=',' close=')'> " +
"#{sku}" +
"</foreach> " +
"GROUP BY t1.product " +
"</script>")
List<Storage> selectAverPurchaseCostBySkus(Set<String> skus);
4. 参考阿里巴巴规范