Appearance
容易被忽略的常识
数据类型
基本数据类型 | 描述 | 占用字节数 | 占用位数 | 取值范围 | 默认值 | 包装数据类型 | 缓存池范围 |
---|---|---|---|---|---|---|---|
boolean | 一个比特位的信息 | 1 | false/true | false | Boolean | true, false | |
char | 单个16位Unicode字符 | 2 | 16 | \u0000-\uffff | 无 | Character | \u0000 ~ \uu007F |
byte | 8位有符号二进制补码整数 | 1 | 8 | -2^7 ~ 2^7-1 (-128 ~ 127) | 0 | Byte | -128 ~ 127 |
short | 16位有符号二进制补码整数 | 2 | 16 | -2^15 ~ 2^15-1 | 0 | Short | -128 ~ 127 |
int | 32位有符号二进制补码整数 | 4 | 32 | -2^31 ~ 2^31-1 | 0 | Integer | -128 ~ 127 |
long | 64位带符号的二进制补码整数 | 8 | 64 | -2^63 ~ 2^63-1 | 0L | Long | -128 ~ 127 |
float | 单精度32位IEEE 754浮点数 | 4 | 32 | -2^7 ~ 2^31-1 | 0.0F | Float | - |
double | 双精度64位IEEE 754浮点数 | 8 | 64 | -2^7 ~ 2^7-1 | 0.0D | Double | - |
包装类型
基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成
java
// 装箱
Integer x = 2;
// 拆箱
int y = x;
缓存池
- new Integer(123) 每次都会创建新对象
- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用
java
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
Integer.valueOf() 方法的实现:
java
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
在 Java8 中,Integer 缓存池的大小默认为 -128~127
java
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
编译器会 在缓存池范围内的基本类型 自动装箱过程调用 valueOf() 方法,多个 Integer 实例使用自动装箱来创建并且值相同,就会引用相同的对象
java
Integer m = 123;
Integer n = 123;
System.out.println(m == n); // true
Integer m = 323;
Integer n = 323;
System.out.println(m == n); // false
位运算
计算机对数据都是以二进制(0、1)的方式存储的,位运算是将数据在内存中已二进制的方式进行运算,所以可以获得非常高的执行效率,但缺点是可读写差
二进制原码、反码、补码
反码和补码是为了解决负数的位运算问题
原码:正数的最高位为 0,负数的最高位为 1
反码:负数的反码,最高位不变,其他位反转,即 1 变为 0,0 变为 1,在位计算完成后再反转回来,正数的反码同原码
补码:负数的补码 = 负数的绝对值取反 + 1,正数的补码同原码
位运算符
按位与 &
将参与运算的两数对应的二进制位相与,当对应的二进制位均为 1
时,结果为 1
,否则结果为 0
,运算符为 &
,以补码方式参与运算
示例:
8 & 5 = 0
0000 1000
&
0000 0101
---- ----
0000 0000
按位或 |
将参与运算的两数对应的二进制位相或,当对应的二进制位有一个为 1
时,结果为 1
,否则结果为 0
,运算符为 |
,以补码方式参与运算
示例
3 & 7 = 7
0000 0011
|
0000 0111
---- ----
0000 0111
按位异或 ^
将参与运算的两数对应的二进制位相异或,当对应的二进制位不同时,结果为 1
,否则结果为 0
,运算符为 ^
,以补码方式参与运算
示例
12 ^ 7 = 11
0000 1100
^
0000 0111
---- ----
0000 1011
按位取反 ~
将参与运算的数对应的二进制位反正,即 1
变为 0
,0
变为 1
,运算符为 ~
,以补码方式参与运算
示例
~9 = -10
~0000 1001
---- ----
1111 0110
负数还原要取反再加 1
1000 1001
1000 1010
-10
左移运算
将数对应的二进制位全部向左移动若干位,高位丢弃,低位补 0
,左移运算符为 <<
示例
3 << 2 = 3 * 4 = 12
0000 0011
<< 2
0000 1100
12
右移运算
将数对应的二进制位全部向右移动若干位,高位正数补 0
,负数可能补 0
或 1
,低位补丢弃,右移运算符为 >>
示例
80 >> 3 = 10
64+16
0101 0000
>> 3
0000 1010
10
应用
判断奇偶数
奇数二进制最后一位为 1,偶数二进制最后一位为 0
java
@Test
public void testOdd() {
// int a = 10;
int a = 9;
if ((a & 1) == 1) {
System.out.println("奇数");
} else {
System.out.println("偶数");
}
}
变量交换
java
@Test
public void testSwap() {
int a = 3, b = 5;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println(a);
System.out.println(b);
}
计算 x * 2 的 n 次方
即左移 x << n
代替地板除
即右移 x >> n
取 x 的第 k 位
即取数字 x 对应的二进制的第 k 位上的二进制值
x >> k & 1
java
@Test
public void testMod() {
// 5
// 0000 0101
int x = 5;
for (int i = 0; i < 8; i++) {
System.out.println(x >> i & 1);
}
}
字符编码
todo