- 一个源文件按顺序包含版权、package、import、顶层类,且用空行
分隔
一个源文件中应按顺序包含以下信息:
- 许可证或版权信息;
- package语句,且语句内不换行;
- import语句,且语句内不换行,不能用通配符*;
- 顶级类(只有一个),所在.java源文件与它同名。
- 应用于类、方法、类属性的每个注解独占一行
- 类和成员修饰符(如果存在)按Java语言规范建议的顺序显示
public protected private abstract default static final transient volatile synchronized
native strictfp
- 数字字面量应该设置合适的后缀, long 类型应该使用L作为后缀
long值必须使用L后缀,不能使用l做后缀,d、f不强制采用大写。
三、编程实践 3.1 声明和初始化
- 每行声明一个变量
- 局部变量被声明在接近它们首次使用的行
类的成员变量要集中声明
- 禁止C风格的数组声明
- 避免枚举常量序号的产生依赖于ordinal()方法
ordinal返回的枚举常量的排列序号。如果有特殊不能使用。
- 禁止将mutable对象定义为常量
mutable:可变。
// 反例:Result类为可变的不能用public static final修饰
class Result {
private int resultCode;
private String resultMsg;
public Result(int resultCode, String resultMsg) {
reset(resultCode, resultMsg);
}
public void reset(int resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
}
// Result是一个mutable类,实例出来的SUCCESS即便用了public static final修饰,也仍是mutable对象
public static final Result SUCCESS = new Result(0, "Success");
public void foo() {
SUCCESS.reset(101, "Failure"); // 不当使用:此时,SUCCESS这个“常量”已经被改变
// 后续代码再引用SUCCESS将带来业务异常
if (bar() == SUCCESS) {
...
}
}
// 反例:List、Map等大多都是mutable类,定义常量应该使用Collections.unmodifiableList() 、 Collections.unmodifiableMap()转化为不可变对象。 // java10以后提供了of()方法,返回的是不可变对象。 public static final ListEMPTY_RESULT_LIST = new ArrayList<>(); public static final List RESULT_LIST = Arrays.asList("result1", "result2");
// 正确示例 // 使用Collections.unmodifiableList()以保证EMPTY_RESULT_LIST不可变 public static final List3.2 数据类型 3.2.1 整数EMPTY_RESULT_LIST = Collections.unmodifiableList(new ArrayList<>()); // 更自然的写法:Collections.emptyList() public static final List EMPTY_RESULT_LIST = Collections.emptyList(); public static final List PRIME_NUMS = List.of(2, 3, 5, 7, 11, 13, 17, 19); // List.of, Set.of, Map.of
- 数值运算时,避免整数溢出。
可能溢出:加减乘除、取绝对值
解决方法:先决条件检测、Math类安全方法、向上类型转换、BigInteger方法
Math.multiplyExact()方法乘积时候不溢出返回结果,溢出的时候抛出异常ArithmeticException。
- 除法和模运算除数不能为0
- 禁止使用浮点数做循环计数器
- 精确计算使用BigDecimal,不要使用float和double
构造BigDecimal的时候应该使用字符串格式的数组,即BigDecimal(String val)
- 浮点型数据判断相等不要直接使用==,浮点型包装类型不要用equals()或者flt.compareTo(another)==0作相等的比较
可在一定误差范围内判断是否相等;符号不同不能使用精度范围;判断浮点数是否为0的时候,可以直接比较。
- 禁止与NaN进行比较运算,相等操作使用Double或Float的isNaN()方法
3.2.3 字符串NaN(Not a Number) <,<=,>.>=,==会返回false,!=会返回true。
- 不要再代码中硬编码用于表示换行、文件路径分隔的字符
ing使用println()来代替华航夫,或者System.lineSeparator()获取运行时环境的换行符。分隔符可以使用java.io.File的separator和pathSeprator静态属性。
- 字符串大小写转化、数字格式化为西方数字时,必须加上Locale.ROOT或者Locale.ENGLISH
大小写转化建议显示指定Locale.Root .toUpperCase(Locale.ROOT) .format(Locale.ROOT, "%d", 2)
字符串转化的时候建议一次性转化,不能转化为字符数组转化。
- 字符与字节的互相转化操作,要指明正确的编码方式
data.getBytes(StandardCharsets.UTF_8) new String(buf, StandardCharsets.UTF_8) new InputStreamReader(fis, StandardCharsets.UTF_8)
- 内存中的敏感信息使用完毕后应立即清0
3.2.4 类型使用与转换用string存储敏感信息,清理可通过反射、调用JNI接口等方式实现;推荐使用char[]byte[]存储,容易清理,直接Arrays.fill(value, (char) 0x00)
- 基本类型优于包装类型,注意合理使用包装类型
- 明确地进行类型转换,避免依赖隐式类型转换
不要把复合赋值运算符应用于byte、short、char类型的变量
- 在引用类型乡下转换前用instanceof进行判断。
3.3表达式不判断可能会因为类型不匹配而导致运行期异常 java.lang.ClassCastException.
- 不要在单个表达式中对相同的变量赋值超过一次
- 用括号明确表达式的操作顺序,避免过分依赖默认优先级
一元运算、不涉及多种运算符、极简三元表达式或条件表达式,不需要使用括号。
位操作、拿其他执行结果作布尔比较的,需要加括号。
- 条件表达式?:的第2和第3个操作数应使用相同的类型。
不一样的操作数类型,会导致意料之外的类型转换,也可能会因为自动拆箱导致NullPointerException
- 表达式的比较,应该遵循左侧倾向于变化、右侧倾向于不变的原则
- 禁止直接使用可能为null的对象,防止出现空指针引用
通过预检查的方式进行消解,而不是try…catch处理
- 代码中不应使用断言assert
3.4控制语句断言判断为false的时候会抛出AssertionError,会导致异常退出。在以下两种场景下不应该使用断言
1.运行态错误检查;2.逻辑代码执行
- 不要再控制性条件表达式执行赋值操作或执行复杂的条件判断
- 含else if分支的条件判断应在最后加一个else分支
- switch语句要有default分支
- 禁止使用空的无限循环
- 方法要简短
代码行数不超过50行(非空非注释);参数不超过5个;最大嵌套深度不超过4层。
- 不要使用已标注为@Deprecated的方法、类、类的属性。
- 不应把方法的参数当做临时变量
为了减轻疏忽导致再次赋值,可在参数前加final关键字;引用类型修改,允许修改内部属性。
- 谨慎使用可变数量参数
接受0到n个参数;当使用可变数量参数时,应避免使用固定数量的同类型参数对方法进行重载,导致代码不读。
- 对于返回数组或者容器的方法,应返回长度为0的数组或者容器,代替返回null。
可以为null的时候应该添加注释充分说明;有些不得不override的方法,可不加;已经用@Nullable等注解为可空的,可不加注释。
- 使用Optional代替null作为返回值或者可能的缺失值;禁止对Optional对象赋值为null。
3.6类、接口与面向对象编程 3.6.1类不应该返回 Optional
、 Optional 、 Optional ,而应该使用
OptionalInt 、 OptionalLong 、 OptionalDouble
- 应避免定义public且非final的类属性
- 不要在父类的构造方法中调用可能被子类覆写的方法
由于在java中,子类初始化时,会调用父类的构造方法。如果调用,会导致子类初始化未完成而导致异常。
- 构造方法如果有多个,尽量重用
- 避免在无关的变量或无关的概念之间重用名字,避免隐藏、遮蔽、遮掩。
覆写override—子类与父类间;(子类示例方法覆写父类中非private非static的方法)
重载overload—类内部;(重载的时候放在一起;引起歧义换方法:可变参数、包装类型)
隐藏hide—子类与父类间;(属性、静态方法、内部类。相当于输出父类)
遮蔽shadow—类内部;(变量、方法、类:局部变量会遮蔽全局变量)
遮掩obscure—类内部;(变量、包、方法、类型)
- 避免基本类型与其包装类型的同名重载方法
重载如果有自动装箱和泛型场景下,各个重载方法之间的边界变模糊,所以避免使用。
- 覆写equals方法时,要同时覆写hashCode方法
如果不正确覆写hashCode方法,则会导致效率低下甚至出错。
hashCode约定:同一次运行,同一个对象equal不变,多次调用hashCode返回值必须相同;两个对象的equal方法相等,则hashCode返回值必相同;如果两个对象调用equal方法不相等,则对这两个对象的hashCode方法不要求返回值不同,但是出于减少哈希碰撞的性能考虑,最好不同。
- 子类覆写父类方法或实现接口时必须加上@Override注解
编译器就发现问题。
- 正确实现单例模式
确保同一个进程内,单例类只有一个对象,并且该对象对所有其他对象提供访问。
要求:构造方法私有;防止对象在初始化被多个线程同时运行;确保该对象不可序列化;确保该对象无法克隆。
- 使用类名调用静态方法,而不要使用实例或表达式来调用。
3.6.2接口使用实例调用静态方法时,父子类有同名静态方法,实例调用实际用的是父类的静态方法。(静态属性同理)



