`
xiaoyu1985ban
  • 浏览: 129935 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Java迷题:等于,还是不等于?

    博客分类:
  • Java
阅读更多

等于还是不等于?

看来看下面的一段代码:

 

   代码片段1

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

 

 这段代码的输出是什么?相信很多人都会很容易的猜到:false,因为a、b两个对象的地址不同,用“==”比较时是false。恭喜你,答对了。

 

再看下面的一段代码:

  

   代码片段2

public static void main(final String[] args) {
    Integer a = 100;
    Integer b = 100;
    System.out.println(a == b); 
}

 

你可能会回答,这没什么不一样啊,所以还是false。很遗憾,如果你执行上面的一段代码,结果是true。

 

上面的代码可能让你有些意外,那好吧,再看看下面的这段代码:

 

    代码片段3

public static void main(final String[] args) {
    Integer a = 156;
    Integer b = 156;
    System.out.println(a == b); 
}

 结果是true吗?很遗憾,如果你执行上面的一段代码,结果是false。

 

 感到吃惊吗?那最后再看下面的一段代码:

 

    代码片段4

public static void main(final String[] args) {
    Integer a = Integer.valueOf(100);
    Integer b = 100;
    System.out.println(a == b); 
}

最后的结果,可能你已经猜到了,是true。

为什么会这样?

现在我们分析一下上面的代码。可以很容易的看出,这一系列代码的最终目的都是用“==”对两个对象进行比较。Java中,如果用“==”比较两个对象结果为true,说明这两个对象实际上是同一个对象,false说明是两个对象。

 

现在,我们来看看为什么会出现上面的现象。

 

我们先看代码片段4:最后的运行结果是true,说明a、b两个对象实际上是同一个对象。但是a对象是通过调用Integer的valueOf方法创建的,而b对象是通过自动装箱创建出来的,怎么会是同一个对象呢?难道问题在字节码那里,毕竟Java程序是依靠虚拟器运行字节码来实现的。

 

通过jdk中自带的工具javap,解析字节码,核心的部分摘取如下:

   0:	bipush	100
   2:	invokestatic	#16; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   5:	astore_1
   6:	bipush	100
   8:	invokestatic	#16; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

代码中我们只调用了一次Integer.valueOf方法,但是字节码中出现了两次对Integer.valueOf方法的调用。那么另一次是哪里呢?只可能在自动装箱时调用的。因此这段代码实际上等价于:

public static void main(final String[] args) {
    Integer a = Integer.valueOf(100);
    Integer b = Integer.valueOf(100);
    System.out.println(a == b); 
}

 现在问题就简单了:看jdk源代码,查看valueOf方法的具体实现:

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

 

看到这儿,上面的代码就很明确了:对于-128到127的数字,valueOf返回的是缓存中的对象。所以两次调用Integer.valueOf(100)返回的都是同一个对象。

我们再先看代码片段3:根据上面的分析,代码片段3实际上等价于以下代码:

public static void main(final String[] args) {
    Integer a = Integer.valueOf(156);
    Integer b = Integer.valueOf(156);
    System.out.println(a == b); 
}

 由于156不在-128到127范围内,所以两个对象都是通过new Integer()的方式创建的,所以最后结果为false。

 

 片段1和片段2就不做具体分析了,相信读者可以自行分析。

 

 最后,请大家思考一下问题:通过上面的分析,了解到整数的自动装箱是通过Integer.valueOf(int number)实现的,那么自动拆箱是如何实现的呢?

    

 

声明:

文章来自于ITeye,欢迎访问我的博客:xiaoyu1985ban.iteye.com

ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。若作者同意转载,必须以超链接形式标明文章原始出处和作者。

63
9
分享到:
评论
38 楼 lantian_123 2012-02-04  
期望更多高质量的文章
37 楼 kjj 2012-02-04  
据说对象相等得用equals,对于int == 足够了, 而 Integer是对象,需要equals ,你懂得!!
36 楼 zhangkaitao 2012-02-04  
35 楼 zhangkaitao 2012-02-04  
之前看到过类似的帖子  http://sishuok.com/forum/blogPost/list/500.html
34 楼 greatwqs 2012-02-03  
    public final class Integer extends Number implements Comparable<Integer> {
    private static class IntegerCache {
	private IntegerCache(){}

	static final Integer cache[] = new Integer[-(-128) + 127 + 1];

	static {
	    for(int i = 0; i < cache.length; i++)
		cache[i] = new Integer(i - 128);
	}
    }


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

}


装箱: 自动Integer.valueOf();
Integer   jjj = 1;
编译后源码:
Integer   jjj = Integer.valueOf(1);


拆箱: 自动对象.intValue();
int       jjj =  new Integer(2);
编译后源码:
int       jjj = (new Integer(2)).intValue();
       
33 楼 shuangpan.zhang 2012-02-03  
受教了。。。
32 楼 zhao_chong 2012-02-03  
多看看源码就什么都有了...
31 楼 JoysXX 2012-02-02  
nice……好文章
30 楼 mj2342003 2012-02-02  
蛮有意思的 呵呵..看源代码有好处...`
29 楼 yekui 2012-02-02  
补充一句,
java使用该机制是为了达到最小化数据输入和输出的目的,这是一种优化措施,提高效率
其他的包装器:
Boolean: (全部缓存)
Byte:    (全部缓存)

