JAVA表达式之谜

一、奇数性

 

下面方法的目的是确定一个数是否为奇数

 

public static boolean isOdd(int i){ 

    return i % 2 == 1; 

} 

分析:这是Java对取余操作符(%)的定义所产生的,上述方法对于所有的负奇数判断都会失败,在任何负整数上调用该方法都会返回false,不管该负整数是奇数还是偶数。因为 -1 % 2 的结果是 -1,有符号。

建议:无论你何时使用到了取余操作符,都要考虑到操作数和结果的符号。

解决:

所以上述代码改正如下:

public static boolean isOdd(int i){ 

    return i % 2 != 0; 

}

如果是在一个强调性能的环境中,使用位操作符 & 替代取余操作符会更好

public static boolean isOdd3(int i){ 

    return (i & 1) != 0; 

} 

二、长整数

 

    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);

 

输出结果为:5

分析:所有的计算因子都是int类型的,所以在连乘的时候,两个int类型的数相乘最终得到的还是int类型的,24*60*60*1000*1000这个计算在乘以最后一个1000的时候结果超出了2147483647(int能表示的最大值)发生了溢出,虽然计算的结果放入Long中,但是这个计算是完全以int类型来计算的。

建议:当操作很大的数字时,要堤防溢出

解决:强制表达式中的所有后缀计算用Long运算来完成

    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);

注:因为连乘是从左到右计算的,所以强制转换应该从第一个参与计算的数据开始

下列转换方式是错误的

    final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000L;

    final long MILLIS_PER_DAY = 24* 60 * 60 * 1000L;

    System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);

 

三、找零时刻

    System.out.println(2.00 - 1.10);

输出结果:0.8999999999999999

分析:并不是所有的小数都可以用二进制浮点数精确表示

建议:在需要精确答案的地方,要避免使用float和double。对于货币计算,要使用int、long、BigDecimal。一定要用BigDecimal(String)构造器,而千万不要用BigDecimal(double)。后一个构造器将用它的参数的精确值来创建一个实例。

解决:

1、JDK1.5或更新的版本,可以使用printf方法

    System.out.printf("%.2f",2.0-1.1); 

2、 使用BigDecimal类

    System.out.println(new BigDecimal("2.00").subtract(new BigDecimal("1.10")));

 

四、多重转型

 

    System.out.println((int) (char) (byte) -1);//65535

        

    System.out.println(0xffffffff);//-1

    byte b= -1; 

    System.out.println((int)(char) b ); //65535

    System.out.println((int)(char)(b & 0xff));//255

    System.out.println((byte)128);//-128

分析如果最初的数值类型是有符号的,就执行符号扩展。如果是char,那么不管它将要被转换成什么类型,都执行零扩展。

一、Java中如何编码负数?

    Java采用”2的补码“(Two's Complement)编码负数,它是一种数值的编码方法,要分二步完成:

    第一步,每一个二进制位都取相反值,0变成1,1变成0。比如,+8的二进制编码是00001000,取反后就是11110111。

    第二步,将上一步得到的值加1。11110111就变成11111000。所以,00001000的2的补码就是11111000。也就是说,-8在计算机(8位机)中就是用11111000表示。

二、什么是符号扩展(Sign Extension)?

    符号扩展(Sign Extension)用于在数值类型转换时扩展二进制位的长度,以保证转换后的数值和原数值的符号(正或负)和大小相同,一般用于较窄的类型(如byte)向较宽的类型(如int)转换。扩展二进制位长度指的是,在原数值的二进制位左边补齐若干个符号位(0表示正,1表示负)。

    举例来说,如果用6个bit表示十进制数10,二进制码为"00 1010",如果将它进行符号扩展为16bits长度,结果是"0000 0000 0000 1010",即在左边补上10个0(因为10是正数,符号为0),符号扩展前后数值的大小和符号都保持不变;如果用10bits表示十进制数-15,使用“2的补码”编码后,二进制码为"11 1111 0001",如果将它进行符号扩展为16bits,结果是"1111 1111 1111 0001",即在左边补上6个1(因为-15是负数,符号为1),符号扩展前后数值的大小和符号都保持不变。

三、Java类型转换规则

1. Java中整型字面量   

 Java中int型字面量的书写方式有以下几种:

    - 十进制方式,直接书写十进制数字

    - 八进制方式,格式以0打头,例如012表示十进制10

    - 十六进制方式,格式为0x打头,例如0xff表示十进制255

 需要注意的是,在Java中012和0xff返回的都是int型数据,即长度是32位。

2. Java的数值类型转换规则

    这个规则是《Java解惑》总结的:如果最初的数值类型是有符号的,那么就执行符号扩展;如果是char类型,那么不管它要被转换成什么类型,都执行零扩展。还有另外一条规则也需要记住,如果目标类型的长度小于源类型的长度,则直接截取目标类型的长度。例如将int型转换成byte型,直接截取int型的右边8位。

四、解析“多重转型”问题

    连续三次类型转换的表达式如下:

(int)(char)(byte)-1

1. int(32位) -> byte(8位)

    -1是int型的字面量,根据“2的补码”编码规则,编码结果为0xffffffff,即32位全部置1.转换成byte类型时,直接截取最后8位,所以byte结果为0xff,对应的十进制值是-1.

2. byte(8位) -> char(16位)

    由于byte是有符号类型,所以在转换成char型(16位)时需要进行符号扩展,即在0xff左边连续补上8个1(1是0xff的符号位),结果是0xffff。由于char是无符号类型,所以0xffff表示的十进制数是65535。

3. char(16位) -> int(32位)

    由于char是无符号类型,转换成int型时进行零扩展,即在0xffff左边连续补上16个0,结果是0x0000ffff,对应的十进制数是65535。

总结:实际上在数值类型转换时,只有当遇到负数时才会出现问题,根本原因就是Java中的负数不是采用直观的方式进行编码,而是采用“2的补码”方式,这样的好处是加法和减法操作可以同时使用加法电路完成,但是在开发时却会遇到很多奇怪的问题,例如(byte)128的结果是-128,即一个大的正数,截断后却变成了负数。

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值