JAVA包装类

2023-05-16

什么是包装类

虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性、没有方法可调用。 沿用它们只是为了迎合人类根深蒂固的习惯,并的确能简单、有效地进行常规数据处理。这种借助于非面向对象技术的做法有时也会带来不便。比如:

  1. 编码过程中只接收对象的情况,比如List中只能存入对象,不能存入基本数据类型;比如一个方法的参数是Object时,不能传入基本数据类型,但可以传入对应的包装类; 比如泛型等等。
  2. 基本数据类型没有toString()方法等

Java中对每种基本类型都有一个对应的包装类:

  • byte -> Byte
  • short -> Short
  • int -> Integer
  • long -> Long
  • float -> Float
  • double -> Double
  • boolean -> Boolean
  • char -> Character
    其中需要注意int对应的是Integer,char对应的Character,其他6个都是基本类型首字母大写即可。

每个包装类的对象可以封装一个相应的基本类型的数据,并提供了其它一些有用的方法。包装类对象一经创建,其内容(所封装的基本类型数据值)不可改变(类似于String类,可看源码)。

基本数据类型和包装类的区别

  1. 定义不同。包装类属于对象,基本数据类型不是
  2. 声明和使用方式不同。包装类使用new初始化,有些集合类的定义不能使用基本数据类型,例如 ArrayList<Integer>
  3. 初始值不同。包装类默认值为null,基本数据类型则不同的类型不一样(具体见上表)
  4. 存储方式和位置不同,从而性能不同。基本数据类型存储在栈(stack)中,包装类则分成引用和实例,引用在栈(stack)中,具体实例在堆(heap)中。可以通过程序来验证速度的不同。

拆箱/装箱

基本类型和对应的包装类可以相互装换:

  • 由基本类型向对应的包装类转换称为装箱,例如把 int 包装成 Integer 类的对象:
Integer i = Integer.valueOf(1); //手动装箱
Integer j = 1; //自动装箱
  • 包装类向对应的基本类型转换称为拆箱,例如把 Integer 类的对象重新简化为 int。
Integer i0 = new Integer(1);
int i1 = i0; //自动拆箱
int i2 = i0.intValue(); //手动拆箱

jdk5.0开始增加自动装箱/拆箱

面试陷阱

  介绍完包装类的基本情况,下面重点讲几个面试/笔试中的常见陷阱。

  1. 关于equals
double i0 = 0.1;
Double i1 = new Double(0.1);
Double i2 = new Double(0.1);
System.out.println(i1.equals(i2)); //true 2个包装类比较,比较的是包装的基本数据类型的值
System.out.println(i1.equals(i0)); //true 基本数据类型和包装类型比较时,会先把基本数据类型包装后再比较

注意equals方法比较的是真正的值

基本数据类型和包装类比较时,会先把基本数据类型包装成对应的包装类型,再进行比较。这一点可以通过查看.class字节码来证明:

为什么需要这么做? 我们来查看JDK中Double包装类的equals方法:

可以看到equals方法中的参数是一个Object对象,而基本数据类型并不是一个对象类型,所以需要先将基本数据类型包装成对应的包装类后才能作为参数传入equals方法中。由此也侧面验证了包装类的必要性。

2. 关于==(双等号)比较

==(双等号)比较的陷阱比较多。对此有必要先讲一个前置知识点:
对于基本数据类型,==(双等号)比较的是值,而对于包装类型,==(双等号)比较的则是2个对象的内存地址。

OK,下面开始讲题:

double i0 = 0.1;
Double i1 = new Double(0.1);
Double i2 = new Double(0.1);
System.out.println(i1 == i2);    //false new出来的都是新的对象
System.out.println(i1 == i0);    //true 基本数据类型和包装类比较,会先把包装类拆箱

注意一点规律,new出来的都是新的对象。2个新的对象内存地址不同,那么==号比较的结果肯定是false。
基本数据类型和包装类型比较时,会先把包装类拆箱再进行值比较(和equals是反的)。这个也可以通过查看字节码来证明:

为什么要这么做?因为如果反过来把基本数据类型进行包装后再比较,那么2个包装类对象的内存地址是不一样的,==号比较肯定一直为false!

继续来看另一个陷阱:  

Double i1 = Double.valueOf(0.1);
Double i2 = Double.valueOf(0.1);
System.out.println(i1 == i2); //false valueOf方法内部实际上也是new

注意这里i1和i2是用包装类的静态方法valueOf来构造的。我们看一下Double.valueOf的源码:

可以看到,valueOf内部也是用的new方法来构造对象的。2个new出来的对象,内存地址肯定是不一样的。

