Skip to content

容易被忽略的常识

数据类型

基本数据类型描述占用字节数占用位数取值范围默认值包装数据类型缓存池范围
boolean一个比特位的信息1false/truefalseBooleantrue, false
char单个16位Unicode字符216\u0000-\uffffCharacter\u0000 ~ \uu007F
byte8位有符号二进制补码整数18-2^7 ~ 2^7-1 (-128 ~ 127)0Byte-128 ~ 127
short16位有符号二进制补码整数216-2^15 ~ 2^15-10Short-128 ~ 127
int32位有符号二进制补码整数432-2^31 ~ 2^31-10Integer-128 ~ 127
long64位带符号的二进制补码整数864-2^63 ~ 2^63-10LLong-128 ~ 127
float单精度32位IEEE 754浮点数432-2^7 ~ 2^31-10.0FFloat-
double双精度64位IEEE 754浮点数864-2^7 ~ 2^7-10.0DDouble-

包装类型

基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成

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 变为 00 变为 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,负数可能补 01,低位补丢弃,右移运算符为 >>

示例

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

其他。。。