java里的双等号运算到底做了什么

写代码经常会遇到if(xx==xx)这样的判断语句,那么什么情况下用==什么时候不该用,用了之后是什么效果,这种效果是不是我们想要的呢?以下是以编译指令的角度分析一下双等号运算什么情况下会相等,为什么会这样。

先看两个int也就是值类型的比较。

public class Foo{
    int  i1 = 100;
    int  i2 = 100;
    System.out.println(i1 == i2);
}

上面这段代码会输出true还是false呢,毫无疑问是true。但是为什么呢?
看一下编译后的操作指令

bipush  100;  //将int类型数字100压入栈中
istore_1;     //将100弹出保存到当前本地变量1中
bipush  100;  //将int类型数字100压入栈中
istore_2;     //将100弹出保存到当前本地变量2中
getstatic       Field java/lang/System.out:"Ljava/io/PrintStream;";
iload_1;      //将本地变量1压入栈中
iload_2;      //将本地变量2压入栈中
if_icmpne       L18;//比较两个int变量是否相同

执行过程就是把100这个数字压入栈中然后弹出保存到本地变量1,再把这个数字压入栈中保存到本地变量2。然后用if_icmpne把两个100进行比较,所以相同。
那再来看看下面这段代码输出的是true还是false呢?

public static void main(String[] args){
        int i1 = 100;
        Integer i2 = 100;
        System.out.println( i1 == i2 ); 
}

如果根据第一个代码的分析,第一个是将int类型数字直接存在了本地变量1中,第二个将引用类型Integer的引用存在类本地变量2中。那么二者是否还相等呢?来看看编译后的指令

bipush  100;
istore_1;
bipush  100;
invokestatic    Method java/lang/Integer.valueOf:"(I)Ljava/lang/Integer;";
astore_2;   //确实将Integar的引用存在了第二个本地变量里
getstatic       Field java/lang/System.out:"Ljava/io/PrintStream;";
iload_1;
aload_2;
invokevirtual   Method java/lang/Integer.intValue:"()I";   //但是在比较前执行了这个指令 调用了intValue这个方法把int值100拿了出来 所以最终还是两个int值100进行if_icmpne的比较 故而输出还是true
if_icmpne       L24;

由上面的编译后指令可以得知,我们再做=赋值和==比较运算的时候编译器是会为我们做一些额外工作的。接下来我们在比较一下int和float吧,看看编译器喵悄的做了什么

public static void main(String[] args){
        int i = 100;
        float f = 100f;
        System.out.println(i == f); 
}

对应的编译后指令

bipush  100;
istore_1;
ldc     float 100.0f;
fstore_2;
getstatic       Field java/lang/System.out:"Ljava/io/PrintStream;";
iload_1;  //把本地变量1中的int类型数字100拿到栈中
i2f;      //---将int类型转换成float类型等待比较---
fload_2;
fcmpl;    //对两个float类型进行比较

可以看到编译指令将int类型的数字转换成了float类型 结果自然也就相等了。
为什么编译器要进行转换工作呢?因为不同类型的变量找不到指定的编译指令让他们进行运算。
根据上面的实例,那么两个Integer呢?

public static void main(String[] args){
        Integer i1 = new Integer(100);
        Integer i2 = new Integer(100);
        System.out.println(i1 == i2);   
}

可以推断编译器会把两个引用变量的引用放在本地变量中然后拿出来进行比较,因为是两个不同引用变量所以虽然值相同(都是100)但是引用不同所以输出false;
那么Integer和Flaot呢(两个都是引用类型)?

public static void main(String[] args){
        Integer i1 = new Integer(100);
        Float f1 = new Float(100);
        System.out.println(i1 == f1);
}

对不起不同的引用类型不能用==运算符,编译会报错~~~
下面这样呢?

public static void main(String[] args){
        int i1 =  new Integer(100);
        Float f1 = new Float(100);
        System.out.println(i1 == f1);
}

根据上面的那些理论,编译器会做些转换所以不仅编译通过而且会输出true(生成Integer变量->执行intValue方法存入本地变量1中->生成Float变量将变量引用存入本地变量2->本地变量1int值拿出来转换成float类型->本地变量2拿到栈中->执行floatVlaue方法->两个float100值比较)