那么问题来了,下面的代码运行结果却让人大跌眼镜:  

System.out.println(Integer.valueOf(1) ==Integer.valueOf(1)); //true 
System.out.println(Integer.valueOf(999) ==Integer.valueOf(999)); //false 

是不是感觉不会再爱了?
  没关系,我们还是从源码来分析问题。查看Integer.valueOf方法的源码:

可以看到,当i的值在low和high范围内是,返回的是一个cache里的Integer对象。
我们继续追踪IntegerCache.cache的源码:

可以看到从-128到到127之间的值都被缓存到cache里了。这个叫做包装类的缓存
Java对部分经常使用的数据采用缓存技术,在类第一次被加载时换创建缓存和数据。当使用等值对象时直接从缓存中获取,从而提高了程序执行性能。(通常只对常用数据进行缓存)
Integer默认缓存是-128到127之间的对象,上限127可以通过修改JVM参数来更改。
回到我们的示例代码中,可以看到Integer.valueOf(1)是会直接从缓存中去取被缓存的同一个对象,所以比较的值是true。而Integer.valueOf(999)是缓存范围外的值,那么就会new一个新的对象出来,所以比较的结果是false。
实际上,包装类中整数型的类型基本上都有缓存数据。可以通过阅读源码了解。不过只有Integer类可以通过修改JVM参数来更改缓存上限。
其他类型的缓存范围为:

  1. Integer类型有缓存-128-127的对象。缓存上限可以通过配置jvm更改
  2. Byte,Short,Long类型有缓存(-128-127)
  3. Character缓存0-127
  4. Boolean缓存TRUE、FALSE

    尤其需要特别注意的是,只有valueOf方法构造对象时会用到缓存,new方法等不会使用缓存!

了解了缓存机制,那么再看下面这道题目:

Integer i4 = Integer.valueOf(1);
Integer i5 =1;
System.out.println(i4 == i5); //true

Integer i7 = Integer.valueOf(999);
Integer i8 = 999;
System.out.println(i7 == i8); //false

是不是觉得脑仁疼?
没关系,我们看一下上面自动包装时的class字节码:

