二、常量、变量、类型转换和进制转换

227 阅读24分钟

1 常量

常量:就是在代码的运行过程中,值不会发生改变的数据。

1.1 常量的分类

常量分为:整数、小数、字符、字符串、布尔、空。

1.1.1 整数常量

整数常量包含所有整数。

1.1.2 小数常量

所有带小数点的常量都可以称为小数常量:比如:1.5、2.0。

注意: 2.0 因为带有小数点,所以也是小数常量。

1.1.3 字符常量

带单引号''的常量,且单引号中必须有且只能有一个内容,比如:

  • 算字符常量的案例:'1'' '(有一个空格也算字符)、'按一次 tab 键'(一次 tab 键产生的效果也是常量);
  • 不算字符常量的案例:'11''a1' (两个空格),内容超过一个以上都不算字符常量

1.1.4 字符串常量

带双引号""的常量,字符串常量中的内容随意填写。

1.1.5 布尔常量

布尔常量只有两种:truefalse

注意: 布尔常量不需要加双引号,加了双引号的"true""false"属于字符串常量,而不属于布尔常量。

1.1.6 空常量

空常量为:null,其代表的意思是数据不存在。

注意: null""其实不是同个意思,null如它的意思,表示数据不存在,而""表示的是空字符串,但这个数据是存在的,只是字符内容为空。

1.2 常量的使用

public class Demo03Constant {
    public static void main (String[] args) {
        // 整数常量
        System.out.println(1);
        System.out.println(-1);

        // 小数常量
        System.out.println(1.5);
        System.out.println(2.0);

        // 字符常量
        System.out.println(' ');
        System.out.println('a');
        System.out.println('	');
        // System.out.println('    ');  // 字符常量只能有一个内容,这里4个空格内容会导致编译报错。

        // 字符串常量
        System.out.println("lang");
        System.out.println("123");

        // 布尔常量
        System.out.println(true);
        System.out.println(false);

        // 空常量 - 一般不能直接使用,至于怎么使用,后面继续学习
        // System.out.println(null);
    }
}

/**
    打印结果:
    1
    -1
    1.5
    2.0

    a

    lang
    123
    true
    false
*/

1.3 常量之间的运算

常量之间的运算,其实就是加减乘除。

注意: 在乘除的时候,如果相乘或相除的双方都是整数,尽管实际结果会有小数(一般是除法的时候),但最终打印显示的是整数。相反,只有任意一方有小数位,那结果都是包含小数位的。

public class Demo04Constant {
    public static void main (String[] args) {
        // 加
        System.out.println(1+1);
        System.out.println(2.1+3);

        // 减
        System.out.println(5-1);
        System.out.println(10.5-2);

        // 乘
        System.out.println(2*3);
        System.out.println(2.0*3);

        // 除
        System.out.println(10/3);
        System.out.println(10.0/3);
    }
}

/**
    打印结果:
    2
    5.1
    4
    8.5
    6
    6.0
    3
    3.3333333333333335
*/

2 变量

变量: 就是在代码运行过程中,值会随着不同的情况而随时发生改变的数据。

2.1 变量的数据类型

变量的数据类型分为基本数据类型和引用数据类型:

  • 基本数据类型,有 4 类 8 种:
    • 整型:byteshortintlong
    • 浮点型:floatdouble
    • 字符型:char
    • 布尔型:boolean
  • 引用数据类型,有 5 种:
    • 数组
    • 接口
    • 枚举
    • 注解
基本数据类型
数据类型 关键字 内存占用 取值范围
字节型 byte 1个字节 -128 至 127,当超出范围时报错
短整型 short 2个字节 -32768 至 32767
整型 int 4个字节 -2147483648 至 2147483647,正负21个亿
长整型 long 8个字节 -9223372036854775808 至 9223372036854775807,19位数字
单精度浮点数 float 4个字节 1.4013^10*-45 至 3.4028^10*38
双精度浮点数 double 8个字节 4.9^10*-324 至 1.7977^10*308
字符型 char 2个字节 0 至 2^16^-1
布尔类型 boolean 1个字节 true,false(可以做判断条件使用)

2.2 变量的定义

2.2.1 定义单个变量 - 方式1

数据类型 变量名 = 值;

如:int num = 1;

2.2.2 定义单个变量 - 方式2

数据类型 变量名;

变量名 = 值;

如:

int num;
num = 1;

2.2.3 定义多个同类型变量 - 方式1

数据类型 变量名1, 变量名2, 变量名3;

变量名1 = 值;

变量名2 = 值;

变量名3 = 值;

如:

int num1, num2, num3;
num1 = 1;
num2 = 2;
num3 = 3;

2.2.4 定义多个同类型变量 - 方式2

数据类型 变量名1 = 值, 变量名2 = 值, 变量名3 = 值;

如: int num1 = 1, num2 = 2, num3 = 3;

2.2.5 关于变量赋值的读法

先看等号右边的值,再看等号左边的变量类型(本质操作过程是把等号右边的数据赋值给等号左边的变量),当右边有运算时,需要等运算结果出来再进行赋值。

2.2.6 关于字符串的类型

字符串不属于基本数据类型,而是属于引用数据类型,用String表示,String是一个类,所以字符串可以说是属于引用数据类型中的类,其在定义的时候可以和基本数据类型格式一样。

2.3 变量的使用

2.3.1 变量的赋值

public class Demo05Var {
    public static void main (String[] args) {
        // byte,注意 byte 的范围是 -128 到 127,超出范围会报错
        byte num1 = 127;
        num1 = 120;
        System.out.println(num1);

        // short
        short num2 = 100;
        num2 = 101;
        System.out.println(num2);

        // int,整数的默认类型
        int num3 = 1000;
        num3 = 1001;
        System.out.println(num3);

        // long,定义long型的变量后面建议加个L
        long num4 = 10L;
        System.out.println(num4);

        // float,定义float型的变量后面建议加个F
        float num5 = 2.5F;
        System.out.println(num5);

        // double,小数的默认类型
        double num6 = 2.5;
        System.out.println(num6);

        // char
        char num7 = 'A';
        System.out.println(num7);

        // boolean
        boolean num8 = true;
        boolean num9 = false;
        num8 = num9;
        System.out.println(num8);

        // String,字符串其实是引用数据类型类的一种,只是定义方式跟基本类型一样。
        String name = "海贼王";
        System.out.println(name);
    }
}

/**
    打印结果:
    120
    101
    1001
    10
    2.5
    2.5
    A
    false
    海贼王
*/

2.3.1 变量的加减乘除

public class Demo06Var {
    public static void main (String[] args) {
        int num1 = 10;
        int num2 = 3;

        // 变量的加法
        int num3 = num1 + num2;
        System.out.println(num3);

        // 变量的减法
        int num4 = num1 - num2;
        System.out.println(num4);

        // 变量的乘法
        int num5 = num1 * num2;
        System.out.println(num5);

        // 变量的除法
        int num6 = num1 / num2;
        System.out.println(num6); // 两个整型相除之后,尽管实际结果是小数,但得出的结果会取整数部分。

        // double,会取小数点位
        double num7 = num1 / num2;
        System.out.println(num7);
    }
}

/**
    打印结果:
    13
    7
    30
    3
    3.0
*/

2.4 转义字符

转义字符可以将普通字符转成具有特殊含义的字符,也能将具有特殊含义的字符转成普通字符,其标识符号为:\

public class Demo07Var {
    public static void main (String[] args) {
        // \n,表示换行符
        System.out.print("床前明月光\n");
        System.out.print("疑是地上霜\n");

        // \t,表示制表符,也就是Tab键
        System.out.println("1\t2\t3");

        // \\,表示打印 \ 字符
        System.out.println("C:\\Users\\games");
    }
}

/**
    打印结果:
    床前明月光
    疑是地上霜
    1       2       3
*/

2.5 float 和 double 的区别

float 的小数位只有 23 位二进制,能表示的最大十进制为 2 的 23 次方(8388608,是7位数),所以float型数据代表的小数位是 7。

double 的小数位只有 52 位二进制,能表示的最大十进制为4 503 599 627 370 496,是 16 位数,所以 double 型代表的小数位是 16。

注意: 将来开发的时候,尽量不要用 float 或者 double 直接参与运算,因为直接参与运算会有精度损失问题。

public class Demo08Var {
    public static void main (String[] args) {
        float num1 = 10;
        float num2 = 3;
        System.out.println(num1 / num2);

        double num3 = 10.213;
        double num4 = 3.113;
        System.out.println(num3 / num4);
        System.out.println(num3 - num4);

        float num5 = 3.55F;
        float num6 = 2.12F;
        System.out.println(num5 - num6); // 直接用float进行运算会有精度损失问题。
    }
}

/**
    打印结果:
    3.3333333
    3.2807581111468034
    7.1
    1.4300001
*/

2.6 变量使用时的注意事项

  • 变量未初始化(首次赋值)之前,不能使用;
public class Demo09Var {
    public static void main (String[] args) {
        int num;
        // num = 10; // 初始化之后,再使用则不会报错。
        System.out.println(num);
    }
}

/**
    打印结果:
    Demo09Var.java:5: 错误: 可能尚未初始化变量num
    System.out.println(num);
                        ^
    1 个错误
*/
  • 在同一个作用域中(一对花括号),不能定义重复的变量;
public class Demo10Var {
    public static void main (String[] args) {
        int num = 10;
        int num = 20;
        System.out.println(num);
    }
}

/**
    打印结果:
    Demo10Var.java:4: 错误: 已在方法 main(String[])中定义了变量 num
                    int num = 20;
                        ^
    1 个错误
*/
  • 不同作用域中的数据尽量不要随意访问,小作用域可以访问大作用域中的变量,但是大作用域不能访问小作用域中的变量。
public class Demo11Var {
    public static void main (String[] args) {
        int num = 10;

        {
                int num2 = 20;
                System.out.println(num); // 小作用域能访问外部大作用域的变量
        }

        System.out.println(num2); // 大作用域不能访问内部小作用域的变量
    }
}

/**
    打印结果:
    Demo11Var.java:10: 错误: 找不到符号
                    System.out.println(num2); // 大作用域不能访问内部小作用域的变量
                                       ^
      符号:   变量 num2
      位置: 类 Demo11Var
    1 个错误
*/

2.6 变量的练习

public class Demo12VarPerson {
    public static void main (String[] args) {
        String name = "张三";
        char sex = '男';
        int year = 18;
        double height = 181.1;
        double weight = 180.5;

        System.out.println(name);
        System.out.println(sex);
        System.out.println(year);
        System.out.println(height);
        System.out.println(weight);
    }
}

/**
    张三
    男
    18
    181.1
    180.5
*/

2.5 标识符

标识符就是给类、方法和变量取的名字。

硬性规定

  • 可以包含 英文字母数字$_
  • 不能以数字开头,如:int 2t = 100,这是错误的;
  • 不能是关键字,如:int public = 100,这也是错误的。

软性建议

  • 给类取名,遵循大驼峰;
  • 给方法和变量取名,遵循小驼峰;
  • 命名要语义化。

3 数据类型转换

  • 关于数据类型转换的触发时机:
    • 等号左右两边的数据类型不一致;
    • 不同数据类型的数据进行运算;
  • 关于类型转换的分类:
    • 自动类型转换。
    • 强制类型转换
  • 基本类型中,按照取值范围从小到大的排序:
    • byte, short, char -> int -> long -> float -> double

3.1 自动类型转换

  • 将取值范围小的数据类型赋值给取值范围大的数据类型 -> 小自动转大
  • 将取值范围小的数据类型和取值范围大的数据类型做运算 -> 小自动转大

赋值和运算引起的自动类型转换

public class Demo01 {
    public static void main (String[] args) {
        /*
            等号右边是整数 int 类型,左边是 long 类型
            这里因为 int 的取值范围小于 long,int 会自动转成 long 类型
        */
        long num = 100;
        System.out.println(num);

        /*
            + 左边是 int 类型,右边是 double 类型
            相加时,int 会自动转成 double 类型
        */
        int num1 = 10;
        double num2 = 2.5;
        double sum = num1 + num2;
        System.out.println(sum);
    }
}

/**
    100
    12.5
*/

3.2 强制类型转换

将取值范围大的数据类型赋值给取值范围小的数据类型 -> 需要强转

public class Demo02 {
    public static void main (String[] args) {
        /*
            等号右边是整数 double 类型(默认),左边是 float 类型
            要将 double 类型赋值给 float 类型,需要将 double 类型转成 float 类型,才能赋值。否则会报错
            转换方式:(float)2.5 或者 2.5F
        */
        // 会报错
        // flout num = 2.5; 
        float num = 2.5F;
        float num1 = (float)2.5;
        System.out.println(num);
        System.out.println(num1);
    }
}
/**
    2.5
    2.5
*/

3.3 强转的注意事项

3.3.1 强转造成的精度损失和数据溢出现象

一般情况下,不要随意写强转格式的代码,因为强转数据对于 浮点数长整型 会有精度损失和数据溢出的问题,除非没有办法。

  • 强转数据对 浮点数 的影响(精度缺失):
public class Demo03 {
    public static void main (String[] args) {
        // 最终打印出2,小数位精度损失
        int num = (int)2.5;
        System.out.println(num);
    }
}
/**
    2
*/
  • 强转数据对 长整型 的影响(数据溢出):
public class Demo04 {
    public static void main (String[] args) {
        // 100 亿对于 int 数据来说太大了,int 类型的数据最大只能到 1410065408
        // int 类型占内存 4 个字节,4 个字节对应 32 位二进制,而 100 亿是34位二进制,所以多出的 2 位二进制会溢出。
        // 100 亿转成二进制是: 10 0101 0100 0000 1011 1110 0100 0000 0000,前面两位溢出掉,剩下的是:101 0100 0000 1011 1110 0100 0000 0000
        // 最大的 int (1410065408) 转成二进制是:101 0100 0000 1011 1110 0100 0000 0000,刚好就是 100 亿溢出的前面两位二进制后,剩余的二进制数。
        int num = (int)10000000000L; 
        System.out.println(num);
    }
}
/**
    1410065408
*/

3.3.2 byteshortchar 强转时的注意点

  • byteshort 定义时如果等号右边是 整数数字,这个 整数数字 在不超过 byte、short 范围的前提下,jvm 会对它进行自动类型转换,不需要我们手动去转换;
public class Demo05 {
    public static void main (String[] args) {
        // 10 默认是 int 类型,因为 10 属于 byte 的范围,且是直接的数字数据,jvm 会自动帮我们将 10 转成 byte 类型。 
        byte num = 10; 
        System.out.println(num);
    }
}
/**
    10
*/
  • byteshort 的等号右边有变量参与时,变量如果是 byteshort 类型,它们会被自动提升为 int 类型(在 Java 中,当进行算术操作时,如果操作数的类型不同,较小的类型会被提升为较大的类型),所以我们还需要对运算之后的结果进行强转到对应类型后再赋值;
public class Demo06 {
    public static void main (String[] args) {
        // 10 默认是 int 类型,因为 10 属于 byte 的范围,且是直接的数字数据,jvm 会自动帮我们将 10 转成 byte 类型,不需要我们手动强转。
        // num + 1 时,num 是 byte 类型,会被自动提升为 int 类型,所以最后,我们需要对运算结果进行强转,再进行赋值。
        byte num = 10;
        num = (byte)(num + 1);
        System.out.println(num);
    }
}
/**
    11
*/
  • char 类型数据如果参与运算,会自动提升为 int 类型。整个过程是 char 会根据数据字符去 ASCII 码表(美国标准交换代码)中去匹配对应的 int 值,如果在这里没找到,会接着去 unicode 码表中寻找。
