包装类的自动装箱拆箱,==运算符及equals方法

梦想不抛弃苦心追求的人,只要不停止追求,你们会沐浴在梦想的光辉之中。再美好的梦想与目标,再完美的计划和方案,如果不能尽快在行动中落实,最终只能是纸上谈兵,空想一番。只要瞄准了大方向,坚持不懈地做下去,才能够扫除挡在梦想前面的障碍,实现美好的人生蓝图。包装类的自动装箱拆箱,==运算符及equals方法,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

包装类的自动装箱拆箱,==运算符及equals方法

一个例子

  我们先来看这样一个简单的程序:

public class SugarTest {
	public static void main(String[] args) {
		Integer a = 1;
		Integer b = 2;
		Integer c = 3;
		Integer d = 3;
		Integer e = 321;
		Integer f = 321;
		Long g = 3L;
		System.out.println(c == d);
		System.out.println(e == f);
		System.out.println(c == (a + b));
		System.out.println(c.equals(a + b));
		System.out.println(g == (a + b));
		System.out.println(g.equals(a + b));
	}
}

  上述程序的输出结果是什么?运行一下上述程序,看看真实输出与你预想的有什么不同。
  上述程序的输出为:
在这里插入图片描述

  对于上述结果我们不禁产生一个疑问,对于包装类,进行==运算符比较和进行equals方法比较有哪些区别,如果==运算符在遇到算术运算的情况下又会如何?

程序反编译的结果及分析

  由于在使用包装类时,Java提供了自动装箱与拆箱的语法糖,对于上述程序,我们可能一时想不明白为什么会输出相应的结果。Javac编译器会进行解语法糖的过程,在使用javac命令将上述.java文件编译成.class文件后,自动装箱、拆箱将转化为对应的包装和还原方法,我们使用XJad反编译工具查看解语法糖后上述程序变成什么样了:

public class SugarTest
{

	public SugarTest()
	{
	}

	public static void main(String args[])
	{
		Integer integer = Integer.valueOf(1);
		Integer integer1 = Integer.valueOf(2);
		Integer integer2 = Integer.valueOf(3);
		Integer integer3 = Integer.valueOf(3);
		Integer integer4 = Integer.valueOf(321);
		Integer integer5 = Integer.valueOf(321);
		Long long1 = Long.valueOf(3L);
		System.out.println(integer2 == integer3);
		System.out.println(integer4 == integer5);
		System.out.println(integer2.intValue() == integer.intValue() + integer1.intValue());
		System.out.println(integer2.equals(Integer.valueOf(integer.intValue() + integer1.intValue())));
		System.out.println(long1.longValue() == (long)(integer.intValue() + integer1.intValue()));
		System.out.println(long1.equals(Integer.valueOf(integer.intValue() + integer1.intValue())));
	}
}

自动装箱

  比较源程序与解语法糖后的程序,首先我们发现了之所以我们能在程序中使用Integer a = 1;这样的语句,是因为自动装箱的存在。原本Integer a是一个对象引用,1是一个数值,我们是不能将数值赋给一个对象的,但借助于自动装箱,上述语句等价于Integer a = Integer.valueOf(1)Integer.valueOf(1)调用valueOf方法返回了一个Integer对象,而a成为了该对象的引用。

看看Integer.valueOf(int i)源码

  接着我们来看第一条和第二条判断语句。第一条判断语句为true,而第二条判断语句为false,同样是整型包装类的==运算符判断,为什么得到了相反的结果?我们知道,对于基本数据类型,==判断的是值是否相等,对于引用类型,==判断的是所引用对象是否为同一个对象。第一条判断为true,说明两个Integer.valueOf(3)返回的对象是同一个对象,第二条判断为false,说明两个Integer.valueOf(321)返回的对象是两个不同的对象。都是使用valueOf方法,为啥返回的对象有时相同有时不同?我们注意到调用上述两个valueOf方法,提供的参数值不同,难道是参数值在作怪吗,我们进入Integer.valueOf()源码内来看一看:
