第02章_变量与运算符
1 关键字
关键字 (keyword):被 Java 语言赋予了特殊含义,用做专门用途的字符串 (或单词)
- 一共 50 个,都是小写字母,其中
const
和goto
是保留字
(reserved word) true
,false
,null
不是关键字,它们其实是字面量,表示特殊的布尔值和空值
作用 | 关键字 |
---|---|
定义数据类型 | class, interface, enum, byte, short, int, long, float, double, char, boolean, void |
定义流程控制 | if, else, switch, case, default, while, do, for, break, continue, return |
定义访问权限修饰符 | private, protected, public |
定义类、函数、变量修饰符 | abstract, final, static, synchronized |
定义类之间的关系 | extends, implements |
建立实例、引用实例、判断实例 | new, this, super, instanceof |
异常处理 | try, catch, finally, throw, throws |
包 | package, import |
其他修饰符 | native, strictfp, transient, volatile, assert, const, goto |
2 标识符
标识符 (identifier):Java中变量、方法、类等要素命名时使用的字符序列
- 凡是自己可以起名字的地方都叫标识符
标识符的命名规则:
- 由字母、数字、下划线
_
、美元符$
组成,不能由数字开头 - 严格区分大小写
- 不能使用关键字和保留字
标识符的命名规范:(建议遵守的软性要求)
- 包名:所有单词的字母都小写
- java.lang、com.atguigu.bean
- 类名、接口名:所有单词的首字母大写(驼峰法)
- HelloWorld,String,System
- 变量名、方法名:第一个单词的首字母小写,第二个单词开始每个单词首字母大写
- age, name, bookName, main, binarySearch, getName
- 常量名:所有字母都大写,每个单词用下划线连接
- MAX_VALUE, PI, DEFAULT_CAPACITY
[!info] 为什么标识符的声明规则里要求不能数字开头?
// 如果允许数字开头,则如下的声明编译就可以通过: int 123L = 12; //进而,如下的声明中 l 的值到底是 123?还是变量 123L 对应的取值 12 呢? => 出现歧义 long l = 123L;
3 变量
变量:内存中的一个存储区域,该区域的数据可以在同一类型范围内不断变化
- 作用:在内存中保留数据
- Java 中变量声明的格式:
数据类型 变量名 = 变量值
- 使用注意点
- Java中每个变量必须先声明,后使用。
- 使用变量名来访问这块区域的数据。
- 变量的作用域:其定义所在的一对{ }内。
- 变量只有在其
作用域
内才有效。出了作用域,变量不可以再被调用。 - 同一个作用域内,不能定义重名的变量。
3.1 变量的数据类型
Java中变量的数据类型分为两大类:
- 基本数据类型:包括
整型
、浮点型
、字符型
、布尔型
。 - 引用数据类型:包括
类
、接口
、数组
、枚举
、注解
、记录
。

[!info] 字节 🆚 比特
字节 (Byte):是计算机用于计量存储容量
的基本
单位,1 Byte = 8 bit。比特 (bit):是数据存储的
最小
单位。二进制数系统中,每个0或1就是一个位,叫做bit (比特)
3.1.1 整型:byte, short, int, long
- Java 各整数类型有固定的表数范围和字段长度,不受具体操作系统的影响,以保证Java程序的可移植性。
- 定义 long 类型的变量,赋值时需要以 “
l
” 或 “L
” 作为后缀。 - Java 程序中变量通常声明为 int 型,除非不足以表示较大的数,才使用 long。
- Java 的整型常量默认为 int 型。
类型 | 占用存储空间(字节 Byte) | 数据范围 |
---|---|---|
byte | 1 | [-128, 127] |
short | 2 | [, ] |
int | 4 | [, ] |
long | 8 | [, ] |
3.1.2 浮点型:float, double
- 与整数类型类似,Java 浮点类型也有固定的表数范围和字段长度,不受具体操作系统的影响。
- 浮点型常量有两种表示形式:
- 十进制数形式。🌰 5.12 512.0f .512 (必须有小数点)
- 科学计数法形式。🌰 5.12e2 512E2 100E-2
- float:
单精度
,尾数可以精确到7位有效数字。很多情况下,精度很难满足需求。 - double:
双精度
,精度是 float 的两倍。通常采用此类型。 - 定义 float 类型的变量,赋值时需要以”
f
“或”F
“作为后缀。 - Java 的浮点型常量默认为 double 型。
- ❗️并不是所有的小数都能可以精确的用二进制浮点数表示。二进制浮点数不能精确的表示 0.1、0.01、0.001 这样 10 的负次幂。
=> 浮点类型 float、double 的数据不适合在不容许舍入误差
的金融计算领域。如果需要精确
数字计算或保留指定位数的精度,需要使用BigDecimal 类
。
类型 | 占用存储空间(字节 Byte) | 数据范围 |
---|---|---|
float | 4 | [-3.403E38, 3.403E38] |
double | 8 | [-1.798E308, 1.798E308] |
3.1.3 字符型:char
- char 型数据用来表示通常意义上“
字符
” (占 2 字节) - Java 中的所有字符都使用 Unicode 编码,故一个字符可以存储一个字母,一个汉字,或其他书面语的一个字符。
- char 类型可以进行运算。因为它都对应有 Unicode 码,可以看做是一个数值。
- 字符型变量的三种表现形式:
- 使用单引号 (' ') 括起来的
单个字符
。- char c1 = 'a'; char c2 = '中';
- 直接使用
Unicode值
来表示的字符型常量:\uXXXX
。其中,XXXX代表一个十六进制整数。- char c1 = 97;
- 使用
转义字符‘\’
将其后的字符转变为特殊字符型常量。- char c3 = '\n';
- 使用单引号 (' ') 括起来的
转义字符 | 说明 | Unicode 表示方式 |
---|---|---|
\n |
换行符 | \u000a |
\t |
制表符 | \u0009 |
\" |
双引号 | \u0022 |
\' |
单引号 | \u0027 |
\\ |
反斜线 | \u005c |
\b |
退格符 | \u0008 |
\r |
回车符 | \u000d |
3.1.4 布尔型:boolean
- boolean 类型用来判断逻辑条件,一般用于流程控制语句中:
- if 条件控制语句;
- while 循环控制语句;
- for 循环控制语句;
- do-while 循环控制语句;
- boolean 类型数据只有两个值:true、false。
- 不能使用 0 或非 0 的整数替代 false 和 true,这点和 C 语言不同。
- 拓展:Java 虚拟机中没有任何供 boolean 值专用的字节码指令,Java 语言表达所操作的 boolean 值,在编译之后都使用 java 虚拟机中的 int 数据类型来代替:true 用 1 表示,false 用 0 表示。——《java虚拟机规范 8 版》
3.1.5 字符串类型:String
- String 不是基本数据类型,是
引用数据类型
- 使用一对
""
来表示一个字符串,内部可以包含 0 个、1 个或多个字符。 - 声明方式与基本数据类型类似。🌰 String str = “尚硅谷”;
- 运算规则
- 任意 8 种基本数据类型的数据与 String 类型只能进行连接 “+” 运算,且结果一定也是 String 类型
- String 类型不能通过强制类型转换转为其他的类型,需借助包装类的方法进行转换
int num = 10;
boolean b1 = true;
String s1 = "abc";
String s3 = num + b1 + s1; // 编译不通过,因为 int 类型不能与 boolean 运算
String s4 = num + (b1 + s1); // 编译通过
System.out.println(3 + 4 + "Hello!"); // 7Hello!
System.out.println("Hello!" + 3 + 4); // Hello!34
System.out.println('a' + 1 + "Hello!"); // 98Hello!
System.out.println("Hello" + 'a' + 1); // Helloa1
String str = "123";
int num1 = (int)str; // 错误
int num2 = Integer.parseInt(str); // 正确
3.1.6 数组
数组的特点:
- 数组本身是
引用数据类型
,而数组中的元素可以是任何数据类型
,包括基本数据类型和引用数据类型。 - 创建数组对象会在内存中开辟一整块
连续的空间
。占据的空间的大小,取决于数组的长度和数组中元素的类型。 - 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组,一旦初始化完成,其长度就是确定的。数组的
长度一旦确定,就不能修改
。 - 我们可以直接通过下标 (或索引) 的方式调用指定位置的元素,速度很快。
- 数组名中引用的是这块连续空间的首地址。
数组的分类:
- 按元素类型分类
- 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
- 引用数据类型元素的数组:每个元素位置存储对象 (本质是存储对象的首地址)
- 按维度分类
- 一维数组:存储一组数据
- 二维数组:存储多组数据,相当于二维表,一行代表一组数据
一维数组
① 声明
元素的数据类型[] 一维数组的名称; // 推荐 int[] arr;
元素的数据类型 一维数组名[]; // 不推荐 int arr[];
数组的声明,需明确:
- 数组的维度:在 Java 中数组的符号是 [],[] 表示一维,[][] 表示二维。
- 数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的 Java 的数据类型。例如:int、String、Student 等。
- 数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。
❗️Java 语言中声明数组时不能指定其长度 (数组中元素的个数)。 例如: int a[5]; // 非法
② 初始化
new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用 new 创建数组实体。
- 静态初始化:数组变量的初始化和数组元素的赋值操作同时进行
- 本质是用静态数据 (编译时已知) 为数组初始化。此时数组的长度由静态数据的个数决定。
- 动态初始化:数组变量的初始化和数组元素的赋值操作分开进行
- 动态初始化中,只确定了元素的个数 (即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。
- 数组有定长特性,长度一旦指定,不可更改。
// 静态初始化
元素的数据类型[] 数组名 = new 元素的数据类型[]{元素1,元素2,元素3,...}; // int[] arr = new int[]{1,2,3,4,5};
元素的数据类型[] 数组名;
数组名 = new 元素的数据类型[]{元素1,元素2,元素3,...}; // int[] arr; arr = new int[]{1,2,3,4,5};
// 动态初始化
元素的数据类型[] 数组名 = new 元素的数据类型[长度]; // int[] arr = new int[5];
元素的数据类型[] 数组名; // int[] arr; arr = new int[5];
数组名 = new 元素的数据类型[长度];
③ 使用
- 数组的长度:每个数组都有一个属性 length 指明它的长度 (即元素个数)。每个数组都具有长度,而且一旦初始化,其长度就是确定,且是不可变的。
- 数组元素的引用:每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为
数组索引(index)或下标
,可以通过数组的索引/下标访问到数组中的元素。- 下标范围是 [0, 数组的长度-1],即 [0, 数组名.length-1]
- 数据元素的默认值:数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值
- 若元素是基本数据类型,默认初始化值为 0 (boolean 类型为 false)
- 若元素是引用数据类型,默认初始化值为 null (即还未分配具体存储元素的空间)
④ 内存分析
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr); // [I@5f150435
}

- 数组下标为什么从0开始:因为第一个元素距离数组首地址间隔0个单元格。
- 若把数据名赋值给另一个数组变量,则二者指向同一个地址,本质上代表同一数组。
如: int[] arr = new int[3]; int[] arr2 = arr;
二维数组
对于二维数组的理解,可以看成是一维数组 array1 又作为另一个一维数组 array2 的元素而存在。
① 声明
元素的数据类型[][] 二维数组的名称; // 推荐
元素的数据类型 二维数组名[][]; // 不推荐
元素的数据类型[] 二维数组名[]; // 不推荐
② 初始化
// 静态初始化
int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};
int[][] arr;
arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};
int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}}; // 声明与初始化必须在一句完成
// 动态初始化1(每行的列数相同)
元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n]; // 确定行数和列数
二维数组名[行下标][列下标] = 值; // 后续再为元素赋值
// 动态初始化2(每行的列数不同)
元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][]; // 先确定总行数(此时只是确定了总行数,每一行里面仍是 null)
二维数组名[行下标] = new 元素的数据类型[该行的总列数]; // 再确定每一行的列数,创建每一行的一维数组
二维数组名[行下标][列下标] = 值; // 后续再为元素赋值
// PS: 不能先确定总列数!
③ 使用
- 二维数组的长度/行数:
二维数组名.length
- 二维数组的某一行:
二维数组名[行下标]
,此时相当于获取其中一组数据。它本质上是一个一维数组。- 行下标的范围:[0, 二维数组名.length-1]。此时若把二维数组看成一维数组,元素是行对象。
- 某一行的列数:
二维数组名[行下标].length
,因为二维数组的每一行是一个一维数组。 - 某一个元素:
二维数组名[行下标][列下标]
,即先确定行/组,再确定列。
④ 内存分析

Arrays 工具类
java.util.Arrays 类即为操作数组的工具类,包含了用来操作数组 (如排序和搜索) 的各种方法。
数组元素的拼接
- static String toString(int[] a):字符串表示形式由数组的元素列表组成,括在方括号 (”[]”) 中。相邻元素用字符 “, “ (逗号加空格) 分隔。形式为:[元素1,元素2,元素3......]
- static String toString(Object[] a):字符串表示形式由数组的元素列表组成,括在方括号(”[]”)中。相邻元素用字符 “, “ (逗号加空格) 分隔。元素将自动调用自己从 Object 继承的 toString 方法将对象转为字符串进行拼接,如果没有重写,则返回类型 @hash 值,如果重写则按重写返回的字符串进行拼接。
数组排序
- static void sort(int[] a):将 a 数组按照从小到大进行排序
- static void sort(int[] a, int fromIndex, int toIndex):将 a 数组的 [fromIndex, toIndex) 部分按照升序排列
- static void sort(Object[] a):根据元素的自然顺序对指定对象数组按升序进行排序
- static void sort(T[] a, Comparator<? super T> c):根据指定比较器产生的顺序对指定对象数组进行排序
数据元素的二分查找
- static int binarySearch(int[] a, int key), static int binarySearch(Object[] a, Object key):要求数组有序,在数组中查找 key 是否存在,如果存在返回第一次找到的下标,不存在返回负数。
数组的复制
- static int[] copyOf(int[] original, int newLength):根据 original 原数组复制一个长度为 newLength 的新数组,并返回新数组
- static T[] copyOf(T[] original,int newLength):根据 original 原数组复制一个长度为 newLength 的新数组,并返回新数组
- static int[] copyOfRange(int[] original, int from, int to):复制 original 原数组的 [from,to) 构成新数组,并返回新数组
- static T[] copyOfRange(T[] original,int from,int to):复制 original 原数组的 [from,to) 构成新数组,并返回新数组
比较两个数组是否相等
- static boolean equals(int[] a, int[] a2):比较两个数组的长度、元素是否完全相同
- static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同
填充数组
- static void fill(int[] a, int val):用 val 值填充整个 a 数组
- static void fill(Object[] a,Object val):用 val 对象填充整个 a 数组
- static void fill(int[] a, int fromIndex, int toIndex, int val):将 a 数组 [fromIndex,toIndex) 部分填充为 val 值
- static void fill(Object[] a, int fromIndex, int toIndex, Object val):将 a 数组 [fromIndex,toIndex) 部分填充为 val 对象
数组的常见异常
- 数组角标越界异常:访问数组元素时,若下标指定超出 [0, 数组名.length-1] 的范围,就会报
ArrayIndexOutOfBoundsException
- 空指针异常:若数组的元素值为 null,表明还未分配具体存储元素的空间,此时访问该元素会报
NullPointerException
3.2 数据类型间的转换
在 Java 程序中,不同的基本数据类型 (只有 7 种,不包含 boolean 类型) 变量的值经常需要进行相互转换。
- 转换的方式有两种:隐式类型转换、显式 (强制) 类型转换。
- 隐式类型转换是 Java 自动执行的,显示类型转换是隐式类型转换的逆运算,需要手动执行。
- ❗️String 类型不能通过强制类型转换转为其他的类型,需借助包装类的方法进行转换。
3.2.1 隐式类型转换
规则:将取值范围小 (或容量小) 的类型自动提升为取值范围大 (或容量大) 的类型。

隐式类型转换发生在:
- 当把存储范围小的值 (常量值、变量的值、表达式计算的结果值) 赋值给了存储范围大的变量时
- 当存储范围小的数据类型与存储范围大的数据类型变量一起混合运算时,会按照其中最大的类型运算
- 当 byte, short, char 数据类型的变量进行算术运算时,按照 int 类型处理
int i = 'A'; // char 自动升级为 int,其实就是把字符的编码值赋值给i变量了
double d = 10; // int 自动升级为 double
int i = 1;
byte b = 1;
double d = 1.0;
double sum = i + b + d; // 混合运算,升级为 double
byte b1 = 1, b2 = 2;
byte b3 = b1 + b2; // 编译报错,b1 + b2 自动升级为 int
char c1 = '0', c2 = 'A';
int i = c1 + c2; // 至少需要使用 int 类型来接收
System.out.println(c1 + c2); // 113
3.2.2 强制类型转换
规则:将取值范围大 (或容量大) 的类型强制转换成取值范围小 (或容量小) 的类型,需手动执行。
显示类型转换可使用在:
- 当把存储范围大的值 (常量值、变量的值、表达式计算的结果值) 强制转换为存储范围小的变量时。但可能会
损失精度
或溢出
。 - 当某个值想要提升数据类型时,也可以使用强制类型转换。这种情况的强制类型转换是
没有风险
的,通常省略。
int i = (int)3.14; // 损失精度
float f3 = (float)12.3; // 12.3 看做是 double,不能自动转换为 float 类型
int i = 1, j = 2;
double bigger = (double)(i / j);
3.3 进制转换
计算机中的所有数据均以二进制补码的形式存储,且最高位为 符号位
(正数为 0,负数为 1)。
- 正数的补码、反码、原码一样,称为
三码合一
- 负数的补码、反码、原码不一样:
- 负数的
原码
:把十进制转为二进制,然后最高位设置为 1 - 负数的
反码
:在原码的基础上,最高位不变,其余位取反 (0 变 1,1 变 0) - 负数的
补码
:反码 + 1
- 负数的
- 25 => 原码、反码、补码均为 0001 1001
-25 => 原码 1001 1001 => 反码 1110 0110 => 补码 1110 0111
// 一个 Byte 可存储的整数范围 [-128, 127]
0000 0001 ~ 0111 111 => [1, 127]
1000 0001 ~ 1111 111 => [-127, -1]
0000 0000 => 0
1000 0000 => -128 (特殊规定)
进制分类
- 十进制:由 0 - 9 组成
- 二进制:以
0b
或0B
开头,由 0 - 1 组成 - 八进制:以
0
开头,由 0 - 7 组成 - 十六进制:以
0x
或0X
开头。由 0 - 9, a - f 组成 (不区分大小写)
进制转换:
- 二进制 => 十进制:按权相加法
- 十进制 => 二进制:除 2 倒取余
- 二进制 => 八进制:二进制中每 3 位转为八进制中的 1 位
- 二进制 => 十六进制:二进制中每 4 位转为十六进制中的 1 位
int num1 = 123; // 十进制
int num2 = 0b101; // 二进制
int num3 = 0127; // 八进制
int num4 = 0x12aF; // 十六进制
4 运算符
运算符:一种特殊的符号,用以表示数据的运算、赋值和比较等。
按功能分类 | 运算符 |
---|---|
算术运算符 (7个) | +、-、*、/、%、++、– |
赋值运算符 (12个) | =、+=、-=、*=、/=、%=、>>=、<<=、>>>=、&=、|=、^=等 |
比较/关系运算符 (6个) | >、>=、<、<=、==、!= |
逻辑运算符 (6个) | &、&&、||、|、!、^ |
位运算符 (7个) | &、|、^、~、<<、>>、>>> |
条件运算符 (1个) | (条件表达式) ? 结果1 : 结果2 |
Lambda 运算符 (1个) | => (第18章) |
按操作个数分类 | 运算符 |
---|---|
一元运算符 (单目运算符) | 正号 (+)、负号(-)、++、–、!、~ |
二元运算符 (双目运算符) | 除了一元和三元运算符剩下的都是二元运算符 |
三元运算符 (三目运算符) | (条件表达式) ? 结果1 : 结果2 |
运算符优先级 | Java运算符 |
---|---|
括号 | ()、[]、{} |
正负号 | +、- |
单元运算符 | ++、--、~、! |
乘法、除法、求余 | *、/、% |
加法、减法 | +、- |
移位运算符 | <<、>>、>>> |
关系运算符 | <、<=、>=、>、instanceof |
等价运算符 | ==、!= |
按位与 | & |
按位异或 | ^ |
按位或 | | |
条件与 | && |
条件或 | || |
三元运算符 | ? : |
赋值运算符 | =、+=、-=、*=、/=、%= |
位赋值运算符 | &=、|=、<<=、>>=、>>>= |
[!info] 开发建议
- 不要过多的依赖运算的优先级来控制表达式的执行顺序,这样可读性太差,尽量
使用()来控制
表达式的执行顺序。- 不要把一个表达式写得过于复杂,如果一个表达式过于复杂,则把它
分成几步
来完成。
🌰 (num1 + num2) * 2 > num3 && num2 > num3 ? num3 : num1 + num2;
4.1 算数运算符
- 整数相除,结果仍为整数
前++
、前--
和后++
、后--
的区别:前
会先自增/自减,再运算;后
会先运算后自增/自减
4.2 赋值运算符
- 赋值运算符
=
:支持连续赋值,当 “=” 两侧数据类型不一致时,可使用隐式/显示类型转换进行处理 - 扩展赋值运算符
+=
、-=
、*=
、/=
、%=
:不会改变变量本身的数据类型
short s1 = 10, s2 = 10;
s2 = s1 + 2; // 编译报错,因为将 int 类型的结果赋值给 short 类型的变量 s 时,可能损失精度
s2 = (short)(s2 + 2); // 编译通过
s1 += 2; // 编译通过,因为在得到 int 类型的结果后,JVM 自动完成一步强制类型转换,将 int 类型强转成 short
4.3比较/关系运算符
- 比较运算符的结果都是 boolean 型
>
,>=
,<
,<=
:只适用于基本数据类型 (除 boolean 类型之外)==
,!=
:适用于基本数据类型、引用数据类型
4.4 逻辑运算符
-
操作的都是 boolean 类型的变量/常量,而运算符的结果也是 boolean 类型
-
运算符说明:
&
,&&
:"且" 关系,当符号左右均为 true 时,结果才为 true。否则为 false。|
,||
:"或" 关系,当符号左右均边 false 时,结果才为 false。否则为 true。!
:"非" 关系,当变量布尔值为 true 时,结果为 false。否则为 true。^
:"异或" 关系,当符号左右两边布尔值不同时,结果为 true。否则为 false。- 理解:异或,追求的是 “异” !
-
&
🆚&&
-
相同点:若符号左边是 true,则二者都执行符号右边的操作
-
不同点:对于
&&
,若符号左边为 false,则不再继续执行符号右边的操作;对于&
,若符号左边是 false,仍会继续执行符号右边的操作。 -
建议:开发中,推荐使用
&&
-
-
|
🆚||
- 相同点:若符号左边是 false,则二者都执行符号右边的操作
-
不同点:对于
||
,若符号左边是 true,则不再继续执行符号右边的操作;对于|
,若符号左边是 true,仍会继续执行符号右边的操作。 -
建议:开发中,推荐使用
||
-
- 相同点:若符号左边是 false,则二者都执行符号右边的操作
4.5 位运算符
位运算符的运算过程都是基于二进制的补码运算。
❗️没有无符号左移 <<<
位运算符 | 运算 | 运算细节 | 例子 |
---|---|---|---|
<< | 左移 | 丢弃被移除的高位,空缺位补 0 | 3 << 2 = 12 相当于 3 *2*2 |
>> | 右移 | 被移位的二进制最高位为 0 时,右移导致的空缺位补 0;否则补 1 | 3 >> 1 = 1 相当于 3/2 |
>>> | 无符号右移 | 无论被移位的二进制最高位是 0 还是 1,右移导致的空缺位都补 0 | 3 >> 1 = 1 相当于 3/2 |
& | 按位与 | 二进制位做 & 运算,只有 1&1 时结果为 1,否则为 0 | 6 & 3 = 2 |
| | 按位或 | 二进制位做 | 运算,只有 0|0 时结果为 0,否则为 1 | 6 | 3 = 7 |
^ | 按位异或 | 二进制位做 ^ 运算,相异二进制位结果为 1,否则为 0 | 6 ^ 3 = 5 |
~ | 按位取反 | 各二进制码按补码各位取反 | ~6 = -7 |
左移 <<
:在一定范围内,数据每向左移动一位,相当于原数据 * 2。【正数、负数都适用】
- ❗️当左移的位数 n 超过该数据类型的总位数时,相当于左移 (n - 总位数) 位
右移 >>
:在一定范围内,数据每向右移动一位,相当于原数据 / 2。【正数、负数都适用】
- ❗️如果不能整除,
向下取整
。
m = k ^ n = (m ^ n) ^ n

