代码规范
概述¶
检查&动态、静态类型¶
-
操作 operation 的三种句法:
- 作为前中后缀运算符
a+b
- 作为对象的方法 method
a.add(b)
- 方法通常是与特定对象关联的函数
- 函数 function
Math(a,b)
- 函数通常是一段独立的代码
- 作为前中后缀运算符
-
静态类型在编译时进行检查,动态类型在运行时进行检查
- 动态类型语言也有一定的静态检查
-
静态检查:静态检查主要关注于变量的类型,而不是它的具体值。它通常在编译时进行,检查代码以确保类型的正确性和兼容性。
- 动态检查:动态检查关注的是由特定值引起的错误。这种检查通常在程序运行时进行。
优秀软件的特征¶
- 防止错误:编写的代码需要确保“正确性”(即当前行为正确)和“防御性”(即未来在不同情况下仍然保持正确行为)。
- 易于理解:编写的代码必须易于他人理解,这对于未来的程序员来说尤为重要。
- 容易修改:软件总是在不断变化的,无论是因为需求的变化、环境的更新还是技术的进步。
编码风格¶
-
避免重复代码:出现 bug 时很容易在修复时出现遗漏
-
恰当的注释
- 在何合适的位置编写说明,如使用 Javadoc 编写方法说明
- 对于从网上复制的代码要注明出处
// read a web page into a string // see http://stackoverflow.com/questions/4328711/read-url-to-string-in-few-lines-of-java-code String mitHomepage = new Scanner(new URL("http://www.mit.edu").openStream(), "UTF-8").useDelimiter("\\A").next();
-
避免使用“魔术”数字
- 避免使用数字直接表示某些特定含义,比如月份
- 应该使用具有良好命名的常量
- 避免直接使用中间结果,应该在代码中保留计算步骤(即使计算很简单可以口算)
-
避免重用变量/参数名,保持相同的含义
-
函数中不应该直接在参数变量上进行修改,这会破坏输入
- 比如使用 final 避免
-
正确缩进,设置编译器使用空格代替制表符(制表符的显示并不统一)
-
避免使用全局变量(public static)
异常(Java)¶
- 使用异常取替特殊返回值,确保问题得到正确的处理
-
使用受检异常返回特殊结果,使用运行异常表示 bugs
-
遗憾的是有时强制检查异常会带来麻烦,比如 queue 希望在为空时 pop 抛出异常,但如果用户每次 pop 已经检查果 size ,却还要 try-catch 这是很麻烦的。
- 因此如果预料到用户哦通常会编写防止异常出现的代码时应该也是用运行异常
避免 bug¶
- 使用常量、静态检查等减少 bug 的出现
-
限制 bug 的范围,让 bug 尽早被发现,使得更容易进行处理(如及时抛出异常),如当发现前置条件不满足立即抛出运行时异常(也称为防御性编程)
- 什么时候使用 assert
- 前置条件的判断(如 sqrt)
- 返回值自检,判断返回值是否有明显错误
- 程序执行到了不该到达的位置
- 断言应该是对内部的检查,而不是对外部环境的检查(如检查文件是否存在哦),断言是用来检查 bug 的,而外部异常通常不是 bug(外部异常应该通过异常来解决)
- 断言通常使用在 debug 期间,并且可以方便的关闭,因此可以使用很耗费时间的断言(也因此,程序的安全性不应过度依赖断言)
- 并且要小心断言的副作用
assert list.remove(x);
- 并且要小心断言的副作用
可变与不可变¶
- 可变的优点:功能强大,便于使用,操作效率通常较高
-
不可变的优点:减少 bug,添加限制,便于理解
-
可变的问题
- 可变参数作为方法参数可能会被方法修改(函数的副作用)
- 可变参数作为返回值,如果返回值是方法中的非临时变量,修改返回值会造成对象的成员变量被修改
- 多个引用指向一个可变对象(aliases)往往会引发问题
-
解决方式:
- 使用不可变对象:
BigInteger
andBigDecimal
Collections.unmodifiableList
Collections.unmodifiableSet
Collections.unmodifiableMap
- 这三个是包装器,可以用于包装可变集合得到不可变集合
- 明确契约边界(与方法说明的约定):
- 使用不可变对象: