u04-运算符号

526 阅读10分钟

1. 数学运算符

概念:

  • 基本运算:包括加(+),减(-),乘(*),除(/)。
    • 除法运算中,如果操作数都是整数,则结果也是整数。
  • 取余运算:取余运算符(%),0取余任何数都等于0,正负号只和等号左边操作数的一致。
  • 除零问题:任何数除以0都会抛异常,任何数除以0.0结果都为无穷大 infinity

源码: /javase-start/

  • src: c.y.calculation.MathOperatorsTest.base()
/**
 * @author yap
 */
public class MathOperatorsTest {

    @Test
    public void base() {
        System.out.println(0 % 2);
        System.out.println(5 % 2);
        System.out.println(5 % -2);
        System.out.println(-5 % 2);
        System.out.println(-5 % -2);
        System.out.println(1 / 0.0);
        System.out.println(1 / 0);
    }
}

1.1 自运算

概念:

  • 自运算 ++ / -- 即给自身加1或减1。
  • 自运算优先级最高。
  • 自运算只能用在变量上,常量没有自运算。
  • 自运算分为先用后加(a++)和先加后用(++a);

1.2 数值溢出问题

概念:

  • 我们都知道变量是有自己的存值范围的,比如 byte 的存值范围是-128 ~ 127之间,那么假设在计算的时候发生了数值溢出,会产生物极必反的现象:
    • 最大值加1变成最小值
    • 最小值减1变成最大值
  • 二进制计算的原理:将原码转成补码,使用补码进行计算,再将结果转回原码。
  • 模拟 2147483647 + 1 的过程
    • 原码: [01111111 11111111 11111111 11111111]
    • 反码: [01111111 11111111 11111111 11111111],与原码相同
    • 补码: [01111111 11111111 11111111 11111111],与原码相同
    • 加一: [10000000 00000000 00000000 00000000],计算会影响符号位,toBinaryString()方法会打印这个值
    • 反码: [11111111 11111111 11111111 11111111],符号位不动,其余按位取反
    • 补码: [10000000 00000000 00000000 00000000],反码 + 1,符号位不参与补码进位
    • 真值: -2147483648(int最小值)
  • 模拟 -2147483648 - 1 的过程
    • 原码: [10000000 00000000 00000000 00000000]
    • 反码: [11111111 11111111 11111111 11111111],符号位不动,其余按位取反
    • 补码: [10000000 00000000 00000000 00000000],反码 + 1,符号位不参与补码进位
    • 减一: [01111111 11111111 11111111 11111111],计算会影响符号位,toBinaryString()方法会打印这个值
    • 反码: [01111111 11111111 11111111 11111111],与原码相同
    • 补码: [01111111 11111111 11111111 11111111],与原码相同
    • 真值: 2147483647(int最大值)

源码: /javase-start/

  • src: c.y.calculation.MathOperatorsTest.numericalOverflow()

    @Test
    public void numericalOverflow() {
        int maxValueOfInt = 2147483647;
        int result = maxValueOfInt + 1;
        System.out.println(result);
    }

2. 赋值运算符

概念: java中使用一个等号表示赋值,赋值要从后向前理解:

  • +=:追加,a += b 就等价于 a = a+b,将 a+b 的结果重新赋值给 a
  • -=:追减,a -= b 就等价于 a = a-b,将 a-b 的结果重新赋值给 a
  • *=:追乘,a *= b 就等价于 a = a*b,将 a*b 的结果重新赋值给 a
  • /=:追除,a /= b 就等价于 a = a/b,将 a/b 的结果重新赋值给 a
  • %=:追取余,a %= b 就等价于 a = a%b,将 a+b 的结果重新赋值给 a

3. 比较运算符

概念: 比较运算一定会返回boolean类型的值,运算符包括:== / > / < / >= / <= / !=

4. 逻辑运算符

概念: 逻辑运算包括逻辑与(&&),逻辑或(||)和逻辑非(!)。

  • 口诀:与中有假(则假),或中有真(则真)。
  • 逻辑运算优先级:非 > 与 > 或。

