1. 先从一道面试题说起

请问下面的

public class Demo {
public static void main(String args[]){
String a = "a" + "b" + 1;
String b = "ab1";
System.out.println(a == b);
}
}

要了解这个问题,需要回答下面的几个问题:

  1. 关于“ ==”是做什么的?
  2. equals 呢?
  3. a和b在内存中是什么样的?
  4. 编译时优化方案。

2. 关于==

在Java语言中,“”就是对比两个内存单元的内容是否一样。

如果是原始类型byte,boolean,short,char,int,long,float,double,就是直接比较它们的值。

如果是引用,比较的就是引用的值,“引用的值”可以被认为对象的逻辑地址。如果两个引用发生“”操作,就是比较相应的两个对象的地址值是否一样。换句话说,如果两个引用所保存的对象是同一个对象,则返回true,否则返回false(如果引用指向的是null,其实这也是一个jvm赋予给它的某个指定的值)。

看下面的代码:

public class Demo {
public static void main(String args[]){
List<String> a = null;
List<String> b = null;
System.out.println(a == b);
}
}
// 输出结果
true

3. 关于“equals()”方法

“equals()”方法,首先是在Object类中被定义的,它的定义中就是使用“==”方式来匹配的。

//equals在Object类中的源码
public boolean equals(Object obj) {
return (this == obj);
}

也就是说,如果不去重写equals()方法,并且对应的类其父类中没有重写过equals()方法,那么默认的equals()操作就是对比对象的地址。

equals()方法之所以存在,是希望子类去重写这个方法,实现对比值的功能。

3. a和b在内存中是什么样的?

a和b在内存中是指向同一块内存空间的。这就得益于Java的编译时优化方案。

我们用反编译软件jd-gui看看编译后的代码是怎么样的?

import java.io.PrintStream;

public class Demo
{
public static void main(String[] args)
{
String a = "ab1";
String b = "ab1";
System.out.println(a == b);
}
}

看到这里结果应该就一目了然了。JVM会把常量叠加在编译时进行优化,因为常量叠加得到的是固定的值,无须运行时再进行计算,所以会这样优化。

看到这里别着急,JVM只会优化它可以帮你优化的部分,它并不是对所有的内容都可以优化。例如,就拿上面叠加字符串来说,如果几个字符串叠加出现了变量,即在编译时还不确定具体的值是多少,那么JVM是不会去做这样的编译时合并的。

如果上面的这段话你理解了,我们再来看一个例子:

public class Demo {
public static void main(String args[]){
String a = "a";
final String c ="a"; String b = a + "b";
String d = c + "b";
String e = getA() + "b";
String compare = "ab"; System.out.println( b == compare);
System.out.println( d == compare);
System.out.println( e == compare);
} private static String getA(){
return "a";
}
}
//输出结果:
false
true
false

根据我们上面的解释,判断bcompare和ecompare输出结果为false,这个比较容易理解,因为a和getA()并不是一个常量,编译时并不会对此进行优化,我们用jd-gui可靠编译后的代码:

import java.io.PrintStream;

public class Demo
{
public static void main(String[] args)
{
String a = "a";
String c = "a"; String b = a + "b";
String d = "ab";
String e = getA() + "b";
String compare = "ab"; System.out.println(b == compare);
System.out.println(d == compare);
System.out.println(e == compare);
} private static String getA()
{
return "a";
}
}

从编译后的代码,我们可以验证我们的结论,b和e并没有被JVM优化。

比较奇怪的是变量d,被JVM优化了。区别在于对叠加的变量c有一个final修饰符。从定义上强制约束了c是不允许被改变的,由于final不可变,所以编译器自然认为结果是不可变的。

4. 内存中的字符串(详细解释)

字符串对象内部是用字符数组存储的,那么看下面的例子:

String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");

