1、包装类都有哪些?

基本类型都有对应的包装类型,这些包装类提供了一种面向对象的方式来处理基本数据类型,允许它们被用于需要对象的场景,如集合框架、泛型等。

对应关系:

基本类型 包装类型
boolean Boolean
byte Byte
char Character
float Float
int Integer
long Long
short Short
double Double

2、包装类特点

  • 封装性:所有的包装类都是 final 类,这意味着它们不能被继承。这种设计确保了包装类的行为和特性的一致性,从而避免了子类可能引入的不确定性。
  • 不可变性:包装类的实例一旦被创建后,其中保存的基本数据类型数据就不能再被改变。这种不可变性使得包装类在多线程环境中更加安全,避免了因数据被意外修改而导致的错误。
  • 提供方法:包装类封装了许多实用的方法,提供了丰富的功能。例如,它们支持数据类型转换、判断字符串的大小写、以及获取最大值和最小值等。
  • 继承关系:除了 Character 和 Boolean 之外,其他所有的包装类都继承自 Number 类。这种继承关系使得这些包装类能够共享一些通用的功能和特性,例如数字的比较和转换,这为在不同数值类型之间的操作提供了一致的接口。

代码为证(继承Number类并实现intValue方法的类):

3、为什么会出现包装类?

既然有基本类型了,为什么还会出现对应的包装类?

我觉得根本原因还是因为Java是面向对象的语言,基本数据类型不能参与面向对象编程:

对象操作:在Java中,许多集合类和框架方法需要对象作为参数,而不是基本数据类型。为了满足这一需求,包装类提供了将基本数据类型转换为对象的机制。通过使用包装类,我们可以轻松地在这些方法中传递基本数据类型。

Null值处理:基本数据类型无法为null,而包装类则可以。这一特性在某些情况下非常有用,例如在方法参数中,需要表示可选值或缺省值时。通过使用包装类,我们能够更灵活地处理这些场景,确保代码的健壮性和可读性。这种设计使得我们在处理数据时,可以更方便地进行null值检查,并在需要时安全地进行区分,从而提高了代码的灵活性。

4、装箱与拆箱

装箱(Boxing):是将基本数据类型转换为相应的包装类的过程。

拆箱(Unboxing):是将包装类转换为基本数据类型的过程。

手动装箱、拆箱

手动装箱:使用一个本地类型的值创建一个对应包装类对象的过程

1 int num = 10;
2
3 Integer int1 = new Integer(num); // 手动装箱方式一
4
5 Integer int2 = Integer.valueOf(num); // 手动装箱方式二

手动拆箱:使用 Integer 类型对象的 intValue() 方法来获取这个对象的 int 值

1 Integer number= new Integer(23);
2 int num = number.intValue(); // 手动拆箱

自动装箱、拆箱

Java 5引入了自动装箱(Auto-boxing)和自动拆箱(Auto-unboxing)机制,简化了基本数据类型与包装类之间的转换过程。

自动装箱是将基本数据类型自动转换为其对应的包装类对象的过程。自动装箱的底层原理其实就是通过调用包装类的valueOf()方法来实现的;同理自动拆箱就是通过调用包装类的xxxValue()方法来实现的。

以Integer 与 int 举例:

 Integer x = 2;     // 装箱 调用了 Integer.valueOf(2)
int y = x; // 拆箱 调用了 x.intValue()

5、包装类的缓存池

简要介绍

Java中的包装类缓存机制是为了优化性能和节省内存而设计的。

它为整型(Byte、Short、Integer、Long)、字符型(Character)和布尔型(Boolean)的包装类提供了缓存,确保在这些类型的小范围值之间可以复用对象。而对于浮点数类型的包装类(Float、Double),则没有这种缓存机制,意味着每次都需要创建新的对象。

这样一来,Java在处理常用值时更加高效,但在浮点数处理上则相对简单直接。

缓存范围

对于 Integer 类,Java会缓存范围在 -128 到 127 之间的所有整数。

对于 Byte、Short 和 Character 类,缓存的范围也是类似的。具体范围如下:

Byte:-128 到 127
Short:-128 到 127
Character:0 到 127(即所有的ASCII字符)
Boolean:只有 true 和 false 两个值会被缓存。

如何触发缓存

只有调用 valueOf() 方法时,如果要创建的值已经被缓存,则会触发缓存机制。如果要创建的 Integer 对象的值在预定范围内,则返回缓存的对象,如果不在范围内,则直接新创建一个对象。

我们来查看 Integer 类的 valueOf() 方法的源代码(valueOf() 方法就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容,不存在就创建一个新对象,跟上面我们所说的逻辑是一致):

 1     /**
2 * Returns an {@code Integer} instance representing the specified
3 * {@code int} value. If a new {@code Integer} instance is not
4 * required, this method should generally be used in preference to
5 * the constructor {@link #Integer(int)}, as this method is likely
6 * to yield significantly better space and time performance by
7 * caching frequently requested values.
8 *
9 * This method will always cache values in the range -128 to 127,
10 * inclusive, and may cache other values outside of this range.
11 *
12 * @param i an {@code int} value.
13 * @return an {@code Integer} instance representing {@code i}.
14 * @since 1.5
15 */
16 public static Integer valueOf(int i) {
17 if (i >= IntegerCache.low && i <= IntegerCache.high)
18 return IntegerCache.cache[i + (-IntegerCache.low)];
19 return new Integer(i);
20 }

