文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java工具类—包装类

2024-12-11 20:51

关注

[[331293]]

一、包装类介绍

1. 为什么需要包装类?

我们知道 Java 语言是一个面向对象的编程语言,但是 Java 中的基本数据类型却不是面向对象的,但是我们在实际使用中经常需要将基本数据类型转换成对象,便于操作,比如,集合的操作中,这时,我们就需要将基本类型数据转化成对象,所以就出现了包装类。

2. 包装类是什么呢?

包装类,顾名思义就是将什么经过包装的类,那么是将什么包装起来的呢,显然这里是将基本类型包装起来的类。包装类的作用就是将基本类型转成对象,将基本类型作为对象来处理。

Java 中我们知道,基本数据类型有8个,所以对应的包装类也是8个,包装类就是基本类型名称首字母大写。但Integer 和 Character 例外,它们显示全称,如下面表格所示:

 基本数据类型对应包装类

byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublecharCharacterbooleanBoolean

 

二、包装类的继承关系

通过阅读 Java8 的 API 官方文档或者看源代码我们可以得知8个包装类的继承关系如下:

通过以上的继承关系图,我们其实可以这样记忆,包装类里面有6个与数字相关的都是继承自 Number 类,而其余两个不是与数字相关的都是默认继承 Object 类。通过看 API 官方文档,我们还可以得知这8个包装类都实现了Serializable , Comparable 接口。比如下图的 Integer 类

  1. public final class Integer extends Number implements Comparable<Integer> {} 

三、包装类的使用方法(基本操作)

接下来关于包装类的讲解我就讲Integer包装类,其他的都依此类推,用法和操作都是差不多的,只是名字不一样而已。

1. 包装类的构造方法

8个包装类都有带自己对应类型参数的构造方法,其中8个包装类中除了Character还有构造方法重载,参数是String类型的。

  1. Integer one = new Integer(666); 
  2. Integer two = new Integer("666"); 

2. 包装类的自动拆装箱

在了解自动拆装箱之前,我们得先知道什么是拆箱和装箱。其实拆装箱主要应对基本类型与包装类型的相互转换问题。

其实,在 JDK1.5 版本之前,是没有自动拆装箱的,开发人员要手动进行装拆箱:

  1. //手动装箱,也就是将基本类型10转换为引用类型 
  2. Integer integer = new Integer(10); 
  3. //或者 
  4. Integer integer1 = Integer.valueOf(10); 
  5.  
  6. //手动拆箱,也就是将引用类型转换为基本类型 
  7. int num = integer.intValue(); 

而在在 JDK1.5 版本之后,为了减少开发人员的工作,提供了自动装箱与自动拆箱的功能。实现了自动拆箱和自动装箱,如下方代码所示:

  1. //自动装箱 
  2. Integer one = 1
  3. //自动拆箱 
  4. int two = one + 10; 

其实以上两种方式本质上是一样得,只不过一个是自动实现了,一个是手动实现了。至于自动拆装箱具体怎么实现的我这里不做深入研究。

四、包装类的缓存机制

我们首先来看看以下代码,例1:

  1. public static void main(String[] args) { 
  2.   Integer i1 = 100
  3.   Integer i2 = 100
  4.   Integer i3 = new Integer(100); 
  5.   Integer i4 = new Integer(100); 
  6.   System.out.println(i1 == i2);//true 
  7.   System.out.println(i1 == i3);//false 
  8.   System.out.println(i3 == i4);//false 
  9.   System.out.println(i1.equals(i2));//true 
  10.   System.out.println(i1.equals(i3));//true 
  11.   System.out.println(i3.equals(i4));//true 

当我们修改了值为200的时候,例2:

  1. public static void main(String[] args) { 
  2.   Integer i1 = 200
  3.   Integer i2 = 200
  4.   Integer i3 = new Integer(200); 
  5.   Integer i4 = new Integer(200); 
  6.   System.out.println(i1 == i2);//false 
  7.   System.out.println(i1 == i3);//false 
  8.   System.out.println(i3 == i4);//false 
  9.   System.out.println(i1.equals(i2));//true 
  10.   System.out.println(i1.equals(i3));//true 
  11.   System.out.println(i3.equals(i4));//true 

通过上面两端代码,我们发现修改了值,第5行代码的执行结果竟然发生了改变,为什么呢?首先,我们需要明确第1行和第2行代码实际上是实现了自动装箱的过程,也就是自动实现了 Integer.valueOf 方法,其次,==比较的是地址,而 equals 比较的是值(这里的 eauals 重写了,所以比较的是具体的值),所以显然最后五行代码的执行结果没有什么疑惑的。既然==比较的是地址,例1的第5行代码为什么会是true呢,这就需要我们去了解包装类的缓存机制。

其实看Integer类的源码我们可以发现在第780行有一个私有的静态内部类,如下:

  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.         hhigh = 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() {} 

我们知道,静态的内部类是在整个 Integer 加载的时候就已经加载完成了,以上代码初始化了一个 Integer 类型的叫 cache 的数组,取值范围是[-128, 127]。缓存机制的作用就是提前实例化相应范围数值的包装类对象,只要创建处于缓存范围的对象,就使用已实例好的对象。从而避免重复创建多个相同的包装类对象,提高了使用效率。如果我们用的对象范围在[-128, 127]之内,就直接去静态区找对应的对象,如果用的对象的范围超过了这个范围,会帮我们创建一个新的 Integer 对象,其实下面的源代码就是这个意思:

  1. public static Integer valueOf(int i) { 
  2.     if (i >= IntegerCache.low && i <= IntegerCache.high) 
  3.         return IntegerCache.cache[i + (-IntegerCache.low)]; 
  4.     return new Integer(i); 

所以 例1 代码里,i1 和i2 是100,值的范围在[-128, 127],所以直接区静态区找,所以i1和i2指向的地址是同一个,所以 i1==i2;而在例2的代码里,i1 和i2 是200,值的范围不在在[-128, 127],所以分别创建了一个新的对象,放在了堆内存里,各自指向了不同的地址,所以地址都不同了,自然 i1 不等于 i2。

通过分析源码我们可以发现,只有 double 和 float 的自动装箱代码没有使用缓存,每次都是 new 新的对象,其它的6种基本类型都使用了缓存策略。 使用缓存策略是因为,缓存的这些对象都是经常使用到的(如字符、-128至127之间的数字),防止每次自动装箱都创建一次对象的实例。

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

(1) 默认值不同

包装类的默认值是null,而基本数据类型是对应的默认值(比如整型默认值是0,浮点型默认值是0.0)

(2) 存储区域不同

基本数据类型是把值保存在栈内存里,包装类是把对象放在堆中,然后通过对象的引用来调用他们

(3) 传递方式不同

基本数据类型变量空间里面存储的是值,传递的也是值,一个改变,另外一个不变,而包装类属于引用数据类型,变量空间存储的是地址(引用),传递的也是引用,一个变,另外一个跟着变。

五、小结

​ 以上就是我对于Java包装类的个人理解,其实学习这些工具类还有一个更好的学习方式,就是去看官方文档(API官方文档地址:

https://docs.oracle.com/javase/8/docs/api/)

 

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