好的代码本身就是注释, 所以我们需要统一命名风格。
在本文中,将从大到小,从外到内,总结Java编程中的命名规范。文中将会涉及到日常工作中常见的命名示例,如包命名,类命名,接口命名,方法命名,变量命名,常类命名,抽象类命名,异常类命名以及扩展类命名等。我将按照项目工程目录结构,从包,类(接口,抽象类,异常类),方法,变量和常量的顺序展开介绍。
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
1、业务含义清晰。
在具名常量时,应该根据该常量所表示的含义,而不是该常量所具有的数值为该抽象事物命名。FIVE 是个很糟的常量名(不论它所代表的值是否为 5.0)。CYCLES_NEBDED 是个不错的名字。CYCLES_NEEDED 可以等于 5.0 或者 6.0。而 FIVE=6.0 就显得太可笑了。出于同样原因,BAKERS_DOZEN 就是个很糟的常量名;而DONUTS_MAX 则很不错。
2、允许任何魔法值(即未经定义的常量)直接出现在代码中。
3、long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字 1 混淆,造成误解。
说明: Long a = 2l; 写的是数字的 21,还是 Long 型的 2?
4、不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。如: 缓存相关的常量放在类: CacheConsts 下;系统配置相关的常量放在类: ConfigConsts 下。
说明 : 大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
5、常量的复用层次有五层: 跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。
类 A 中: public static final String YES = "yes";
类 B 中: public static final String YES = "y";
A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。
变量名是名词,要正确和清晰地描述业务语义。
1、业务含义清晰。避免无业务语义的命名,如:list、val、a...;
2、语境范围精准。
避免小范围词套大范围数据,反之亦然,不使用过于宽泛的名词,如:date 看上去不错,但经过最后推敲它也只是个坏名字,因为这里所说的日期并不是所有的日期均可,而只是特指当前日期;而 date 本身并未表达出这层含义。
3、名词复数。统一风格,加s或List尾缀,变量名建议使用s尾缀,函数名建议使用List尾缀;
4、前置对仗词。位于变量前置部分,用于修饰后面的名词。
5、后置限定词。描述名词的作用范围属性,例如:
6、变量长度控制。
当变量名的平均长度在10 到16 个字符的时候,调试程序所需花费的气力是最小的(1990)。平均名字长度在8到20个字符的程序也几乎同样容易调试。这项原则并不意味着你应该尽量把变量名控制在9到15或者10到16 个字符长。它强调的是,如果你查看自己写的代码时发现了很多更短的名字,那么你需要认真检查,确保这些名字含义足够清晰。
7、为特定类型的数据命名。
在为数据命名的时候,除了通常的考虑事项之外,为一些特定类型数据的命名还要求做出一些特殊的考虑。下面将讲述与循环变量、状态变量、临时变量、布尔变量、枚举类型和具名常量有关的考虑事项。
8、POJO、DO等实体类中的变量不要以is开头。有些框架解析的时候会出错。
9、RPC‘接口返回值’以及‘接口入参值’,禁止使用枚举;枚举在你的系统内部使用即可。推荐在这个字段上注释一个枚举类型。
基本数据类型是构建其他所有数据类型的构造块 (building blocks )。本章包含了使用数(普遍意义上)、整数、浮点数、字符和字符串、布尔变量、枚举类型、具名常量以及数组的一些技巧。本章的最后一节将讲述如何创建自己的数据类型。
传送门 - 《代码大全2》第12章 基本数据类型
通常采用动词+名词。函数命名要体现做什么,而不是怎么做,要清楚表达出操作意图和业务语义。
1、动名词搭配,动词表达操作意图,名词表达业务语义。
public interface MemberFacade {
/**
* 查询会员信息
*
* 命名的问题:
* 1.没有表达操作意图:build是构建,没有表达查询的意图。
* 2.没有表达业务语义:没有表达build的目标对象是什么。
**/
SingleResult build(MemberBuildRequest request);
/**
* 更新会员信息
*
* 命名的问题:
* 1.没有表达业务语义:不知道具体更新会员的哪些信息
* 2.操作意图表达过于宽泛:update可以把所有会员信息更新都放在这个函数里。
**/
SingleResult update(MemberUpdateRequest request);
}
public interface MemberFacade {
/**
* 查询会员信息
**/
SingleResult queryMember(MemberQuery query);
/**
* 更新会员昵称
**/
SingleResult updateNick(MemberUpdateRequest request);
}
2、名词复数。变量名建议使用s尾缀,函数名建议使用List尾缀;
3、正反操作使用对仗词,例如:
类是面向对象中最重要的概念,是一组关联数据的相关操作的封装,通常可以把类分为两种:
1)实体类:通常采用名词。承载业务的核心数据和业务逻辑,命名要充分体现业务语义,比如Order/Buyer/Item。
2)辅助类:通常采用名词+功能后缀。协调实体类完成业务逻辑,命名通常加后缀体现出其功能性,比如OrderQueryService/OrderRepository。
1、辅助类尽量避免用 Helper/Util 之类的后缀,因为其含义过于笼统,容易破坏单一职责原则。
2、针对某个实体的辅助操作过多,或单个操作很复杂,可通过 “实体 + 操作类型 + 功能后缀”来命名,同时符合职责单一和接口隔离的原则,比如OrderService:
3、如果使用了设计模式,类名中应该体现出具体的模式。将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
public class OrderFactory;
public class LoginProxy;
public class ResourceObserver;
接口(Interface)是一种表述某一类型对象动作的特殊类;简单来说,接口也是类(不太严谨),所以,接口的名称的书写也应该符合类名书写规范。
与普通类名不同的是,接口命名时通常采用形容词或动词来描述接口的动作行为,也可以配合名词一起使用。
1、可以interface后缀结尾。
2、Oracle Java中一些标准库的接口使用形容词命名示例:
public interface Closeable{
//...
}
public interface Cloneable{
//...
}
public interface RunnableP{
//...
}
public interface Comparable{
//...
}
public interface CompletionService{
//...
}
public interface Iterable{
//...
}
public interface EventListener{
//...
}
3、在Spring Framework标准库中,通常采用名词+动词/形容词的组合方式来命名接口,下列是Spring Framework中一些接口命名示例:
public interface AfterAdvice{
//...
}
public interface TargetClassAware{
//...
}
public interface ApplicationContextAware{
//...
}
public interface MessageSourceResolvable{
//...
}
抽象类(Abstract Class)是一种特殊的类,其命名与普通类的命名规范相当。
一般地,为了将抽象类与普通类和接口做出区别,提高抽象类的可读性,在命名抽象类时,会以“Abstract”/“Base”作为类命的前缀。
1、以“Abstract”/“Base”作为类命的前缀。
2、编程中一些常规的命名示例:
public abstract class AbstractRepository{
//...
}
public abstract class AbstractController{
//...
}
public abstract class BaseDao{
//...
}
public abstract class AbstractCommonService{
//...
}
3、Spring Framework中常见的抽象类示例:
public abstract class AbstractAspectJAdvice{
//...
}
public abstract class AbstractSingletonProxyFactoryBean{
//...
}
public abstract class AbstractBeanFactoryPointcutAdvisor{
//...
}
public abstract class AbstractCachingConfiguration{
//...
}
public abstract class AbstractContextLoaderInitializer{
//...
}
异常类(Exception Class)也是类的一种,但与普通类命名不同的是,异常类在命名时需要使用“Exception”作为其后缀。
另外,在Java中还有另外一类异常类,它们属于系统异常,这一类异常类的命名使用“Error”作为其后缀,以区分Exception(编码,环境,操作等异常)。
1、Exception或者Error结尾。
2、exception示例:
public class FileNotFoundException{
//...
}
public class UserAlreadyExistException{
//...
}
public class TransactionException{
//...
}
public class ClassNotFoundException{
//...
}
public class IllegalArgumentException{
//...
}
public class IndexOutOfBoundsException{
//...
}
3、error示例:
public abstract class VirtualMachineError{
//...
}
public class StackOverflowError{
//...
}
public class OutOfMemoryError{
//...
}
public class IllegalAccessError{
//...
}
public class NoClassDefFoundError{
//...
}
public class NoSuchFieldError{
//...
}
public class NoSuchMethodError{
//...
}
在项目中,测试类采用被测试业务模块名/被测试接口/被测试类+“Test”后缀的方法进行书写,测试类中的测试函数采用“test”+用例操作_状态的组合方式进行书写。
1、测试类以Test结尾。
2、测试方法test+用例操作_状态。如:
public class UserServiceTest{
public void testFindByUsernameAndPassword(){
//...
}
public void testUsernameExist_notExist(){
//...
}
public void testDeleteById_isOk(){
//...
}
}
通常,包命使用小写英文字母进行命名,并使用“.”进行分割,每个被分割的单元只能包含一个名词。
一般地,包命名常采用顶级域名作为前缀,例如com,net,org,edu,gov,cn,io等,随后紧跟公司/组织/个人名称以及功能模块名称。
package org.springframework.boot.autoconfigure.cloud
package org.springframework.boot.util
package org.hibernate.action
package org.hibernate.cfg
package com.alibaba.druid
package com.alibaba.druid.filter
package com.alibaba.nacos.client.config
package com.ramostear.blog.web
1、包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
2、包的命名要大小适中,不能太具体,也不能太抽象。
3、实际工程中,常见的分类维度主要是两种,按功能性或业务域分类。
4、同一层级的包,要严格保持分类维度的一致性,要么先按业务域分类,再按功能性分类;要么就先按功能性分类,再按业务域分类。
client
|----request
|----order
|----item
|----response
|----order
|----item
|----service
|----OrderQueryService.java
|----ItemQueryService.java
在书写泛型类时,通常做以下的约定:
下面时泛型类的书写示例:
public class HashSet extends AbstractSet{
//...
}
public class HashMap extends AbstractMap{
//...
}
public class ThreadLocal{
//...
}
public interface Functor{
T val() throws X;
}
public class Container{
private K key;
private V value;
Container(K key,V value){
this.key = key;
this.value = value;
}
//getter and setter ...
}
public interface BaseRepository{
T findById(ID id);
void update(T t);
List findByIds(ID...ids);
}
public static List methodName(Class clz){
List dataList = getByClz(clz);
return dataList;
}