包装类

  1. Integer是int对应的包装类,里面有一个int类型的字段存储数据,并提供了基本的操作
  2. 在Java 5,引入了自动装箱自动拆箱(boxing/unboxing),Java可以根据上下文,自动进行转换
  3. 在Java 5,还引入了值缓存(静态工厂方法valueOf),默认缓存范围为**-128 ~ 127**
    • Boolean,缓存Boolean.TRUE/Boolean.FALSE
    • Short,缓存**-128 ~ 127**
    • Byte,数值有限,全部缓存
    • Character,缓存\u0000 ~ \u007F

自动装箱 + 自动拆箱

1
2
Integer integer = 1;
int unboxing = integer++;
1
2
3
4
5
6
1: invokestatic     // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
8: invokevirtual // Method java/lang/Integer.intValue:()I
11: iconst_1
12: iadd
13: invokestatic // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
21: invokevirtual // Method java/lang/Integer.intValue:()I
  1. 自动装箱和自动拆箱是一种语法糖,发生在编译阶段(生成一致的字节码
  2. javac自动把装箱转换为Integer.valueOf(可以利用值缓存机制),把拆箱转换为Integer.intValue
  3. 性能敏感的场合,要尽量避免无意中的自动装箱和自动拆箱;但在大多数产品代码里,还是以开发效率优先

线程安全的计数器

原子类实现

1
2
3
4
5
6
7
8
// 简单明了
public class Counter {
private final AtomicLong counter = new AtomicLong();

public void increase() {
counter.incrementAndGet();
}
}

原始类型实现

1
2
3
4
5
6
7
8
9
10
// 复杂
public class CompactCounter {
private volatile long counter;
private static final AtomicLongFieldUpdater<CompactCounter> UPDATER =
AtomicLongFieldUpdater.newUpdater(CompactCounter.class, "counter");

public void increase() {
UPDATER.incrementAndGet(this);
}
}

不变类

1
private final int value;

BYTES

1
2
3
4
5
6
// Integer
@Native public static final int SIZE = 32;
public static final int BYTES = SIZE / Byte.SIZE;

// Byte
public static final int SIZE = 8;

原始类型的线程安全

  1. 原始类型的变量,需要使用并发相关手段,才能保证线程安全
  2. 如果有线程安全的计算需要,优先考虑AtomicInteger、AtomicLong等线程安全类
  3. 部分比较宽的数据类型,如float、double,都不能保证更新操作的原子性(可能读到只更新了一半数据位的数值)

局限性

  1. 原始类型与Java泛型不能配合使用
    • Java的泛型是伪泛型,属于编译期的技巧(_类型擦除+强制转换_)
    • 原始类型无法转换为Object,因此无法与泛型配合使用
  2. 无法高效表达数据
    • 原始类型数组,在内存里是一段连续的内存
    • 引用类型数组,存储的是引用,实际的对象分散在堆里,导致低效的数据操作,也无法充分利用CPU缓存