java包装类型详解

一、用途


在Java语言中,包装类型(Wrapper Classes)是一种特殊的类,它们将八个基本数据类型(byte、short、int、long、float、double、char、boolean)封装在一个类中。这些包装类(如Integer、Long、Double、Character等)在java.lang包中定义,提供了一系列实用的类方法和属性。尽管在很多情况下使用基本数据类型就足够了,包装类型的存在却对Java编程模式和功能的扩展至关重要。下面详细探讨Java为什么需要包装类型。

1. 对象需求

Java是一种面向对象的编程语言,这意味着操作的基本单位是对象。包装类允许程序员将基本数据类型当作对象来处理。这在编程中是非常有用的,尤其是当你需要将基本类型作为参数传递给期望对象的方法,或者需要将它们放入集合类中时。

2. 集合框架

Java的集合框架(如ArrayList、HashMap等)只能存储对象,不能存储基本数据类型。包装类解决了这一限制,使基本类型可以被存储在这些集合中。例如,要在ArrayList中存储整数,你必须使用Integer类,而不是int基本类型。

3. 提供更多方法

包装类提供了许多有用的方法来操作基本类型的数据。例如,Integer类提供了将字符串转换为整数的方法,还提供了处理整数时常用的常量和方法,如最大值、最小值等。

4. Null值的支持

基本数据类型不支持null值(它们总是有一个实际的值,比如int的默认值是0),但是在很多应用场景中,表示一个变量没有值的能力是必需的。包装类通过允许引用为null来提供这种可能性,这在数据库交互和错误处理中尤其有用。

5. 泛型支持

Java的泛型在编译时不支持基本数据类型。例如,你不能创建ArrayList<int>的实例。包装类使得可以通过使用ArrayList<Integer>来间接支持基本类型。

6. 类型安全

包装类提高了程序的类型安全,可以在编译时检测到类型错误,减少运行时的错误。这种类型检查对于构建大型、可维护的系统至关重要。

7. 自动装箱与拆箱

Java 5 引入了自动装箱与拆箱机制,使得基本类型与对应的包装类型之间可以自动转换。例如,当你将一个int赋值给一个Integer对象时,Java自动将int装箱为Integer。同样,当你将一个Integer对象赋值给一个int变量时,Java会自动拆箱。这简化了在需要对象时使用基本类型的语法。

应用案例--控制器方法

在Spring MVC中,控制器方法经常使用包装类型(如 IntegerBoolean)而不是基本类型(如 intboolean)来接收数据的原因与包装类型的特性和Web开发的需求紧密相关。下面详细探讨为什么在Spring MVC的控制器方法中更倾向于使用包装类型。

1. 允许Null值

Web应用常常需要处理来自用户输入或者外部系统的数据,这些数据可能是不完整的或者部分缺失的。基本类型如 intboolean 不能接受 null 值,它们有默认的值(如 int 的0,boolean 的false),这在某些情况下会引入潜在的错误或误解。例如,如果一个表单中的整数字段未被填写,将其映射为基本类型 int 将会得到0,这可能与用户忘记填写该字段的意图不符。使用包装类型 Integer,未填写的字段可以保持为 null,这样程序可以明确区分“未填写”和“填写了0”。

2. 类型安全

使用包装类型增加了类型安全,特别是在处理可能为 null 的数据时。这允许开发者在逻辑处理中显式检查 null,从而根据不同的情况执行不同的操作或显示不同的错误信息。这对于验证和错误处理非常有用,可以避免 NullPointerException

3. 泛型支持

Spring MVC经常使用泛型,如 List<Integer>Optional<Double> 等。泛型在Java中不支持基本数据类型。因此,为了能够使用诸如集合框架之类的泛型数据结构,控制器方法中的参数必须使用包装类型。

4. 自动装箱与拆箱

Java的自动装箱与拆箱特性使得使用包装类型和基本类型几乎无缝切换,增加了代码的灵活性。在Spring MVC中,当框架将HTTP请求参数绑定到控制器方法的参数时,这一特性尤为重要。如果HTTP请求中的某个参数缺失,对应的包装类型可以自然地被设置为 null,而不是去处理基本类型默认值可能带来的逻辑错误。

