1 前言

1.1 内存分区

Java中有6种存储区域(参考Java的六大存储区域),如下:

  1. 寄存器(register):位于处理器内部,处理速度最快,空间比较珍贵;
  2. 栈(stack):位于通用RAM中,处理速度仅次于寄存器,空间较小。常用于存放对象引用基本类型数据方法调用
  3. 堆(heap):位于通用RAM中,空间较大,常用于存放对象
  4. 静态存储(static):用关键字static来标识一个对象的特定元素是静态的,但对象不会存放在静态存储空间里;
  5. 常量池(constant):Java中常量共有5类:整型常量、浮点型常量、布尔型常量、字符型常量、字符串常量。使用final可定义符号常量,一经赋值,不能修改;
  6. 非RAM存储:存活于程序之外,不受程序控制的数据,在程序没有运行时也可以存在。如:流对象和持久化对象。

1.2 “==”的应用

  • 对于基本类型数据,表示对值进行比较(本质还是比较地址);

基本类型数据存放在栈上,栈具有共享性。比如:int i = 0; int j = 0; i 和 j 共同指向值为0的内存区,当 i 改变时,如 i = 1,i 指向了新的内存区,并没有改变 j 的值。

  • 对于引用数据类型,表示对地址(引用指向的对象地址)进行比较;

1.3 equals() 的应用及源码分析

  • Object类中,表示对地址进行比较;
public boolean equals(Object obj) {
return (this == obj);
}
  • String、Integer、Float、Double、Boolean类中重写了equals() 方法,表示对值进行比较;
String:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
} Integer:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
} Float:
public boolean equals(Object obj) {
return (obj instanceof Float)
&& (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
} Double:
public boolean equals(Object obj) {
return (obj instanceof Double)
&& (doubleToLongBits(((Double)obj).value) == doubleToLongBits(value));
} Boolean:
public boolean equals(Object obj) {
if (obj instanceof Boolean) {
return value == ((Boolean)obj).booleanValue();
}
return false;
}
  • Character类中,使用final修饰了equals() 方法,表示比较值(具体啥原因笔者也不太明白,知道的读者请留言)。
public final boolean equals(Object obj) {
return (this == obj);
}
  • 用户自定义类中,若不重写equals() 方法,表示对地址进行比较;如果想表示对对象内的属性进行比较,可以重写equals() 方法。

2 实验验证

2.1 基本类型数据

为方便打印变量地址,笔者参考了java中打印变量地址,主要调用printAddresses() 方法,

public static void basic() {
int i=0;
printAddresses("i-0",i);
int j=0;
printAddresses("j-0",j);
i=1;
printAddresses("i-1",i);
printAddresses("j-0",j);
}
i-0: 0x6afbe3300
j-0: 0x6afbe3300
i-1: 0x6afbe3380
j-0: 0x6afbe3300

刚开始 i 和 j 都等于0,指向同一个常量,所有地址一样;当 i=1 时,i 指向了新的常量,地址也更换了,但不影响 j 的地址。

这里给出一个逆天的例子,如下:

public static void basic() {
printAddresses("-128",-128); //0x6afbdf300
printAddresses("-128",-128); //0x6afbdf300
printAddresses("127",127); //0x6afbe7280
printAddresses("127",127); //0x6afbe7280 printAddresses("-129",-129); //0x6afe89800
printAddresses("-129",-129); //0x6afe8a400
printAddresses("128",128); //0x6afe8b180
printAddresses("128",128); //0x6afe8bd80
}

通过多次实验发现,只有-128~127之间的常量,相同值地址也相同,超出此范围的常量,内存地址不同。

2.2 String类

public static void string() {
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc"); System.out.println(s1==s2); //true
System.out.println(s1==s3); //false
System.out.println(s3==s4); //false System.out.println(s1.equals(s2)); //true
System.out.println(s1.equals(s3)); //true
System.out.println(s3.equals(s4)); //true
}

内存状态图

字符串常量存储在字符串常量池中,由于String是不可变类,一旦创建好了就不能修改,因此String对象可以被共享而不会导致程序的混乱。

当创建一个字符串常量时,首先在字符串常量池中查找是否已经有相同的字符串被定义,若已经被定义,则直接获取其引用,此时不需要再创建对象;若没有定义,则创建该对象,然后把它加入到字符串常量池中,再将它的引用返回。所以s1和s2指向的常量池中同一字符串对象。

new String("abc") <==> "abc" 和 new String()两个操作,若在字符串常量池中不存在 "abc" ,则会创建一个字符串常量 "abc" ,并将其添加到字符串常量池中;若存在,则不创建。然后 new String() 会在堆中创建一个新的对象,并引用字符串常量 "abc"。即 new String("abc") 可能会创建1个或2个对象(取决于常量池中原来是否有字符串"abc")。所以s3和s4指向的对象不同。

2.3 Integer类

public static void integer() {
int i0=123;
Integer i1=123;
Integer i2=123;
Integer i3=new Integer(123);
Integer i4=new Integer(123); System.out.println(i0==i1); //true
System.out.println(i1==i2); //true
System.out.println(i1==i3); //false
System.out.println(i3==i4); //false System.out.println(i1.equals(i0)); //true
System.out.println(i1.equals(i2)); //true
System.out.println(i1.equals(i3)); //true
System.out.println(i3.equals(i4)); //true
}

Float、Double、Boolean、Character类测试结果一样。

注意:Integer 类中,只有-128~127之间的常量,相同值地址也相同,超出此范围的常量,内存地址不同。笔者追了下源码,发现Integer.valueOf() 方法中设置了常量池的界限,如下:

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

2.4 自定义类Cat

class Cat {
String name;
Integer age; Cat(String name,Integer age){
this.name=name;
this.age=age;
} public boolean equals(Cat cat) {
if (name.equals(cat.name) && age.equals(cat.age)) {
return true;
}
return false;
}
}
public static void cat() {
Cat cat1=new Cat("Tom",1);
Cat cat2=new Cat("Tom",1);
Cat cat3=new Cat(new String("Tom"),new Integer(1));
Cat cat4=new Cat(new String("Tom"),new Integer(1)); System.out.println(cat1==cat2); //false
System.out.println(cat1==cat3); //false
System.out.println(cat3==cat4); //false
System.out.println(cat1.name==cat2.name); //true
System.out.println(cat1.name==cat3.name); //false
System.out.println(cat3.name==cat4.name); //false
System.out.println(cat1.age==cat2.age); //true
System.out.println(cat1.age==cat3.age); //false
System.out.println(cat3.age==cat4.age); //false System.out.println(cat1.equals(cat2)); //true
System.out.println(cat1.equals(cat3)); //true
System.out.println(cat3.equals(cat4)); //true
System.out.println(cat1.name.equals(cat2.name)); //true
System.out.println(cat1.name.equals(cat3.name)); //true
System.out.println(cat3.name.equals(cat4.name)); //true
System.out.println(cat1.age.equals(cat2.age)); //true
System.out.println(cat1.age.equals(cat3.age)); //true
System.out.println(cat3.age.equals(cat4.age)); //true
}

​ 声明:本文转自Java中“==”与equals()

Java中“==”与equals()的更多相关文章

  1. 浅谈Java中的equals和==(转)

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...

  2. 浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

  3. Java中的equals和hashCode方法

    本文转载自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要 ...

  4. Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)

    Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例  原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...

  5. java集合(3)- Java中的equals和hashCode方法详解

    参考:http://blog.csdn.net/jiangwei0910410003/article/details/22739953 Java中的equals方法和hashCode方法是Object ...

  6. 【Java学习笔记之二十九】Java中的"equals"和"=="的用法及区别

    Java中的"equals"和"=="的用法及区别 在初学Java时,可能会经常碰到下面的代码: String str1 = new String(" ...

  7. Java中的equals和hashCode方法详解

    Java中的equals和hashCode方法详解  转自 https://www.cnblogs.com/crazylqy/category/655181.html 参考:http://blog.c ...

  8. 关于Java中的equals方法

    关于Java中的equals方法 欢迎转载,但是请填写本人的博客园原址https://www.cnblogs.com/JNovice/p/9347099.html 一.什么是equals方法 equa ...

  9. 沉淀再出发:java中的equals()辨析

    沉淀再出发:java中的equals()辨析 一.前言 关于java中的equals,我们可能非常奇怪,在Object中定义了这个函数,其他的很多类中都重载了它,导致了我们对于辨析其中的内涵有了混淆, ...

  10. 转:Java中的equals和hashCode方法详解

    转自:Java中的equals和hashCode方法详解 Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需要实现特定需求,可能要重写这 ...

随机推荐

  1. 【TouchGFX】Callback

    回调函数模板定义 单参数回调函数模板 实现回调函数接口: 实现合法性检查接口: 实现执行接口: 按键触发回调实现 定义回调数据结构对象 使用回调数据结构构造函数 执行接口实现 整个切换机制的管理主体对 ...

  2. [转帖]umount -fl用法

    https://www.cnblogs.com/xingmuxin/p/8446178.html umount, 老是提示:device is busy, 服务又不能停止的.可以用"umou ...

  3. [转帖]关系模型到 Key-Value 模型的映射

    https://cn.pingcap.com/blog/tidb-internal-2 在这我们将关系模型简单理解为 Table 和 SQL 语句,那么问题变为如何在 KV 结构上保存 Table 以 ...

  4. [转帖]模拟enq: TX - row lock contention争用

    https://www.modb.pro/db/623036 enq: TX - row lock contention它表示一个事务正在等待另一个事务释放被锁定的行.这种等待事件通常发生在并发访问数 ...

  5. Oracle使用临时表与直接关联的性能比较

    Oracle使用临时表与直接关联的性能比较 摘要 自己的数据库水平还是太low了. 之前有很多店理解过. 但是一直理解的不深入. 比如我们这边有很多使用临时表存储中间结果数据 然后对结果数据进行关联查 ...

  6. [转帖]面对龙芯3A5000的逼迫,3A4000要为生存抗争!

      https://baijiahao.baidu.com/s?id=1709233817860985518&wfr=spider&for=pc 龙芯3A5000是龙芯中科自主设计的最 ...

  7. [转帖]Linux性能测试之LTP

    https://www.modb.pro/db/487946 hello,大家好,今天为大家更新一篇关于Linux性能测试的文章,大家都知道在Windows下测试计算机的性能,我们可以使用鲁大师等软件 ...

  8. 编译打包rabbitmq然后一键部署的简单方法

    摘要 之前总结过一版,但是感觉不太全面 想着本次能够将使用中遇到的问题总结一下. 所以本次是第二版 介质下载 rabbitmq 不区分介质的打包文件 rabbitmq-server-generic-u ...

  9. UOS 以及 部分NetworkManager管理linux服务器 设置固定IP地址的办法.

    UOS 以及 部分NetworkManager管理linux服务器 设置固定IP地址的办法. 很多操作系统没法右键 网络连接进行处理 但是发现大部分机器都增加了 NetworkManager 的网络管 ...

  10. Fabric-ca client端初始化过程源码分析

    本文从Fabric-ca源码入手,以newRegisterCommand()函数为例,简单分析client启动时的过程.Fabric-ca源码可以从github.com下载,本文以v1.4.6为例进行 ...