当前位置:

首页 > 编程开发 > Java包装类怎么应用

Java包装类怎么应用

一、包装类概述Java有8种基本数据类型:整型(byte、short、int、long)、浮点型(float、double)、布尔型boolean、字符型char,相对应地,Java提供了8种包装类Byte、Short、Integer、Long、Float、Double、Boolean、Character。包装类创建对象的方式就跟其他类一样。Integernum=newInteger(0);//创建一个数值为0的Integer对象二、包装类的自动装箱、自动拆箱机制上面的构造对象语句实际上是基本数据类型向包

本文目录

      一、包装类概述

      Java有8种基本数据类型:整型(byte、short、int、long)、浮点型(float、double)、布尔型boolean、字符型char,相对应地,Java提供了8种包装类Byte、Short、Integer、Long、Float、Double、Boolean、Character。包装类创建对象的方式就跟其他类一样。

      Integer num = new Integer(0);    //创建一个数值为0的Integer对象

      二、包装类的自动装箱、自动拆箱机制 

      上面的构造对象语句实际上是基本数据类型向包装类的转换。在应用中我们经常需要进行基本类型数据和包装类对象之间的互转。

      Integer num1 = new Integer(1);	//基本数据类型转为包装类
      int num2 = num1.intValue();		//包装类型转为基本数据类型
      System.out.println(num1 +"	"+ num2);

      而Java为了方便我们使用,以及出于其他目的如性能调优,给我们提供了自动装箱、拆箱机制。这种机制简化了基本类型和包装类型的转换。

      //1、包装类中的自动装箱拆箱机制
      Integer  num1 = 1;		//自动装箱
      int num2 = num1;		//自动拆箱
      System.out.println(num1 +"	"+ num2);

      当使用jad工具对上面的代码进行反编译时,结果如下。

      Integer integer = Integer.valueOf(1);
      int i = integer.intValue();
      System.out.println((new StringBuilder()).append(integer).append("\t").append(i).toString());

      可见,Java编译器帮我们完成了转换操作。另外,我们可以看到,除了使用new关键字,还可以使用Integer类的valueOf()方法创建一个Integer对象。这两个方式是有所区别的,我们下面会说到。

      三、包装类中的缓存机制

      前面说到创建包装类对象有两种方式:new关键字、valueOf()方法。我们来看一段代码感受一下它们的区别。

      //2、包装类中的缓存机制
      Integer num3 = 10;
      Integer num4 = 10;
      Integer num5 = new Integer(20);
      Integer num6 = new Integer(20);
      Integer num7 = 128;
      Integer num8 = 128;
      System.out.println((num3==num4) +"	"+ num3.equals(num4));
      System.out.println((num5==num6) +"	"+ num5.equals(num6));
      System.out.println((num7==num8) +"	"+ num7.equals(num8));

      运行结果为

      Java包装类怎么应用

      我们看下它的反编译代码

      Integer integer = Integer.valueOf(10);
      Integer integer1 = Integer.valueOf(10);
      Integer integer2 = new Integer(20);
      Integer integer3 = new Integer(20);
      Integer integer4 = Integer.valueOf(128);
      Integer integer5 = Integer.valueOf(128);
      System.out.println((new StringBuilder()).append(integer == integer1).append("\t").append(integer.equals(integer1)).toString());
      System.out.println((new StringBuilder()).append(integer2 == integer3).append("\t").append(integer2.equals(integer3)).toString());
      System.out.println((new StringBuilder()).append(integer4 == integer5).append("\t").append(integer4.equals(integer5)).toString());

      首先,我们查看Integer的valueOf()方法的源码

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

      再查看下Integer的内部类IntegerCache的cache数组成员、low、high成员

              static final int low = -128;
              static final int high;
              static final Integer cache[];
       
              static {
                  // high value may be configured by property
                  int 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_VALUE
                          h = 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;
              }

      可以发现,只要Integer类第一次被使用到,Integer的静态内部类就被加载,加载的时候会创建-128到127的Integer对象,同时创建一个数组cache来缓存这些对象。当使用valueOf()方法创建对象时,就直接返回已经缓存的对象,也就是说不会再新建对象;当使用new关键字or使用valueOf()方法创建小于-128大于127的值对象时,就会创建新对象。

      //2、包装类中的缓存机制
      Integer num3 = 10;
      Integer num4 = 10;
      Integer num5 = new Integer(20);
      Integer num6 = new Integer(20);
      Integer num7 = 128;
      Integer num8 = 128;

      由于num3、num4都小于等于127,它们指向的是同一个缓存的Integer对象,所以用==进行比较的结果是true;num5、num6由于使用new关键字指向的是两个不同的新对象,结果为false;num7、num8虽然是采用自动装箱的方式,但执行valueOf()方法的时候,由于不满足条件i >= IntegerCache.low && i <= IntegerCache.high,而同样新建了两个不同的新对象,结果同样是false。

      接着,我们再来看看源码中Integer的equals()方法的实现

          public boolean equals(Object obj) {
              if (obj instanceof Integer) {
                  return value == ((Integer)obj).intValue();
              }
              return false;
          }

      可见equals()方法比较的是Integer对象的值,而不是像==一样比较的是对象是否是同一个对象。所以,当需要比较两个Integer对象的值是否相等时,记住要用equals()方法。用==比较的话由于缓存机制的存在,可能产生一些让人困扰的结果。

      此外,在8种包装类型中,有缓存区的有Character、Byte、Short、Integer、Long,而且它们的实现方式基本一样,都是-128到127的缓存范围。Boolean虽然没有缓存区,但是因为只有两个值true、false,所以Boolean在成员变量中就创建了两个相应的对象。没有缓存区的只有Float、Double,之所以没有原因很简单,即便是0到1这么小的范围,浮点数也有无数个,使用缓存区缓存它们不具备可能性和实用性。

      缓存区的存在使得常用的包装类对象可以得到复用,这有利于提升性能。当我们需要创建新对象的时候再new一个,增加了灵活性。

      四、包装类的四则运算、位运算、比较运算、逻辑运算

      1、四则运算和位运算

      //四则运算、位运算
      Integer num9 = 1;
      Integer num10 = 2;
      Integer num11 = num9 + num10; 
      Short num12 = 5;
      Integer num13 = num9 + num12;
      Long num14 = num9 + 10L;
      System.out.println(num9 << 1);	//位运算
      System.out.println(num9 +"	"+ num10 +"	"+ num11 +"	"+ num12 +"	"+ num13 +"	"+ num14);

      Java包装类怎么应用

      反编译结果如下

              Integer integer = Integer.valueOf(1);
              Integer integer1 = Integer.valueOf(2);
              Integer integer2 = Integer.valueOf(integer.intValue() + integer1.intValue());
              Short short1 = Short.valueOf((short)5);
              Integer integer3 = Integer.valueOf(integer.intValue() + short1.shortValue());
              Long long1 = Long.valueOf((long)integer.intValue() + 10L);
              System.out.println(integer.intValue() << 1);
              System.out.println((new StringBuilder()).append(integer).append("\t").append(integer1).append("\t").append(integer2).append("\t").append(short1).append("\t").append(integer3).append("\t").append(long1).toString());

      可以看到Integer num11 = num9 + num10; 这一句被划分为3个步骤:将两个Integer对象分别进行拆箱;将拆箱得到的两个int数值相加求其和;将和值进行装箱,从而将num11指向缓存数组中值为3的Integer对象。

      而Short num12 = 5; 这一句则先将5强制转换成short类型,再将其装箱把值为5的Short对象的引用赋给num12。

      而Integer num13 = num9 + num12; 这一句除了Integer num11 = num9 + num10;的3个步骤,中间还有short+int=int的类型自动提升的过程。

      而Long num14 = num9 + 10L; 这一句Integer num11 = num9 + num10;的3个步骤,中间还有强制类型转换的过程。需要注意的是,如果是Long num14 = num9 + num10; 的话就会出现类型不匹配的错误,因为num9、num10拆箱之后相加的和是int类型,而Long.valueOf(long)需要的形参是long类型,自然会出错。我们也可以看到,当包装类型对象和基本类型数据进行四则运算的时候,对象是会被拆箱的,然后再按基本类型数据的运算规则进行运算。

      另外,如果仅仅是打印两个包装类型对象求和的结果,是不会有将和值重新转换成该包装类型的步骤的,如下面所示

      System.out.println(num9 + num10);
      System.out.println(integer.intValue() + integer1.intValue());

      这里还需要注意一点,尽管基本类型与自动类型提升/强制类型转换,包装类是没有类似的用法的。下面的做法是错的。

      Short num3 = 10;
      Integer num4 = num3;	//错误: 不兼容的类型: Short无法转换为Integer
      Long num5 = (Long)num4;	//错误: 不兼容的类型: Integer无法转换为Long
      Double num6 = num5;	//错误: 不兼容的类型: Long无法转换为Double

      小结:不同包装类型对象是不能直接转换的,不过有两种途径可以代替:一种是上面讨论的不同包装类对象进行四则运算后赋给某一种类型;另一种就是利用包装类的方法

      Integer a = 20;
      Long b = a.longValue();
      Short c = b.shortValue();
      System.out.println(a +"	"+ b +"	"+ c);

      2、比较运算和逻辑运算

      Integer num9 = 100;
      Integer num10 = 200;
      Short num11 = 50;
      Long num12 = 50L;
      System.out.println((num9

      反编译结果为

      Integer integer = Integer.valueOf(100);
      Integer integer1 = Integer.valueOf(200);
      Short short1 = Short.valueOf((short)50);
      Long long1 = Long.valueOf(50L);
      System.out.println((new StringBuilder()).append(integer.intValue() < integer1.intValue()).append("\t").append(integer.intValue() < 200).append("\t").append(integer.intValue() < short1.shortValue()).append("\t").append((long)integer.intValue() < long1.longValue()).append("\t").append((long)integer.intValue() < 10L).toString());

      可以看到,两个同类型的包装类对象进行比较时比较的其实是各自的基本类型数值,如num9 < num10;两个不同类型的包装类对象进行比较时则在比较基本类型数值之前,会有类型提升or强制类型转换,如num9 < num11,num9 < num12。

      当想比较两个对象是否相等时,注意要使用equals()方法,从前面的讨论也知道,使用==的话比较的其实是引用的对象是否同一个,一般不满足我们的需求。

      Integer num13 = new Integer(100);
      System.out.println(num9.equals(num13) +"	"+ num9.equals(50));

      反编译结果为

      Integer integer2 = new Integer(100);
      System.out.println((new StringBuilder()).append(integer.equals(integer2)).append("\t").append(integer.equals(Integer.valueOf(50))).toString());

      逻辑运算举例:

      System.out.println((num9&1));

      反编译结果为

      System.out.println(integer.intValue() & 1);

      五、包装类作为方法的形参、返回值

      //包装类作为方法的形参、返回值
      	public static Integer intToInteger(int i) {
      		return i;
      	}  
      	public static int integerToInt(Integer i) {
      		return i;
      	}

      反编译结果为

          public static Integer intToInteger(int i)
          {
              return Integer.valueOf(i);
          }
       
          public static int integerToInt(Integer integer)
          {
              return integer.intValue();
          }

      六、包装类作为集合的元素

      //包装类作为集合元素
      List list = new ArrayList();
      list.add(1);
      list.add(new Object());
      Iterator it = list.iterator();
      while (it.hasNext()) {
      	System.out.println(it.next());
      }

      反编译结果为

      ArrayList arraylist = new ArrayList();
      arraylist.add(Integer.valueOf(1));
      arraylist.add(new Object());
      for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));

      可以发现,虽然集合元素要求是对象,add()方法的形参也是对象(public boolean add(E e)),但由于自动装箱,基本数据类型也可以直接加入集合中。

                      List list = new ArrayList<>();
      		for (int i=0; i<5; i++) {
      			list.add(i);
      		}
      		Iterator it = list.iterator();
      		while (it.hasNext()) {
      			System.out.println(it.next());
      		}

      反编译结果为

              ArrayList arraylist = new ArrayList();
              for(int i = 0; i < 5; i++)
                  arraylist.add(Integer.valueOf(i));
       
              for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));

      七、包装类使用过程中有可能引起的空指针异常

      //注意包装类可能产生的空引用异常
      		Boolean flag1 = false;
      		System.out.println(flag1?"命题为真":"命题为假");
      		Boolean flag2 = null;
      		System.out.println(flag2?"命题为真":"命题为假");
      		Boolean flag3 = true;

      运行结果为

      Java包装类怎么应用

      这里只是简单演示空指针异常。平时使用时需要注意这一点,比如当Boolean的对象作为形参时,在方法执行体的头部需要做下null检测。

      上述代码的反编译结果为

              Boolean boolean1 = Boolean.valueOf(false);
              System.out.println(boolean1.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");
              Boolean boolean2 = null;
              System.out.println(boolean2.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");
              Boolean boolean3 = Boolean.valueOf(true);

      可见三目运算符的条件表达式的位置一定是boolean值,如果你传入的是Boolean对象,则会自动拆箱转换为boolean值。

      另外,三目运算符的其他两个表达式位置也是如此,会把包装类对象转换为相应的基本类型对象。

      八、为什么需要包装类?有了包装类又为什么要保留基本数据类型?(包装类的优缺点)

      为什么需要包装类?

      首先,Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,将每个基本数据类型设计一个对应的类进行代表,这种方式增强了Java面向对象的性质。

      其次,如果仅仅有基本数据类型,那么在实际使用时将存在很多的不便,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的,因为集合的容器要求元素是Object类型。而包装类型的存在使得向集合中传入数值成为可能,包装类的存在弥补了基本数据类型的不足。

      此外,包装类还为基本类型添加了属性和方法,丰富了基本类型的操作。如当我们想知道int取值范围的最小值,我们需要通过运算,如下面所示,但是有了包装类,我们可以直接使用Integer.MAX_VALUE即可。

      //求int的最大值
      int max = 0;
      int flag = 1;
      for (int i=0; i<31; i++) {
      	max += flag;
      	flag = flag << 1;
      }
      System.out.println(max +"	"+ Integer.MAX_VALUE); //2147483647      2147483647

      为什么要保留基本数据类型?

      我们都知道在Java语言中,用new关键字创建的对象是存储在堆里的,我们通过栈中的引用来使用这些对象,所以,对象本身来说是比较消耗资源的。对于经常用到的类型,如int等,如果我们每次使用这种变量的时候都需要new一个对象的话,就会比较笨重了。所以,Java提供了基本数据类型,这种数据的变量不需要使用new在堆上创建,而是直接在栈内存中存储,因此会更加高效。

      本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
      作者最新文章
      编程开发 Java
      相关文章 更多
      Java单元测试入门:使用JUnit 5编写第一个测试用例
      Java单元测试入门:使用JUnit 5编写第一个测试用例

      面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

      Java变量未初始化报错解析:局部变量与成员变量默认值差异
      Java变量未初始化报错解析:局部变量与成员变量默认值差异

      深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
      Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

      详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

      Java中final与static final常量的定义及初始化规则
      Java中final与static final常量的定义及初始化规则

      深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

      Java抽象方法与接口方法的区别及选型建议
      Java抽象方法与接口方法的区别及选型建议

      深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

      Java实现HTTP请求与JSON解析的完整代码示例
      Java实现HTTP请求与JSON解析的完整代码示例

      本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

      Java递归出现StackOverflowError的原因分析及栈空间优化方案
      Java递归出现StackOverflowError的原因分析及栈空间优化方案

      详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

      Java空指针异常(NPE)的代码排查与修复实例
      Java空指针异常(NPE)的代码排查与修复实例

      针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

      selenium上传文件教程本地文件路径设置方法
      selenium上传文件教程本地文件路径设置方法

      深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
      大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

      针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

      查看更多
      精品专题 更多
      装机必备
      装机必备

      正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

      Windows
      Windows

      正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

      PDF教程
      PDF教程

      正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

      Mac软件 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      Aperty macOS版
      Aperty macOS版
      macOS

      Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

      WINDOWS 更多
      讯飞智作 网页版
      讯飞智作 网页版
      Windows/ macOS/ Linux

      讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

      火山引擎 网页版
      火山引擎 网页版
      Windows/ macOS/ Linux

      火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

      PixTV 网页版
      PixTV 网页版
      Windows

      PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。