2.1 Java编程陷阱
程序陷阱是指那些能够正常编译,但是在执行时却产生事与愿违的,有时候甚至是灾难性后果的程序代码。广义上任何可能导致程序员把大量的时间浪费在开发工具的使用上而不是最终软件的进展上的语言特性、API或系统,都可以称为陷阱。陷阱能够造成程序员的迷惑,所以要严格分析陷阱。①症状或者问题,首先找到是哪一个代码造成的问题,陷阱的类型是什么。②问题的根源,这个是揭示陷阱最重要的一个部分,要深入底层,了解可能导致程序员绊脚的详细内部工作过程、无效的假设或者API的缺陷。③解决方案,这个是分析陷阱的最后一个步骤,最终给出一个程序实现和运行结果。
2.1.1 Java基础编程陷阱
1. 奇数误判
要判断一个数i是不是奇数,有程序员使用了这样的判别方法:return i % 2==1;,这就产生一个陷阱。这个判别式在判断负数的奇偶性时,会把所有负整数判断为假。这个陷阱的根源是仅仅考虑了正数的情况。应该改为return i % 2!= 0;。
2. 浮点数
浮点数是计算机特有的表示数值的方法,特点是小数点可以浮动,这与人理解数字的方式是不一样的。例如,System.out.printf2.0-1.1;显示出的不是0.9,而是0.8999999999999999,人会认为计算机出错了。但是,计算机并没有出错,仅仅是小数点的浮动方式没有按照人的想象。如果想得到正确答案0.9,应该改为System.out.printf"%.1f", 2.0-1.1,指定小数点后面只有1位;或者System.out.printlnnew BigDecimal"2.0".subtract new BigDecimal"1.1";。
3. 长整除
下面的代码出现的问题也是与Java表示数值的方式有关的。
public class LongDivision {
public static void mainString[] args {
final long MICROS_PER_DAY=24*60*60*1000*1000; 微秒
final long MILLIS_PER_DAY=24*60*60*1000;毫秒
System.out.printlnMICROS_PER_DAYMILLIS_PER_DAY; 结果并不是1000,而是5。原因是Java把等式右端当作int型,产生了越界。如果要得到正确答案,应该改为final long MICROS_PER_DAY=24L*60*60*1000*1000;
final long MILLIS_PER_DAY=24L*60*60*1000;
}
}
4. 字符串和字符
public class CharAndString {
public static void mainString[] args {
System.out.println"H" "a";输出Ha
System.out.println''H'' ''a'';输出169,字符被自动提升为int型
}
}
5. 字符数组
public class CharArray {
public static void mainString[] args {
String letters="ABC";
char[] numbers={''1'', ''2'', ''3''};
System.out.printletters " easy as " numbers; 输出ABC easy as [C@c17164
System.out.printnumbers;print方法用的是重构,输出的是123
}
}
6. 转义字符
public class UnicodeTest {
public static void mainString[] args {
public class RandomTest {
private static Random rnd=new Random;
public static void mainString[] args {
StringBuffer word=null;
switchrnd.nextInt2 { rnd.nextInt2只能取到0和1,2根本就取不到,应该改为rnd.nextInt3
case 1: word=new StringBuffer''P''; 应该增加break; ''P''应该改为"P",''P''表示字符被StringBuffer转化为ASCII码,表示StringBuffer的容量
case 2: word=new StringBuffer''G''; 应该增加break; ''G''应该改为"G"
default: word=new StringBuffer''M''; ''M''应该改为"M"
}
word.append''a'';
word.append''i'';
word.append''n'';
System.out.printlnword; 结果输出为ain,而非Pain、Gain、Main之一
}
}
9.j=j
public class ForTest {
public static void mainString[] args {
int j=0;
for int i=0; i j=j; 应该改为j,或者j=j
}
System.out.printlnj; 输出结果为0,而不是预想中的100,出现错误
}
}
10.整数的边界
public class WhileTest {
public static final int END=Integer.MAX_VALUE;
public static final int START=END - 100;
public static void mainString[] args {
int count=0;
for int i=START; icount;
System.out.printlni;所输出的i变成了负数
}
System.out.printlncount; 出现死循环,程序不能正常执行
}
}
11.计数器的问题
public class Clock {
public static void mainString[] args {
int minutes=0;
for int ms=0; ms if ms % 60*1000==0 ms % 60*1000==0相当于ms % 60*1000==0,应该修改为ms %60*1000==0
minutes;
System.out.printlnminutes; 输出为60000,不是60,出现错误
}
}
12.try-finally
public class ReturnValue {
public static void mainString[] args {
System.out.printlndecision; 到底返回什么值呢?返回值为false,最终执行的是finally语句
}
public BadLoggerLevel l{
FileHandler fh=null;
m_log=Logger.getLogger"BadLogger.logger";
m_log.setLevell;
}
public void test{
System.out.println"The level for the log is: " m_log.getLevel;
m_log.finest"This is a test for finest";
m_log.finer"This is a test for finer";
m_log.fine"This is a test for fine";
m_log.info"This is a test for info";
m_log.warning"This is a warning test";
m_log.severe"This is a severe test";
}测试结果仅仅能输入info的情况,其他情况无法输出
public void test{
System.out.println"The level for the log is: " m_log.getLevel;
m_log.finest"This is a test for finest";
m_log.finer"This is a test for finer";
m_log.fine"This is a test for fine";
m_log.info"This is a test for info";
m_log.warning"This is a warning test";
m_log.severe"This is a severe test";
}
public static void mainString[] args{
Level loglevel=Level.INFO;
if args.length!=0 {
if args[0].equals"ALL" {
loglevel=Level.ALL;
}
else if args[0].equals"FINE" {
loglevel=Level.FINE;
}
else if args[0].equals"FINEST" {
loglevel=Level.FINEST;
}
else if args[0].equals"WARNING" {
loglevel=Level.WARNING;
}
else if args[0].equals"SEVERE" {
loglevel=Level.SEVERE;
}
}
BadLogger logex=new BadLoggerloglevel;
logex.test;
}
}要有一个能控制level的东西,否则只能显示info下的,需要增加ConsoleHandler
3.单例模式
错误的做法:
public class Apple {
private static Apple apple=null;
private Apple{}
public static Apple getApple{
ifapple==null {
apple=new Apple;
}
return apple;
}
}
正确的做法:
1 public class Apple {
private static Apple apple=new Apple;
private Apple{}
public static Apple getApple{
return apple;
}
}
2 public class Apple {
private static Apple apple=null;
private Apple{}
public static synchronized Apple getApple{
ifapple==null {
apple=new Apple;
}
return apple;
}
}
public class SingletonTest {
public static void mainString[] args {
test1;
}
public static void test1{
Apple apple1=Apple.getApple;
Apple apple2=Apple.getApple;
ifapple1==apple2 {
System.out.println"是同一个对象";
}else{
System.out.println"不是同一个对象";
}
}