7.2.1 精心地构造、划分子模块,并按“接口”部分及“内核”部分合理地组织子模块,以提高“内核”部分的可移植性和可重用性。
说明:对不同产品中的某个功能相同的模块,若能做到其内核部分完全或基本一致,那么无论对产品的测试、维护,还是对以后产品的升级都会有很大帮助。
7.2.2 精心构造算法,并对其性能、效率进行测试。
7.2.3 时刻注意表达式是否会上溢、下溢。
示例:如下程序将造成变量下溢。
unsigned char size ;
while (size-- >= 0) {// 将出现下溢
... // program code
}
当size等于0时,再减1不会小于0,而是0xFF,故程序是一个死循环。应如下修改。
char size; // 从unsigned char 改为char
while (size-- >= 0) {
... // program code
}
7.2.4 使用变量时要注意其边界值的情况。
示例:如C语言中字符型变量,有效值范围为-128到127。故以下表达式的计算存在一定风险。
char chr = 127;
int sum = 200;
chr += 1; // 127为chr的边界值,再加1将使chr上溢到-128,而不是128。
sum += chr; // 故sum的结果不是328,而是72。
若chr与sum为同一种类型,或表达式按如下方式书写,可能会好些。
sum = sum + chr + 1;
7.2.5 在运算中不要减小数据的精度。
7.2.6 为用户提供良好的接口界面,使用户能较充分地了解系统内部运行状态及有关系统出错情况。
7.2.7 系统应具有一定的容错能力,对一些错误事件(如用户误操作等)能进行自动补救。
7.2.8 对一些具有危险性的操作代码(如写硬盘、删数据等)要仔细考虑,防止对数据、硬件等的安全构成危害,以提高系统的安全性。
7.2.9 使用第三方提供的软件开发工具包或控件时,要注意以下几点:
(1)充分了解应用接口、使用环境及使用时注意事项。
(2)不能过分相信其正确性。
(3)除非必要,不要使用不熟悉的第三方工具包与控件。
说明:使用工具包与控件,可加快程序开发速度,节省时间,但使用之前一定对它有较充分的了解,同时第三方工具包与控件也有可能存在问题。
7.2.10 尽可能的对接口进行 instanceof 运算。
说明:instanceof运算符用于强制类型转换之前检查对象 的真实类型以避免类型转换异常,从而提高代码健壮性。
7.2.11 使用大写'L'表示 long 常量
示例: 如下代码:
long a = 100000L;
如果写成小写l,容易被误认为是数字1
long a = 1000001;
7.2.12 在switch 中每个 case 语句都应该包含 break 或者 return 。
说明:因为特殊情况需要处理完一个case后进入下一个case处理,必须在该case语句处理完、下一个case语句前加上明确的注释。
7.2.13 使用ObjectStream 的方法后,调用reset() ,释放对象。
说明:ObjectStream中会缓存收发的对象,当再次发送同一个对象时,实际发送的不是对象的内容,而是对象的索引信息。
当持续发送新对象时,ObjectStream收发端的缓存会持续增加,如果不调用reset(), 将会导致内存耗尽。
示例:通过ObjectStream收发如下对象。
public class Person implements java.io.Serializable {
private final String firstName;
private final String surname;
private int age;
public Person(String firstName, String surname, int age) {
this.firstName = firstName;
this.surname = surname;
this.age = age;
}
public String toString() {
return firstName + " " + surname + ", " + age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Sender {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 7000);
ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream());
Person p = new Person("Heinz", "Kabutz", 0);
for (int age=0; age < 1500 * 1000; age++) {
p.setAge(age);
oos.writeObject(p);
}
}
}
执行Sender时,接收端收到的数据age一直是0,而不是顺序增加的,因为ObjectOutputStream第一次发送p时,发送的是完整数据,后续只发送了对象索引,接收端将第一次接收到的数据存入缓存,后续读取的都是缓存中的数据。
public class Sender2 {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 7000);
ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream());
for (int age=0; age < 1500 * 1000; age++) {
oos.writeObject(new Person("Heinz", "Kabutz", age));
}
}
}
Sender2每次发送的都是新对象,接收端收到的数据age会持续增加,与发送的数据一直,但运行一段时间后将导致内存耗尽。
public class Sender3 {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 7000);
ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream());
for (int age=0; age < 1500 * 1000; age++) {
oos.writeObject(new Person("Heinz", "Kabutz", age));
oos.reset();
}
}
}
Sender3每次调用writeObject后又调用了reset()方法,接收端的数据正确,而且不会导致内存耗尽,但reset()耗时比较多,Sender3效率太低。
public class Sender4 {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 7000);
ObjectOutputStream oos = new ObjectOutputStream(
s.getOutputStream());
for (int age=0; age < 1500 * 1000; age++) {
oos.writeObject(new Person("Heinz", "Kabutz", age));
if (age % 1000 == 0) oos.reset();
}
}
}
Sender4每发送1000个Person对象reset一次,既保证了接收数据的正确性,又兼顾效率。