数据分类
Java数据类型分为:基本数据类型 和 对象(引用)数据类型。程序中需要处理许多数据,对于不同数据都有其对应的数据类型,其实就是在内存中开辟一个存储空间来存放数据,不同数据所开辟的内存大小也会不一样。
数据存储的单位
位(bit)、字节(byte)是计算机数据存储的单位。位是最小的存储单位,每一个位存储一个1位的二进制码(即每一个逻辑0或者1便是一个位),一个字节由8位组成。

| 类型名称 | 关键字 | 占用内存 | 取值范围 |
|---|---|---|---|
| 字节型 | byte | 1 字节 | -128 ~ 127(-27 ~ 27-1) |
| 短整型 | short | 2 字节 | -32768 ~ 32767(-215 ~ 215-1) |
| 整型 | int | 4 字节 | -231 ~ 231-1 |
| 长整型 | long | 8 字节 | -263 ~ 263-1 |
| 单精度浮点型 | float | 4 字节 | -231 ~ 231-1(6~7 个有效位) |
| 双精度浮点型 | double | 8 字节 | -263 ~ 263-1 (15 个有效位) |
| 字符型 | char | 2 字节 | 0 ~ 216 -1(ISO 单一字符集) |
| 布尔型 | boolean | 1 字节 | true 或 false |
字节(B)是计算机信息技术用于计量存储容量和传输容量的一种计量单位,1个字节等于8位二进制,即1B=8bit。
// 整数
byte v1 = 123; // 十进制
short v2 = 0b11001; // 0B11001; 二进制(以 0b 或 0B 开头)
int v3 = 0xF78A; // 0XF78A、0Xf78a; 十六进制(以 0x 或 0X 开头)
long v4 = 199L; // 199l; long 类型以 L 或者 l 结尾
// 浮点数
float v5 = 123.4F; // 123.4f;float 类型以 F 或者 f 结尾
double v6 = 123.4D; // 123.4d;double 类型以 D 或者 d 结尾
double v7 = 123.4; // 默认就是 double 类型,可以省略 D 和 d 结尾符
// 可以用科学计数法(E 或 e 表示的是 10 的多少次方)
float v8 = 1.234E2F; // 表示的是 1.234F × 10^2
double v9 = 1.234e2; // 表示的是 1.234 × 10^2
// 字符和字符串
char ch = 'A'; // 单引号表示字符
String str = "ABCD"; // 双引号表示字符串
// 在数字中使用下划线(从Java 7开始,可以给数字添加下划线增强可读性)
int a = 1_0000_0000; // 100000000
// 注意1:不能在数字的前后使用下划线
// int x = _123;
// int x = 123_;
// 注意2:不能在小数点、X、B、F、D、L、E等特殊字符的前后使用下划线
// byte x = 0x_12;
// byte x = 0_b10010;
// float x = 1._23;
// float x = 1.23F_;
// long x = 189_L;
原码、反码、补码
数据在计算机中都是用 二进制 表示的,并且是用 补码 进行数据计算。在学习原码、反码和补码之前,需要先了解机器数和真值的概念。
机器数:表示一个数在计算机的二进制表示形式。机器数它是带符号的,在最高位用来存放符号,正数为0,负数为1。
真值:带符号表示的机器数对应的真正的数值称为真值。
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。
如:有符号数10000011,其最高位1代表负,其真正数值是-3,而不是形式值131(10000011转换成十进制等于131)
- 原码:就是符号位加上其真值的绝对值,即用第一位表示符号,其余位表示值。
// 举个例子:
+1的原码:00000001
-1的原码:10000001
- 反码:如果是正数,正数的反码等于原码;如果是负数,除符号位不变,其它位按位取反(1变0;0变1)。
// 举个例子:
+1的反码:00000001 ===> 00000001
-1的反码:10000001 ===> 11111110
- 补码:如果是正数,正数的补码等于原码;如果是负数,在原码基础上符号位不变,其它各位取反,最后再+1。
// 举个例子:
+1的原码:00000001
+1的反码:00000001
+1的补码:00000001
-1的原码:10000001
-1的反码:11111110
-1的补码:11111111
- 数据(±1)在内存中存储细节
public class TestClass {
public static void main(String[] args) {
// 数据±11在内存中存储细节:在计算机中存储的是一个数的补码
byte a = 0b00001011; // 11的补码 (机器数)
byte b = (byte) 0b11110101; // -11的补码 (机器数)
byte c = (byte) 0b10001011; // -117的补码 (机器数)
System.out.println(a); // 打印结果:11 (真值)
System.out.println(b); // 打印结果:-11 (真值)
System.out.println(c); // 打印结果:-117 (真值)
}
// 真值:+11
// 原码:00001011
// 反码:00001011 (取反)
// 补码:00001011 (加1)---->是+11在内存中存储的数(机器数)
// 真值:-11
// 原码:10001011
// 反码:11110100 (取反)
// 补码:11110101 (加1)---->是-11在内存中存储的数(机器数)
// 补码:0b10001011 -------->是-117在内存中存储的数(机器数)
// 反码:0b10001010 (补码-1)
// 原码:0b11110101 (取反)
// 真值:-117
}
- 为什么要设计反码、补码?
因为计算机 只有加法,没有减法。在做减法运算的时候,可以认为是加上一个负数,这样可以 减少计算机电路的复杂度。
举例说明:
十进制的加法:1+1=2;
二进制的加法:1+1=00000001+00000001=00000010=2;
十进制的减法:1-1=0;
二进制的减法:1-1=1+(-1)=00000001+10000001=10000010=-2
从上面的运算中,我们使用的是原码的方式进行计算,加法没问题,但是减法结果居然为-2,于是针对这个情况(原码做减法),出现了反码,我们来看看效果:
十进制的加法:1+1=2;
二进制的原码加法:1+1=00000001+00000001=00000010=2;
二进制的反码加法:1+1=00000001+00000001=00000010=2;
十进制的减法:1-1=0;
二进制的原码减法:1-1=1+(-1)=00000001+10000001=10000010=-2;
二进制的反码减法:1-1=1+(-1)=00000001[反]+11111110[反]=11111111[反]=10000000[原]=-0;
这里可以得到反码11111111,然后我们需要将其反码换成人看得懂的原码,11111111[反]=10000000[原]=-0。通过反码计算确实可以得到正确答案,这也就是为什么要有反码的出现。
但是,还有一个问题:就是 00000000 可以表示 +0,10000000 可以表示 -0,从人的角度来说 +0 和 -0 是一样的,0带符号没有任何意义,用2个编码实在是浪费。
于是补码的出现,解决了0的符号以及两个编码的问题
十进制的减法:1-1=0;
二进制的原码减法:1-1=1+(-1)=00000001+10000001=10000010=-2;
二进制的反码减法:1-1=1+(-1)=00000001[反]+11111110[反]=11111111[反];
二进制的补码减法:1-1=1+(-1)=00000001[补]+11111111[补]=00000000[补]=00000000[原];
得到结果是 00000000,为什么是 00000000 而不是 100000000,因为一个字节只能装8位,现在后面的8位就是 00000000,于是就得到了0,而以前出现问题的 -0 则不存在了。
而且可以用 1000 0000(-0的补码) 表示-128, 所以-128并没有原码和反码表示。于是我们的计算机当中规定-0的补码就是-128在计算机当中的存储方式(即规定用10000000来代表-128的补码参与计算),这样也可好可以满足计算机当中的计算。
使用补码,不仅仅修复了0的符号以及存在两个编码的问题,而且还能够多表示一个最低数。这就是为什么8位二进制,使用原码或者反码表示的范围是[-127 ~ 127],而使用补码表示的范围为[-128 ~ 127]。
-128为什么可以用100000000表示?
这里我分析的是byte,它就8位。在无符号位的二进制中128的表示为1000 0000。有符号位的情况下byte好像无法表示+128或-128。
如果我们假设byte不是占用8位,而是9位,最高位是符号位。你们-128的表示为1 10000 0000,计算其补码也是1 1000 0000,很神奇吧,一样的。-128补码尾8位就是1000 0000。那就规定1000 0000是-128的补码,而-128是没有原码和反码的,即不能利用10000 0000反推其反码和原码。
用10000000来代表-128的补码参与计算,举例说明:
// 例1:
-128 + 127 = 1000 0000(补码)+ 0111 1111(补码)= 1111 1111(补码)= 1111 1110(反码) = 1000 0001(原码)= -1
// 例2:
十进制:-1+(-127)=-128
二进制原码:10000001+11111111
二进制反码:11111110+10000000
二进制补码:11111111+10000001=10000000[只保留8位]
所以在原码和反码中, byte 的范围是[-127,127];在使用补码后,不仅可以解决±0的符号以及两个编码的问题,而且还能多一个最低数-128(人为规定用 10000000 来占 -128 这个位置,即10000000就是-128的补码),这就是为什么 byte 的范围是[-128,127]。
- 总结:
- 原码是人能理解的,反码和补码需要转换为原码我们才能看懂;
- 反码是为了解决减法运算。
- 补码是为了解决反码产生的±0的问题。
- 在原码、反码、补码相互转换以及求对应的十进制求值时,符号位是绝不参与的;但是在加减过程中,是参与位运算的。
- 对于一个数,计算机要使用一定的编码方式进行存储;原码、反码、补码是机器存储一个具体数字的编码方式;
- 数据在计算机内部是以补码的形式储存的。
ASCII码
ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。
- 常用ASCII 编码:
| 十进制 | 字符 |
|---|---|
| 48 | '0' |
| 65 | 'A' |
| 97 | 'a' |
进制
- 基本概念
进制:是一种计数的方式,数值的表示形式
// 十进制:逢十进一 (如:13 == 1 * 10 + 3)
// 八进制:逢八进一 (如:15 == 1 * 8 + 5)
// 二进制:逢二进一 (如:1101 == 1 * 2 * 2 * 2 + 1 * 2 * 2 + 0 * 2+ 1)
// 十六进制:逢十六进一 (0 ~ 9 A ~ F 如:d == 13)
int num = 12; // 默认就是10进制
int num1 = 014; // 在前面加上一个0就代表八进制
System.out.format("%d\n", num1); // %d是以十进制的方式输出一个整数
System.out.format("%o\n", num); // %o是以八进制的方式输出一个整数
int num2 = 0b1100; // 在数值前面加上0b或0B就代表二进制
System.out.format("%d\n", num2); // 在C语言中没有提供二进制的输出格式符
int num3 = 0x6c; // 在数值前面加上0x就代表十六进制
System.out.format("%d\n", num3);
System.out.format("%x\n", num); // %x是以十六进制的方式输出一个整数
- 常见的进制转换
10 转 2:除2取余法,把10进制数除以2,然后取得余数的序列,再倒序。
方法:整数部分“除2取余法”,小数部分“乘2取整法”,高位补0,将得到的余数倒序得到的序列就是二进制的形式。
例如:将十进制(97)转换为二进制数(1100001)

2 转 10:所有位的位权相加:101 = 1 * 20 + 0 * 21 + 1 * 22
2 转 16:4合1法。把一个二进制数,整数部分从右向左(小数部分从左向右)4位结合成1位,不足部分补0。

16 转 2:1拆4法,16进制的1位拆成二进制的4位。

2 转 8:3合1

8 转 2:1拆3
10 转 8:除8取余
位运算符
位运算:用于整数的二进制位之间的运算。
- 计算机是以其补码的形式存储数据;
- 位运算是补码之间的运算,运算结果是补码;
- 最后要补码 转 原码,根据原码去算十进制数值。
- & 按位与

- | 按位或

- ~ 按位取反

- ^ 按位异或

- << 左移位

- >> 右移位