Character (   <=127 缓存)
Short     (-128~127 缓存)
Long      (-128~127 缓存)

Float     (没有缓存)
Doulbe    (没有缓存)
28 楼 yekui 2012-02-02  

自动拆箱就更为简单了。

废话不说 看代码
举例:
int i = new Integer(2);
System.out.println(i);

编译后源码为
int i = (new Integer(2)).intValue();
         System.out.println(i);
 

/**
     * The value of the <code>Integer</code>.
     *
     * @serial
     */
    private final int value;

    /**
     * Constructs a newly allocated <code>Integer</code> object that
     * represents the specified <code>int</code> value.
     *
     * @param   value   the value to be represented by the
     * <code>Integer</code> object.
     */
    public Integer(int value) {
this.value = value;
    }

      /**
     * Returns the value of this <code>Integer</code> as an
     * <code>int</code>.
     */
    public int intValue() {
return value;
    }

相信看过源码后,不需要在解释了。
27 楼 taoge2121 2012-02-02  
好文章……
26 楼 yekui 2012-02-02  
解释:
第一个为什么是false呢?很明显Integer b = 100被编译成了Integer b = Integer.valueOf(100);

public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }
100在-128到127之间,这时编译器会从缓存取,继续看缓存代码

private static class IntegerCache {
        static final int high;
        static final Integer cache[];

        static {
            final int low = -128;

            // high value may be configured by property
            int h = 127;
            if (integerCacheHighPropValue != null) {
                // Use Long.decode here to avoid invoking methods that
                // require Integer's autoboxing cache to be initialized
                int i = Long.decode(integerCacheHighPropValue).intValue();
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - -low);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }


IntegerCache 第一次用时依然会被new出来,换句话说,

   Integer a = new Integer(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b);

等同于

Integer a = new Integer(100);
Integer b = new Integer(100);
System.out.println(a == b);


故为false。



第二种情况,
//true
  Integer c = 100; 
  Integer d = 100; 
  System.out.println(c == d); 

编译后Integer c = Integer.valueOf(100);
Integer d = Integer.valueOf(100);
System.out.println(c == d);
实际上第等同于Integer c =  new Integer(100);
Integer d = Integer.valueOf(100); //取缓存,

故为true


第三种情况
Integer e = Integer.valueOf(156);
Integer f = Integer.valueOf(156);
System.out.println(e == f);


由于不在-128到127之间所以不会取缓存。
等同于

Integer e = new Integer(100);
Integer f = new Integer(100);
System.out.println(e == f);

故为false

第四种情况
Integer g = Integer.valueOf(100);
Integer h = Integer.valueOf(100);
System.out.println(g == h);

等同于

Integer g =  new Integer(100);
Integer h = Integer.valueOf(100);//取缓存。

故为true



以上  ‘等同于’    解答适用于单独执行。若一次性执行。Integer g = Integer.valueOf(100);由于第一次已经执行了,后面的都会区缓存。
25 楼 yekui 2012-02-02  
编译之后的结果:
//false
                        Integer a = new Integer(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b);

//true
Integer c = Integer.valueOf(100);
Integer d = Integer.valueOf(100);
System.out.println(c == d);
//false

Integer e = Integer.valueOf(156);
Integer f = Integer.valueOf(156);
System.out.println(e == f);
//true

Integer g = Integer.valueOf(100);
Integer h = Integer.valueOf(100);
System.out.println(g == h);
24 楼 yekui 2012-02-02  
看过代码之后更正一下,

                //false
  Integer a = new Integer(100); 
  Integer b = 100; 
  System.out.println(a == b);  

//true
  Integer c = 100; 
  Integer d = 100; 
  System.out.println(c == d); 
 
 
//false
  Integer e = 156; 
  Integer f = 156; 
  System.out.println(e == f); 
 
//true
  Integer g = Integer.valueOf(100); 
  Integer h = 100; 
  System.out.println(g == h);
23 楼 yekui 2012-02-02  
燈小嗨 写道
通过此方法实现
public int intValue() {return value;}

返回的是自动装箱之前的int类型的值




自动封箱,自动拆箱是编译器内部实现的,至于编译器的具体实现就要看java虚拟机编译原理了。
22 楼 volking 2012-02-02  
我用的jdk是1.6.0_30
Integer valueOf代码有点变化,最大缓存已经不是固定127了,可以手动设定
21 楼 pingfan 2012-02-02  
受教了,谢谢!
20 楼 燈小嗨 2012-02-01  
hanmiao 写道
做J2EE开发快三年了,直到今天才明白这里面的道理,通过这篇文章,我联想下Long,Float和Double是不是都有类似的情况,改天仔细钻研下。

基本类型的包装类除了Float和Double之外都是实现了缓冲机制,而且范围都是-128~127
19 楼 hanmiao 2012-02-01  
做J2EE开发快三年了,直到今天才明白这里面的道理,通过这篇文章,我联想下Long,Float和Double是不是都有类似的情况,改天仔细钻研下。

相关推荐