编译器会在自动装箱过程调用 valueOf() 方法,因此多个值相同且值在缓存池范围内的 Integer 实例使用自动装箱来创建,那么就会引用相同的对象。

1 Integer m = 123;
2 Integer n = 123;
3 System.out.println(m == n); // true

在 Java 8 中,Integer 缓存池的大小默认为 -128~127。

在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax=<size> 来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定上界。

下面是IntegerCache的源码(其中红色部分正是获取系统属性重新设置上界的逻辑)

 1     private static class IntegerCache {
2 static final int low = -128;
3 static final int high;
4 static final Integer cache[];
5
6 static {
7 // high value may be configured by property
8 int h = 127;
9 String integerCacheHighPropValue =
10 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
11 if (integerCacheHighPropValue != null) {
12 try {
13 int i = parseInt(integerCacheHighPropValue);
14 i = Math.max(i, 127);
15 // Maximum array size is Integer.MAX_VALUE
16 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
17 } catch( NumberFormatException nfe) {
18 // If the property cannot be parsed into an int, ignore it.
19 }
20 }
21 high = h;
22
23 cache = new Integer[(high - low) + 1];
24 int j = low;
25 for(int k = 0; k < cache.length; k++)
26 cache[k] = new Integer(j++);
27
28 // range [-128, 127] must be interned (JLS7 5.1.7)
29 assert IntegerCache.high >= 127;
30 }
31
32 private IntegerCache() {}
33 }

验证包装类的缓存池(以Integer为例)

1 Integer x = new Integer(123);
2 Integer y = new Integer(123);
3 System.out.println(x == y); // false
4 Integer z = Integer.valueOf(123);
5 Integer k = Integer.valueOf(123);
6 System.out.println(z == k); // true

6、补充

基本类型与包装类如何选择

  • 内存占用和性能:基本数据类型直接在栈中分配内存,占用空间较少,性能更高。而包装类是对象类型,需要在堆中分配内存,GC管理,因此会稍微影响性能
  • 使用场景:一般来说,在性能要求较高的代码中,我们优先使用基本数据类型。而在需要面向对象的场景下(例如集合类中需要使用对象类型),我们会选择包装类。包装类还提供了一些静态方法和常量,比如Integer.parseInt()、Double.NaN等,这些方法和属性是基本数据类型所不具备的

基本类型与包装类==比较:

只要判断中有基本数据类型,则判断的就是值是否相等,也就是说包装类在这时会自动拆箱。

1 public class Example {
2 public static void main(String[] args) {
3 Integer i1 = 10;
4 int i2 = 10;
5
6 System.out.println(i1 == i2);
7 }
8 }

基本类型与包装类存储区别(基本类型(primitive types)和包装类的存储位置取决于它们是在哪里声明):

局部变量(Local Variables)

栈(Stack):如果基本类型是作为方法中的局部变量声明的,那么它们会被存储在栈中。栈用于管理方法调用和局部变量,具有后进先出(LIFO)的行为。自动分配和释放:当方法被调用时,其局部变量会在栈上分配空间,并且在方法执行完毕后自动释放。

成员变量(Instance Variables)

堆(Heap):如果基本类型是类的成员变量(即实例变量),那么它们会随着对象一起被存储在堆中。每个对象都有自己的成员变量副本,这些数据与对象本身一同存放在堆内存中。生命周期依赖于对象:成员变量的生命周期与所属的对象相同,只要对象存在,成员变量就存在;对象被垃圾回收时,成员变量也会被回收。

静态变量(Static Variables)

方法区/元空间(Method Area/Metaspace):静态变量(无论是否为基本类型)属于类的一部分,而不是某个特定的对象实例。它们通常存储在方法区(Java 7及之前)或元空间(Java 8及之后)。不过,在某些实现中,静态变量也可能直接存放在堆中,因为方法区本身可以被视为堆的一部分。
共享性:静态变量由所有对象实例共享,因此它们不是随单个对象创建而创建,而是随着类加载到 JVM 时初始化 。

代码举例:

 1 public class Example {
2 // 成员变量,存储在堆中
3 int instanceVar;
4
5 // 静态变量,存储在方法区或元空间
6 static int staticVar;
7
8 public void method() {
9 // 局部变量,存储在栈中
10 int localVar = 10;
11 }
12 }