5. 灵活的数据处理

使用包装类型可以更灵活地处理数据,尤其是在需要区分缺失数据和实际数据时(如前面提到的0和 null 的区分)。这在进行数据验证、预处理、转换时特别有价值,能够让程序更加健壮。

在Spring MVC中,控制器方法使用包装类型而非基本类型的做法,主要是为了增加程序的健壮性和灵活性,以及更好地处理来自用户的输入数据。包装类型的使用允许数据保持为 null,从而提供了对特殊情况(如数据未填写、数据验证失败等)的更准确处理。此外,它还支持更广泛的编程模式,如使用集合和泛型,这在现代Web开发中极为重要。

包装类型在Java中扮演着重要的角色,不仅仅是因为它们提供了将基本数据类型用作对象的能力,还因为它们在Java的类型系统、集合框架、泛型支持、以及API中提供了增强的功能和灵活性。这些特性使Java成为一种更强大、更灵活的编程语言,能够满足现代软件开发的复杂需求。

二、原理分析


自动拆装箱

在Java中,自动装箱(autoboxing)和自动拆箱(unboxing)是Java 5引入的两个特性,它们使得基本类型(如 intdouble 等)和相应的包装类类型(如 IntegerDouble 等)之间的转换过程自动化,大大简化了程序员在处理对象时的代码编写。这两个特性背后的原理是编译器的自动转换,而不是运行时的动态转换,我们来详细了解一下这两个过程。

自动装箱(Autoboxing)

自动装箱是将基本数据类型自动转换为对应的包装类对象的过程。例如,当你将一个 int 赋值给一个 Integer 对象时,Java自动将这个基本类型转换为对象类型。

示例代码:

Integer i = 10;  // 自动装箱,将int转换为Integer

装箱过程原理:

  • 在编译阶段,编译器会识别出需要将基本类型转换为包装类对象的情形。
  • 编译器会自动插入调用包装类的 valueOf() 方法的代码,这个方法会从缓存中返回已存在的对象或创建一个新的对象。例如,对于 intInteger 的转换,编译器将上述代码转换为:
Integer i = Integer.valueOf(10);

自动拆箱(Unboxing)

自动拆箱是将包装类对象自动转换为基本数据类型的过程。例如,当你将一个 Integer 对象赋值给一个 int 变量时,Java自动将这个对象转换为基本类型。

示例代码:

Integer i = new Integer(10);
int val = i;  // 自动拆箱,将Integer转换为int

上面这两行代码对应的字节码为:

L1LINENUMBER 8 L1ALOAD 0BIPUSH 10INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;PUTFIELD AutoBoxTest.i : Ljava/lang/Integer;L2LINENUMBER 9 L2ALOAD 0ALOAD 0GETFIELD AutoBoxTest.i : Ljava/lang/Integer;INVOKEVIRTUAL java/lang/Integer.intValue ()IPUTFIELD AutoBoxTest.n : IRETURN

从字节码中,我们发现装箱其实就是调用了 包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。

因此,

  • Integer i = 10 等价于 Integer i = Integer.valueOf(10)
  • int n = i 等价于 int n = i.intValue();

拆箱过程原理:

  • 在编译阶段,编译器会识别出需要将包装类对象转换为基本类型的情形。
  • 编译器自动插入调用对应包装类对象的 xxxValue() 方法的代码,这个方法会返回对象中存储的基本类型值。例如,对于 Integerint 的转换,编译器将上述代码转换为:

注意事项

尽管自动装箱和拆箱提供了极大的便利,它们也引入了一些需要注意的问题:

  1. 性能问题:频繁的装箱和拆箱操作可能会导致性能下降,特别是在大量计算的场景中,因为每次装箱和拆箱都可能涉及到对象的创建和方法调用。
  2. 空指针异常:在拆箱过程中,如果包装类对象为 null,拆箱时会抛出 NullPointerException。例如:
Integer i = null;
int val = i;  // 抛出NullPointerException

