【JAVA基础】一:聊聊笔试常见到的 “==、equal” 比较是否相等的内在差别

开始本文之前,先让我们记住一个口诀(这个口诀只针对基础的类比如String、Integer等,如果是自定义的类,需要看equal的具体实现):
equal比较其值,== 比较地址

这两天在走查代码的时候发现一个童鞋,在判断两个Integer类型的值是否相等的时候,用了==来判断,运行结果没错,因为这两个值在-128~127之间。 只是这种写法不符合规范,有隐患,我不是挑刺,但是觉得有必要给出个所以然来为啥要按照规范使用equal来比较对象值是否相等。

让我们先来看个笔试的例子:请问写出下面的代码输出结果。

public class JavaTest {public static void main(String args[]){Integer a = 127;Integer b = 127;Integer c = new Integer(127);Integer d = new Integer(127);int e = 127;// 1System.out.println(a == b);// 2System.out.println(a.equals(b));// 3System.out.println(a == c);// 4System.out.println(a.equals(c));// 5System.out.println(a == e);// 6System.out.println(a.equals(e));a = 200;b = 200;// 7System.out.println(a == b);// 8System.out.println(a.equals(b));}
}

答案是:
true
true
false
true
true
true
false
true

运用口诀:equal比较其值,==比较地址
第12行输出:true;因为它们的引用地址是同一个,这个地址存的值是127。
第14行输出:true;因为它们的值都是127,所以equal比较其值,结果是相等。
第16行输出:false;因为它们两个对象的引用地址不一样。
第18行输出:true;同上面的第2处。
第20行输出:true;这里会把e做装箱操作(从基本类型提升为包装对象类型),之后它们的引用地址是同一个,同第1处,后面解析java装箱、拆箱的概念。
第22行输出:true;此处一样会对e装箱提升为Integer对象,然后它们的值都是127,所以equal结果为true。
第28行输出:false;因为它们的引用地址不一样,它们不一样~,跟第1处不一样…下面会讲解。
第30行输出:true;因为它们的值都是200,同前面第2、4、6。

  • 前面讲到java自动装箱、拆箱 ,简单点说,装箱就是把基本类型升级为对象包装类型;拆箱就是把对象包装类型转换为对应的基本类型。

会自动拆箱、装箱的数据类型为:

对象类型基本类型
Integerint (4字节)
Shortshort (2字节)
Floatfloat (4字节)
Doubledouble (8字节)
Bytebyte (1字节)
Longlong (8字节)
Characterchar (2字节)
Booleanboolean (未定)

下面让我们来看下具体的装箱、拆箱过程:我们编写一个Test类文件保存在Test.java文件中,UTF-8编码存储,内容如下:

public class Test{public static void main(String[] args) {// 装箱Integer numInteger = 120;// 拆箱int num_int = numInteger;}
}

我们编译一下:

javac Test.java

如果提示“GBK编码…”之类的错误,在指令加个编码选项:

javac -encoding utf-8 Test.java

然后会在当前目录生成一个Test.class文件,我们通过javap工具反汇编看下这个class的内容:

javap指令这里就不展开了,感兴趣的可以看文档:Javap官方文档

javap -c Test.class

上面的指令会直接显示反编译的内容,或者你也可以通过下面的指令来把反汇编的内容输出到文件中,比如输出当前目录的Test.bin文件里面:

javap -c Test.class>Test.bin

反汇编的内容如下:

Compiled from "Test.java"
public class Test {public Test();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: bipush        1202: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;5: astore_16: aload_17: invokevirtual #3                  // Method java/lang/Integer.intValue:()I10: istore_211: return
}

执行第6行代码Integer numInteger = 120;时,系统底层是执行了Integer.valueOf 来把数值120转换为一个新的Integer包装对象类型。

执行第9行代码int num_int = numInteger;的时候,系统底层实际是执行了Integer.intValue 来把包装对象numInteger的intValue值取出来赋值给int类型变量num_int:

