本文纲要
- 进制介绍与Java书写格式
- 任意进制转十进制
- 十进制转任意进制
- 快速进制转换法(8421码)
- 原码、反码、补码
- 位运算 - 基本位运算符
- 位运算 - 位移运算符
- 案例:数据交换
- 案例:数组反转
进制介绍与Java书写格式
计算机底层使用二进制进行运算与存储,但在开发中我们也会接触到八进制、十进制、十六进制。
掌握这些进制及它们在Java中的书写方式,有助于理解底层原理。
| 进制 | 规则 | Java书写格式 |
|---|---|---|
| 十进制 | 逢十进一,借一当十,默认数值类型 | 直接书写,如 10 |
| 二进制 | 逢二进一,借一当二,只有 0 和 1 | 0b 开头,如 0b10,b 大小写均可 |
| 八进制 | 逢八进一,借一当八 | 0 开头,如 010 |
| 十六进制 | 逢十六进一,借一当十六,用 0-9 和 a-f 表示 | 0x 开头,如 0x10,x 大小写均可 |
注意事项
- 上述进制标识从 JDK7 开始支持。
- 控制台打印时,无论代码中书写何种进制,最终展示的都是 十进制 数值。
示例
// Demo1.java
package com.wb.demo;
public class Demo1 {
public static void main(String[] args) {
System.out.println(10);
System.out.println("二进制数据0b10的十进制表示为:" + 0b10); // 2
System.out.println("八进制数据010的十进制表示为:" + 010); // 8
System.out.println("十六进制数据0x10的十进制表示为:" + 0x10); // 16
}
}
任意进制转十进制
1 )公式
系数 × 基数^权次幂 再求和
- 系数:每一位上的数字
- 基数:当前进制的基,如二进制基数是2,十六进制是16
- 权:从数字最右边开始,编号为0,依次向左递增
- 再求和: 各项结果相加
示例:二进制 0b100 转十进制
| 系数 | 基数 | 权次幂 | 计算结果 |
|---|---|---|---|
| 1 | 2 | 2 | 1×2²=4 |
| 0 | 2 | 1 | 0 |
| 0 | 2 | 0 | 0 |
求和:4 + 0 + 0 = 4
示例:十六进制 0x100 转十进制
| 系数 | 基数 | 权次幂 | 计算结果 |
|---|---|---|---|
| 1 | 16 | 2 | 1×16²=256 |
| 0 | 16 | 1 | 0 |
| 0 | 16 | 0 | 0 |
求和:256。
该公式适用于 任意进制转十进制。
十进制转任意进制
公式:除基取余,商为0时停止,余数倒序排列。
示例:十进制 11 转二进制
| 除基 | 商 | 余数 |
|---|---|---|
| 11÷2 | 5 | 1 |
| 5÷2 | 2 | 1 |
| 2÷2 | 1 | 0 |
| 1÷2 | 0 | 1 |
余数倒序:1011,所以 11(10) = 1011(2)
示例:十进制 60 转十六进制
| 除基 | 商 | 余数 |
|---|---|---|
| 60÷16 | 3 | 12© |
| 3÷16 | 0 | 3 |
余数倒序:3C,所以 60(10) = 0x3C
快速进制转换法(8421码)
1 ) 8421码(BCD代码)
将二进制每一位对应的固定值记下,直接累加即可快速得到十进制值
| 二进制位权重 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| 对应二进制位 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
- 二进制位为 1 时,取该位权重并相加;为 0 则忽略
2 ) 二进制转十进制示例
二进制:0b 1 1 0 1权重:8 4 2 1取1的位:8 + 4 + 1 = 13
3 ) 二进制转八进制
将二进制从右向左每 3位 一组(因为八进制最大为7,3位二进制最大为7),每组按8421码求和,结果拼在一起。
二进制: 0b 11 1100
分组: 011 110 (补零对齐)
8421: 0+2+1=3 4+2+0=6
八进制: 36
Java中没有直接的八进制转换API,但此方法适合手算理解。
4 ) 二进制转十六进制
每 4位 一组(十六进制最大为15),每组求和,大于9的用字母表示。
二进制: 0b 11 1100
分组: 0011 1100
8421: 2+1=3 8+4=12 → C
十六进制: 3C
原码、反码、补码
计算机中的数据以 二进制补码 形式运算,而原码用来直观查看数值大小,反码是转换过程的中间状态。
- 原码:最高位为符号位(0正1负),其余位表示数值。
- 反码:正数的反码等于原码;负数反码为原码除符号位外按位取反。
- 补码:正数的补码等于原码;负数补码为反码末位加一。
示例:int 强转为 byte 的解释
Java中 int 占4字节,byte 占1字节。将 130 强转为 byte 会发生精度损失,结果为 -126。
过程如下:
130的int原码(补码相同,正数):
00000000 00000000 00000000 10000010- 强转为
byte截取低8位:10000010(此时已是补码,且符号位为1,表示负数) - 补码求原码:
补码减1得反码:10000001
反码取反(符号位不变)得原码:11111110
原码对应的十进制:-(2+4+8+16+32+64) = -126
位运算 - 基本位运算符
位运算符直接对整数的二进制位操作,运算速度快。二进制位中,1 表示 true,0 表示 false。
| 运算符 | 名称 | 运算规则 |
|---|---|---|
& | 位与 | 全1才1(有0则0) |
| ` | 位或 | 有1则1 |
^ | 位异或 | 相同为0,不同为1 |
~ | 取反 | 所有位(含符号位)0变1,1变0 |
示例代码
// Demo2.java
package com.wb.demo;
public class Demo2 {
public static void main(String[] args) {
System.out.println(6 & 2); // 2
System.out.println(~6); // -7
}
}
运算过程分析
6 & 2:
00000000 00000000 00000000 00000110 (6)`
& 00000000 00000000 00000000 00000010 (2)`
-----------------------------------------
00000000 00000000 00000000 00000010 → 2
~ 6:
00000000 00000000 00000000 00000110 (6 的补码)
~ 11111111 11111111 11111111 11111001 (取反后仍为补码)
得到的是负数的补码,反推原码:
补码减 1 : 11111111 11111111 11111111 11111000 (反码)
反码取反(符号位不变): 10000000 00000000 00000000 00000111 → -7
位运算 - 位移运算符
| 运算符 | 名称 | 运算规律 |
|---|---|---|
| << | 有符号左移 | 左移 n 位相当于乘 2^n,右侧补 0 |
| >> | 有符号右移 | 右移 n 位相当于除 2^n,左侧补符号位 |
| >>> | 无符号右移 | 右移 n 位,左侧一律补 0(很少使用) |
示例代码
// Demo3.java
package com.wb.demo;
public class Demo3 {
public static void main(String[] args) {
System.out.println(12 << 1); // 24
System.out.println(12 << 2); // 48
}
}
12 << 2 运算过程:
12 二进制(补零至8位示意): 00001100
左移2位: 00110000 → 十进制 48
符号位丢弃,右侧补 0
案例:数据交换
1 ) 方式一:使用临时变量
最常用、最易读的交换方式
// Test1.java
package com.wb.test;
public class Test1 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int temp = a;
a = b;
b = temp;
System.out.println("a=" + a); // 20
System.out.println("b=" + b); // 10
}
}
2 ) 方式二:利用异或运算
利用异或特性:一个数异或另一个数两次,结果不变
// Test1_1.java
package com.wb.test;
public class Test1_1 {
public static void main(String[] args) {
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b; // b = (a^b) ^ b = a
a = a ^ b; // a = (a^b) ^ a = b
System.out.println("a=" + a);
System.out.println("b=" + b);
}
}
验证异或特性的例子
// Demo4.java
package com.wb.demo;
public class Demo4 {
public static void main(String[] args) {
System.out.println(10 ^ 5 ^ 10); // 结果还是 5
}
}
实际开发建议使用临时变量方式,异或方式多出现在面试题中
案例:数组反转
需求:将数组 {19, 28, 37, 46, 50} 反转为 {50, 46, 37, 28, 19}
核心思路
使用 双指针 分别指向数组首尾,交换元素后指针向中间移动,直到指针相遇
1 ) 第一步:手动交换首尾元素(理解基础)
// Test2_2.java
package com.wb.test;
public class Test2_2 {
public static void main(String[] args) {
int[] arr = {11, 22, 33, 44, 55};
// 交换 arr[0] 和 arr[4]
int temp = arr[0];
arr[0] = arr[4];
arr[4] = temp;
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
2 )第二步:使用循环完成整个反转
// Test2.java
package com.wb.test;
public class Test2 {
public static void main(String[] args) {
int[] arr = {19, 28, 37, 46, 50};
// 双指针
int start = 0;
int end = arr.length - 1;
// 当 start < end 时进行交换
for ( ; start < end; start++, end--) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
// 遍历输出反转后的数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
3 ) 循环的另一种紧凑写法(在 for 初始化中定义双指针):
for(int start = 0, end = arr.length - 1; start < end; start++, end--) {
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
总结
本文介绍了Java中常见的进制表示与转换、原反补码的运作原理以及位运算、位移运算,并结合经典案例“数据交换”和“数组反转”加深理解。
这些基础知识是理解Java底层运算和阅读源码的重要铺垫。
1028

被折叠的 条评论
为什么被折叠?