在这里插入图片描述
在这里插入图片描述

  没看到源码,我们可能直观地认为Integer.valueOf()方法就是创建一个新的Integer对象并返回,但事实上并非如此。Integer.valueOf(int i)方法会先对i的范围进行判断,若其在-128~127的范围内,则直接按照数组下标索引从预先创建好的Integer[]数组中取出对应的Integer对象返回,否则才会创建一个新的Integer对象。为什么要采用这样的设计,其实已经在注释中说明了,-128~127范围内的值对应的Integer对象在程序中会被经常使用,设置缓存数组能加快valueOf()方法执行的速度,避免创建过多对象,提高空间利用效率。
  看到了源码,之前的疑问也引刃而解了,对于Integer c = Integer.valueOf(3);Integer d = Integer.valueOf(3);,由于3在-128~127范围内,实际上c和d引用的是缓存数组中的同一个对象,执行上述代码并没有进行新对象的创建。c == d的判断结果为true。对于Integer e = Integer.valueOf(321);Integer f = Integer.valueOf(321);,由于321超过了-128~127的范围,因此调用Integer.valueOf(321)方法会创建一个新的对象返回,e和f引用的是两个不同的对象,e == f的判断结果为false。

自动拆箱

  来看看==运算符在遇到算术运算的情况下变成什么样了?对于System.out.println(c == (a + b));,解语法糖后变成了System.out.println(c.intValue() == a.intValue() +b.intValue());
  我们之所以可以对两个包装类实例进行加减等运算操作,是因为有自动拆箱的存在。对象和对象之间是不能加减的,但基于自动拆箱,我们将其变为了包装类实例中value值之间的加减操作。
  需注意==运算符在没有遇到算术运算时,是不会自动拆箱的,我们可以看反汇编程序中第一和第二条判断语句。因为在没遇到算术运算时,Javac编译器会认为==是在进行引用类型之间的判断,不需要自动拆箱,而遇到算术运算时,会认为==是在进行数值类型之间的判断,这时候就需要自动拆箱了。而自动拆箱后,对于不同数据类型之间的判断,Javac编译器会进行数据类型间的自动转换,即由低字节向高字节进行转换。于是我们看到了System.out.println(g.longValue() == (long)(a.intValue() + b.intValue()));
  

equals方法

  我们知道,每个类都有从Object类中继承的equals()方法,可以重写equals()方法,若不进行重写,所继承的equals()方法其实就是在进行==运算符判断:
在这里插入图片描述
  那么,包装类有没有重写equals方法呢?答案是肯定的,我们判断两个包装类是否相等,肯定是想判断两者的值是否相等,而不是判断两者是否引用同一个对象。看看Integer.equals()方法源码来验证我们的想法:
在这里插入图片描述
  对于Integer包装类的equals(Object obj)方法,我们会先判断obj是否是Integer类的实例,若不是,则直接return false,否则进行包装类实例对应的值是否相等的判断。
  System.out.println(c.equals(a + b));在解语法糖后变成了这样:System.out.println(integer2.equals(Integer.valueOf(integer.intValue() + integer1.intValue())));,由于遇到了算术运算,先进行了自动拆箱,又因为只有两个包装类对象之间才能用包装类的equals方法进行判断,所以又进行了自动装箱,最后调用equals方法进行值是否相等的判断。而System.out.println(c.equals(a + b));输出true,System.out.println(g.equals(a + b));输出false的原因想必不用多说了,前者两个Integer包装类实例的值相等,后者由于是两个不同包装类实例间的equals比较,直接返回false。

总结:在以后写程序中需要注意的点

  经过上述分析,造成文章开头例子中的输出结果的理由我们已经全部弄清楚了,那么我们在写程序时需要注意什么呢?这里我直接引用阿里Java开发手册中的一条强制性编码规范:
在这里插入图片描述

  对于Integer类型,我们就知道了由于缓存数组导致的大坑,无法使用==进行值的比较。因此当我们进行相同类型包装类对象之间值的比较时,就别用==判断了,都使用equals进行判断。
  在IDEA中装上阿里代码规范插件后,也会提醒大家这一点:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153735.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!