[Java解惑]读书笔记分享

[Java解惑]读书笔记分享
这本书主要讲了Java中经常遇到的坑, 并给出了解释以及解决办法. 读完之后很有收获
在这里插入图片描述

  • 读书笔记
    • 表达式之谜

      • 奇数性

        用下面的表达式判断一个数是否是奇数

        public static boolean isOdd(int i) {
              return i % 2 == 1;
          }
        

        问题: 负数无法得出正确的结果. 例如当 i = -3时, 上述结果是-1.

        可以修改为如下形式

        public static boolean isOdd(int i) {
              return i % 2 != 0;
          }
        

        更进一步的, 可以考虑把区域操作改为AND(&).

        public static boolean isOdd(int i) {
              return (i & 1) != 0;
          }
        
      • 找0时刻

        计算表达式

        System.out.println(2.00 - 1.10);
        // 0.8999999999999999
        

        原因: 并非所有的小数都可以用二进制浮点数明确表示

        请注意, 虽然表达式System.*out*.printf("%.2f", (2.00 - 1.10));会显示出正确的结果, 但是这并不表示是对底层通用的解决方案.

        因为二进制浮点数对于货币运算是非常不合适的.

        一种方案是以分为单位, 另外一种是使用BigDecimal.

        BigDecimal bigDecimalA = new BigDecimal("2.0");
        BigDecimal bigDecimalB = new BigDecimal("1.1");
        System.out.println(bigDecimalA.min(bigDecimalB));
        

        一定要使用字符串构造参数BigDecima(String), 不要使用BigDecima(double).

        BigDecimal bigDecimal = new BigDecimal(.1);
        System.out.println(bigDecimal);
        // 0.1000000000000000055511151231257827021181583404541015625
        
      • 长整除

        两个long类型的数相除.

        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

        MICROS_PER_DAY溢出了. 虽然放在long中是可以的, 但是放在int中会溢出.

        这个计算是按照int来计算的, 只有在运算完之后, 才会被提升到long, 而此时已经溢出了.

        为什么会按照int的方式计算? 因为所有的因子都是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);
        
      • 初级问题

        计算表达式

        System.out.println(12345 + 5432l);
        // 17777
        

        为什么会这样呢?

        5432l中的l, 表示该数字是long类型. 因此建议写成大写.

      • 十六进制的趣事

        计算下面表达式

        System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));
        // cafebabe
        

        为什么不是1cafebabe

        要注意到, 十六进制不像十进制一样字面量都是正的, 如果想表示负数在前面加-号就可以了.

        System.out.println(0xcafebabe); // -889275714
        System.out.println(0x1cafebabeL); // 7700658878
        

        因此, 该表达式左侧是一个long类型数字, 右侧是一个int类型的数字, 且为负数. 为了执行该运算, 会将int类型转化为long类型. 提升之后的数字是0xffffffffcafebabe.

        修改方法如下. 即把两个数字都变成long类型.

        System.out.println(Long.toHexString(0x100000000L + 0xcafebabeL));
        
      • 多重转型

        观察下面表达式

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

        其结果不是-1, 而是65535

        转化顺序是 int → byte → char → int

        该程序的行为紧密依赖于转型的符号扩展行为. Java使用基于2的补码的二进制运算, 因此int类型的数值-1的所有32位都是置位的.

        第一次从int → byte, 窄化转换, 仍是-1.

        第二次byte → char, 拓展并窄化的转化. byte是有符号类型的, char是无符号类型的. 不能用char表示一个负的byte. 因此这个过程是byte → int → char.

        可以将规则描述如下: 如果最初的数值类型是有符号的,那么就执行符号扩展。如果他是char那么不管他将要被转化成什么类型,都执行零扩展。

      • 互换内容

        int x = 1984;
        int y = 2001;
        x^= y^= x^= y;
        System.out.println("x = " + x + " y = " + y);
        // x = 0 y = 1984
        

        一种可行的方式如下

        int x = 1984;
        int y = 2001;
        x = x ^ y;
        y = y ^ x;
        x = x ^ y;
        System.out.println("x = " + x + " y = " + y);
        // x = 2001 y = 1984
        

        这种方式不推荐使用.

        Java的操作符操作是从左到右执行的.

        给我们的经验就是不要对相同的变量赋值两次.

      • Dos Equis

        观察下面表达式

        char x = 'X';
        int i = 0;
        System.out.println(true ? x : 0);
        System.out.println(false ? i : x);
        // X
        // 88
        

        转化规则如下

        最好在条件表达式中使用相同的类型.

      • 半斤

        不能简单的认为下面两个表达式是相等的

        x += i;
        x = x + 1;
        

        原因如下

        可以观察下面的例子

        short x = 0;
        int i = 123456;
        x += i;
        System.out.println(x);
        // -7616
        

        因此, 不要将符合运算符用于byte, short, char类型. 避免符合类型的转型.

      • 八两

        但是, 上面的例子反过来, 也不能就认为高枕无忧了.

        复合赋值操作符要求两个操作数都是原生类型的,例如int。或包装了的原生类型。例如integer. 但是有一个例外, 那就是如果操作符左侧的操作数是string类型的。那么他允许右侧的操作数是任意类型。

    • 字符之谜

      • 最后的笑声
      • ABC
      • 动物庄园
      • 转义字符的溃败
      • 令人晕头转向的Hello
      • 行打印程序
      • 嗯?
      • 字符串奶酪
      • 漂亮的火花
      • 我的类是什么
      • 我的类是什么? 镜头2
      • URL的愚弄
      • 不劳无获
    • 循环之谜

    • 异常之迷

    • 类之迷

    • 库之迷

    • 更多类之谜

    • 更多库之迷

    • 高级谜题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值