阿里巴巴Java开发规范学习——编程规约(1)

阿里巴巴Java开发规范

一、编程规约

(一) 命名风格

1.【强制】类名使用UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO /

PO / UID 等。
正例 :MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion

  1. DO (Data Object):用于表示领域模型中的数据对象,通常包含业务相关的属性和行为。例如,CustomerDO, ProductDO
  2. BO (Business Object):用于封装业务逻辑和规则的类,可能包含对多个DO的操作。例如,OrderBO, InvoiceBO
  3. DTO (Data Transfer Object):主要用于在不同层(如表现层与业务层、服务层之间)传递数据的简单对象,只包含数据成员及简单的getter/setter方法。例如,UserDTO, AddressDTO
  4. VO (Value Object):在领域驱动设计(DDD)中,用于表示一个不变的、具有语义概念的对象,其属性值共同定义了对象的完整状态,如 ColorVO, MoneyVO
  5. AO (Application Object):在某些架构中,用于表示应用程序层的实体,负责协调业务逻辑和数据访问。例如,UserAO, RoleAO
  6. PO (Plain Old Object):通常指那些没有继承任何特殊框架或库提供的基类、未实现特定接口、未被注解标记、不含有任何门面方法(如getter/setter以外的方法)的简单对象。例如,CustomerPO, ItemPO
  7. UID (Unique Identifier):用于表示唯一标识符的类或接口,如全局唯一ID生成器 UUIDGenerator 或特定类型ID的类 UserId, OrderId
2. 【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

正例:MAX_FILE_SIZE

反例:maxFileSize

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

    这个规则建议在命名抽象类时,以 AbstractBase 作为前缀,以明确表示此类是一个抽象类,即不能被实例化,主要用于定义通用行为和接口,供子类继承实现。这样命名有助于开发者一眼识别出这是一个抽象类,了解其设计意图和用途。例如:AbstractAnimalBaseUserService

  2. 异常类命名使用 Exception 结尾

    异常类通常用于表示程序运行过程中遇到的错误情况。遵循这一规则,将 Exception 作为后缀添加到异常类名中,可以清晰地标识出此类是一个异常类,便于理解和管理程序中的错误处理逻辑。例如:InvalidInputExceptionResourceNotFoundException

"抽象类中,可以以 Abstract 结尾吗?"
虽然在技术层面上,以 Abstract 结尾命名抽象类并不违反任何编程语言的语法规定,但在实践中,以 AbstractBase 作为前缀已经是一种广泛接受且易于理解的抽象类命名约定。将 Abstract 放在类名末尾可能会导致以下问题:

  • 可读性降低 :阅读代码时,开发人员习惯于从左到右扫描类名,将表示类性质的关键字放在开头有助于快速识别。将 Abstract 放在末尾可能需要额外的时间和注意力去判断类的性质。
  • 一致性受损 :大部分编程社区和项目倾向于遵循将 AbstractBase 放在抽象类名开头的约定。若您的项目中出现以 Abstract 结尾的抽象类,可能会与团队内的其他代码或业界普遍做法产生不一致,增加理解成本。

综上所述,尽管没有严格禁止以 Abstract 结尾命名抽象类,但从可读性、一致性以及遵循最佳实践的角度考虑,建议遵循将 AbstractBase 放在抽象类名开头的命名规则。

4. 【强制】 POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。

反例:定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),RPC框架在反向解析的时候,"误以为"对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

正例: 避免使用 is 前缀命名布尔类型的变量

直接使用描述状态的词汇作为变量名,如 deletedenabledvisible 等。相应的 getter 方法可以保持 JavaBean 规范,即对于布尔类型使用 is 前缀(如 isDeleted()),但对于属性名本身,则不带 is

问题背景:

在 Java 中,布尔类型的变量通常用来表示某种状态或属性的真假值。按照习惯,对于表示"是否具有某特性"的布尔变量,有时会采用 is 前缀来命名,如 isDeletedisEnabledisVisible 等。这种命名方式符合自然语言习惯,能够直观地传达变量的意义。

然而,在涉及序列化和反序列化的场景中,特别是与某些框架(如 RPC 框架、ORM 框架等)交互时,这种命名可能会导致问题。这是因为部分框架在自动处理对象的序列化和反序列化时,依赖于特定的命名约定来正确映射对象属性到外部格式(如 JSON、XML 或数据库字段)。

问题原因:

  1. JavaBean 规范与框架预期的差异

    JavaBean 规范规定,布尔类型的 getter 方法可以使用 is 前缀(如 isDeleted()),而其他类型的属性则使用 get 前缀(如 getName())。当框架尝试根据 getter 方法名反推属性名(去掉 isget 并将首字母小写)时,如果遇到以 is 开头的方法,可能会误以为对应的属性名为 deleted 而不是 isDeleted。这会导致框架在反序列化时找不到实际存在的 isDeleted 属性,从而引发错误。

  2. 外部格式映射规则

    在某些框架中,可能直接基于属性名进行序列化或反序列化。如果外部格式(如 JSON)要求属性名遵循特定的命名约定(如全小写、下划线分隔等),那么带有 is 缀的属性名在转换时可能会与预期不符,导致映射失败或错误。

5. 【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。

正例:应用工具类包名为 com.alibaba.ai.util、类名为MessageUtils (此规则参考spring

的框架结构)

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

说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。

正例:

  • public class OrderFactory;
  • public class LoginProxy;
  • public class ResourceObserver;
7. 【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加)

保持代码的简洁性,并加上有效的Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。