发现什么了? 自动包装时实际上还是调用的valueOf方法。而上面我们讲过的,valueOf方法用到了缓存池。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA包装类 的相关文章

  • Java中字符串中特殊字符的替换

    Java中如何替换字符串 E g String a adf sdf 如何替换和避免特殊字符 您可以删除除此之外的所有字符可打印的 ASCII 范围 http en wikipedia org wiki ASCII ASCII printab
  • Eclipse 在源代码管理中保存操作

    我们希望找到一种在签入之前执行代码标准的 轻量级 方法 我们真的很喜欢使用 Eclipse 内置的想法保存操作 go to Preferences gt gt Java gt gt Editor gt gt Save Actions 其中有
  • 如何使用 Java 处理 Selenium WebDriver 中的新窗口?

    这是我的代码 driver findElement By id ImageButton5 click Thread sleep 3000 String winHandleBefore driver getWindowHandle drive
  • Java AES 128 加密方式与 openssl 不同

    我们遇到了一种奇怪的情况 即我们在 Java 中使用的加密方法会向 openssl 生成不同的输出 尽管它们在配置上看起来相同 使用相同的键和 IV 文本 敏捷的棕色狐狸跳过了懒狗 加密为 Base64 字符串 openssl A8cMRI
  • 比较两个文本文件的最快方法是什么,不将移动的行视为不同

    我有两个文件非常大 每个文件有 50000 行 我需要比较这两个文件并识别更改 然而 问题是如果一条线出现在不同的位置 它不应该显示为不同的 例如 考虑这个文件A txt xxxxx yyyyy zzzzz 文件B txt zzzzz xx
  • JAVA - Xuggler - 组合 MP3 音频文件和 MP4 电影时播放视频

    使用 JAVA 和 Xuggler 以下代码组合 MP3 音频文件和 MP4 电影文件并输出组合的 mp4 文件 我希望在合并音频和视频文件时应自动播放输出视频文件 String inputVideoFilePath in mp4 Stri
  • wait() 在游戏中如何工作?

    在 playframework 的文档中here http www playframework org documentation 1 2 1 asynchronous已写 public static void loopWithoutBlo
  • 如何在不超过最大值的情况下增加变量?

    我正在为学校开发一个简单的视频游戏程序 我创建了一个方法 如果调用该方法 玩家将获得 15 点生命值 我必须将生命值保持在最大值 100 并且由于我目前的编程能力有限 我正在做这样的事情 public void getHealed if h
  • 如何安全地解决这个 Java 上下文类加载器问题?

    我的数百名用户中只有一位在启动我的 Java 桌面应用程序时遇到问题 他只有大约三分之一的时间开始 另外三分之二的时间在启动时抛出 NullPointerException Exception in thread AWT EventQueu
  • 画透明圆,外面填充

    我有一个地图视图 我想在其上画一个圆圈以聚焦于给定区域 但我希望圆圈倒转 也就是说 圆的内部不是被填充 而是透明的 其他所有部分都被填充 请参阅这张图片了解我的意思 http i imgur com zxIMZ png 上半部分显示了我可以
  • hibernate锁等待超时超时;

    我正在使用 Hibernate 尝试模拟对数据库中同一行的 2 个并发更新 编辑 我将 em1 getTransaction commit 移至 em1 flush 之后我没有收到任何 StaleObjectException 两个事务已成
  • Calendar.getInstance(TimeZone.getTimeZone("UTC")) 不返回 UTC 时间

    我对得到的结果真的很困惑Calendar getInstance TimeZone getTimeZone UTC 方法调用 它返回 IST 时间 这是我使用的代码 Calendar cal Two Calendar getInstance
  • react-native run-android 失败并出现错误:任务 ':app:dexDebug' 执行失败

    我使用的是 Windows 8 1 和react native cli 1 0 0 and react native 0 31 0 添加后react native maps对于该项目 我运行了命令react native upgrade并给
  • 欧洲中部时间 14 日 3 月 30 日星期五 00:00:00 至 日/月/年

    我尝试解析格式日期Fri Mar 30 00 00 00 CET 14至 日 月 年 这是我的代码 SimpleDateFormat formatter new SimpleDateFormat dd MM yyyy System out
  • Struts 2 + Sitemesh 3 集成 - FreemarkerDecoratorServlet 中的 NPE

    我将 Struts 2 版本 2 3 14 3 与 Sitemesh 3 版本 3 0 alpha 2 一起使用 并且在某些情况下遇到 NullPointerException 首先 这是我的 web xml 中的 struts2 site
  • 测试弱引用

    在 Java 中测试弱引用的正确方法是什么 我最初的想法是执行以下操作 public class WeakReferenceTest public class Target private String value public Targe
  • HQL Hibernate 内连接

    我怎样才能在 Hibernate 中编写这个 SQL 查询 我想使用 Hibernate 来创建查询 而不是创建数据库 SELECT FROM Employee e INNER JOIN Team t ON e Id team t Id t
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String
  • 调整添加的绘制组件的大小和奇怪的摆动行为

    这个问题困扰了我好几天 我正在制作一个特殊的绘画程序 我制作了一个 JPanel 并添加了使用 Paint 方法绘制的自定义 jComponent 问题是 每当我调整窗口大小时 所有添加的组件都会 消失 或者只是不绘制 因此我最终会得到一个
  • GUI Java 程序 - 绘图程序

    我一直试图找出我的代码有什么问题 这个想法是创建一个小的 Paint 程序并具有红色 绿色 蓝色和透明按钮 我拥有我能想到的让它工作的一切 但无法弄清楚代码有什么问题 该程序打开 然后立即关闭 import java awt import

