深入理解 Java 中的整数类型:int、Integer 和 AtomicIntegerJava 中的整数类型好比高楼的
Java 中的整数类型好比高楼的根基,坚实而基础,为整个程序的稳定和高效运算打下了不可或缺的基石。正如万丈高楼平地起,没有坚固的砖块,就没有屹立不倒的大厦。
一、基本数据类型 int
int
类型是 Java 编程中最常用的数据类型之一,它的设计兼顾了性能和广泛的应用范围,使得它在各种编程任务中都非常适用。
1.1 定义与范围
-
int
是一种 32 位有符号的整数类型,意味着它可以用来存储一个介于-2^31
到2^31-1
之间的整数值。 -
具体来说,
int
类型可以表示的数值范围是从-2,147,483,648
到2,147,483,647
。
1.2 内存占用
int
类型的变量在内存中占用 32 位(4 字节)的空间。这是因为int
类型被设计为固定长度,以确保不同平台和不同架构的计算机之间数据的一致性。
1.3 性能优势
-
访问速度:由于
int
是基本数据类型,它比包装类Integer
的对象表示更接近硬件层面,因此访问速度更快。 -
存储效率:
int
直接存储在栈上或内存的连续空间内,不需要额外的对象头信息,因此在内存使用上更为紧凑。 -
缓存友好:基本数据类型的操作通常更易于被 CPU 缓存优化,因为它们是简单的数据而非复杂的对象。
1.4 使用场景
二、包装类 Integer
包装类(Wrapper Class)是 Java 中的一个概念,用于将基本数据类型包装成对象。这样做的好处是可以使用对象的特性来操作基本数据类型,比如把它们存储在集合中或传递给需要对象参数的方法。
2.1 包装类的概念
- 在 Java 中,基本数据类型不能直接作为对象使用,因为它们没有对象的属性和方法。包装类提供了一种机制,将基本数据类型转换成对象,这样就可以使用面向对象的特性。
2.2 Integer 类的特点
-
Integer
是int
基本数据类型的包装类,它提供了一个int
类型的字段来存储数据。 -
Integer
类实现了Comparable
接口,可以比较两个Integer
对象的大小。 -
Integer
类是不可变的,一旦创建了Integer
对象,它的值就不能被更改。
2.3 自动装箱与拆箱
-
自动装箱(Autoboxing):在 Java 5 及以后的版本中,可以将基本数据类型的
int
直接赋值给Integer
类型的对象,编译器会自动将int
转换为Integer
对象。 -
自动拆箱(Unboxing):相反,也可以将
Integer
对象直接赋值给int
类型的变量,编译器会自动将Integer
对象转换为int
类型。
2.4 缓存机制(Integer.valueOf 和 Integer.intValueOf)
-
Integer
类型有一个缓存机制,用于提高性能。Integer.valueOf(int i)
方法会使用一个缓存好的Integer
对象,如果输入的int
值在 -128 到 127 之间(包含),则会直接返回一个缓存中的对象,而不是每次都创建一个新的对象。 -
这个缓存机制是通过
IntegerCache
实现的,它是Integer
类的一个内部类。
2.5 与 int 的相互转换
-
从
Integer
到int
:可以通过调用Integer
对象的intValue()
方法来获取其对应的int
值。 -
从
int
到Integer
:可以直接将int
类型的值赋给Integer
类型的对象,或者使用Integer.valueOf(int i)
静态方法来创建一个新的Integer
对象。
int num = 100; // 基本数据类型
Integer intObj = Integer.valueOf(num); // 自动装箱
System.out.println(intObj); // 输出 Integer 对象
int numInt = intObj.intValue(); // 自动拆箱
System.out.println(numInt); // 输出 int 值
三、原子类 AtomicInteger
原子类 AtomicInteger
是 Java 中一个非常关键的并发工具,它提供了一种在多线程环境下对整数进行原子操作的方式。
3.1 原子操作的概念
原子操作是指在一个步骤中完成的操作,这个操作要么完全执行,要么完全不执行,不会出现中间状态,从而保证了在多线程环境中数据的一致性和线程安全性
。
3.2 AtomicInteger 的用途
AtomicInteger
主要用于多线程环境中,当需要对一个整数进行自增、自减或其他数值操作时,能够保证这些操作是原子性的。这使得 AtomicInteger
成为实现计数器或在没有线程间通信时控制变量更新的理想选择
3.3 基本操作:get、set、incrementAndGet 等
AtomicInteger
的官方文档参考AtomicInteger。
它的一些核心方法如下:
int | accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) 使用将给定函数应用于当前值和给定值的结果原子更新当前值,返回更新后的值。 |
---|---|
int | addAndGet(int delta) 将给定的值原子地添加到当前值。 |
boolean | compareAndSet(int expect, int update) 如果当前值 == 为预期值,则将该值原子设置为给定的更新值。 |
int | decrementAndGet() 原子减1当前值。 |
double | doubleValue() 返回此值 AtomicInteger 为 double 一个宽元转换后。 |
float | floatValue() 返回此值 AtomicInteger 为 float 一个宽元转换后。 |
int | get() 获取当前值。 |
int | getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) 使用给定函数应用给当前值和给定值的结果原子更新当前值,返回上一个值。 |
int | getAndAdd(int delta) 将给定的值原子地添加到当前值。 |
int | getAndDecrement() 原子减1当前值。 |
int | getAndIncrement() 原子上增加一个当前值。 |
int | getAndSet(int newValue) 将原子设置为给定值并返回旧值。 |
int | getAndUpdate(IntUnaryOperator updateFunction) 用应用给定函数的结果原子更新当前值,返回上一个值。 |
int | incrementAndGet() 原子上增加一个当前值。 |
int | intValue() 将 AtomicInteger 的值作为 int 。 |
void | lazySet(int newValue) 最终设定为给定值。 |
long | longValue() 返回此值 AtomicInteger 为 long 一个宽元转换后。 |
void | set(int newValue) 设置为给定值。 |
String | toString() 返回当前值的String表示形式。 |
int | updateAndGet(IntUnaryOperator updateFunction) 使用给定函数的结果原子更新当前值,返回更新的值。 |
boolean | weakCompareAndSet(int expect, int update) 如果当前值 == 为预期值,则将值设置为给定更新值。 |
3.4 线程安全特性
AtomicInteger
的实现基于 volatile
关键字和 Unsafe
类提供的硬件级别的原子操作。volatile
确保了变量的内存可见性,而 Unsafe
类的 compareAndSwapInt
方法则实现了无锁的原子操作。
3.5 与 synchronized 的比较
与 synchronized
相比,AtomicInteger
使用了乐观锁策略,它不会让线程在获取不到锁时阻塞,而是通过循环尝试的方式直到操作成功。
这种机制在竞争不激烈的情况下通常比 synchronized
有更好的性能表现。
然而,世事无绝对,在高竞争环境下,synchronized
可能因为其锁机制的优化(如偏向锁、轻量级锁等)而表现更好。
四、int、Integer 和 AtomicInteger 的比较
特性/类型 | int | Integer | AtomicInteger |
---|---|---|---|
使用场景 | 通常用于表示数值,适用于简单的数值计算和数组索引 | 是 int 的包装类,适用于需要使用对象特性的场合,如存储在集合中或作为方法的参数 | 用于多线程环境中的原子类,适用于需要原子操作的计数器或其他数值,如实现锁和同步机制 |
性能考量 | 作为基本数据类型,在性能上通常是最高的,直接存储数值,没有额外的对象开销 | 性能相对较低,涉及对象的创建和垃圾回收,频繁的自动装箱拆箱可能带来性能开销 | 在多线程环境下性能较高,避免使用传统同步机制,但单线程性能可能不如 int |
线程安全性 | 基本数据类型,不存在线程安全问题,但多线程环境可能产生竞争条件 | 不可变的,创建后值不可变,线程安全;但多个线程修改同一引用需外部同步 | 提供原子操作,无需额外同步即可保证线程安全 |
内存占用 | 占用固定的内存空间(32位系统上为4字节) | 除了存储值,还包含对象头和其他元数据,占用更多内存 | 包含值和实现原子操作的额外状态或控制信息,可能比 Integer 占用更多内存 |
五、总结
总结一下今天的主角们:int
、Integer
和 AtomicInteger
。
- int:它就像是一位老派的银行家,穿着经典的三件套西装,一丝不苟地管理着数字世界。它快速、精确,但略显古板,不懂变通。
- Integer:这位是银行家的侄子,穿着时尚,喜欢社交。他可以轻松地融入各种派对(对象集合),并且总是乐于接受新的角色(方法参数)。
- AtomicInteger:现在,这位是穿着高科技装备的忍者,悄无声息地在多线程的高楼大厦间穿梭。它行动迅速,总是在不被发现的情况下完成任务。
在性能的赛道上,int
以其轻装上阵的优势一马当先。Integer
虽然华丽,但身上的装饰(对象头信息)让它跑得慢了一些。而 AtomicInteger
,虽然装备了最尖端的科技,但在单线程的直线赛道上,它那些复杂的装备反而成了累赘。
在线程安全的堡垒中,int
就像一座没有守卫的金库,容易受到攻击。Integer
虽然坚固,但如果多条线程都想进去,还是需要外部的守卫(同步)来维持秩序。AtomicInteger
则是那座配备了最先进安全系统的金库,即使在最混乱的场面中也能保持安全。
最后,让我们以一个笑话结束这场舞会:为什么 AtomicInteger
从来不去健身房?因为它已经足够核心了!
希望本文对您有所帮助。如果有任何错误或建议,请随时指正和提出。
同时,如果您觉得这篇文章有价值,请考虑点赞和收藏。这将激励我进一步改进和创作更多有用的内容。
感谢您的支持和理解!
转载自:https://juejin.cn/post/7411168497466916900