正例:

  • 接口方法签名 void commit();
  • 接口基础常量 String COMPANY = "alibaba";
8. 接口和实现类命名的两套规则:

1)【强制】对于 Service 和DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部

的实现类用Impl 的后缀与接口区别。

正例:CacheServiceImpl 实现CacheService 接口。

2) 【推荐】 如果是形容能力的接口名称,取对应的形容词为接口名 (通常是--able 的形式)。

正例:AbstractTranslator 实现 Translatable 接口。

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

说明:枚举其实就是特殊的类,域成员均为常量,且构造方法被默认强制是私有。

正例:枚举名字为ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。

10 .【参考】各层命名规约:

A) Service/DAO 层方法命名规约

1) 获取单个对象的方法用 get 做前缀。

2) 获取多个对象的方法用 list 做前缀,复数形式结尾如:listObjects。

3) 获取统计值的方法用 count 做前缀。

4) 插入的方法用save/insert 做前缀。

5) 删除的方法用remove/delete 做前缀。

6) 修改的方法用update 做前缀。
B) 领域模型命名规约

1) 数据对象:xxxDO,xxx 即为数据表名。

2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。

3) 展示对象:xxxVO,xxx 一般为网页名称。

4) POJO 是DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

(二) 常量定义

1. 【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。

反例:

java 复制代码
String key ="Id#taobao_" + tradeId;
cache.put(key, value);
2. 【强制】在long 或者Long 赋值时,数值后使用大写的L,不能是小写的 l,小写容易跟数字1 混淆,造成误解。

说明:Long a = 2l; 写的是数字的21,还是 Long 型的2?

3. 【推荐】不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。

说明: 大而全的常量类, 杂乱无章, 使用查找功能才能定位到修改的常量,不利于理解和维护。

正例:缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下

4. 【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。

1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的constant 目录下。

2) 应用内共享常量:放置在一方库中,通常是子模块中的constant 目录下。

反例:易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示"是"的变量:

类A 中:public static final String YES = "yes"

类B 中:public static final String YES = "y";

A.YES.equals(B.YES),预期是true,但实际返回为false,导致线上问题。

3) 子工程内部共享常量:即在当前子工程的 constant 目录下。

4) 包内共享常量:即在当前包下单独的 constant 目录下。

5) 类内共享常量:直接在类内部 private static final 定义。

5. 【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。

枚举(Enum)类型的概念与特性:

enum(枚举)是一种特殊的类型,它允许定义一组命名的常量,这些常量通常是相互独立且互斥的。枚举类型在很多编程语言中都有支持,如 Java、C#、Python(通过枚举模块实现)等。枚举类型的优点包括:

  • 类型安全:枚举类型的变量只能被赋予其定义范围内预设的值,不允许赋值为其他未定义的值,增强了代码的健壮性和安全性。
  • 可读性强:枚举成员以具名常量的形式存在,使用有意义的名称代替原始值(如整数或字符串),提高了代码的可读性和自文档化能力。
  • 易于维护:当枚举成员有所增减或值发生变化时,只需修改枚举定义一处,所有引用该枚举的地方都会自动更新,降低了维护成本。

何时使用枚举来定义变量值范围:

当变量的取值仅在一个固定的、有限的范围内变化,并且这些值具有特定的语义时,非常适合使用枚举类型来定义。以下是一些典型的应用场景:

  1. 状态标识
    例如,订单的状态可能只有几个固定选项:PENDING(待处理)、PROCESSING(处理中)、COMPLETED(已完成)、CANCELLED(已取消)等。使用枚举可以清晰地表示这些状态,并确保状态值不会被错误地设置为无效值。
java 复制代码
public enum OrderStatus {
    PENDING,
    PROCESSING,
    COMPLETED,
    CANCELLED
}
  1. 星期、月份等预定义集合
    例如,表示一周中的星期或一年中的月份,这类值都是固定的且具有明确语义。使用枚举可以使代码更具可读性,并防止拼写错误或使用非法值。
java 复制代码
public enum DayOfWeek {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

public enum Month {
    JANUARY,
    FEBRUARY,
    MARCH,
    APRIL,
    MAY,
    JUNE,
    JULY,
    AUGUST,
    SEPTEMBER,
    OCTOBER,
    NOVEMBER,
    DECEMBER
}
  1. 方向、颜色、角色类型等具有固定选项的分类
    例如,表示二维平面上的方向(NORTH, SOUTH, EAST, WEST),用户角色类型(ADMIN, MODERATOR, USER),或者颜色(RED, GREEN, BLUE 等)。这些情况都可以使用枚举来限制变量的取值范围。
java 复制代码
public enum Direction {
    NORTH,
    SOUTH,
    EAST,
    WEST
}

public enum UserRole {
    ADMIN,
    MODERATOR,
    USER
}

public enum Color {
    RED,
    GREEN,
    BLUE,
    // ...
}

总结来说,当变量的取值范围固定且具有明确语义时,使用枚举类型来定义不仅可以提高代码的可读性和安全性,还能简化维护工作。遵循"如果变量值仅在一个固定范围内变化,用 enum 类型来定义"这一规范,有助于提升代码质量,符合良好的编程实践。

相关推荐
弗拉唐38 分钟前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi771 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3431 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀1 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
蓝黑20202 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深2 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
shuangrenlong2 小时前
slice介绍slice查看器
java·ubuntu
牧竹子2 小时前
对原jar包解压后修改原class文件后重新打包为jar
java·jar
数据小爬虫@2 小时前
如何利用java爬虫获得淘宝商品评论
java·开发语言·爬虫