注:本文来自粉丝[菜鸟逆袭]投稿
1.奇数性
看下面代码时候是否能判断参数 i 是奇数?
public static boolean isOdd(int i){
return i % 2 == 1;
}
答案是: No!
int数值a, 与非零int数值b 都满足下面的等式:
(a / b) * b + (a % b) == a
从上面就可以看出, 当取余操作返回一个非零的结果时, 左右操作数具有相同的正负号, 所以当取余在处理负数的时候, 以及会考虑负号.
而上面的这个问题, 解决方法就是避免判断符号:
public static boolean isOdd(int i){
return i % 2 != 0;
}
让结果与0比较, 很容易避免正负号判断.思考:
2.浮点数产生的误差
看下面代码会打印出什么样的结果?
public class Change{
public static void main(String args[]){
System.out.println(2.00 - 1.10);
}
}
从主观上看, 打印的结果必然是0.90, 然后这却是一个主观错误.
import java.math.BigDecimal;
public class Change1{
public static void main(String args[]){
System.out.println(new BigDecimal("2.00").subtract(new BigDecimal("1.10")));
}
}
通过上面的代码就能得到一个精确的值.注: 使用BigDecimal的时候, 不要使用BigDecimal(double d)的构造方法, 在double与double之间传值的时候依旧会引起精度损失. 这是一个严重的问题.思考:
3.长整数造成数据溢出
看下面的代码会打印什么?
public class LongDivision{
public static void main(String args[]){
final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
}
}
整个过程, 除数与被除数都是long型, 很容易保存这两个数, 结果一定是1000, 但是结果让你失望了, 结果是5.606010001000总是在int类型的基础上进行计算. 即表达式是按照int的规则计算.606010001000按照long进行存储.必须指定数据类型, 才能按照指定的规则进行运算.
public class LongDivision{
public static void main(String args[ ]){
final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
System.out.println(MICROS_PER_DAY/MILLIS_PER_DAY);
}
}
思考:
4.long的 "L" 与 "l" 所引发的错误
从上面 "长整数运算造成数据溢出" 引发又一个问题, 看下面例子:
public class Elementary{
public static void main(String[] args){
System.out.println(12345+5432l);
}
}
乍一看, 这很简单, 计算结果时是 6666, 但是打印的结果是 17777, 我开始头晕了, 这很不合理.思考:
5.多重类型转换引发的数值变化
看这样的一个例子:
public class Multicast{
public static void main (String[] args){
System.out.println((int)(char)(byte) -1);
}
}
看似结果是 -1, 但是运行之后, 结果变为 65535分析一下:
byte下的-1 => 变为:
1111,1111,1111,1111,1111,1111,1111,1111
32位(4个字节) 首位1表示负号.
byte到char => 变为:
0000,0000,1111,1111
16位(2个字节),首位0, 就此负号变正号.
char到int => 变为:
0000,0000,0000,0000,0000,0000,1111,1111
32位(4个字节)
由此可见, 在byte到char的变化过程中出现符号转换的问题. char首位总是0使得负号变正号.类型转换的过程存在这样的简单规则: 如果最初的数值类型是有符号的,那么就执行符号扩展;如果它是 char,那么不管它将要被转换成什么类型,都执行零扩展。因此这也就解释了为什么byte到char的过程存在负号变正号.
char c = (char)(b & 0xff);
这样就能保证符号具有保留思考:
6.避免所谓聪明的编程技巧
对于交换两个变量的内容, 在C/C++中存在一种这样的编程技巧:
int x = 1111;
int y = 2;
x^=y^=x^=y;
cout<<x<<" "<<y;
这样一个简单的连续异或就能解决变量的交换问题, 这种写法在很久以前是为了减少临时变量的使用, 所以这种做法在现在也得到了保留.Java的语言规范描述: 操作符的操作数是从左往右求值.x^ =y^ =x^ =y
表达式中的第二个x的时候是在计算x^ =y
之前的值( x的值依旧是1111 ), 并不是x^=y后的值, 这就导致了计算上的错误.
y = ( x^=( y^=x ) )^y
思考:
7.避免使用混合运算
看如下代码:
public class DosEquis{
public static void main(String[] args){
char x = 'X';
int i = 0;
System.out.println(true ? x : 0);
System.out.println(false ? i : x);
}
}
看似将打印: XX, 但是结果却是X88.
A ? B : C
B, C为相同类型, 那么表达式的计算结果就是B, C的类型
B, C不是相同类型的时候, 那么计算结果就按照B的类型(此时B必须是式子中最高类型).
此时C的类型就自动上升为式子中最高的类型, 例: false ? x : i, 输出是0, 而不是0对应的字符.
上面的规则决定了, 将调用哪一个print的重载函数.思考:
8.发现隐藏的类型转换
在这样的表达式: x += i; 按照平常的理解, 它一定是x = x + i; 可是这样的运算表达式是建立在x与i具有相同的类型的基础上的, 如果当x, i类型不相同的时候, 将会引发一个问题就是精度损失.
short x = 0;
int i = 99999;
x += i;
现在的x不是99999, 而是-31073.思考:
9.字符串的"+"运算符
看如下代码:
public class LastLaugh{
public static void main(String[] args){
System.out.print("H"+"a");
System.out.print('H'+'a');
}
}
由于长期受 "+" 运算符的影响, 上面的代码, 很容易把 'H'+'a' 也看作是字符串的连接, 这是一种惯性的思考方式.
1.使用 StringBuffer/StringBuild 做 append 运算.
StringBuild s = "".append('H');
2.使用String s = "" + 'H' +'a'; 使用字符串连接.
String s1 = "" + 'H' + 'a';
String s2 = 'a' + 'H' + "";
System.out.println(s1);
System.out.println(s2);
注: 避免 s2 的写法, 这样写 'a'+'H' 依旧做 int 的数值运算
思考:
看完字符的 "+" 运算符, 现在再来字符数组的 "+"运算符 :
public class A{
public static void main(String[] args){
String letters = "ABC";
char[] numbers = {'1', '2', '3'};
System.out.println(letters + " easy as " + numbers);
}
}
上面的代码, 最终的打印结果不是 ABC easy as 123, 而是ABC easy as [C@16f0472.
1.使用String.valueOf(number); 转字符串后再进行连接操作.
2.使用System.out.println(number); 调用重载的println(char[] c);
而在C/C++中, char numbers[4] = {'1', '2', '3', '\0' }; 代表的就是一个字符串.思考:
10."=="运算符进行比较
问题1:但是这里要注意的是:
Integer中的equals方法:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
这个过程中实现的是将Integer拆包,-128~127不需要拆包,可直接使用==比较.
Integer的缓存池-128~127: 自动装箱过程中使用valueOf创建对象,因此直接会使用缓存池中对象.思考:
问题2:
public class AnimalFarm{
public static void main(String[] args){
final String pig = "length: 10";
final String dog = "length: " + pig.length();
System.out. println("Animals are equal: " + pig == dog);
}
}
我想去比较pig与dog引用值关系, pig 与 dog 的引用值肯定是相同的, 但是最后的输出结果却是false.
1.System.out.println("Animals are equal: " + (pig == dog));
2.System.out.println("Animals are equal: " + pig.equals(dog));
思考:
往期推荐文章
[校招系列]:
hot
[软件质量系列]:
转载:https://blog.csdn.net/u010459192/article/details/101731235