那些坑爹的比较

一般的数值类型的比较用==就可以了,但其他情况就需要注意了。

实例的比较

对两个对象实例的比较不能使用==,必须使用equal()。尤其要注意字符串、数组、对象实例

  1. public class Main {
  2. public static void main(String[] args) {
  3. String s1 = "hello";
  4. String s2 = "HELLO".toLowerCase();
  5. System.out.println(s1 == s2);
  6. System.out.println(s1.equals(s2));
  7. }
  8. }
  9. # output
  10. false
  11. true

BigDecimal类型的比较

建议使用compareTo()方法比较,因为使用equals()方法不但要求两个BigDecimal的值相等,还要求它们的scale()相等:

  1. BigDecimal d1 = new BigDecimal("123.456");
  2. BigDecimal d2 = new BigDecimal("123.45600");
  3. System.out.println(d1.equals(d2)); // false,因为scale不同
  4. System.out.println(d1.equals(d2.stripTrailingZeros())); // true,因为d2去除尾部0后scale变为2
  5. System.out.println(d1.compareTo(d2)); // 0

必须使用compareTo()方法来比较,它根据两个值的大小分别返回负数、正数和0,分别表示小于、大于和等于。

静态工厂方法

我们把能创建“新”对象的静态方法称为静态工厂方法。Integer.valueOf()就是静态工厂方法,它尽可能地返回缓存的实例以节省内存。当然这也意味着创建的多个对象引用的可能是同一个实例。

  1. Integer m = Integer.valueOf(3);
  2. Integer n = Integer.valueOf(3);
  3. Integer o = Integer.valueOf(3889);
  4. Integer p = Integer.valueOf(3889);
  5. // 打印各引用的内存地址
  6. System.out.println(System.identityHashCode(m)); //5592464
  7. System.out.println(System.identityHashCode(n)); //5592464
  8. System.out.println(System.identityHashCode(o)); //1830712962
  9. System.out.println(System.identityHashCode(p)); //1112280004

所以我们创建新对象时,优先选用静态工厂方法而不是new操作符。

内存中的数字表示

byte类型在内存中表示

byte型的-1在内存中的二进制表示是11111111,第一位是高位,亦是符号位。如果我们将它转换为无符号值,则是255。

  1. byte x = -1;
  2. byte y = 127;
  3. System.out.println(Byte.toUnsignedInt(x)); // 255
  4. System.out.println(Byte.toUnsignedInt(y)); // 127

浮点型数值在内存中的表示

在学习java的过程中遇到一个问题,float的取值范围比long大得多,可达10的38次方,可float只占用四个字节,long占用8个字节。
原因在于浮点类型在内存中的存储方式与整型相去甚远。float使用了IEEE 754标准中的binary32表示,double使用的是binary64。

首先我们要理解内存中的小数表示,以16.625为例,转化为二进制,则整数部分为10000,那么小数部分呢?
我们反过来看,一个二进制数10000.101的十进制为多少?要注意,这里的小数点两边相差2倍,而非10倍。所以转化为二进制应该为16.625。

那么关键点来了,通常我们希望浮点数有尽可能多的有效位,于是我们考虑了一些折中的策略。以10000.101为例,我们可以借鉴十进制的科学计数法,将之记为1.0000101E100。由于整数部分一定为1,所以需要存储的只有小数和指数部分,再加上符号位。
内存中是这样存储的:
tips - 图1

S为一位的符号位,0为正,1为负。E为8为的指数为,M为23为的小数位。需要注意的是,E是有符号的,所以取值范围为-127~127。网上有说E并没有符号的设置,而是通过实际值十进制(可能为负值)+127在转换为二进制赋值给E存储,取出时则是转换后再减去127。
按这种实现方式,16.625在内存中为01000001 10000101 00000000 00000000。

回过头来,显然float的取值范围为1.1111 1111 1111 1111 1111 1111 1111 111E1111 111正负之间(即约为-2^128到2^128,当然负值应该比正值多一个值)
image.png