自动装箱和拆箱使得Java编程更加简洁,但也隐藏了一些可能影响性能和引起错误的行为。了解这些特性的背后原理可以帮助开发者更好地利用它们,同时避免可能的问题。在设计和实现涉及基本类型和包装类的系统时,应当考虑到这些因素,以确保代码的性能和健壮性。

包装类的缓存机制

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

  • Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据
  • Character 创建了数值在 [0,127] 范围的缓存数据
  • Boolean 直接返回 True or False

Integer 缓存源码:

public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);
}
private static class IntegerCache {static final int low = -128;static final int high;static {// high value may be configured by propertyint 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_VALUEh = 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;}
}

  • 如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
  • 两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。
  • 注意:所有整型包装类对象之间值的比较,尽量全部使用 equals 方法比较

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/2617.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

识别有效的IP地址和掩码并进行分类统计

问题概要 请解析IP地址和对应的掩码&#xff0c;进行分类识别。要求按照A/B/C/D/E类地址归类&#xff0c;不合法的地址和掩码单独归类。 所有的IP地址划分为 A,B,C,D,E五类 A类地址从1.0.0.0到126.255.255.255; B类地址从128.0.0.0到191.255.255.255; C类地址从192.0.0.0到223.…

opencv改变像素点的颜色---------c++

改变像素点的颜色 #include <opencv2/opencv.hpp> #include <opencv2/core.hpp> #include <filesystem>bool opencvTool::changeColor(const std::string image_p, int x_coor, int y_coor, const cv::Scalar color) {std::filesystem::path file(image_p);…

大模型检索召回系统:RAG技术的全面调查与未来展望

随着人工智能技术的飞速发展&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在自然语言处理&#xff08;NLP&#xff09;领域取得了显著成就。然而&#xff0c;这些模型在处理特定领域或知识密集型任务时仍面临挑战&#xff0c;如产生错误信息或“幻觉”。为了克服这些难…

MC33665 + MC33774 控制流程及 TPL3 帧结构介绍

一. 概述&#xff1a; MC33665A&#xff1a;通用电池管理通信网关和变压器物理层 (TPL) 收发器。该设备通过标准通信协议转发来自不同 TPL&#xff08;NXP 的隔离菊花链协议&#xff09;端口的消息&#xff0c;标准通信协议可确保与市场上可用的微控制器兼容。 MC33774&…

vue实现进入某个页面后替换地址栏路径

需求背景&#xff1a;a系统进入b系统首页&#xff08;"/index"&#xff09;需要携带token&#xff0c;如 example.com/index?token"thisIsMyToken" 需要再b系统中将地址栏携带的token清除 getBtnType(type) {this.$router.push({path: "/",quer…

Fork for Mac v2.42 激活版 Git客户端

Fork for Mac是一款运行在Mac平台上的Git客户端&#xff0c;Fork Mac版具备基本的取、推、提交、修改、创建和删除分支和标签、创建和删除远程备份等功能&#xff0c;还有实用的差异查看器&#xff0c;你可以通过清晰的视图快速发现源代码中的更改。 Fork for Mac v2.42 激活版…

Golang | Leetcode Golang题解之第42题接雨水

题目&#xff1a; 题解: func trap(height []int) (ans int) {n : len(height)if n 0 {return}leftMax : make([]int, n)leftMax[0] height[0]for i : 1; i < n; i {leftMax[i] max(leftMax[i-1], height[i])}rightMax : make([]int, n)rightMax[n-1] height[n-1]for i…

常见术语:DI/DO、AI/AO 和 I/O

在自动化和控制系统领域&#xff0c;DI/DO、AI/AO 和 I/O 是常见的术语&#xff0c;分别代表不同类型的输入输出接口。它们在工业自动化、楼宇自动化、机器人技术等领域有广泛应用。下面详细解释这些术语及其使用示例。 一、DI/DO&#xff08;数字输入/数字输出&#xff09; 数…

React vs React Native写法上的不同

标签 <div> -> <View> / <ScrollView><p> -> <Text><input> -> <TextInput><image> -> <Image><button> -> <Button>css background-image -> <ImageBackground> 除此之外还有一…

一个简单的kafka 消费者

写一个简单的kafka 消费者 1. 依赖 <dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId></dependency>2. 消费者 import com.xxxx.npi.module.common.msg.enums.Topic; import org.apache.…

watchEffect的使用

watchEffect 是 Vue 3 Composition API 中的一个函数&#xff0c;它用于在响应式数据变化时自动执行一个副作用函数。与 watch 不同&#xff0c;watchEffect 会自动收集其执行过程中访问到的响应式依赖&#xff0c;并在这些依赖发生变化时重新运行副作用函数。这意味着你不需要…

预防oracle的漏洞及其提权

防止Oracle数据库的漏洞及其潜在的权限提升&#xff0c;需要实施一系列综合的安全措施。这些措施不仅涉及技术配置&#xff0c;还包括过程管理和持续的安全评估。以下是有效防御Oracle数据库漏洞和提权攻击的一些关键步骤&#xff1a; 1. 安装和配置 安装最新安全补丁 定期更…

git提交注释规范插件

1、前言 为什么要注重代码提交规范&#xff1f; 在团队协作开发时&#xff0c;每个人提交代码时都会写 commit message。 每个人都有自己的书写风格&#xff0c;翻看我们组的git log, 可以说是五花八门&#xff0c;十分不利于阅读和维护。 一般项目开发都是多分支共存&#x…

关于CPP类中字符串成员初始化

直接看代码吧 #include <iostream> #include <string>/* A string is actually an object of the C++ Standard Library class string. This class is defined in header <string>, and the name string, like cout, belongs to namespace std. To enable …

kvm使用virt-clone克隆虚拟机

首先使用命令查看系统安装的所有虚拟机: virsh list --all然后使用如下命令进行虚拟机的克隆: virt-clone -o generic -n generic-1 -f /var/lib/libvirt/images/generic-1.qcow2-o后面要克隆的虚拟机名称 -n是新的虚拟机的名称 -f是生成的新的虚拟机磁盘文件路径(一般是/var…

Seal^_^【送书活动第2期】——《Flink入门与实战》

Seal^_^【送书活动第2期】——《Flink入门与实战》 一、参与方式二、本期推荐图书2.1 作者简介2.2 编辑推荐2.3 前 言2.4 本书特点2.5 内容简介2.6 本书适用读者2.7 书籍目录 三、正版购买 一、参与方式 评论&#xff1a;"掌握Flink&#xff0c;驭大数据&#xff0c;实战…

Ubuntu下部署gerrit+报错分析(超详细)

Ubuntu下部署gerrit代码平台 之前安装过几次 最后都在Apache代理这里失败了&#xff0c;如下图&#xff0c;总是gerrit.config与Apache2.config配置有问题&#xff0c;后面换了使用ngnix代理&#xff0c;简单多了 安装Mysql、gerrit、jdk、git 这一步也是非必须得&#xff0…

【c++】list类接口函数介绍与深度剖析模拟实现

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章来到list有关部分&#xff0c;这一部分函数与前面的类似&#xff0c;我们简单讲解&#xff0c;重难点在模拟实现时的迭代器有关实现 目录 1.List介绍2.接…

Go 之为什么 rune 是 int32 的别名而不是 uint32 的别名

我对这个问题其实也是一直有疑问的&#xff0c;毕竟像 byte 都是 uint8 的别名。然后找了一些问答资料&#xff0c;不知道还没有没其他更好的解释。 范围足够 在 Unicode 字符集中&#xff0c;一个字符的码点范围是从 U0000 到 U10FFFF&#xff0c;共计 1114112 个码点&#…

转向敏捷财务规划,实现更快更自信的决策

随着数字化的到来&#xff0c;原本基于电子表格的时代正逐渐拉下帷幕&#xff0c;大部分企业开始摆脱依赖于电子表格进行计划、预算和预测的传统规划系统&#xff0c;寻求更符合当今市场要求的敏捷财务规划。但不得不承认&#xff0c;当下电子表格仍然是多数企业使用最广泛的工…