编码前全面考虑所有可能的输入,确保写出的代码在完成了基本功能之外,还考虑了边界条件,并做好了错误处理。只要全面考虑到这三方面的代码才是完整的代码。
要重视代码的鲁棒性,确保自己写出的程序不会轻易崩溃。平时在写代码时,养成防御式编程的习惯,在函数入口判断输入是否有效并对各种输入做好相应的处理。
目的:提高代码可读性、维护性,降低bug,提高程序性能,最终保证代码质量。
建议+案例+原因;
checklist
阅读代码:了解其运行机制、内部结构;学习其技巧。
阅读代码可作为学习程序设计的方法。
花时间经常阅读高质量的代码——>提高编写代码的能力!“为什么要这样写?”
提高自身的开发和设计能力——>广泛阅读现有的构架,了解它们是如何组织的。
编写的代码总是存在改进的空间。
编码时就要考虑使之易于阅读。
分析设计良好的软件系统的内部系统可以学到新的构架模式、数据结构、编码方法、算法、风格和文档规范、应用程序编程接口、甚至新的计算机语言。
从小型的程序开始阅读,通过运行程序来得到反馈。主动修改代码来检验对代码的理解是否正确。要从小的改动做起,逐渐增大它们的范围。
考虑如何改进它?——更好的设计、算法或功能。为代码编写文档。
自身价值的体现——不是掌握了多少知识,而是体现在我们创造的系统上!
物理模型
与代码相关的概念:
编程构造(基本编程元素)、数据类型、数据结构、控制流程、项目组织、代码规范、文档和构架。
在软件系统的工作投入中,40%~70%是用在系统首次编写完成之后。
1、原则
a、OCP开放-关闭原则:指导封装;
b、SRP单一职责原则:粒度控制;
c、DIP依赖倒置原则:
d、LSP里氏替换原则:指导多态;
e、ISP接口隔离原则:粒度控制;
2、避免错误
(1)合理使用注释
同步修改注释,和代码保持一致;
注释加在接口上;
(2)避免空指针:调用对象的方法,一定要明确该对象是否会空,不能确定时要判断!开发者常常自以为该对象不为null,而没有去看代码的实现或没有思考周全,漏掉了一些分支情况。
对于允许其值为null的变量,在对其操作前,需要预先判断其是否为null。
(3)Serializable对象
所有Serializable对象必须设置serialVersionUID,除非特殊情况。
序列化是为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
(4)在finally中释放资源——数据库连接、打开的文件等。
3、维护性
(1)重复代码:
a、保持类和方法职责单一。(粒度划分)
b、尽量利用框架提供的功能。
c、采用合理的设计模式。
(2)异常处理:打印合理的异常。
a、只捕获对自己有意义的特定异常,一般不要捕获Exception异常;
b、抛出对自己无意义的异常,并保留原始的异常对象(嵌套异常),并描述异常的原因;
如:
try{
createMember();
} catch(SQLException e) {
throw new MemberCreationException("Failed to create member", e);
}
c、所有异常要么捕获、要么抛出、要么记录。
(3)常量定义(什么时候定义常量?在哪里定义?如何定义?
a、多次引用的字符串和数字需要定义常量;
b、根据引用的范围(同一个类、不同类):
类中;
单独的常量类或接口;
c、常量定义处需要详细的注释;
(4)在配置文件中指定字符集编码,由框架来转换字符串。避免在代码中假设用户和系统的字符集编码。
如:
下列代码“假设”了用户的字符集编码:
// 如果源代码是以GBK编译的,那么该URL也是GBK编码的,然而用户可能使用UTF-8编码
response.sendRedirect(http://localhost/mypage.htm?p=中文);
//总是以GBK输出页面
response.setContentType("text/html; charset=GBK");
下列代码的结果依赖于操作系统的字符集编码:
Stringurl =URLEncoder.encode(str); // 将str转换成URL兼容的格式byte[]
bytes =str.getBytes(); // 将string转成bytes
Stringstr = new String(bytes); //将bytes转成string
(5)避免硬编码URL、文件路径:因为URL、文件路径等资源的不确定性,应避免在程序中硬编码这些内容。
4、性能
(1)拼装字符串:用StringBuffer,而不用"+"或"+="来拼装大量字符串。可以使用StringBuilder来代替StringBuffer。前者是后者的非同步版本,性能更优。
如:StringBuffer sb = new StringBuffer();
for(int i=0; i<100; i++) {
sb.append("aa").append(" ");
}
String str = sb.toString();
(2)预编译正则表达式
利用java.util.regex.*:
Pattern pattern = Pattern.compile("^\\d+$");
Matcher matcher = pattern.matcher(str);
if(matcher.matches()) {
}
而不是:
if(str.matches("^\\d+$")) {
}
(3)日志输出:要明白什么时候需要打印日志,采用什么级别打印日志。
不要以System.out和System.err来输出信息,而应该采用框架提供的Logging API。
在生产环境中必须关闭DEBUG日志。
正式使用日志时,尽量不要使用warn级别,避免日志文件臃肿。
(4)批量操作SQL
批量插入、更新操作:
PreparedStatement ps = conn.preparedStatement("insert into test_table(...) values(?,?,...)");
ps.setString(1,"aaa")
ps.addBatch();
.....
ps.executeBatch();
(5)线程同步:避免长时间地锁定线程。
4、开发技巧
(1)尽可能使用泛型而不是直接使用集合类:将运行时错误转化为编译时错误。
(2)开发小工具
Findbugs:
Relo:帮助开发人员研究大型代码库的好工具,它能一步步的跟踪你所展开的代码包,并快速生成类似UML的类图。
(3)阅读源码:JDK、Spring等。
5、TIPS
(1)不要往cookie中设置过多过大的数据。
(2)任何数据放入cache中都要考核这些数据是否符合三个指标(更新频率、访问量、命中率)。要把更新不频繁、访问量高、命中率高的数据放入cache中。同时慎用OSCache的文件持久化。
(3)在使用SQL语句时一定要清楚它的执行频率。
(4)一条正确的SQL语句随着时间的推移,数据量越来越大,也可能不合适了。在使用SQL语句时一定要弄清楚它的数据量的增长。
(5)如何区分好代码与坏代码
(6)阅读代码的方法
6、PS
(1)《effective java》、《重构》