5 [拓展] 字符集
计算机中储存的信息都是用二进制数
表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码
。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码
。
- 字符编码(Character Encoding) : 就是一套自然语言的字符与二进制数之间的对应规则。
- 字符集:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
5.1 ASCII 码
- ASCII 码用于显示
现代英语
,主要包括控制字符 (回车键、退格、换行键等) 和可显示字符 (英文大小写字符、阿拉伯数字和西文符号)。 - 基本的 ASCII 字符集,使用7位 (bits) 表示一个字符 (最前面的1位统一规定为0),共
128个
字符。🌰 空格“SPACE”是32 (二进制00100000),大写的字母A是65 (二进制01000001)。 - 缺点:不能表示所有字符。
5.2 ISO-8859-1 字符集
- 拉丁码表,别名 Latin-1,用于显示欧洲使用的语言,包括荷兰语、德语、意大利语、葡萄牙语等
- ISO-8859-1 使用单字节编码,兼容 ASCII 编码。
5.3 GBxxx 字符集
- GB 就是国标的意思,是为了
显示中文
而设计的一套字符集。 - GB2312:简体中文码表。一个小于 127 的字符的意义与原来相同,即向下兼容 ASCII 码。但两个大于 127 的字符连在一起时,就表示一个汉字,这样大约可以组合了包含
7000多个简体汉字
,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,这就是常说的”全角”字符,而原来在127号以下的那些符号就叫”半角”字符了。 - GBK:最常用的中文码表。是在 GB2312 标准基础上的扩展规范,使用了
双字节
编码方案,共收录了21003个
汉字,完全兼容 GB2312 标准,同时支持繁体汉字
以及日韩汉字等。 - GB18030:最新的中文码表。收录汉字
70244个
,采用多字节
编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
5.4 Unicode 码
- Unicode 编码为表达
任意语言的任意字符
而设计,也称为统一码、标准万国码。Unicode 将世界上所有的文字用2个字节
统一进行编码,为每个字符设定唯一的二进制编码,以满足跨语言、跨平台进行文本处理的要求。 - Unicode 的缺点:
- 英文字母只用一个字节表示就够了,如果用更多的字节存储是
极大的浪费
。 - 如何
区分 Unicode 和 ASCII
,计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢? - 如果和 GBK 等双字节编码方式一样,用最高位是1或0表示两个字节和一个字节,就少了很多值无法用于表示字符,
不够表示所有字符
。
- 英文字母只用一个字节表示就够了,如果用更多的字节存储是
- Unicode 在很长一段时间内无法推广,直到互联网的出现,为解决 Unicode 如何在网络上传输的问题,于是面向传输的众多 UTF (UCS Transfer Format) 标准出现。具体来说,有三种编码方案,UTF-8、UTF-16、UTF-32。
5.5 UTF-8
- Unicode 是字符集,UTF-8、UTF-16、UTF-32 是三种
将数字转换到程序数据
的编码方案。顾名思义,UTF-8 就是每次 8 个位传输数据,而 UTF-16 就是每次 16 个位。其中,UTF-8 是在互联网上使用最广
的一种 Unicode 的实现方式。 - 互联网工程工作小组 (IETF) 要求所有互联网协议都必须支持 UTF-8 编码。所以,我们开发 Web 应用,也要使用UTF-8编码。UTF-8 是一种
变长的编码方式
。它可以使用 1-4 个字节表示一个符号它使用一至四个字节为每个字符编码,编码规则:- 128 个 US-ASCII 字符,只需 1 个字节编码。
- 拉丁文等字符,需要 2 个字节编码。
- 大部分常用字 (含中文),使用 3 个字节编码。
- 其他极少使用的 Unicode 辅助字符,使用 4 字节编码。
Unicode 符号范围 | UTF-8编码方式:
(十六进制) | (二进制)
————————————————————|—–—–—–—–—–—–—–—–—–—–—–—–—–—–
0000 0000-0000 007F | 0xxxxxxx(兼容原来的ASCII)
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
小结
[!note]
在中文操作系统上,ANSI (美国国家标准学会) 编码即为 GBK;在英文操作系统上,ANSI 编码即为 ISO-8859-1。

本文作者:Joey-Wang
本文链接:https://www.cnblogs.com/joey-wang/p/18424980
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步