5. 位运算符

概念: 位运算发生在操作数的补码间,对补码计算结果再次求补转回原码后可得真值:

  • 短路现象:逻辑运算会发生短路现象,而位运算不会。
  • 按位与 $:有0则0,全1才1。
  • 按位或 |:有1则1,全0才0。
  • 按位取反 ~:0变1,1变0,包括符号位。
  • 按位异或 ^:相同为0,不同为1,等效于按位无进位相加:
    • 异或操作满足交换律和结合律,即a^(b^c) 等同 (c^b)^z。
    • 规律:N^0=N,N^N=0。
  • 左移动 <<:按位左移动时,溢出忽略,右边用0补位:
    • a左移动b位,相当于a乘以2的b次方。
  • 带符号右移动 >>:按位带符号右移动时,溢出忽略,左边用符号位补位:
    • a右移动b位,相当于a除以2的b次方。
  • 无符号右移动 >>>:按位无符号右移动时,溢出忽略,左边用0补位。

源码: /javase-start/

  • src: c.y.calculation.MathOperatorsTest
    /**
     * <h1>4 & 6 过程分析</h1>
     *
     * <p> 00000000 .. 00000100:4原码
     * <p> 00000000 .. 00000100:取反得到4反码,同原码
     * <p> 00000000 .. 00000100:加1得到4补码,同原码
     * <p> 00000000 .. 00000110:6原码
     * <p> 00000000 .. 00000110:取反得到6反码,同原码
     * <p> 00000000 .. 00000110:加1得到6补码,同原码
     * <p> 00000000 .. 00000100:4 & 6结果,有0则0
     * <p> 00000000 .. 00000100:取反得到结果的反码,同原码
     * <p> 00000000 .. 00000100:加1得到结果的补码,同原码
     * <p> 4:真值
     */
    @Test
    public void andOfBit() {
        System.out.println(Integer.toBinaryString(4 & 6));
        System.out.println(4 & 6);
    }

    /**
     * <h1>4 | 6 过程分析</h1>
     *
     * <p> 00000000 .. 00000100:4原码
     * <p> 00000000 .. 00000100:取反得到4反码,同原码
     * <p> 00000000 .. 00000100:加1得到4补码,同原码
     * <p> 00000000 .. 00000110:6原码
     * <p> 00000000 .. 00000110:取反得到6反码,同原码
     * <p> 00000000 .. 00000110:加1得到6补码,同原码
     * <p> 00000000 .. 00000100:4 | 6结果,有1则1
     * <p> 00000000 .. 00000110:取反得到结果的反码,同原码
     * <p> 00000000 .. 00000110:加1得到结果的补码,同原码
     * <p> 6:真值
     */
    @Test
    public void orOfBit() {
        System.out.println(Integer.toBinaryString(4 | 6));
        System.out.println(4 | 6);
    }

    /**
     * <h1> ~4 过程分析</h1>
     *
     * <p> 00000000 .. 00000100:4原码
     * <p> 00000000 .. 00000100:取反得到4反码,同原码
     * <p> 00000000 .. 00000100:加1得到4补码,同原码
     * <p> 11111111 .. 11111011:~4结果,所有位取反,包括符号位
     * <p> 10000000 .. 00000100:取反得到结果的反码
     * <p> 10000000 .. 00000101:加1得到结果的补码
     * <p> -5:真值
     */
    @Test
    public void notOfBit() {
        System.out.println(Integer.toBinaryString(~4));
        System.out.println(~4);
    }

    /**
     * <h1>4 ^ 6 过程分析</h1>
     *
     * <p> 00000000 .. 00000100:4原码
     * <p> 00000000 .. 00000100:取反得到4反码,同原码
     * <p> 00000000 .. 00000100:加1得到4补码,同原码
     * <p> 00000000 .. 00000110:6原码
     * <p> 00000000 .. 00000110:取反得到6反码,同原码
     * <p> 00000000 .. 00000110:加1得到6补码,同原码
     * <p> 00000000 .. 00000010:4 ^ 6结果,无进位相加
     * <p> 00000000 .. 00000010:取反得到结果的反码,同原码
     * <p> 00000000 .. 00000010:加1得到结果的补码,同原码
     * <p> 2:真值
     */
    @Test
    public void xorOfBit() {
        System.out.println(Integer.toBinaryString(4 ^ 6));
        System.out.println(4 ^ 6);
    }

    /**
     * <h1>-2 << 3 过程分析</h1>
     *
     * <p> 10000000 .. 00000010:-2原码
     * <p> 11111111 .. 11111101:取反得到-2反码
     * <p> 11111111 .. 11111110:加1得到-2补码
     * <p> 11111111 .. 11110000:-2 << 3结果,左移动3,溢出忽略,用0补位。
     * <p> 10000000 .. 00001111:取反得到结果的反码
     * <p> 10000000 .. 00010000:加1得到结果的补码
     * <p> -16:真值
     */
    @Test
    public void leftMoveOfBit() {
        System.out.println(Integer.toBinaryString(-2 << 3));
        System.out.println(-2 << 3);
    }

    /**
     * <h1>-2 >> 3 过程分析</h1>
     *
     * <p> 10000000 .. 00000010:-2原码
     * <p> 11111111 .. 11111101:取反得到-2反码
     * <p> 11111111 .. 11111110:加1得到-2补码
     * <p> 11111111 .. 11111111:-2 >> 3结果,右移动3,溢出忽略,用符号位补位。
     * <p> 10000000 .. 00000000:取反得到结果的反码
     * <p> 10000000 .. 00000001:加1得到结果的补码
     * <p> -1:真值
     */
    @Test
    public void rightMoveOfBit() {
        System.out.println(Integer.toBinaryString(-2 >> 3));
        System.out.println(-2 >> 3);
    }

    /**
     * <h1>-2 >>> 3 过程分析</h1>
     *
     * <p> 10000000 .. 00000010:-2原码
     * <p> 11111111 .. 11111101:取反得到-2反码
     * <p> 11111111 .. 11111110:加1得到-2补码
     * <p> 00011111 .. 11111111:-2 >>> 3结果,右移动3,溢出忽略,用0补位。
     * <p> 00011111 .. 11111111:取反得到结果的反码,同原码
     * <p> 10000000 .. 00000001:加1得到结果的补码,同原码
     * <p> 536870911:真值
     */
    @Test
    public void noSignRightMoveOfBit() {
        System.out.println(Integer.toBinaryString(-2 >>> 3));
        System.out.println(-2 >>> 3);
    }

6. 三元运算符

概念: 三目运算符,也叫三部运算符,也叫三元运算符。

  • 公式:X ? Y : Z,其中X为布尔类型表达式,当X的结果为true时返回Y,否则返回Z。
  • Y和Z的返回值类型必须一致。
  • 三目运算符一定要接它的返回值,它的返回值类型就是你Y和Z的返回值类型。

7. Math工具类

概念: java.lang.Math 工具类提供了大量用于数学运算的方法,Math类是final类,因此不能从Math类继承,Math类中的方法都是static方法,因此不必创建Math类的对象就可以直接使用类的方法。

  • Math.abs(-10):绝对值。
  • Math.sqrt(16):平方根。
  • Math.cbrt(8):立方根。
  • Math.ceil(2.1):向上取整。
  • Math.floor(2.9):向下取整。
  • Math.max(1, 6):最大值。
  • Math.min(1, 6):最小值。
  • Math.pow(2, 3):a的b次幂。
  • Math.round(2.4):四舍五入。
  • Math.random():随机数:每次随机都生成一个[0-1)之间的数字。

随机数生成的代码,更建议使用 new Random().nextInt(5);,此时可以直接随机生成一个0到5范围内的int值。