• Java开发规范


    Java开发规范

    随着《阿里巴巴Java开发手册》的公开,重新又掀起一股编码规范的风口。结合《华为java编程规范》以及团队内部的实践,我们也做了一段开发规范。不求最全,但求有效。

    里面的规范,暂时只分两类。“强制”,即如果违反就不能使用级别。比如说,在codereview有遇到 ,那就会直接把pull request打回去,拒绝合并到开发者稳定分支上。“推荐”,即建议怎么做,但是不强制,根据不同的水平可以做一些参考。

    通用规范

    所有的情况下都通用

    1、 【强制】命名全部使用英文,禁止中文或者中英混合。项目名除外,因为有的项目是按域名来命名的,域名本身有可能是中文拼音。

    例子

    域名:kecheng.xxx.com
    项目名:xxx-web-kecheng

    2、 【强制】禁止使用缩写,除非提供一个缩写列表

    反例

    # 这里的t到底是什么意思?topic_id?还是teacher_id?
    字段:t_id

    3、 【强制】禁止出现除了后缀或者前缀3个单词。如果超过3个,说明想表达的职责太多,可以拆分或者封装。

    编程语言

    这里主要指的是Java语言,其他的语言也可以借鉴这些准则

    1、 【强制】需要有统一的后缀或者前缀。为了一看类名,就知道这个类干什么的。

    前缀列表:

    • 抽象类(Abstract)
    • 接口(I)

    正例

    接口:IViewTag
    抽象类:AbstractViewTag  
    具体实现类:UserViewTag

    后缀列表:

    • 实体(Entity)。数据库持久对象。
    • 表单(Form)。用于封装、校验http参数。
    • 数据传输对象(DTO)。用于暴露接口的返回数据
    • 基础服务(BaseService)。单实体可以自描述的服务。
    • 业务服务(BusinessService)。集合多个单实体的服务。
    • 页面服务(ViewService)。涉及到视图页面的服务。
    • 模块(Module)。http入口模块。
    • 异常(Exception)
    • 工具(Util)
    • 枚举(Enum)
    • 视图标签(ViewTag)
    • ....(其他的,比如:Filter之类)

    正例

    实体:UserEntity
    基础服务:UserBaseService 
    业务服务:AuthorityBusinessService

    2、 【强制】所有参与业务的类禁止使用内部类。

    属性

    1、 【强制】常量必须是:大写+下划线,禁止多个单词连在一起

    正例

    private final static String PAGE_SIZE=10;

    反例

    private final static String PAGESIZE=10;
    private final static String pageSize=10;

    2、 【强制】布尔类型禁止添加"is"前缀。部分框架解析会引起序列化错误。

    反例

    # 对应的getter和setter为:isRead和setRead
    private boolean isRead

    正例:

    # 对应的getter和setter为:isRead和setRead
    private boolean read;

    3、 【强制】计数器禁止使用复数

    反例

    private int readCounts;

    正例:

    private int readCount;

    4、 【强制】自描述属性里不要出现类名的描述

    反例

    #UserEntity类
    private String userName;
    private int userAge;

    正例

    #UserEntity类
    private String name;
    private int age;

    5、【强制】关联其他实体的属性命名规则:对应的实体去掉后缀+用途

    正例

    属性名:teacherId ,对应的实体是TeacherEntity
    属性名:favorCount,对应的实体是FavorEntity

    反例

    属性名:tId。根本不知道是哪个实体的外键。有可能是Teacher有可能是Topic,还得猜半天

    6、 【强制】禁止通过定义定义成常量(1,2)来维护类型值,需要通过枚举

    反例

    private final static int SUCESS=1;
    private final static int FAIL=2;

    正例

    定义一个枚举

    方法

    1、 【强制】接口里的方法禁止有修饰符。

    反例

    #接口里的方法
    public void eat();

    正例

    #接口里的方法
    void eat();

    2、 【推荐】方法参数必须使用final来修饰。final可提高程序响应效率。可以通过Eclipse的cleanup来实现。

    正例

    public void eat(final int size);

    反例

    public void eat(int size);

    3、 【强制】每一个方法参数都需要被处理。module层的方法里的对象参数可以不判空,因为架构已经做处理了,不可能为空。

    被处理指的是:

    • 抛异常。
    • 直接返回。
    • 有对应的业务处理逻辑。

    例子

    public void add(long userId,String content){
        //异常验证
        ExceptionUtil.checkId(userId,"用户id")
        //直接返回
        if(Util.isEmpty(content)){
            return ;
        }
    }
    
    public List<CourseEntity> list(int type){
        Cnd cnd = Cnd.limit();
        //有对应的业务逻辑处理
        if(type>0){
            cnd.and("type","=",type);
        }
        return dbDao.query(CourseEntity.class,cnd,null);
    }

    4、 【强制】同一个类里有多个一致的参数(3个以上)的方法,需要抽取接口或者通过实体来承载

    反例

    public FavorEntity add(int type,long sourceId,long userId);
    public FavorEntity delete(int type,long sourceId,long userId);

    正例

    public FavorEntity add(IFavor favor);
    public FavorEntity delete(IFavor favor);

    5、 【强制】方法名必须是动词或者动宾。http接口需要知明达意,可以不按这个规则。比如:mycourse,home,banner

    方法命名格式:

    • is+动词|形容词
    • 动词【+名词|形容词】

    例子

    public void isSucess();
    public void on();
    public void sendEmail();

    统一命名列表:

    • add 新增
    • update 修改
    • delete 删除
    • get 获取单个对象
    • list 获取集合对象
    • getMap 获取map数据
    • count 数量

    方法前缀后缀命名说明:

    原则上不添加后缀,如果添加后缀的话,如果有添加,命名格式为:updatexxxx4yyyyByzzzz

    • xxxx:表示对象的属性
    • yyyy:表示查询的条件(根据自描述属性查询)
    • zzzz:表示查询的条件(根据其他描述属性查询)

    例子

    --xxxx情况:用户
    public void updateName();();
    public void updateNickName();
    --yyyy情况:资讯
    public List<NewsEntity> list4Latest();
    public List<NewsEntity> list4Top();     
    --zzzz情况:课程
    public List<CourseEntity> listByTeacher();
    public List<CourseEntity> listByKnowledge();
    
    --综合使用
    public List<CourseEntity> listCourse4TopByTeacher();

    6、 【强制】一个方法里代码行数不能超过1屏(即30行)。一般来说超过30的行,业务关注点、复杂数比较高,很难维护。超过30行需要封装方法

    7、 【强制】局部变量命名不能有连续的名称。连续的命名不具有可维护性。每个变量都需要有清晰的概念。

    反例

    String head1;
    String head2;

    正例

    String title;
    String content;

    8、 【强制】禁止有任何魔鬼数据独立存在。可以定义一个有含义的变量来承载

    反例

    if(type ==1){
        //审核成功 
        下面15行代码
    } 

    正例

    private final static int SUCESS=1;
    ....
    if(type ==SUCESS){
        下面15行代码
    } 

    9、 【强制】判断表达式要使用布尔变量或者封装方法。表达式是变化点。在维护的时候,表达式不知名达意。

    反例

    if(user!=null&&!Util.isEmpty(user.name)&&!Util.isEmpty(user.provicne)){
        //下面15行代码
    }

    正例

    if(isFilledBaseInfo(user)){
        //下面15行代码
    }

    10、【强制】if()...else if()...else个数不能多于4个,嵌套不能深于3层

    可以通过以下的方法来消除:

    • 设计模式
    • 抽取方法
    • 使用return

    反例

    if(isAdmin()){
        ...
    }else if(isTeacher()){
        ...
    }

    正例

    if(isAdmin()){
        ...
        return;
    }
    if(isTeacher()){
        ...
        return;
    }

    11、 【推荐】采用防御式编程,先判断错误的业务,然后再写正确的业务。防御式编程结构清晰分明:先把所有错误穷举,然后集中处理正确逻辑。

    反例

    if(null!=user && user.hasAuth()){
        正确逻辑
    }

    正例

    if(null==user || !user.hasAuth()){
        return;
    }
    正确逻辑

    12、 【推荐】for里不建议写io。io包括:数据库、缓存,文件读写等

    13、 【强制】多个不同的结构(业务相近的代码),需要有且只有一个空行

    反例

    long userId = fetchUser.getCurrentUserId();
    Sql sql = latentCustomerQueryForm.pager(sqlManager);
    Map<String, Object> map = FormUtil.list(dbDao, sql, pager);
    List<DictInfoEntity> grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
    List<DictInfoEntity> infoOrigins = dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey());
    map.put("queryForm", latentCustomerQueryForm);
    map.put("grades", grades);
    map.put("infoOrigins", infoOrigins);
    return map;

    正例

    long userId = fetchUser.getCurrentUserId();
    Sql sql = latentCustomerQueryForm.pager(sqlManager);
    Map<String, Object> map = FormUtil.list(dbDao, sql, pager); 
    
    List<DictInfoEntity> grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
    List<DictInfoEntity> infoOrigins = dictBaseService.listDict
    
    map.put("queryForm", latentCustomerQueryForm);
    map.put("grades", grades);
    map.put("infoOrigins", infoOrigins);
    return map;

    14、 【推荐】不参与计算的变量不要定义变量

    反例

    long userId = fetchUser.getCurrentUserId();
    Sql sql = latentCustomerQueryForm.pager(sqlManager);
    Map<String, Object> map = FormUtil.list(dbDao, sql, pager); 
    
    List<DictInfoEntity> grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
    List<DictInfoEntity> infoOrigins = dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey());
    
    map.put("queryForm", latentCustomerQueryForm);
    map.put("grades", grades);
    map.put("infoOrigins", infoOrigins);
    return map;

    正例

    long userId = fetchUser.getCurrentUserId();
    Sql sql = latentCustomerQueryForm.pager(sqlManager);
    Map<String, Object> map = FormUtil.list(dbDao, sql, pager); 
    
    map.put("grades", dictBaseService.listDict(DictInfoEnum.GRADE.stringKey()));
    map.put("infoOrigins", dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey()));
    return map;

    15、 【强制】如果有捕获异常,必须有对应的处理业务。如果没有对应的处理业务,不要捕获,可以直接throw,让架构统一处理

    你自己catch,肯定不希望用户看到错误日志,那么 从用户那边看到是正常业务。catch了什么都没干,用户往往看到的是什么都没发生,他会以为网站挂了或者功能快。

    16、 【强制】禁止使用exception.getMessge()处理错误信息。应该使用exception.toString()。因为exception.getMessage(),在npe抛出异常的时候,什么信息都不显示。

    反例

        } catch (Exception e) {
            logger.error(e.getMessage());
        }

    正例

        } catch (Exception e) {
            logger.error(e.toString());
        }

    17、 【强制】禁止使用System.out.print。统一使用Eclipse的log4e插件生成日志(不要定义具体的日志实现,要定义的是slf4j的接口)

    18、 【推荐】公开的接口,一旦发布成稳定版,禁止修改方法签名(方法名,参数)

    如果要修改,需要提供新的接口,老的不能修改。老的接口上添加@Deprecated注解并且使用@see清晰的说明采用新接口或新服务是什么

    因为一修改方法签名。比如:js调用可能就报错了,功能就没办法使用;工具类接口一变,其他项目就会报错了,没办法向下兼容。

    19、 【推荐】方法放置顺序:public-->protected-->private。一个类,往往使用者更关注的是public的。构造方法、重载方法、雷同方法,按顺序放在一起

    注释

    1、 【强制】格式结构统一使用eclipse模板,禁止自定义。

    2、 【强制】类、方法、属性都必须有注释。如果实在来不及,可以先生成TODO。因为可以通过TODO视图,把注解补回来。

    3、 【强制】类上必须要有作者,如果有修改,还要添加上修改者,如果有结队也要写上。要有用户名还要有邮箱

    例子

    /**
     * 字典
     *
     * @author   ZhuangJunxiang(529572571@qq.com)
     * @version 2017-03-06
     */

    4、 【强制】注释要直译,描述要写算法或者思路或者注意事项。不要在注释上代码里的每一行完全暴露出来,使用者根本不关注实现。

    反例

    /**
     * 学生分页查询   #方法里根据没有学生。。。。
     *
     * @param page 分页对象
     * @param studentForm 学生
     * @return 分页对象
     */
    public Page<Map<String, Object>> findPageList(Page<Map<String, Object>> page, StudentForm studentForm) {

    5、 【强制】方法里禁止写注释。不要有多余的注释,让变量和属性自描述或者抽取方法。如果有算法写到方法注释上。

    反例

    //填充基本信息
    fillBaseInfo();
    //填充账号信息
    fillAccoutInfo();

    6、 【强制】方法里禁止注释掉代码。统一通过版本控制软件(git)来解决。逻辑是正确的,但是现在暂时不能使用,可以暂时注释,但是必须写上TODO。

    TODO格式: TODO 标记人 原因

    例子

    //TODO 张三 当前用户还未处理,因为登录还没有调通
    //long userId = fetchUser.getCurrentUserId();
    long userId = 1L;
    ....

    数据库

    这里主要指的是MySQL,其他的数据库也可以借鉴这些准则

    1、【强制】统一使用表名命名规范

    表名规范:分层+项目名缩写+下划线+实体名(小写字母)【+下划线+实体名(小写字母)+rel】

    例子

    表名:bc_course  对应的信息:基础服务层,项目缩写为c里对应的CourseEntity实体对应的表
    
    表名:sc_course_knowledge_rel 对应的信息:综合服务层,项目缩写为c里对应的CourseEntity实体和KnowledgeEntity实体的关系表

    分层:

    • 基础服务:b
    • 综合服务:s
    • webapp服务:a

    表的种类:

    • 映射实体的表:前缀_实体名(小写字母)。实体里有多个单词,用下划线隔开。
    • 关系表:前缀_实体名1(小写字母)_实体名2(小写字母)_rel。同一个实体里有多个单词,拼接在一块

    例子

    bc_course_group -->CourseGroupEntity
    sc_coursegroup_coursepack_rel -->CourseGroupEntity和CoursePackEntity的关系表

    2、 【强制】统一使用innoDB引擎。

    3、 【推荐】表名不要关联其他表名信息
    反例

    bc_course
    bc_course_video
    bc_course_video_study_log

    正例

    bc_course
    bc_video
    bc_study_log

    索引

    1、 【强制】业务上具有唯一特性的字段,即使是组合字段,必须使用唯一索引。比如: 用户名,编号等。如果没有添加唯一索引,即使在应用层做了非常完善的校验和控制,只要没有唯一索引,必然有脏数据

    2、 【强制】唯一索引命名:uk_字段名,普通索引命名:idx_字段名

    3、 【强制】禁止对text定义索引。如果有对这类字段搜索的需求,可以通过全文索引方法来实现功能。

    4、 【推荐】varchar定义索引长度。一般有搜索的话,用户也不会输入太多字。长度统一10为倍数,不要超过50

    字段

    1、 【强制】主键禁止使用自增。不同库同步数据的时候,会出问题。影响插入性能。

    2、 【强制】字段全部禁止为空。为空的话,很容易在使用的时候出现npe。如果可以不填,通过默认值方法来处理。

    3、 【强制】禁止使用外键,只能在概念和应用层次使用外键

    外键的字段命名:表名去除前缀+id

    4、 【强制】禁止使用枚举、集合类型

    5、 【强制】禁止在数据库使用blog存在文件。数据库只存相对的url路径

    6、 【强制】类型使用规范

    • 布尔:bit
    • 时间(精确到天):date
    • 时间(精确到秒):datetime
    • 浮点:deciaml
    • 字符串(长度小于10或者长度大于10但是长度相同):char
    • 字符串(长度为10~5000,长度不相同):varchar
    • 字符串(富文本):text

    7、 【强制】必有字段

    • 主键
    • 创建时间
    • 修改时间
    • 假删状态(如果是资源数据必须有,如果是关系数据禁止有)

    8、 【推荐】可以适合添加冗余数据,这样可以增快查询数据。

    冗余类型:

    • 计数器、计分器等统计数据
    • 一旦生成不会修改的数据

    9、 【推荐】字段顺序:自描述-->关联其他表的描述-->功能性-->必有字段

    注释

    1、 【强制】表名或者字段注释的格式:直译【(补充说明)】

    反例

    表名:t_first_login,注释:本表用于处理河北联通卡的临时业务问题,记录已经使用web登录过的卡号

    正例

    表名:t_first_login,注释:首次登录(用于处理河北联通卡的临时业务问题,记录已经使用web登录过的卡号)

    2、 【强制】禁止带“表”,“数据”等多余的字眼

    反例

    表名:aw_input_batch,注释:输入批次表
    表名:t_admission_110000,注释:北京院校专业数据

    3、 【推荐】如果类型字段,有变更,同步注释

    例子

    status   状态(0:成功,1:失败)
    //过了一段时间,又多了一个冻结的状态
    status   状态(0:成功,1:失败,2:冻结)

    sql

    1、 【强制】禁止select *。数据库查看执行时间性能没有响应。但是返回的数据量会变大,对网络开销有影响,最终还是会影响性能,而且也会影响数据库的二进步日志

    2、 【强制】使用select count(*)。select count(name) 不记录null的行数。而且官方已经澄清过,不影响性能 。

    反例

    select count(1)
    select count(id)

    3、 【强制】使用sum函数时,必须使用IFNULL(sum(),0)。如果sum函数没有查到结果返回null,容易出现npe。

    4、 【推荐】禁止出现or。可以通过使用in或者unit all来替换

    5、 【推荐】order by的场景,创建索引时order by 后面的字段也必须是组合索引的一部分,并且放在索引顺序的最后,避免出现file_sort

    正例

    where a=? and b=? order by c; 索引:a_b_c

    6、 【推荐】创建组合索引时,区分度最高的放在最左边

    正例

    where a=? and b=?  如果a几乎接近唯一,那么只要建idx_a即可。

    7、 【推荐】禁止更新表的所有字段,必须指定要更新的字段

    禁止项

    原则上,禁止采用这些技术。除非架构上有这些考虑。

    • 存储过程
    • 外键
    • 视图
    • 触发器
    • 分库、分表、分区。(单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表)

    工程|项目

    项目

    语法:平台-分层职责-服务名称

    例子

    # uxuexi是平台,web是分层,course是服务名称(因为是web,所以对应的是子域名),对应的域名是course.uxuexi.com
    uxuexi-web-course
    # uxuexi是平台,business是分层,sso是服务名称
    uxuexi-business-sso
    # uxuexi是平台,base是分层,course是服务名称
    uxuexi-base-course
    # we是平台(因为是通用的,没有对应的域名,所以使用we),business是分层,sso是服务名称
    we-business-sso
    # we是平台(因为是通用的,没有对应的域名,所以使用we),core是分层,db是服务名称
    we-core-db

    1、 平台

    使用的域名去掉组织后缀。

    例子

    域名:www.uxuexi.com
    -------
    子域名:www
    平台名:uxuexi
    组织:com

    2、 分层职责

    • core(能力层。与具体业务无关,提供能力)
    • base(基础服务层。可以独立存在,有且只有一个具有实际意义的服务,不依赖于其他的服务)
    • business(业务服务层。依赖多个基础服务,一般是一个流程性的服务)
    • webapp(应用层。对互联网用户提供直接服务)

    3、 服务名称

    如果是web项目,使用子域名当作服务名称。其他的项目,根据职责划分来自行命名。

    分包

    java源码

    1、 根目录

    语法:域名组织-项目名。如果web层项目,直接使用对应的域名倒置即可。

    例子

    # web层项目,直接使用对应的域名倒置即可
    项目名:uxuexi-web-course 
    根目录为:com.uxuexi.course
    # 其他层项目,使用:域名组织+项目名
    项目名:uxuexi-business-sso 
    根目录为:com.uxuexi.business.sso

    2、 java包

    语法:分包【+子模块】+文件(类+后缀)。其中子模块是参考业务的包才有,通用功能可以没有子模块。

    分包清单:

    • module:http路由,负责根据不同的业务跳转到不同的url。
    • form:负责http参数的封装、验证、传输。负责sql的编写
    • entity:数据库持久对象。数据的持久及对象本身业务的实现
    • dto:接口返回的实体
    • service:逻辑单元。逻辑处理或者叫计算单元
    • util:工具
    • enums:枚举
    • vt:视图标签
    • ...

    有子模块的业务包有:module,form,entity,dto,service。如果有通用的逻辑,可以使用common子模块包名

    例子

    包名:module.student.course ,类名:StudentCourseModule
    包名:module.user,类名:UserModule
    包名:util,类名:StringUtil

    资源文件

    1、 根目录

    resources

    2、 sql包

    和service及form的包名一致,文件名和java调用的类名一致

    例子

    # java 
    # com.uxuexi.www 根目录
    # module 分包
    # student.course 子模块
    # StudentCourseViewService.java 文件
    com.uxuexi.wwww.module.student.course.StudentCourseViewService.java
    
    # sql
    # resources 根目录
    # sql 分包
    # student.course 子模块
    # StudentCourseViewService.sql 文件
    resources.sql.student.course.StudentCourseViewService.sql

    视图文件

    1、 根目录

    WEB-INF

    2、 视图包

    module类单词小写分隔,文件名和module里的方法名一致

    例子

    # java 
    # com.uxuexi.www 根目录
    # module 分包
    # student.course 子模块
    # StudentCourseModule 文件
    # list 方法
    com.uxuexi.wwww.module.student.course.StudentCourseModule.list()
    
    # 视图
    # WEB-INF 根目录
    # student.course 子模块
    # list.jsp 文件
    WEB-INF.student.course.list.jsp

    完整例子








  • 相关阅读:
    RMAN 增量备份 的 对象测试
    小论工具类App的盈利之道
    linux下二进制文件比较程序
    [置顶] 对iOS开发有用的一些自动化处理脚本
    [Win8]Windows8开发笔记(八):数据绑定的基础
    NetBeans 时事通讯(刊号 # 116 Sep 11, 2010)
    域名信息证实 JavaEye 已被 CSDN 收购
    插件架构简介
    GAE for Java exception: no matching index found.
    Java 7 最快要到 2012 年中发布
  • 原文地址:https://www.cnblogs.com/ljmatlight/p/9060788.html
Copyright © 2020-2023  润新知