public class Demo07 {
    public static void main (String[] args) {
        // a 对应的数字是从 ASCII 码表中查询到对应的数字,而 "中" 是从 unicode 码表中查询对应的数字。
        // a = 97
        // 中 = 20013
        char num1 = 'a';
        int num2 = num1 + 1;
        char num3 = '中';
        int num4 = num3 + 1; 
        System.out.println(num2);
        System.out.println(num4);
    }
}
/**
    98
    20014
*/

3.3.3 byte 赋值超范围分析

public class Demo08 {
    public static void main (String[] args) {
        // 对于超出 byte 范围的数,需要手动强转
        // 方式1:通过原码、反码、补码来计算超出 byte 范围的数
        // 200的原码为:1100 1000
        // 200的反码为(对原码取首位不变,其余取反):1011 0111
        // 200的补码为(对反码取首位为正负,然后+1):1011 1000,首位 1 对应负号,所以就是 -111000,其对应的十进制就是 -56
        byte b1 = (byte)200;
        System.out.println(b1);

        // 方式2:通过从 -128~127 从头循环来计算出超出 byte 范围的数。
        // 128 比 byte 最大的 127 还多一位,所以从范围的头开始计算一位,也就是 -128,以此类推,int 200 强转成 byte,也是 -56
        byte b2 = (byte)128;
        System.out.println(b2);
    }
}
/**
    -56
    -128
*/

4 进制转换

4.1 二进制和十进制

4.1.1 十进制转成二进制

一般十进制转成二进制,采用辗转相除法,即:拿一个十进制数循环除以 2,然后取余数(从下到是上取)。

比如:12 对应的二进制是 1100

计算过程如下:

image.png

4.1.2 二进制转成十进制

对于二进制转成十进制,一般是使用 8421 规则来进行计算。

8421 规则,就是:
  1. 按照二进制的位数来定义位数个 2 的 位数-1 次幂;
  2. 然后将得出的结果和对应的二进制位数相乘;
  3. 最后将结果相加。
具体计算过程如下:

假设我们现在有一个二进制数:110 1001,那么这个二进制数的位数为 7 位,每位的权重如下:

  • 第 1 位(最右边)权重是 2^0 = 1
  • 第 2 位权重是 2^1 = 2
  • 第 3 位权重是 2^2 = 4
  • 第 4 位权重是 2^3 = 8
  • 第 5 位权重是 2^4 = 16
  • 第 6 位权重是 2^5 = 32
  • 第 7 位(最左边)权重是 2^6 = 64

逐位计算:

  • 第 1 位是 1,对应权重是 1,计算 1 × 1 = 1
  • 第 2 位是 0,对应权重是 2,计算 0 × 2 = 0
  • 第 3 位是 0,对应权重是 4,计算 0 × 4 = 0
  • 第 4 位是 1,对应权重是 8,计算 1 × 8 = 8
  • 第 5 位是 0,对应权重是 16,计算 0 × 16 = 0
  • 第 6 位是 1,对应权重是 32,计算 1 × 32 = 32
  • 第 7 位是 1,对应权重是 64,计算 1 × 64 = 64

将所有结果相加:

64 + 32 + 0 + 8 + 0 + 0 + 1 = 105

4.2 二进制和八进制

二进制转八进制的时候,需要将二进制数分开(分开的时候,3个二进制数为一组)

示例如下:

假设有二进制数为 110011,现将它每三个分为一组:则为 110 011,然后分组计算:

  • 110 -> 1 * 2^2 + 1 * 2^1 + 0 * 2^0 = 6
  • 011 -> 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 3

最后将分组计算出来的值拼接(这里是6和3拼接),就是该二进制数对应的八进制数了。

所以, 110011 对应的八进制数为 63

4.3 二进制和十六进制

二进制转十六进制的时候,需要将二进制数分开(分开的时候,4个二进制数为一组)

示例如下:

假设有二进制数为 10011011,现将它每四个分为一组:则为 1001 1011,然后分组计算:

1001 -> 1 * 2^3 + 0 * 2^2 + 0 * 2^1 + 1 * 2^0 = 9

1011 -> 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 11

11 在十六进制中,表示 b(十六进制 9 之后就是 a、b、c、d、e、f)。

最后将分组计算出来的值拼接(这里是 9 和 b 拼接),就是该二进制数对应的十六进制数了。

所以, 10011011 对应的十六进制数为 9b

5 位运算

5.1 常识介绍

  • 符号的介绍:
    • &(与)-> 有假则假;
    • |(或)-> 有真则真;
    • ~(非)-> 取反;
    • ^(异或)-> 符号前后结果一致则为 false,否则为 ture。
  • 1 代表 true,0 代表 false
  • 计算机都是用存储数据的 补码 进行存储,计算也是。而我们在电脑看到的数据则为原码,原码的正数和补码一样,负数原码则是经过电脑对补码数据的转换之后,才显示为负数原码的。
  • 正数二进制最高位为 0,负数二进制最高位为 1
  • 正数的 原码反码补码 一致,负数的 原码反码补码 不一样,需要采取对应的规则进行处理(如:反码取原码首位不变,其余取反;补码取反码首位为正负,然后 +1)。

5.2 左移 <<

快速算法:左移几位就等于乘以 2 的几次方。

注意: 当左移的位数 n 超过该数据类型的总位数时,相当于左移(n - 总位数)位(比如:一个数据类型是 8 位,左移了 10 位,则相当于左移了 10 - 8 = 2 位)。

5.2.1 正数的左移

示例如下:

2 << 2

通过快速算法:2 * 2^2 可得出结果等于 8。

此外,还有二进制算法:

2 转成二进制为:0000 0000 0000 0000 0000 0000 0000 0010 -> 然后左移两位 0000 0000 0000 0000 0000 0000 0000 10 -> 再补零,得出 0000 0000 0000 0000 0000 0000 0000 1000 -> 然后转成十进制,就是 8。

5.2.2 负数的左移

示例如下:

-2 << 2

通过快速算法:-2 * 2^2 可得出结果等于 -8。

此外,还有二进制算法:

2 转成二进制为:1000 0000 0000 0000 0000 0000 0000 0010 -> 将原码转成反码 1111 1111 1111 1111 1111 1111 1111 1101 -> 在反码基础上首位不变,再 +1 得出补码 1111 1111 1111 1111 1111 1111 1111 1110 -> 将补码左移 2 位,再补零,得出 1111 1111 1111 1111 1111 1111 1111 1000 -> 将补码转成反码,在补码的基础上 -1,得出 1111 1111 1111 1111 1111 1111 1111 0111 -> 将反码转成原码,得出 1000 0000 0000 0000 0000 0000 0000 1000,结果就是 -8。

5.3 右移 >>

快速算法:右移几位就等于除以 2 的几次方,如果不能整除,则向下取整。

5.3.1 正数的右移

示例如下:

9 >> 2

通过快速算法:9 / 2^2 可得出结果等于 2.25,向下取整得出 2。

此外,还有二进制算法:

9 转成二进制为:0000 0000 0000 0000 0000 0000 0000 1001 -> 然后右移两位 0000 0000 0000 0000 0000 0000 0000 10 -> 再补零,得出 0000 0000 0000 0000 0000 0000 0000 0010 -> 然后转成十进制,就是 2。

5.3.2 负数的右移

示例如下:

-9 >> 2

通过快速算法:-9 / 2^2 可得出结果等于 -2.25,向下取整得出 -3。

此外,还有二进制算法:

-9 转成二进制为:1000 0000 0000 0000 0000 0000 0000 1001 -> 将原码转成反码 1111 1111 1111 1111 1111 1111 1111 0110 -> 在反码基础上首位不变,再 +1 得出补码 1111 1111 1111 1111 1111 1111 1111 0111 -> 将补码右移 2 位,再补一(注意: 负数补一而不是零),得出 1111 1111 1111 1111 1111 1111 1111 1101 -> 将补码转成反码,在补码的基础上 -1,得出 1111 1111 1111 1111 1111 1111 1111 1100 -> 将反码转成原码,得出 1000 0000 0000 0000 0000 0000 0000 0011,结果就是 -3。

5.4 无符号右移 >>>

运算规则:往右移动后,左边空出来的位直接补 0,不管最高位是 0 还是 1,空出来的位都用 0 来补。