【Java 温故而知新系列】基础知识-03 基本类型对应之包装类的更多相关文章

  1. Java多线程系列--“基础篇”03之 Thread中start()和run()的区别

    概要 Thread类包含start()和run()方法,它们的区别是什么?本章将对此作出解答.本章内容包括:start() 和 run()的区别说明start() 和 run()的区别示例start( ...

  2. Java多线程系列 基础篇03 线程的优先级和守护线程

    1. 线程优先级 现代操作系统中基本上使用时间分片的方式调度线程,通过设置线程优先级,使优先级高的线程获得时间片的次数多于优先级低的线程. 在java 线程中,通过一个整形变量prority来控制优先 ...

  3. Java多线程系列--“基础篇”04之 synchronized关键字

    概要 本章,会对synchronized关键字进行介绍.涉及到的内容包括:1. synchronized原理2. synchronized基本规则3. synchronized方法 和 synchro ...

  4. Java多线程系列--“基础篇”09之 interrupt()和线程终止方式

    概要 本章,会对线程的interrupt()中断和终止方式进行介绍.涉及到的内容包括:1. interrupt()说明2. 终止线程的方式2.1 终止处于“阻塞状态”的线程2.2 终止处于“运行状态” ...

  5. Java多线程系列--“基础篇”11之 生产消费者问题

    概要 本章,会对“生产/消费者问题”进行讨论.涉及到的内容包括:1. 生产/消费者模型2. 生产/消费者实现 转载请注明出处:http://www.cnblogs.com/skywang12345/p ...

  6. Java多线程系列--“基础篇”05之 线程等待与唤醒

    概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. wait()和notify()3. wait(long t ...

  7. Java多线程系列--“基础篇”06之 线程让步

    概要 本章,会对Thread中的线程让步方法yield()进行介绍.涉及到的内容包括:1. yield()介绍2. yield()示例3. yield() 与 wait()的比较 转载请注明出处:ht ...

  8. Java多线程系列--“基础篇”07之 线程休眠

    概要 本章,会对Thread中sleep()方法进行介绍.涉及到的内容包括:1. sleep()介绍2. sleep()示例3. sleep() 与 wait()的比较 转载请注明出处:http:// ...

  9. Java多线程系列--“基础篇”08之 join()

    概要 本章,会对Thread中join()方法进行介绍.涉及到的内容包括:1. join()介绍2. join()源码分析(基于JDK1.7.0_40)3. join()示例 转载请注明出处:http ...

  10. Java多线程系列--“基础篇”10之 线程优先级和守护线程

    概要 本章,会对守护线程和线程优先级进行介绍.涉及到的内容包括:1. 线程优先级的介绍2. 线程优先级的示例3. 守护线程的示例 转载请注明出处:http://www.cnblogs.com/skyw ...

随机推荐

  1. 为你的Windows用户设置无边框半透明头像

    步骤一:锁定头像 Win+R输入gpedit.msc进入组策略编辑器,按图中所示,选择"计算机管理->管理模板->用户账户设定",将里面唯一选项"对所有账户使 ...

  2. C++之OpenCV入门到提高001:使用 Visual Studio2022 配置 OpenCV 环境

    一.介绍 从今天开始,我们又要开始一个新的系列了,这个系列就是<C++之 Opencv 入门到提高>.这个系列是有关如何使用 C++ 语言,通过 Opencv 来实现图像处理.缺陷检测.视 ...

  3. SpreadJS 在数据填充时的公式填充方案

    需求介绍 很多用户使用了 SpreadJS 的数据填报功能.大致用法为:设计模板,填充数据源.在这个过程中,可能会出现模板中设置了公式,而在数据源填充时,公式没有携带下来的问题. 比如我们定义一个模板 ...

  4. 2.17 新手必看的Linux服务器管理和维护注意事项

    本节介绍有关服务器管理和维护过程中的一些注意事项,都是笔者的经验之谈,相信对新手会有一定的启发和帮助. 很多初学者接触Linux 时间不长,还未完整地学习一遍 Linux,理解本节内容可能有些困难,可 ...

  5. 开源 PHP 商城项目 CRMEB 二次开发和部署教程

    上篇文章给大家介绍了如何使用 Sealos 应用商店一键部署 CRMEB 开源商城系统,那速度真叫一个快啊,比宝塔快多了! 但是有些读者还不满足于此,问我能不能边运行边改代码,而且还得用 Cursor ...

  6. CF716B Complete the Word 题解

    CF716B Complete the Word 题解 分析 首先观察数据范围是 \(50000\),可以考虑 \(O(n)\) 暴力. 在字符串中枚举子串开始的位置 \(i\),然后再枚举 \(i\ ...

  7. element 动态合并表格---进阶版

    错误展示 正确展示 如果使用上次博客写的element 动态合并表格那么会出现上述图片情况,所以对其进行完善实现下图(正确展示)情况 处理函数 mergeTableRow(data, merge) { ...

  8. python3的json数据库-TinyDB效率篇

    安装了这个TinyDB库后,我突然想到一般来说python执行的速度并不算高,那这个库写文件速度如何呢? 测试代码如下: from tinydb import TinyDB import time # ...

  9. Deque的应用案例-回文检查

    7.Deque的应用案例-回文检查 回文检测:设计程序,检测一个字符串是否为回文. 回文:回文是一个字符串,读取首尾相同的字符,例如,radar toot madam. 分析:该问题的解决方案将使用 ...

  10. java应用详解

    java应用详解 文档介绍: 1.nio应用(ServerSocketChannel.FileChannel). 2.优化jvm参数提升eclipse运行速度. 3.maven3.0.3安装及入门例子 ...