java代码开发规范详细解读

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: java代码开发规范详细解读

1.1 命名规范

代码中的命名均不能以特殊字符(如下划线、$#符号)开始或结束。
反例: _name / #Object

  1. 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。

反例: toubao / lipei。

  1. 类名使用UpperCamelCase风格,必须遵从驼峰形式。

正例:CommonUtils / BaseVo

  1. 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格,必须遵从驼峰形式。

正例: orderService / getOrderService()

  1. 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

正例: ZK_CONFIG_ROOTNODE

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

  2. 包名使用com.hetai.服务名.分层名。

正例: oauth系统的DAO, com.hetai.oauth.dao

  1. 如果使用到了设计模式或具有明确职责,建议在类名中体现出具体模式或职责。

正例:ExecutorFactory / AbstractProducer

    9. 各分层都需要接口和实现类,实现类用Impl作后缀与接口区别。

正例:OrderServiceImpl实现OrderService接口。

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

正例:枚举名字:PolicyIdTypeEnum,成员名称:ID_CARD/ PASSPORT。

  1. 各层方法命名规范: 1) 查询的方法用get/ query做前缀。

2) 插入的方法用add 或insert做前缀。

3) 删除的方法用remove 或delete做前缀。

4) 修改的方法用modify/ update做前缀。

5) 获取多个对象的方法用List做结尾。

6) 获取统计值的方法用Count做结尾。

  1. 全局常量类名建议“模块名Constant”。

正例:枚举名字:OrderConstant / CommonConstants。

    13. 不要出现任何魔法值(即未经定义的常量)直接出现在代码中,应作常量声明。

反例: return "UNDERWRITERESULT" + order_no;

1.2 OOP规范

对外暴露的接口签名,原则上不允许修改方法签名,避免对接口调用方产生影响。
序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值。
说明:注意serialVersionUID不一致会抛出序列化运行时异常。

POJO类必须写toString方法。使用IDE的中工具:source> generate toString时,如果继承了另一个POJO类,注意在前面加一下super.toString。
说明:在方法执行抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题。

当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读。
POJO类中变量需要有get/set方法,通过使用IDE Generate自动生成。
循环体内字符串的联接方式,使用StringBuilder的append方法进行扩展。
final可提高程序响应效率,声明成final的情况: 1) 不需要重新赋值的变量,包括类属性、局部变量。
2) 对象参数前加final,表示不允许修改引用的指向。

3) 类方法确定不允许被重写。

慎用Object的clone方法来拷贝对象。 说明:对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝
不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁

  1. 使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历。 说明:keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。

正例:values()返回的是V值集合,是一个list集合对象;keySet()返回的是K值集合,是一个Set集合对象;entrySet()返回的是K-V值组合集合

  1. 高度注意Map类集合K/V能不能存储null值的情况,如下表格:

集合类

Key

Value

Super

说明

Hashtable

不允许为null

不允许为null

Dictionary

线程安全

ConcurrentHashMap

不允许为null

不允许为null

AbstractMap

分段锁技术

TreeMap

不允许为null

允许为null

AbstractMap

线程不安全

HashMap

允许为null

允许为null

AbstractMap

线程不安全

1.3 控制规范

在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;
在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。

在if/else/for/while/do语句中必须使用大括号,即使只有一行代码,避免使用下面的形式:if (condition) statements;
除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的try-catch操作(这个try-catch是否可以移至循环体外)。
方法中需要进行参数校验的场景: 1)调用频次低的方法。
2) 执行时间开销很大的方法,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那得不偿失。

3) 需要极高稳定性和可用性的方法。

4) 对外提供的开放接口,不管是RPC/API/HTTP接口。

5) 敏感权限入口。

1.4 格式规范

代码格式化参考<>

1.5 并发规范

获取单例对象需要保证线程安全,其中的方法也要保证线程安全。 说明:资源驱动类、工具类、单例工厂类都需要注意;
创建线程或线程池时请指定有意义的线程名称,方便出错时回溯;
线程资源必须通过线程池提供,不允许在应用中自行显式创建线程;
SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类;
正例:注意线程安全,使用DateUtils。亦推荐如下处理:

private static final ThreadLocal df = new ThreadLocal() {

@Override

protected DateFormat initialValue() {

return new SimpleDateFormat("yyyy-MM-dd");

}

};
//代码效果参考:http://0791zd.com/zx/art_714.html

说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替Simpledateformatter,官方给出的解释:simple beautiful strong immutable thread-safe。

高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁;
对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。 说明:线程一需要对表A、B、C依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是A、B、C,否则可能出现死锁;
使用CountDownLatch进行异步转同步操作,每个线程退出前必须调用countDown方法,线程执行代码注意catch异常,确保countDown方法可以执行,避免主线程无法执行至await方法,直到超时才返回结果。 说明:注意,子线程抛出异常堆栈,不能在主线程try-catch到;
避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降;
说明:Random实例包括java.util.Random 的实例或者 Math.random()实例。 正例:在JDK7之后,可以直接使用API ThreadLocalRandom,在 JDK7之前,可以做到每个线程一个实例。

HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中注意规避此风险;

  1. ThreadLocal无法解决共享对象的更新问题,ThreadLocal对象建议使用static修饰。这个变量是针对一个线程内所有操作共有的,所以设置为静态变量,所有此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量;

  2. 单例模式推荐使用静态内部类方式实现, 参考com.hetai.common.util. PropertiesUtils;

二 异常规范
2.1 异常规范

不要捕获Java类库中定义的继承自RuntimeException的运行时异常类,如:IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。
正例:if(obj != null) {...}

反例:try { obj.method() } catch(NullPointerException e){...};

异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低;
捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容;
有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务;
finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch;
不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块中的return语句;
方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回null值。调用方需要进行null判断防止NPE问题。
说明:本规约明确防止NPE是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败,运行时异常等场景返回null的情况;

在接口设计中使用“返回错误码”,不要直接抛异常给到调用方;
定义时区分unchecked / checked 异常,避免直接使用RuntimeException抛出,更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,如:DAOException / ServiceException等;

  1. 避免出现重复的代码(Don’t Repeat Yourself),即DRY原则。 说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是共用模块;

    //代码效果参考:http://0791zd.com/zx/art_1938.html

三 日志规范
3.1日志规范

应用中不要直接使用日志系统(Log4j、Logback)中的API,而应使用common包LogUtils中的API;
避免重复打印日志,浪费磁盘空间,在log4j.xml中设置additivity=false。
正例:;

异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,则往上抛。
正例:log.error(各类参数或者对象toString + "_" + e.getMessage(), e);

注意日志输出的级别,error级别只记录系统逻辑出错、异常等重要的错误信息。如非必要,请不要在此场景打出error级别;
日志级别按debug/info/warn/error四个级别输出,生产环境禁止输出debug日志;有选择地输出info日志;warn级别记录系统逻辑出错、异常等重要的错误信息;error级别只记录致命的、导致流程中止的错误或异常信息;
debug/info级别的日志输出,必须使用使用占位符的方式,不建议使用条件输出形式。
说明:logger.debug("Processing trade with id: " + id + " symbol: " + symbol); 如果日志级别是warn,上述日志不会打印,但是会执行字符串拼接操作,如果symbol是对象,会执行toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印;

四 工程规范
4.1工程规范

包名com.hetai.系统名,代码目录结构按以下表格:
模块

说明

resource

服务资源接口层,REST Resource,定义服务对外的REST 接口

service

服务业务层,在service组装业务逻辑

sao

服务远程方法调用层,封装调用其他服务接口

dao

服务持久化层,调用DB接口

vo

服务bean对象

common

服务公共代码,如工具类、常量类等

mapping

调用DB sql配置文件

extension

过滤器、拦截器filter/Interceptor

handler

线程处理实现类,参考《线程池使用指引》

配置文件目录结构按以下表格:
模块

说明

dev

开发环境配置文件

commit

测试环境(api)配置文件

commit_stg2

测试环境(api_stg2)配置文件

commit_app

测试环境(app)配置文件

formal

正式环境配置文件

formal_app

正式环境(app)配置文件

common

服务公共配置文件

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
65 1
|
3天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
17 3
|
29天前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
50 24
|
10天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
48 2
|
25天前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
65 5
|
25天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
49 5
|
27天前
|
Java API 开发者
Java中的Lambda表达式:简洁代码的利器####
本文探讨了Java中Lambda表达式的概念、用途及其在简化代码和提高开发效率方面的显著作用。通过具体实例,展示了Lambda表达式如何在Java 8及更高版本中替代传统的匿名内部类,使代码更加简洁易读。文章还简要介绍了Lambda表达式的语法和常见用法,帮助开发者更好地理解和应用这一强大的工具。 ####
|
1月前
|
Java API Maven
商汤人像如何对接?Java代码如何写?
商汤人像如何对接?Java代码如何写?
39 5
|
1月前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
47 5
Java反射机制:解锁代码的无限可能
|
1月前
|
安全 IDE Java
Java常见规范及易忘点
遵循Java编程规范和注意易忘点是提高代码质量和可维护性的关键。通过规范的命名、格式、注释和合理的代码组织,可以让代码更加清晰和易于维护。同时,注意空指针检查、线程安全、集合框架和字符串操作等常见易忘点,可以减少程序错误,提高运行效率。结合单一职责原则、面向接口编程和合理的异常处理,能够编写出高质量的Java代码。希望本文能够帮助Java开发者提升编码水平,写出更高效、更可靠的代码。
25 2