java开发编码规范
- 方法的返回类型, 方法参数, 变量声明时均采用接口类型, 而不是实际类型:
/**
定义接口变量为接受类型的好处:
1).面向接口编程
一种规范约束
制定者(或者叫协调者),实现者(或者叫生产者),调用者(或者叫消费者)。
接口本质上就是由制定者来协调实现者和调用者之间的关系。
只有实现者和调用者都遵循“面向接口编程”这个准则,制定者的协调目的才能达到。
接口的语义是can-do语义,表约束(Constraint)。
像JDBC的规范API,不管你使用哪一套实现,我们使用的时候都是使用相同的API.
分离设计与实现
使得系统可以支持开闭原则和依赖倒转原则。设计师可以设计出接口,而程序员可以依照接口去写实现。
2).解耦合
在一定程度上解耦合,依赖接口还不依赖具体实现,在替换实现类的时候,可以将影响减到最小.
3).在依赖接口的情况下,单元测试更容易,使用mock也更容易,在TDD中,测试驱动就是要让程序易于测试。
4).与设计有关
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。
在这种情况下,各个对象内部是如何实现自己的对系统设计人员来讲就不那么重要了;
而各个对象之间的协作关系则成为系统设计的关键。
在OSGI规范中,接口与实现的分离是用得最淋漓尽致的。
5).参考spring中的IOC的实现
*/
public Map<String, Object> getStockWarrantIV(String ul, String updateDate) {
//HashMap<String, Object> ivMap = new HashMap<>();
Map<String, Object> ivMap = new HashMap<>();
try {
String key = STOCK_WARRANT_IV_PREFIX + ul;
String hashKey = updateDate;
ivMap = (HashMap<String, Object>) redisTemplate.boundHashOps(key).get(hashKey);
} catch (Exception e) {
LOG.error("getStockWarrantIV", e);
}
return ivMap;
}
-
代码中不要有多余的空行, 无效的注释,无意义的注释要删掉; 注释要规范, 符合javadoc原则;注释和日志细节写清楚,不要出错误;变量命名规范,不要存在歧义和冲突;
-
方法命名符号常规英语表达习惯, 能从名字明白方法的作用;
-
调用工具类, 尽量调用已经封装好的工具类方法,减少自定义工具类方法编码;
-
判断字符串非空, 调用StringUtils类中方法, 注意isBlank和isEmpty的区别
1.StringUtils里的isEmpty方法和isBlank方法的区别
isEmpty()
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
isBlank()
public static boolean isBlank(String str) {
int strLen;
if (str != null && (strLen = str.length()) != 0) {
for(int i = 0; i < strLen; ++i) {
// 判断字符是否为空格、制表符、tab
if (!Character.isWhitespace(str.charAt(i))) {
return false;
}
}
return true;
} else {
return true;
}
}
结论
1).isEmpty 没有忽略空格参数,是以是否为空和是否存在为判断依据 2).isBlank 是在 isEmpty 的基础上进行了为空(字符串都为空格、制表符、tab 的情况)的判断。(一般更为常用)
StringUtils.isEmpty("yyy") = false
StringUtils.isEmpty("") = true
StringUtils.isEmpty(" ") = false
StringUtils.isBlank("yyy") = false
StringUtils.isBlank("") = true
StringUtils.isBlank(" ") = true
6.处理时间用joda库中的函数和java8中的时间函数,保证线程安全,避免使用Calendar以及jdk8以前的方法;
7.判断集合非空, CollectionUtils.isNotEmpty; 判断Map非空, MapUtils.isNotEmpty; 尽量少用原始方法判断, 能用工具包尽量用工具包;
8.集合排序, 自然逆序用Comparator.reverseOrder(), 不要用重写compare方法实现;
9.new的对象不能能为null, 不做为null的判断, 并且不做无效的new操作, 有赋值就不new;
10.在设计到时间的格式转换时注意时区的影响, 如下:
//用format会导致时区的错误
hkStockRedisDao.setCallOrPullIVsRegion(ul, String.format("%tF", time.toDate()), ivMaps);
//考虑时区的影响, 尤其是在设计到不同交易所的情况
hkStockRedisDao.setCallOrPullIVsRegion(ul, TimeUtils.printHkDate(time.getMillis()), ivMaps);
public static final DateTimeZone TIMEZONE_HK = DateTimeZone.forID("Asia/Hong_Kong");
private static final DateTimeFormatter COMMON_DATE_FORMAT_HK = DateTimeFormat.forPattern("yyyy-MM-dd")
.withZone(TIMEZONE_HK);
public static String printHkDate(long timestamp) {
return COMMON_DATE_FORMAT_HK.print(timestamp);
}
11.在做条件判断时考虑周全,无效无意义的判断要减少,没有必要的else尽量减少, return语句在if, else里尽量减少, 在外面return;
12.在做时间的比较计算时, 用joda库或者java8中的日期操作方法: 比如判断时间相差三个月, 用DateTime中的minusMonths函数,考虑11月和2月之间的差
int timeDiff = expireDateTime.minusMonths(3).compareTo(currentDate);
-
无效的import要及时去掉, StringUtils用lang3的;
-
操作数据库时需要考虑到性能因素, 不能只认结果;
-
异常处理, 强转需要捕获异常;
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
-
类型相同的变量直接赋值, 无需在重新new
-
出现太多常量, 需要提取公共常量;
-
分页时主要默认最小页大小和最大页大小, 避免返回全部;
-
常量命名规范, 大写下划线;
-
方法权限, 类中调用private, 子类调用protected;
-
代码格式调整option+command+l, "{"前有空格, 语句对齐, 用option+command+l快捷键对齐;
-
如果用//注释, 需要与注释内容之间有一个空格;
-
相同业务类型, 相同意义用才用可变参数, 可变参数放在最后一个参数;
-
包装类型的值比较用equals方法, 不用==, 因为java在包装类型有缓存;
-
构造方法里不加任何业务逻辑, 放在init方法里;
-
只要重写equals就必须重写hashCode
-
关于通配符: 泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用add方 法,而<? super T>不能使用get方法,做为接口调用赋值时易出错;PECS(Producer Extends Consumer Super)原则:第一、频繁往外读取内 容的,适合用<? extends T>。第二、经常往里插入的,适合用<? super T>;
-
不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
-
集合初始化时,指定集合初始值大小
-
使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历; keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。
-
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
-
SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为 static,必须加锁,或者使用 DateUtils 工具类。
注意线程安全,使用 DateUtils。亦推荐如下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
-
public方法传参注意, 防止调用者调错
-
if else 考虑清楚, 需要让看的人觉得逻辑清晰
-
分页实现, 需要totalPage, pageSize, page三个参数, totalPage和page需要返回给客户端
-
maven在打包发布前要clean下, 防止本地的target下的classess影响发布后的结果
-
字符串截图判断是否越界
stockStat.getNameCN().substring(2, 4);
比较: redisTemplate.boundValueOps(key).get() redisTemplate.opsForValue().get(key);