    回复:Java迷题:等于,还是不等于?

    NULL 博文链接:https://fengshayage.iteye.com/blog/1390146

    《Java迷题解惑》PDF

    有意思的一本书,都是有点难度且又比较有意思的JAVA问题,比如表达式奇数性解惑、找零时刻、长整除、十六进制的趣数等等,特别是这本书的语言也很会激起我们的兴趣,先从实际生活中的故事说起,然后了解所需,最后用...

    java 解惑 挺多java方面的迷题

    挺多java方面的迷题,下载了看看还不错,上传

    java迷题

    描述java语言的设计缺陷

    Java的终极迷题及答案

    这次Java SE的开发工具( JDK )包括了有利于开发applet和应用程序的Java运行环境( JRE环境)及命令行开发工具。 此更新版本的完整内部版本号是1.6.0_10 - b33。外部版本

    java试题精选(带有参考答案)

    java试题精选,里面带有参考答案来的。。。。。

    java 解惑 doubt

    java 解惑 doubt Java迷题

    java基础入门教程

    香 港 则 在 今 年 4月 就 举 行 了 全 岛 的 Java杯 比 赛 ,在 计 算 机界掀 起 了 学 习 Java的热 潮 (尤 其 是 在 大 学 生 们 中 ,出 现 了 一 批 Java迷 )。 有 人 预 言 :Java将 是 网 络 上 的 "世 界 语 ...

    java期末考题&Dreamweaver试题

    java期末考题和Dreamweaver考题各12套

    计算机等级考试Java强化测试题及参考答案.docx

    计算机等级考试Java强化测试题及参考答案.docx

    aiyinsitan.rar_Java 8_aiyinsitan_java smoke_jmonkey_脑筋

    原题为: 1.有5栋5种颜色的房子 2.每一位房子的主人国籍都不同 ... 这道迷题出自1981年柏林的德国逻辑思考学院。 据说世界上只有2%的人能出答案。 就连大名鼎鼎的爱因斯坦也成为此题大伤脑筋。

    java-web网上书店系统课程设计.doc

    烟庇眶例西涝器垣衡庶锹灸龋利席凹肇棺风霄亚核镰玉萎贰攀蔽齿月檬汾原赛丝匝澡当 橱瘴翟篙筑涡喷迷惺琅惯啪镊妒贯撮或勘淌根褂慎精柳涯颂借杖扣执捐篮育膏揪涯肝滇 摆涨梅锐踪劣渣烯谩莆茨芭予蒲沤柜裙篱嗽汰抵还培...

    万得信息技术Java开发面经.pdf

    场景题:集群服务器宕机解决⽅案 8.MongoDB和redis区别,分别何时使⽤,MongoDB讲⼀下 9.完全平衡⼆叉树简述,红⿊树简述 10.mysql引擎底层 11.索引优化(什么时候⽤索引,什么时候不能⽤) 12.索引⼯作流程 13....

    数据结构与算法java版.rar

    本书提供了学习经典数据结构和算法的新...本书主要特点:在全书中使用Java 1.5的新特性,如泛型类型;使用行业标准统一建模语言来绘制类图和实例图;包含数百个习题、复习题和项目;本书给出了所有代码半均可在线获得。

    java变量作用域笔试题-arquillian-testrunner-spock:ArquillianSpockTestRunner

    java引发作用域笔试题Spock Arquillian 扩展 在容器中进行 BDD 测试! 它是什么? Arquillian 是在 JBoss.org 开发的测试框架,它使开发人员能够为在嵌入式或远程容器内执行的业务对象编写集成测试——选项包括 ...

    初级java笔试题-nand2tetris:nand2tetris.org课程的解决方案

    初级java笔试题nand2tetris.org 第 1 部分课程文件和答案 课程详情 Nand to Tetris 课程将带您进行自定进度的迷人发现之旅,您将一路从布尔代数和基本逻辑门到构建中央处理单元、存储系统和硬件平台,直至一台可以...

    leetcode第四题-rprater:代理

    第四题工程师和研究迷 我目前正在瑞尔森大学学习工程学,同时正在寻找我的第四次也是最后一次实习。 另一方面,我构建了很酷的 Python 项目并研究了混沌理论。 跟我来这里 我喜欢研究的东西 编码和辅助项目 机器学习...

Global site tag (gtag.js) - Google Analytics