`
xiaoyu1985ban
  • 浏览: 132566 次
  • 性别: 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迷题:等于,还是不等于?

    在阅读博客文章《Java迷题:等于,还是不等于?》时,你可能会发现更多关于这个话题的深入讨论,包括在特定场景下的最佳实践和陷阱。通过不断学习和实践,我们可以提升自己的编程技能,避免常见的错误,从而提高代码...

    《Java迷题解惑》PDF

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

    java迷题

    Java中的String对象是不可变的,这意味着每次对String进行修改都会创建新的对象,这可能导致不必要的内存消耗。为避免这个问题,可以使用StringBuilder或StringBuffer类在需要多次修改字符串时。 3. null引用异常...

    java 解惑 挺多java方面的迷题

    为了解决这个问题,我们可以修改条件判断,使其比较`i % 2`是否不等于0,这样就能正确处理负数和正数的情况。此外,如果性能是关键因素,可以使用位操作符`&`代替取余操作符,以提高效率: ```java public static ...

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

    Java是一种广泛使用的面向对象的编程语言,以其跨平台、高性能和丰富的类库而著名。这份“java试题精选”显然旨在帮助学习者检验和提升他们的Java编程技能。试题通常包括基础语法、面向对象概念、异常处理、集合框架...

    Java的终极迷题及答案

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

    java期末考题&Dreamweaver试题

    Java和Dreamweaver是两种截然不同的技术,但它们都在IT行业中扮演着重要的角色。Java是一种广泛使用的编程语言,而Dreamweaver则是Adobe公司开发的一款专业网页设计与开发工具。 在"java期末考题"中,我们可以期待...

    java基础入门教程

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

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

    6. **final关键字**:在Java中,`final`关键字用于声明常量,表示变量一旦赋值后就不能再改变。与C/C++中的`const`类似,但Java没有`const`关键字。 7. **boolean类型**:Java的布尔类型是基本数据类型之一,类型名...

    八数码算法java实现

    为了测试和验证这个Java实现,可能包含了一系列的测试用例,包括但不限于初始状态和目标状态已知的情况,以及一些具有挑战性的迷题。这有助于确保算法的正确性和效率。 总的来说,这个Java项目不仅展示了如何使用...

    Java-Algorithm:算法培训项目

    Java算法是计算机科学中的核心部分,它涉及到一系列用于解决复杂问题和优化计算过程的方法。在"Java-Algorithm:算法培训项目"中,我们主要探讨如何使用Java编程语言来实现各种经典的算法。这个项目旨在帮助开发者...

    aiyinsitan.rar_Java 8_aiyinsitan_java smoke_jmonkey_脑筋

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

    万得信息技术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 项目并研究了混沌理论。 跟我来这里 我喜欢研究的东西 编码和辅助项目 机器学习...

    华为OD题解,共计112道题

    文章作者参考了MISAYAONE提供的题库,并提供了自己的Java解决方案,力求简洁高效,同时也对原有的错误进行了校对。尽管如此,由于没有全部上机实践,不能保证所有代码都能100%通过。 这些题目与LeetCode平台上的...

    12月全国计算机二级考试考前复习点破.docx

    1. 分析错误原因,是因为知识点掌握不牢,还是解题策略不当。 2. 复习相关知识点,确保理解和记忆。 3. 重新做题,直到能够独立正确解答。 4. 定期进行模拟测试,保持对考试环境的适应性。 在复习过程中,考生还应...

Global site tag (gtag.js) - Google Analytics