1、【强制】对于方法中采用逻辑判断后返回 boolean 类型值的情况
强制: 不要使用 if-else 结构中直接返回 Boolean 值。
反例:
public Boolean isEmpty(String chars) { if ("".equals(chars)) { return true; } else { return false; }}
复制代码
正例:
public boolean isEmpty(String chars) { return "".equals(chars);}
复制代码
2、用静态工厂方法代替构造器;
根据《Effective Java(第 III 版)》第二章第一条:对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器。还有一种方法,类可以提供一个公有的静态工厂方法( static factory method ),它只是一个返回类的实例的静态方法。
示例:
public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE;}
复制代码
3、【强制】不应通过派生类型访问“静态”基类成员
基类的静态成员不应使用派生类型的名称进行访问。可能会令人产生两个不同的静态成员存在的假象困惑。
正例:
public void export(HttpServletResponse response) throws IOException { List<Object> list = new ArrayList<>(); EasyExcelFactory.write(response.getOutputStream(), ExportWorkOrder.class).autoCloseStream(Boolean.FALSE) .sheet("sheet1").doWrite(list);}
复制代码
反例:
public void export(HttpServletResponse response) throws IOException { List<Object> list = new ArrayList<>(); EasyExcel.write(response.getOutputStream(), ExportWorkOrder.class).autoCloseStream(Boolean.FALSE) .sheet("sheet1").doWrite(list);}
复制代码
4、【强制】指定编码使用 JDK 提供的字符编码常量类 StandardCharsets
示例:
public String getFileName() throws UnsupportedEncodingException { return URLEncoder.encode("XXXXXX - " + LocalDate.now().toString(), StandardCharsets.UTF_8.name());}
复制代码
5、【强制】代码中禁止使用 Exception#printStackTrace();打印异常堆栈信息
反例:
public String getFileName() { try { return URLEncoder.encode("XXXXXX - " + LocalDate.now().toString(), StandardCharsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; }}
复制代码
6、工具类(实用类)不应具备公开构造函数
正例:
public class AesUtil { // 防止实例化 private AesUtil() { } public String getPrivateKey() { // coding }}
复制代码
7、try-with-resources 结构优先于 try-finally
反例:
// try-finally - No longer the best way to close resources!public static String firstlineOfFile (String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { br.close(); }}
复制代码
正例:
// try-with -resourcespublic static String firstlineOfFile (String path) throws IOException { try(BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); }}
复制代码
8、写在类上面的 Javadoc
写在类上的文档标注一般分为三段:
第一段:概要描述,通常用一句或者一段话简要描述该类的作用,以英文句号作为结束
第二段:详细描述,通常用一段或者多段话来详细描述该类的作用,一般每段话都以英文句号作为结束
第三段:文档标注,用于标注作者、创建时间、参阅类等信息
在注释中出现以 @开头东东被称之为 Javadoc 文档标记,是 JDK 定义好的,如 @author、@version、@since、@see、@link、@code、@param、@return、@exception、@throws 等。
1)@link {@link 包名.类名 #方法名(参数类型)} 用于快速链接到相关代码
2)@cod {@code text} 将文本标记为 code
3)@param 一般类中支持泛型时会通过 @param 来解释泛型的类型
4)@author 详细描述后面一般使用 @author 来标记作者,如果一个文件有多个作者来维护就标记多个 @author;
@author 后面可以跟作者姓名(也可以附带邮箱地址)、组织名称(也可以附带组织官网地址)
5)@see 一般用于标记该类相关联的类,@see 即可以用在类上,也可以用在方法上。
6)@since 从以下版本开始,一般用于标记文件创建时项目当时对应的版本
一般后面跟版本号,也可以跟是一个时间,表示文件当前创建的时间
7)@version 用于标记当前版本,默认为 1.0
示例:
/** * The class {@code String} includes methods for examining * individual characters of the sequence, for comparing strings, for * searching strings, for extracting substrings, and for creating a * copy of a string with all characters translated to uppercase or to * lowercase. Case mapping is based on the Unicode Standard version * specified by the {@link java.lang.Character Character} class. * * <p> Unless otherwise noted, passing a <tt>null</tt> argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. * * <p>A {@code String} represents a string in the UTF-16 format * in which <em>supplementary characters</em> are represented by <em>surrogate * pairs</em> (see the section <a href="Character.html#unicode">Unicode * Character Representations</a> in the {@code Character} class for * more information). * Index values refer to {@code char} code units, so a supplementary * character uses two positions in a {@code String}. * <p>The {@code String} class provides methods for dealing with * Unicode code points (i.e., characters), in addition to those for * dealing with Unicode code units (i.e., {@code char} values). * * @author Lee Boynton * @author Arthur van Hoff * @author Martin Buchholz * @author Ulf Zibis * @see java.lang.Object#toString() * @see java.lang.StringBuffer * @see java.lang.StringBuilder * @see java.nio.charset.Charset * @since JDK1.0 */
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { // code}
复制代码
9、写在方法上的 Javadoc
写在方法上的文档标注一般分为三段:
第一段:概要描述,通常用一句或者一段话简要描述该方法的作用,以英文句号作为结束
第二段:详细描述,通常用一段或者多段话来详细描述该方法的作用,一般每段话都以英文句号作为结束
第三段:文档标注,用于标注参数、返回值、异常、参阅等
1)@param 后面跟参数名,再跟参数描述
2)@return 跟返回值的描述
3)@throws 跟异常类型 异常描述 , 用于描述方法内部可能抛出的异常
4)@exception 用于描述方法签名 throws 对应的异常
5)@see 既可以用来类上也可以用在方法上,表示可以参考的类或者方法
6)@value 用于标注在常量上,{@value} 用于表示常量的值
/** 默认数量 {@value} */private static final Integer QUANTITY = 1;
复制代码
7)@inheritDoc 用于注解在重写方法或者子类上,用于继承父类中的 Javadoc
示例:
package org.springframework.util;
public abstract class StringUtils { /** * Decode the given encoded URI component value. Based on the following rules: * <ul> * <li>Alphanumeric characters {@code "a"} through {@code "z"}, {@code "A"} through {@code "Z"}, * and {@code "0"} through {@code "9"} stay the same.</li> * <li>Special characters {@code "-"}, {@code "_"}, {@code "."}, and {@code "*"} stay the same.</li> * <li>A sequence "{@code %<i>xy</i>}" is interpreted as a hexadecimal representation of the character.</li> * </ul> * * @param source the encoded String * @param charset the character set * @return the decoded value * @throws IllegalArgumentException when the given source contains invalid encoded sequences * @since 5.0 * @see java.net.URLDecoder#decode(String, String) */ public static String uriDecode(String source, Charset charset) { // code }}
复制代码
10、Java Doc 示例
package com.example.demo;
/** * 类 {@code OrderService} 订单服务层. * * <p> 主要包括 创建订单、取消订单、查询订单等功能更 * * @see Order * @author <a href="mailto:2867665887@qq.com">SunlightBright</a> * @since 2021/1/1 */public class OrderService {
/** 默认数量 {@value} */ private static final Integer QUANTITY = 1;
/** * 创建订单. * * <p> 创建订单需要传用户id和商品列表(商品id和商品数量). * * <pre> // <pre>标签在代码格式化时不会发生改变 * <code> * // 演示如何使用该方法 * List<Goods> items = new ArrayList<>(); * Goods goods = new Goods(1L, BigDecimal.ONE); * Goods goods2 = new Goods(2L, BigDecimal.TEN); * items.add(goods); * items.add(goods2); * * Order order1 = new Order(); * order.setUserId("1"); * order.setItems(items); * OrderService#createOrder(order); * </code> * </pre> * * @param order 订单信息 * @throws NullPointerException 参数信息为空 * @exception IllegalArgumentException 数量不合法 * @return 是否创建成功 * @version 1.0 * @see {@link Order} */ public boolean createOrder(Order order) throws IllegalArgumentException{ Objects.requireNonNull(order); List<Goods> items = order.getItems(); items.forEach(goods -> { BigDecimal quantity = goods.getQuantity(); if (quantity == null || BigDecimal.ZERO.compareTo(quantity) == 0) { throw new IllegalArgumentException(); } }); System.out.println("create order..."); return true; }}
复制代码
11、【强制】所有的覆写方法,必须加 @Override 注解。
说明:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加 @Override 可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。
12、【强制】不能使用过时的类或方法。
说明:java.net.URLDecoder 中的方法 decode(String encodeStr) 这个方法已经过时,应该使用双参数 decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。
13、【强制】Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
正例:"test".equals(object);
反例:object.equals("test");
说明:推荐使用 java.util.Objects#equals(JDK7 引入的工具类)
14、DO/DTO/VO 等 POJO 类时必须实现 Serializable 类,并生成 serialVersionUID
示例:
/** * 用户数据模型 * * @author <a href="mailto:2867665887@qq.com" title="SunBright">ShuNing</a> */@Datapublic class SysUserDTO implements Serializable { // IDE生成的serialVersionUID private static final long serialVersionUID = 3616208581527945802L;
private Long id; private String username; private String loginName;}
复制代码
15、【推荐】 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter 方法。
说明: 公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现;因为承载的信息价值较低,所有 Service 和 DAO 的 getter/setter 方法放在类体最后。
16、【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
示例:
// Java迭代器的使用 IteratorList<Object> list = new ArrayList<>();for(Iterator<Map<String, Object>> it = list.iterator(); it.hasNext();){ Map<String, Object> map = it.next(); if (Objects.nonNull(map.get("name")) && "OPERATOR_NAME".equals(map.get("name").toString())) { roleId = map.get("id").toString(); break; } it.remove();}
复制代码
17、Java 集合(Collections)的非空判断使用 Collection#isEmpty()
正例:
public List<SysUser> collectionsIsEmpty(int status) { List<SysUser> userListCollections = sysUserService.findByStatus(status); if (userListCollections.isEmpty()) { throw new SysUserIsEmptyException("There is no system user collection"); } return userListCollections;}
复制代码
18、【强制】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。
19、【推荐】避免如下误解:敏捷开发 = 讲故事 + 编码 + 发布。
说明:敏捷开发是快速交付迭代可用的系统,省略多余的设计方案,摒弃传统的审批流程,但核心关键点上的必要设计和文档沉淀是需要的。
反例:某团队为了业务快速发展,敏捷成了产品经理催进度的借口,系统中均是勉强能运行但像面条一样的代码,可维护性和可扩展性极差,一年之后,不得不进行大规模重构,得不偿失。
20、静态常量定义规则:
// 正例private static final String UNKNOWN = "unknown";
// 反例static private final String UNKNOWN_IS = "unknown";private final static String UNKNOWN_IS_DEL = "unknown";
复制代码
评论