【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;
// 1
System.out.println(a == b);
// 2
System.out.println(a.equals(b));
// 3
System.out.println(a == c);
// 4
System.out.println(a.equals(c));
// 5
System.out.println(a == e);
// 6
System.out.println(a.equals(e));
a = 200;
b = 200;
// 7
System.out.println(a == b);
// 8
System.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自动装箱、拆箱 ,简单点说,装箱就是把基本类型升级为对象包装类型;拆箱就是把对象包装类型转换为对应的基本类型。
会自动拆箱、装箱的数据类型为:
对象类型 | 基本类型 |
---|---|
Integer | int (4字节) |
Short | short (2字节) |
Float | float (4字节) |
Double | double (8字节) |
Byte | byte (1字节) |
Long | long (8字节) |
Character | char (2字节) |
Boolean | boolean (未定) |
下面让我们来看下具体的装箱、拆箱过程:我们编写一个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_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 120
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: invokevirtual #3 // Method java/lang/Integer.intValue:()I
10: istore_2
11: 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 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;
}
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;
}
@Override
public 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比较的是值了,跟前面的口诀保持一致了。
【JAVA基础】一:聊聊笔试常见到的 “==、equal” 比较是否相等的内在差别的更多相关文章
- Java基础——面试、笔试
网址来源: http://www.nowcoder.com/discuss/5949?type=0&order=0&pos=4&page=2 参考资料:(java方面的一些面试 ...
- 【Java基础】浅谈常见设计模式
Num1:单例模式 基本概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 常见写法: 懒汉式 public class Singleton { /* 持有私有静态实例,防止被引用,此处赋值 ...
- Java基础常见英语词汇
Java基础常见英语词汇(共70个) ['ɔbdʒekt] ['ɔ:rientid]导向的 ['prəʊɡræmɪŋ]编程 OO: object ...
- Android开发面试经——3.常见Java基础笔试题
Android开发(29) 版权声明:本文为寻梦-finddreams原创文章,请关注:http://blog.csdn.net/finddreams 关注finddreams博客:http:/ ...
- 聊聊java基础,int值强制类型转换成byte
聊聊java基础,int值强制类型转换成byte 知识点:byte.short.char在表达式中会自动提升为int 之前做一个应用时,打印IP地址,因为是用4个byte存储的,所以打印的时候值范围是 ...
- Java英文单词Java基础常见英语词汇
Java英文单词Java基础常见英语词汇(共70个) ...
- Java基础-JAVA中常见的数据结构介绍
Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...
- Java基础-数组常见排序方式
Java基础-数组常见排序方式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 数据的排序一般都是生序排序,即元素从小到大排列.常见的有两种排序方式:选择排序和冒泡排序.选择排序的特 ...
- Java基础语法(8)-数组中的常见排序算法
title: Java基础语法(8)-数组中的常见排序算法 blog: CSDN data: Java学习路线及视频 1.基本概念 排序: 是计算机程序设计中的一项重要操作,其功能是指一个数据元素集合 ...
随机推荐
- Numpy系列(十二)- 矩阵运算
numpy模块中的矩阵对象为numpy.matrix,包括矩阵数据的处理,矩阵的计算,以及基本的统计功能,转置,可逆性等等,包括对复数的处理,均在matrix对象中. class numpy.matr ...
- kubernetes云平台管理实战:deployment通过标签管理pod(十)
一.kubectl run命令拓展 1.RC创建 [root@k8s-master ~]# kubectl run web --generator=run/v1 --image=10.0.128.0: ...
- Fiddler--Filters
本篇主要介绍Fiddler中Filters(过滤器)选项功能. 先看看Filters的界面: 一.模块一 当勾选“Use Filters”,Filters才开始工作:否则Filters中的设置内容将无 ...
- 新年 flag
在浮躁的年代本不该如此贪多,奈何鸭梨山大...温故知新吧 GO中文社区 深入学习一两门新的编程语言: -Go编程基础 -Go Web基础 -Go名库讲解 rustlang 中文文档 知乎板块 GO 知 ...
- oldboy s21day07(深浅拷贝及文件操作)
#!/usr/bin/env python# -*- coding:utf-8 -*- # 1.看代码写结果'''v1 = [1, 2, 3, 4, 5]v2 = [v1, v1, v1]v1.app ...
- HTML(八)HTML meta标签&base标签
HTML meta元素 标签(meta-information)用于提供页面有关的元数据,除了提供文档字符集.使用语言.作者等基本信息外,还涉及对关键词和网页等级的设定.通过设置不同的属性,元数据可以 ...
- 阿里巴巴图标库iconfont上传svg后,显示不了图片
AI里面选中图形,点对象-路径-轮廓化描边
- [再寄小读者之数学篇](2014-06-20 求极限---Jordan 不等式的应用)
证明: 当 $\lm<1$ 时, $\dps{\lim_{R\to+\infty} R^\lm\int_0^{\pi/2} e^{-R\sin\tt}\rd \tt=0}$. 证明: 由 $$\ ...
- DUMP101 企业级电商FE
需求拆分原则 1. 单个迭代不能太大 2. 需求可交付,功能闭环 3. 成本意识 二八法则 4. 预期价值体现 ……………………………………………………………………………… 做 [直接 git cl ...
- Windows 下 exe 服务注册到 service 服务中
window 下注册 exe 程序.添加到 服务中. window 注册服务方式 一. regsvr 方式 直接在 程序目录执行: Regsvr 32 命令是Windows中控件文件 ( 如扩展名为D ...