两个字符串比较

public static void main(String[] args){
        String s1 = "aaa";
        String s2 = "aaa";
        System.out.println(s1 == s2);
}

编译后的指令

ldc     String "aaa";//将aaa这个字符串的引用压入栈中
astore_1;            //保存aaa的引用到本地变量1
ldc     String "aaa";//将aaa这个字符串的引用压入栈中
astore_2;            //保存aaa的引用到本地变量2
getstatic       Field java/lang/System.out:"Ljava/io/PrintStream;";
aload_1;             //取得本地变量1入栈
aload_2;             //取得本地变量2入栈
if_acmpne       L18; //比较连个引用是否相等

两个相同的引用比较,结果固然相等
如果一个是new的String对象呢?

public static void main(String[] args){
        String s1 = "aaa";
        String s2 = new String("aaa");
        System.out.println(s1 == s2);
}

编译后指令

ldc     String "aaa";
astore_1;
new     class java/lang/String; //为new的String对象开辟内存空间
dup;
ldc     String "aaa"; //将aaa的引用押入栈中给接下来的String构造方法使用
//调用String构造方法,方法执行完将会返回一个String对象的引用
invokespecial   Method java/lang/String."<init>":"(Ljava/lang/String;)V";
astore_2; //将新的对象引用存在本地变量2中
getstatic       Field java/lang/System.out:"Ljava/io/PrintStream;";
aload_1;
aload_2;
if_acmpne       L25;

可以看到两个变量引用不再是同一个所以进行比较时得到的结果就是false
如果有一个变量有相加运算呢?

public static void main(String[] args){
        String s1 = "aaa";
        String s2 = "aa"+"a";
        System.out.println(s1 == s2);
}

编译后指令

ldc     String "aaa";
astore_1;
ldc     String "aaa";
astore_2;
getstatic       Field java/lang/System.out:"Ljava/io/PrintStream;";
aload_1;
aload_2;
if_acmpne       L18;

看来编译器直接在编译的时候就把加法算完了。我们看到这个编译结果和上面第一个字符串比较的例子相同。
如果是一个字符串和一个new出来的String相加呢?结果如何?

public static void main(String[] args){
        String s1 = "aaa";
        String s2 = "aa"+new String("a");
        System.out.println(s1 == s2);
}

最主要的就是看+这个运算这次怎么做的

ldc     String "aaa";
astore_1;
new     class java/lang/StringBuilder;
dup;
invokespecial   Method java/lang/StringBuilder."<init>":"()V";
ldc     String "aa";
invokevirtual   Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;";
new     class java/lang/String;
dup;
ldc     String "a";
invokespecial   Method java/lang/String."<init>":"(Ljava/lang/String;)V";
invokevirtual   Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;";
invokevirtual   Method java/lang/StringBuilder.toString:"()Ljava/lang/String;";
astore_2;
getstatic       Field java/lang/System.out:"Ljava/io/PrintStream;";
aload_1;
aload_2;
if_acmpne       L43;

结果就是s2以StringBuilder的方式最后调用toString方法保存了下来那么toString方法有是做了什么呢

public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
}

最后是return了一个new String 那么和第二个字符串比较的例子是相同的了。
总结一下:
如果是基本类型会直接进行比较。
封装基础类型的引用类型和基本类型比较会把指向的基本类型拿出来进行比较。
引用类型就是把引用类型的引用拿来比较。
String也是引用类型,所以也是把引用拿来比较。
不同的类型进行比较时用到的比较指令是不同的;如int用if_icmpne而引用类型用if_acmpne不过这对外在的结果并不太重要。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,631评论 18 399
  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 11,263评论 4 56
  • Java byte code 的学习意义 为啥要学java bytecode,这就跟你问我已经会python了为...
    shanggl阅读 1,668评论 0 3
  • 大概人都有惰性,会在前进的路上,没有同行者而放弃前行。 或者懒得说服或者想的太多。 软件的学习本来学的很早,因为同...
    安妲阅读 183评论 1 0
  • 从回家以后就一直梦到你,梦里的你还是让我紧张、让我卑微,让我觉得自己配不上如此闪耀的你。梦醒后的我有点自嘲,...
    honey菇凉阅读 238评论 0 0