这些语句会发生什么事情?大概是这样的:

  1. 会分配一个11长度的char数组,并在常量池分配一个由这个char数组组成的字符串,然后由m去引用这个字符串。
  2. 用n去引用常量池里边的字符串,所以和m引用的是同一个对象
  3. 生成一个新的字符串,单内部的字符数组引用着m内部的字符数组。
  4. 同样会生成一个新的字符串,但内部的字符数组引用常量池里边的字符串内部的字符数组,意思是和u是同样的字符数组。

我们使用图来表示的话,情况就大概是这样的:

结论就是,m和n是同一个对象,但m,u,v都是不同的对象,但都使用了同样的字符数组,并且用equal判断的话也会返回true。

我们可以使用反射修改字符数组来验证一下效果:

public class Demo {
public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException {
String m = "hello,world";
String n = "hello,world";
String u = new String(m);
String v = new String("hello,world");
Field f = m.getClass().getDeclaredField("value");
f.setAccessible(true);
char[] cs = (char[]) f.get(m);
cs[0] = 'H';
String p = "Hello,world";
System.out.println(m.equals(p));
System.out.println(n.equals(p));
System.out.println(u.equals(p));
System.out.println(v.equals(p));
}
}
//输出结果:
true
true
true
true

从上面的例子可以看到,经常说的字符串是不可变的,其实和其他final类没有什么区别,还是引用不可变的意思。虽然String类不开放value,但同样是可以通过反射进行修改。

5. 关于String中的intern方法

public class Demo {
public static void main(String args[]){
String a = "a";
String b = a + "b";
String c = "ab";
String d = new String(b);
System.out.println(b == c);
System.out.println(c == d);
System.out.println(c == d.intern());
System.out.println(b.intern() == d.intern());
}
}
//输出结果
false
false
true
true

String引用所指向的对象,它们存储在常量池中,同一个值的字符串保证全局唯一。

如何保证全局唯一呢? 当调用intern()方法时,JVM会在这个常量池中通过equals()方法查找是否存在等值的String,如果存在,则直接返回常量池中这个String对象的地址;若没有找到,则会创建等值的字符串,然后再返回这个新创建空间的地址。只要是同样的字符串,当调用intern()方法时,都会得到常量池中对应String的引用,所以两个字符串通过intern()操作后用等号是可以匹配的。

关于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. Excel公式-求最低价网站名字

    p{ font-size: 15px; } .alexrootdiv>div{ background: #eeeeee; border: 1px solid #aaa; width: 99%; ...

  2. POPTEST老李分享session,cookie的安全性以及区别 1

    POPTEST老李分享session,cookie的安全性以及区别   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程 ...

  3. 老李分享: 并行计算基础&编程模型与工具 2

    2.并行编程模型和工具 – MPI – MPI(Message Passing Interface)是一种消息传递编程模型,服务于进程通信.它不特指某一个对它的实现,而是一种标准和规范的代表,它是一种 ...

  4. Codeforces 392C Yet Another Number Sequence (矩阵快速幂+二项式展开)

    题意:已知斐波那契数列fib(i) , 给你n 和 k , 求∑fib(i)*ik (1<=i<=n) 思路:不得不说,这道题很有意思,首先我们根据以往得出的一个经验,当我们遇到 X^k ...

  5. 博客搬到CSDN了

    新博客地址: http://blog.csdn.net/enlangs

  6. AOP杂谈

    1.什么是AOP? Spring 2大特性: IOC (Inverse of Control)和 AOP(Aspect Oriented Programming) PS: AOP:面向切面编程  设计 ...

  7. Python学习_argsparse

    # -*- coding: utf-8 -*- import argparse args = "-f hello.txt -n 1 2 3 -x 100 -y b -z a -q hello ...

  8. PAT 1057

    Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...

  9. clamav 杀毒软件安装及使用配置

    安装clamav 之前还需要安装zlib 要不然安装过程中会报错的. tar -zxvf  zlib-1.2.3.tar.gz cd zlib-1.2.3 ./configure make make ...

  10. Devexpress 中对RedailMenu的使用

    最近项目中用到RadialMenu,效果图如下所示: 闲下来就对,devexpress中的RedialMenu的使用总结一下. 第一:假设RedialMenu中全部是BarButtonItem的情况. ...