  • 那前面的JavaTest类中第12行跟28行的结果为啥不一样?
Integer a = 127;
Integer b = 127;// 1、结果为true
System.out.println(a == b);a = 200;
b = 200;// 7、结果为false
System.out.println(a == b);

感觉有点坑吧?我觉得出这个题目的考官是不是有点刁钻呀… 这个并没有啥神秘的,我们直接看源码吧。

当代码中第1处的括号运算:a == b的时候 ,根据口诀,是判断这两个包装对象的地址是否一样,根据上面的反汇编代码可以看出来,代码中的Integer a = 127;对应的操作是Integer a = Integer.valueOf (127); Integer类的源码的valueOf如下:

public final class Integer extends Number implements Comparable<Integer> {// ....../*** Cache to support the object identity semantics of autoboxing for values between* -128 and 127 (inclusive) as required by JLS.** The cache is initialized on first usage.  The size of the cache* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.* During VM initialization, java.lang.Integer.IntegerCache.high property* may be set and saved in the private system properties in the* sun.misc.VM class.*/private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];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;}private IntegerCache() {}}/*** Returns an {@code Integer} instance representing the specified* {@code int} value.  If a new {@code Integer} instance is not* required, this method should generally be used in preference to* the constructor {@link #Integer(int)}, as this method is likely* to yield significantly better space and time performance by* caching frequently requested values.** This method will always cache values in the range -128 to 127,* inclusive, and may cache other values outside of this range.** @param  i an {@code int} value.* @return an {@code Integer} instance representing {@code i}.* @since  1.5*/public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}// ......
}

从上面的源码可以看出来,对于-128 ~ 127之间(闭包)的int值,通过valueOf得到其对应的Integer对象,是已经预先静态生成好的存放在数组中的对象,所以只要这个int值是在这区间范围的,得到的对象都是同一个。而如果传过来的int值不在 -128 ~ 127 区间范围,直接在最后new 一个新的Integer对象返回的,这就解释了为啥两个自动装箱数值为200的对象,用==来判断其地址,是不一样的,因为两个对象都不一样了。

这里JDK应该是对于这些1字节长度的数值,总共28 = 256个,从-128~127,预先存起来,加快包装的速度、节省存储空间。那为啥不把两个字节、三个字节的都预先存起来呢?who knows?… 那为啥不把float、double的值也存起来?这两种类型的区间数是无限的好吗,看官你别逗了…

综上所述:

  • == 对于基本类型的数值,是比较它们的值是否相等;对于包装对象这种引用型的变量,比较的是它们在堆中存储的地址是否相同。
  • equals 表示这两个变量的value数值是否相同。

如果是自定义的类调用equal,这个需要具体看此类的equal有没有重写,具体如何实现的,比如:

public class Test extends Object{int num = 0;public Test(int i){this.num = i;}public static void main(String[] args) {Test t1 = new Test(1);Test t2 = new Test(1);System.err.println(t1.equals(t2));System.err.println(t1 == t2);}
}

上面的程序执行输出为:
false
false

第二个是false,这个很容易理解,但是第一个为啥呢,不是说好了equal是比较值么?但是这个Test自定义类并没有重写equal方法,所以调用的是Object里面的equal,我们看下它的源码:
在这里插入图片描述

也就是,Object的equal实现默认还是通过 == 来实现的… 明白了吧,所以我们自己来实现equal如下:

public class Test extends Object{int num = 0;public Test(int i){this.num = i;}@Overridepublic boolean equals(Object obj) {if(!(obj instanceof Test) || null == obj){return false;}return (num == ((Test) obj).num);}public static void main(String[] args) {Test t1 = new Test(1);Test t2 = new Test(1);System.err.println(t1.equals(t2));System.err.println(t1 == t2);}
}

这样,就能实现equal比较的是值了,跟前面的口诀保持一致了。

转载于:https://www.cnblogs.com/xiaocy66/p/10589257.html

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

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

相关文章

postgres 退出_postgresql – 如何修复Postgres以便在突然关闭后启动它?

由于突然断电,在我的本地机器上运行的PostGres服务器突然关闭.重新启动后,我尝试重新启动postgres,我收到此错误&#xff1a;$pg_ctl -D /usr/local / pgsql / data restartpg_ctl: PID file "/usr/local/pgsql/data/postmaster.pid" does not existIs server runnin…

php生成appid,PHP生成腾讯云COS签名

目标使用 PHP 创建 COS 接口所需要的请求签名步骤按照官方示例(也许是我笨&#xff0c;我怎么读都觉得官方文档结构费劲&#xff0c;示例细节互相不挨着&#xff0c;容易引起歧义)&#xff0c;请求签名应用在需要身份校验的场景&#xff0c;即非公有读权限时。否则在请求API接口…

es6作用域

作用域 一个需要注意的地方是&#xff0c;如果参数默认值是一个变量&#xff0c;则该变量所处的作用域&#xff0c;与其他变量的作用域规则是一样的&#xff0c;即先是当前函数的作用域&#xff0c;然后才是全局作用域。 var x 1;function f(x, y x) {console.log(y); }f(2) …

java 有参数的构造函数如何注入_Spring5参考指南:依赖注入

依赖注入依赖注入就是在Spring创建Bean的时候&#xff0c;去实例化该Bean构造函数所需的参数&#xff0c;或者通过Setter方法去设置该Bean的属性。Spring的依赖注入有两种基于构造函数的依赖注入和基于setter的依赖注入。基于构造函数的依赖注入构造函数的注入是通过构造函数的…

深度学习去燥学习编码_我们问了15,000个人,他们是谁,以及他们如何学习编码

深度学习去燥学习编码More than 15,000 people responded to the 2016 New Coder Survey, granting researchers an unprecedented glimpse into how adults are learning to code.超过15,000人对2016年新编码器调查做出了回应&#xff0c;使研究人员对成年人如何学习编码有了前…

CentOS 7 vi编辑命令

用vi打开一个yum文件 vi /usr/bin/yum 按 i 键后 进入insert模式&#xff0c;进入insert模式后才能进行修改 修改完成后 按esc键进入command模式&#xff0c; 然后:wq 保存文件并退出vi&#xff08;注意先冒号&#xff09; 保存命令 按ESC键 跳到命令模式&#xff0c;然后&…

C#学习笔记四: C#3.0自动属性匿名属性及扩展方法

前言 这一章算是看这本书最大的收获了, Lambda表达式让人用着屡试不爽, C#3.0可谓颠覆了我们的代码编写风格. 因为Lambda所需篇幅挺大, 所以先总结C#3.0智能编译器给我们带来的诸多好处, 下一遍会单独介绍Lambda表达式. 这篇主要包括的内容有: 自动属性,隐式类型,对象集合初始化…

array keys php,php array_keys与array_search的简单使用

函数说明array_keys():返回数组中所有的键名。array_search():在数组中搜索给定的值&#xff0c;如果成功则返回相应的键名。//根据一个key返回关联数组中的另一个key,并且不使用foreach// function array_key_relative(array $array, string $current_key, int $offset)functi…

RESTful服务的第三部分:HATEOAS和Richardson成熟度模型

by Sanchit Gera通过Sanchit Gera RESTful服务的第三部分&#xff1a;HATEOAS和Richardson成熟度模型 (RESTful Services Part III : HATEOAS and The Richardson Maturity Model) In Part I of this series, you learned the very basics of HTTP. We went over common HTTP …

mongdb集群3.4 shard 模式

从图中可以看到有四个组件&#xff1a;mongos、config server、shard、replica set。mongos&#xff1a;数据库集群请求的入口&#xff0c;所有的请求都通过mongos进行协调&#xff0c;不需要在应用程序添加一个路由选择器&#xff0c;mongos自己就是一个请求分发中心&#xff…

我想变得富有的10个理由

1.我想和娘家人住得近一些&#xff0c;可以经常见面、聊天、逛街、吃饭。我们需要彼此的时候&#xff0c;可以马上赶到。 2.我想在家人病痛的时候&#xff0c;能得到最好的救治。 3.我想住在干净宽敞的大房子里&#xff0c;不要和长辈住^_^ 4.我希望不用我动手&#xff0c;家里…

alpha值计算 qcolor_量化交易与机器学习(四):如何研究alpha因子

算法交易策略由指示何时购买或出售资产以产生相对于基准&#xff08;例如指数&#xff09;的较高回报的信号驱动。 资产回报率中未通过暴露于该基准而无法解释的部分称为alpha&#xff0c;因此旨在产生这种不相关收益的信号也称为alpha因子。本章主要介绍alpha因子一、从数据到…

项目启动及需求分析(靳嘉豪、胡新宇、李晨曦、杨航、李瑶)团队作业

&#xff08;1&#xff09; 这次团队我们给我们团队起的名字是&#xff1a;桥上吊刀刀倒吊着 队员分别为&#xff1a;靳嘉豪、胡新宇、李晨曦、李瑶、杨航。 队训为&#xff1a;黑化肥挥发发灰会挥发。 胡新宇&#xff1a;http://www.cnblogs.com/hxy94264/ 靳嘉豪&#xff1a;…

java两种传参,有关java参数的两种传递机制

值传递&#xff1a;方法调用时&#xff0c;实际参数把它的值传递给对应的形式参数&#xff0c;方法执行中形式参数值的改变不影响实际参 数的值。引用传递&#xff1a;也称为传地址。方法调用时&#xff0c;实际参数的引用(地址&#xff0c;而不是参数的值)被传递给方法中相对应…

tcp选项部分编码_学习编码中最难的部分也是最有趣的部分

tcp选项部分编码by Corey Slaven通过Corey Slaven 学习编码中最难的部分也是最有趣的部分 (The hardest part of learning to code is also the funnest part) “The more you know, the more you know you don’t know.”“知道的越多&#xff0c;知道的越多。” ― Aristotl…

SCU 4439 Vertex Cover(二分图最小覆盖点)题解

题意&#xff1a;每一条边至少有一个端点要涂颜色&#xff0c;问最少涂几个点 思路&#xff1a;最小顶点覆盖&#xff1a;用最少的点&#xff0c;让每条边都至少和其中一个点关联&#xff0c;显然是道裸最小顶点覆盖题&#xff1b; 参考&#xff1a;二分图 代码&#xff1a; #i…

20155229 实验一《Java开发环境的熟悉》实验报告

20155229 实验一《Java开发环境的熟悉》实验报告 实验内容 1.使用JDK编译、运行简单的Java程序&#xff1b; 2.使用Idea 编辑、编译、运行、调试Java程序。 实验步骤 &#xff08;一&#xff09;命令行下Java程序开发 输入 mkdir 20155229命令建立实验目录&#xff0c;用ls查看…

js时间搓化为今天明天_js转时间戳,时间戳转js

js转时间戳转此时此刻的时间1、var timestamp1 (new Date()).valueOf();valueOf() 方法返回指定对象的原始值2、var timestamp2 new Date().getTime();Date.prototype.getTime()方法的返回值一个数值&#xff0c;表示从1970年1月1 日0时0分0秒(UTC&#xff0c;即协调世界时)距…

PHP代码20个实用技巧(转)

这些技巧特别是封装的&#xff0c;相对路径的还是挺好的&#xff0c;本身来自微信公众号&#xff0c;但是我担心以后删除&#xff0c;所以在我的博客上备份一下&#xff08;微信公众号为:菜鸟教程&#xff09; 在这篇文章中我们将看看一些关于PHP开发有用的提示和技巧&#xff…

需求简报_代码简报:NASA将所有研究成果发布为开放数据

需求简报Here are three stories we published this week that are worth your time:这是我们本周发布的三个值得您关注的故事&#xff1a; With open data, you finally get what you’ve paid for all these years: 4 minute read 有了开放的数据&#xff0c;您终于可以得到…