`
skcks
  • 浏览: 2039 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
文章分类
社区版块
存档分类

探索java基本类型和包装类型的使用运算符==进行比较的底层细节

    博客分类:
  • java
 
阅读更多

       昨天和其他项目的同事进行了关于基本类型和包装类型的一个比较问题的讨论,某项目组经理质疑比较代码不能正确进行比较。后来和那项目组的同事翻看源码确定了一下Integer Integer.valueOf(String)和int Integer.parseInt(String)内部解析过程区别,但从代码层面确实是看不到一个包装类型Integer和int比较的底层实现方法,就比较主观臆断的想jdk编译后的操作指令将会是int值转化相应的Integer对象,再比较引用。但事后始终觉得不看字节码指令始终不太确定,于是使用javap反编译了生成的抽取关键比较写法的TestIntegerCompare类的字节码,才发现自己的主观臆断是错误的。

      方法valueOf实际是调用了方法parseInt,在没有指定基数的时候都是以十为基数。通过以下代码我们可以清楚的了解到Integer valueOf(String s)实际是调用Integer valueOf(int i),而Integer valueOf(int i)以int parseInt(s,radix))的返回值为参数进行调用;所以当int parseInt(s,radix))的返回值小于等于127、大于等于-128的Integer valueOf(String s)返回的Integer对象是从缓存中取出来的Integer的实例,否则返回一个新的Integer实例,而parseInt则是每次都是返回一个基本类型的int值,和缓存毫无关系。

      而然某项目经理质疑的是一个包装对象和一个基本类型的值进行比较,究竟是比较值还是比较引用。而某项目经理则认为是比较引用,一下做法是错误的。但事实上是比较值,这里jdk编译的是否对我们的对象和值的表的语句做了处理,最后都是比较基本类型的值?一下为讨论的代码,原出于hibernate的query通过unique方法取出一个Object值,实际上该值实际为Integer实例,所以使用一下类似测试代码先转换再比较的代码作为我们这次探索的问题代码。

测试比较代码:

public class TestIntegerCompare{
    public static void main(String[] args) {
        Object obj = new Integer(1024);
        if(Integer.valueOf(obj.toString())==1024){
            System.out.println("bing go! Integer.valueOf(obj.toString())==1024 is true");
        }
    }

}

 jdk 1.6.34 java.lang.Integer.class源码:

public static Integer valueOf(String s, int radix) throws NumberFormatException {
    return Integer.valueOf(parseInt(s,radix));
}
public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}
public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
}
public static int parseInt(String s) throws NumberFormatException {
    return parseInt(s,10);
}
 public static int parseInt(String s, int radix)
                throws NumberFormatException
    {   
//省略一系列参数检测
        int result = 0;
        boolean negative = false;
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;
        int multmin;
        int digit;

        if (len > 0) {
            char firstChar = s.charAt(0);
            if (firstChar < '0') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);

                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                digit = Character.digit(s.charAt(i++),radix);
                if (digit < 0) {
                    throw NumberFormatException.forInputString(s);
                }
                if (result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
        } else {
            throw NumberFormatException.forInputString(s);
        }
        return negative ? result : -result;
    }
    

     让我们运行测试代码可以看到测试代码if里面的语句块是可以运行的且输出文字的,但自此我们仍对一个对象和一个基本类型的值进行比较的过程存在疑问。究竟是编译后jdk将int值转换成了Integer实例再与实例Integer实例进行比较引用,还是反之呢。这个疑问让javap帮我们看个究竟,输入javap -v -l Test TestIntegerCompare.class,可以看到以下反编译的jvm指令的呈现。



 
       从上述jvm指令中可以看出来,code 的标号15:确实是调用了valueOf方法解析字符串为Integer实例,但其后标号18:有调用了Integer实例的intValue()方法返回了该实例相应的int值,从而最后比较的实际是int和int进行比较,所以比较结果是true;

      通过上述分析可以确定 “包装对象==相应值” 这样的比较是可行的,但却不是推荐的。因为解析String实例为Integer实例,然后在去Integer实例里面去取的int值进行比较,在此Integer实例就多此一举了,还不如直接使用parseInt解析字符为相应的int值直接进行比较,节省创建一个Integer实例所浪费的资源。

ps:如上述解析有问题,还望各位资深专家前来指正。

 

  • 大小: 34.3 KB
0
0
分享到:
评论
5 楼 skcks 2012-10-20  
shmily2038 写道
Integer i = 128;
System.out.println("bijiao="+i.compareTo(128));
 /**
     * Compares two <code>Integer</code> objects numerically.
     *
     * @param   anotherInteger   the <code>Integer</code> to be compared.
     * @return	the value <code>0</code> if this <code>Integer</code> is
     * 		equal to the argument <code>Integer</code>; a value less than
     * 		<code>0</code> if this <code>Integer</code> is numerically less
     * 		than the argument <code>Integer</code>; and a value greater 
     * 		than <code>0</code> if this <code>Integer</code> is numerically
     * 		 greater than the argument <code>Integer</code> (signed
     * 		 comparison).
     * @since   1.2
     */
    public int compareTo(Integer anotherInteger) {
	int thisVal = this.value;
	int anotherVal = anotherInteger.value;
	return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
    }
封装类从1.2开始就有比较大小的方法了,没你描述的那么麻烦吧

亲,咱们关注的不是同一个特性。你的是使用compareTo方法进行比较,
Integer i = 128;
System.out.println("bijiao="+i.compareTo(128));
这两句表现在字节码指令上会是
 Code:
      stack=4, locals=2, args_size=1
         0: sipush        128
         3: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1      
         7: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: new           #4                  // class java/lang/StringBuilder
        13: dup           
        14: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        17: ldc           #6                  // String bijiao=
        19: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: aload_1       
        23: sipush        128
        26: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        29: invokevirtual #8                  // Method java/lang/Integer.compareTo:(Ljava/lang/Integer;)I
        32: invokevirtual #9                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        35: invokevirtual #10                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        38: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        41: return  

这和使用==编译出来的字节码是完全不同的,请留意0-3、22-29上面字节码相当于
Integer i = Integer .valueOf(128);
System.out.println("bijiao="+i.compareTo(Integer .valueOf(128)));

所以很多时候很多特性都是语言的语法糖。上述分析如有不对,欢迎拍砖。
4 楼 shmily2038 2012-10-19  
Integer i = 128;
System.out.println("bijiao="+i.compareTo(128));
 /**
     * Compares two <code>Integer</code> objects numerically.
     *
     * @param   anotherInteger   the <code>Integer</code> to be compared.
     * @return	the value <code>0</code> if this <code>Integer</code> is
     * 		equal to the argument <code>Integer</code>; a value less than
     * 		<code>0</code> if this <code>Integer</code> is numerically less
     * 		than the argument <code>Integer</code>; and a value greater 
     * 		than <code>0</code> if this <code>Integer</code> is numerically
     * 		 greater than the argument <code>Integer</code> (signed
     * 		 comparison).
     * @since   1.2
     */
    public int compareTo(Integer anotherInteger) {
	int thisVal = this.value;
	int anotherVal = anotherInteger.value;
	return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
    }
封装类从1.2开始就有比较大小的方法了,没你描述的那么麻烦吧
3 楼 skcks 2012-10-19  
lizhiyun21 写道
博主屡次提到某项目经理云云,难道因为讨论技术问题产生过节了 

木有,不是我老大。
2 楼 lizhiyun21 2012-10-19  
博主屡次提到某项目经理云云,难道因为讨论技术问题产生过节了 
1 楼 TXterran 2012-10-19  
写得很细哈,文章有点长,收藏下来慢慢学习了呵呵

相关推荐

    自增(++)和自减(-)运算符.pdf

    - 从Java 5.0开始,它们也适用于基本类型的包装器类(Byte, Short, Integer, Long, Float, Double, Character)。 4. **示例**: ```java public class Test { public static void main(String[] args) { byte b...

    Java基础笔记(包括底层原理)

    运算符包括算术、比较和逻辑运算符,用于控制程序的逻辑流程。用户交互主要涉及标准输入输出流。程序结构如条件语句和循环语句是构建复杂程序的基础。方法是代码的复用单元,包括可变参数。数组是存储相同类型元素的...

    java面试必考点.pdf

    因此,byte、short和char类型及其包装类可以使用switch,因为它们可以隐式转换为int类型。而long和String类型由于无法隐式转换为int类型,所以不支持在switch语句中使用。 7. **自动类型提升与强制类型转换**:Java...

    java2年工作经验简历_java简历.doc

    2. **集合框架**:熟悉Java集合框架,包括ArrayList、LinkedList、HashSet、HashMap等容器的使用,了解它们的底层实现和性能特点,以及如何根据需求选择合适的集合类型。 3. **异常处理**:了解如何使用try-catch-...

    Java与C++的比较

    Java在自动装箱和拆箱机制下,可以将基本数据类型和它们的包装类之间自动转换。而C++支持操作符重载,使得对运算符的使用更为灵活。 3. 继承与多态: Java和C++都支持单继承和多态,但是Java中所有类都隐式继承自...

    Java 疑难杂症_笔记.pdf

    - **装箱**:将基本数据类型转换为对应的包装类型。 - **拆箱**:将包装类型转换回基本数据类型。 #### Integer等包装类型不可变 - `Integer`等包装类的对象一旦创建后,其值不能改变。这使得它们非常适合作为缓存...

    java学习之路(代码小白)

    在Java中,Int是基本数据类型,Integer是其包装类,两者之间可以通过自动装箱和拆箱进行转换。在数据类型转换时,可能会涉及强制类型转换,但可能丢失精度或引发异常。此外,基本数据类型与String之间的转换需要特定...

    JAVA视频教程.pdf

    同时,讲解了StringBuilder和StringBuffer的高效字符串操作,模拟ArrayList容器的底层实现,以及多维数组的使用和矩阵运算。 最后,教程还涵盖了常用的类,如包装类、Date类的使用,以及自动装箱和拆箱的原理,这些...

    柯莱特Java面试题收集

    - **知识点概述**:`switch`语句可以作用在`byte`、`short`、`char`及其包装类型、`int`及其包装类型`Integer`以及枚举类型上。但是不适用于`long`和`String`类型,因为它们无法隐式转换为`int`类型。 ### 6. `...

    Java面试大全

    而使用复合赋值运算符(如+=)时,Java编译器会进行特殊处理,允许该表达式正确编译。 7. Unicode编码与字符存储:Java中的char类型变量用于存储Unicode编码的字符,因此可以存储中文汉字。Unicode编码采用两个字节...

    java考试复习资料

    Java为每种基本数据类型都提供了一个包装类,例如: - `byte`: `Byte` - `short`: `Short` - `int`: `Integer` - `long`: `Long` - `float`: `Float` - `double`: `Double` - `char`: `Character` - `boolean`: `...

    2018年java面试题大全(整理版).docx

    ### 2018年Java面试题大全知识点详解 ...通过以上分析,我们可以看到Java面试题中涉及到了面向对象的基本概念、访问修饰符的使用、基本数据类型与包装类型的区别等多个方面,这些都是Java开发人员必须掌握的核心知识。

    Java面试宝典2011版.

    《Java面试宝典2011版》涵盖了Java面试中常见的各类问题,旨在帮助准备面试的程序员们系统地复习和掌握Java的核心知识和技术要点。以下是对文档标题、描述及部分内容中提到的关键知识点的深入解析: ### Java基础...

    java面试题

    从给定的文件信息中,我们可以提炼出一系列重要...通过以上解析,我们可以看到Java作为一种广泛使用的编程语言,其设计考虑了多种场景下的需求,从基本的数据类型到高级的并发控制,都体现了语言设计的周全性和前瞻性。

    java资料面试题

    - **封装**:将数据和对这些数据的操作包装在一起,隐藏实现细节,仅暴露出必要的接口。 - **继承**:允许创建一个新类来继承现有类的属性和方法,从而实现代码复用。 - **多态**:指同一个接口在不同的上下文中具有...

    《Java开发手册(泰山版)》灵魂13问.pdf

    问题出现在使用自动拆箱机制时,如果&lt;表达式2&gt;或&lt;表达式3&gt;是包装类,如Integer,并且值为null,在自动拆箱转换为基本数据类型时会抛出NPE。开发者在使用三目运算符时需要特别注意这一问题,尤其是涉及到可能为null的...

    java学习路线.pdf

    包装类(如Integer、Double)用于将基本类型和引用类型进行转换,同时提供了一些额外功能。Object类中的常用方法如toString()用于返回对象的字符串表示,getClass()获取对象的运行时类,equals()和hashCode()用于...

    34个java问题.pdf

    - **自动装箱**:将基本数据类型自动转换为对应的包装类,如`int`转为`Integer`。 - **自动拆箱**:将包装类自动转换为基本数据类型。 #### 14. 在一个静态方法内调用一个非静态成员为什么是非法的 静态方法属于类...

Global site tag (gtag.js) - Google Analytics