随机推荐

  • 在 Ubuntu 18.04 上安装 SmartGit

    在开始安装之前 xff0c 很有趣 确保我们系统中的所有软件包都是最新的 为此 xff0c 在终端 Ctl 43 Alt 43 T 中 xff0c 我们只需编写 xff1a 1 sudo apt update sudo apt upgrad
  • 复工第一事:干掉 Notepad++

    点击上方 芋道源码 xff0c 选择 设为星标 管她前浪 xff0c 还是后浪 xff1f 能浪的浪 xff0c 才是好浪 xff01 每天 10 33 更新文章 xff0c 每天掉亿点点头发 源码精品专栏 原创 Java 2021 超神之
  • Ubuntu18.04编译Android8.0系统源码

    首先 需要一个台式电脑 xff0c 有个i7处理器 xff0c 有一个1T的机械 43 500G的固态 xff0c 如果条件允许改一个服务器也可以 我这里是一个台式电脑 在台式电脑上安装一个虚拟机 xff0c 基本是使用的VMware xf
  • m, mm以及mmm编译命令以及make snod的使用

    1 xff09 编译指定Package Android源码目录下的build envsetup sh文件 xff0c 描述编译的命令 croot 切到Android源码树的根目录 当你深入Android源码树的子目录 xff0c 想回到根目
  • 复杂条件逻辑的梳理

    为什么会感觉有些需求无从下手 在产品需求梳理或者业务逻辑调研阶段 xff0c 有时会遇到产品需求无从下手的问题 xff0c 分析下来 xff0c 一般情况如下 xff1a 需求边界不明确 xff0c 输入和输出的界定不清晰 xff0c 无法
  • Android源码刷机步骤

    打开OEM开关 xff1a 先点击设置 关于手机 版本号七次 开发者选项 打开OEM解锁 xff08 这步必须可以上网 xff0c 否则打不开 xff09 进入bootloader页面 使用方法1必须安装adb platform tools
  • Android Studio导入和调试Android8.0源码

    生成IDE相关文件 idegen专门为IDE环境调试源码而设计的工具 xff0c 依次执行如下命令 xff1a source build envsetup sh mmm development tools idegen developmen
  • make snod注意事项-刷机后启动异常

    1 正确执行顺序 需要执行 source build envsetup sh lunch 2 单独编译 xff0c 刷机后运行异常 全编andorid后 xff0c 单独修改编译一个framwork模块 xff0c make snod会有如
  • adb remount 系统提示只读文件系统Read-only file system,解决用adb disable-verity

    在Android6 0 xff08 Android M xff09 userdebug版本上 eng版本不存在该问题 xff0c 发现使用adb remount 系统之后 xff0c 还是不能对system分区进行操作 xff0c 提示没有
  • 枚举 switchcase 标签必须为枚举常量的非限定名称

    enum switch case label must be the unqualified name of an enumeration constant 或 错误 枚举 switchcase 标签必须为枚举常量的非限定名称case Co
  • VMware为什么会越用占用的内存越大?该如何清理?

    现象描述 xff1a VMware用了一段时间后发现原来刚开始只占5G左右的内存 xff0c 慢慢的会占用几十个G xff0c 甚至更多 xff0c 磁盘空间占用越来越大 解决办法 xff1a 虚拟机内部执行cat dev zero gt
  • H264中的时间戳(DTS和PTS)

    xff08 1 xff09 Ffmpeg中的DTS 和 PTS H264里有两种时间戳 xff1a DTS xff08 Decoding Time Stamp xff09 和PTS xff08 Presentation Time Stamp
  • UEFI/Legacy两种启动模式下安装Win10/Ubuntu双系统

    文章目录 更多操作细节请移步到 UEFI Legacy两种启动模式下安装Win10 Ubuntu双系统 http www aigrantli com archives uefilegacy E4 B8 A4 E7 A7 8D E5 90 A
  • H264视频编码原理

    一 为什么要对视频编码 视频是由一帧帧的图像组成 xff0c 就像gif图片一样 一般视频为了不会让人感觉到卡顿 xff0c 一秒钟至少需要16帧画面 一般30帧 加入该视频是一个1280x720的分辨率 xff0c 那么不经过编码一秒钟传
  • H.264基础知识总结

    H264是视频编解码格式 xff1b 学习H264之前首先要搞明白一个问题 xff0c 视频为什么要编码 xff0c 编码传输不行吗 xff1f 视频就是一堆图片按时间顺序播放 xff0c 在编码标准出现之前 xff0c 不经过编码的原始码
  • linux文件分割(将大的日志文件分割成小的)

    linux文件分割 xff08 将大的日志文件分割成小的 xff09 linux下文件分割可以通过split命令来实现 xff0c 可以指定按行数分割和安大小分割两种模式 Linux下文件合并可以通过cat命令来实现 xff0c 非常简单
  • 华为AGC性能管理功能sdk集成

    集成SDK 1 xff09 在AGC网站的我的项目中选择需要启用性能管理的应用 xff0c 点击质量 gt 性能管理 xff0c 进入性能管理服务页面 xff0c 立即开通服务 2 xff09 添加AGC插件 xff0c 在Android
  • Android平台集成华为AGC性能管理服务问题处理指南

    最近尝试集成了华为AGC的性能管理服务 xff0c 集成过程中也遇到一些问题 本文就对我在集成性能管理服务的踩坑记录进行总结 xff0c 希望能帮到大家 问题一 xff1a 刚集成性能管理服务 xff0c 报错miss client id
  • Android ANR全解析&华为AGC性能管理解决ANR案例集

    1 ANR介绍 1 1 ANR是什么 ANR xff0c 全称为Application Not Responding xff0c 也就是应用程序无响应 如果 Android 应用的界面线程处于阻塞状态的时间过长 xff0c 就会触发 应用无
  • JAVA包装类

    什么是包装类 虽然 Java 语言是典型的面向对象编程语言 xff0c 但其中的八种基本数据类型并不支持面向对象编程 xff0c 基本类型的数据不具备 对象 的特性 不携带属性 没有方法可调用 沿用它们只是为了迎合人类根深蒂固的习惯 xff