5.4.1 正数的无符号右移

运算规则和右移一样。

9 >>> 2 = 2

5.4.1 负数的无符号右移

运算规则:右移出去几位,左边就补几个 0,结果变为正数。

-9 >>> 2 = 2

-9 转成二进制为:1000 0000 0000 0000 0000 0000 0000 1001 -> 将原码转成反码 1111 1111 1111 1111 1111 1111 1111 0110 -> 在反码基础上首位不变,再 +1 得出补码 1111 1111 1111 1111 1111 1111 1111 0111 -> 将补码右移 2 位,再补零(注意: 无符号右移是补零,而不是补一),得出 0011 1111 1111 1111 1111 1111 1111 1101 -> 将补码转成反码,在补码的基础上 -1,得出 0011 1111 1111 1111 1111 1111 1111 1100 -> 将反码转成原码,得出 0100 0000 0000 0000 0000 0000 0000 0011,结果就是 1073741827。

注意:

  • 对于一些二进制,在 32 位系统中,>>> 32 位,相当于右移 0 位,所以结果还是原来的值;
  • 而如果是 >>> 34 位,则相当于右移了 2 位。

5.5 位运算符

在这部分的学习中,因为涉及位运算符和二进制位之间的运算,可以将 true 看作 1false 看作 0

5.5.1 按位与 &

运算规则:对应的位都是 1 才为 1,也就是说符号左右两边都为 true,结果才为 true

  • 1 & 1 结果为 1;
  • 1 & 0 结果为 0;
  • 0 & 1 结果为 0;
  • 0 & 0 结果为 0。

运算示例,5 & 3,结果为 1,运算过程如下:

0000 0000 0000 0000 0000 0000 0000 0101 -> 5 的二进制

&

0000 0000 0000 0000 0000 0000 0000 0011 -> 3 的二进制 ———————————————————— 0000 0000 0000 0000 0000 0000 0000 0001 -> 结果为 1

5.5.2 按位或 |

运算规则:对应的位只要有一个为 1 即为 1,也就是说符号左右两边有一个为 true,结果就为 true

  • 1 | 1 结果为 1;
  • 1 | 0 结果为 1;
  • 0 | 1 结果为 1;
  • 0 | 0 结果为 0。

运算示例,5 | 3,结果为 7,运算过程如下:

0000 0000 0000 0000 0000 0000 0000 0101 -> 5 的二进制

&

0000 0000 0000 0000 0000 0000 0000 0011 -> 3 的二进制 ———————————————————— 0000 0000 0000 0000 0000 0000 0000 0111 -> 结果为 7

5.5.3 按位异或 ^

运算规则:对应的位两个都一样的为 0,否则为 1。

  • 1 ^ 1 结果为 0;
  • 1 ^ 0 结果为 1;
  • 0 ^ 1 结果为 1;
  • 0 ^ 0 结果为 0。

运算示例,5 ^ 3,结果为 6,运算过程如下:

0000 0000 0000 0000 0000 0000 0000 0101 -> 5 的二进制

&

0000 0000 0000 0000 0000 0000 0000 0011 -> 3 的二进制 ———————————————————— 0000 0000 0000 0000 0000 0000 0000 0110 -> 结果为 6

5.5.4 按位取反 ~

运算规则:~ 会对每个二进制位进行取反操作,即将每一位的 0 变为 1,将 1 变为 0。当你对一个数字使用 ~ 运算符时,它会将该数字的所有二进制位取反。

运算示例,~10 的结果为 -11,运算过程如下:

0000 0000 0000 0000 0000 0000 0000 1010 -> 10 的二进制补码(正数的原码、反码、补码都一样) -> 1111 1111 1111 1111 1111 1111 1111 0101 -> ~10 的补码 1111 1111 1111 1111 1111 1111 1111 0100 -> ~10 的反码 1000 0000 0000 0000 0000 0000 0000 1011 -> ~10 的原码,结果为 -11

5.5.5 运算符的优先级

image.png

注意:

  • 表达式不要太复杂;
  • 优先计算 () 里面的。