Java中的自动装箱/拆箱是如何工作的?

23
自从JDK 5.0以来,Java引入了自动装箱/拆箱。这个技巧简单又有用,但当我开始测试包装类和基本类型之间的不同转换时,我真的很困惑自动装箱的概念在Java中是如何工作的。例如:

装箱

int intValue = 0;
Integer intObject = intValue;
byte byteValue = 0;
intObject = byteValue; // ==> Error

在尝试不同情况(short, long, float, double)后,唯一被编译器接受的情况是右侧赋值操作符的值类型为int

当我查看Integer.class源代码时,我发现它只实现了一个带有int参数的构造函数。

所以我的结论是,自动装箱的概念基于包装类中实现的构造函数。我想知道这个结论是否正确,或者自动装箱是否使用了其他概念?

拆箱

Integer intObject = new Integer(0);
byte byteValue = intObject; // ==> Error (the same Error with short)
int intValue = intObject; 
double doubleValue = intObject;
我的结论是,包装类将对象中封装的值转换为相应类型(例如Integer ==> int),然后编译器使用通常的原始类型转换规则(byte => short => int => long => float => double)进行自动拆箱。我想知道这个结论是否正确,或者自动拆箱还有其他的概念?

5
你是否阅读了Java语言规范中有关自动装箱的部分?http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.7 - Louis Wasserman
byte byteValue = intObject; - 无论 intObject 是对象还是原始类型,您都意识到这是一个错误,对吗? - vanza
我认为编译器使用Integer.intValue()方法来取消包装值,这正确吗? - Naruto Biju Mode
@LouisWasserman,非常感谢您的链接,它解答了我的问题。 - Naruto Biju Mode
4个回答

20

当有疑问时,请检查字节码:

Integer n = 42;

变成:

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

实际上,在Java中,与构造函数相比(对于其他包装类也是如此),valueOf()被使用。这是有益的,因为它允许缓存,并且不会在每次装箱操作时强制创建一个新对象。

相反的情况如下:

int n = Integer.valueOf(42);

变成:

0: bipush        42
2: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: invokevirtual #22                 // Method java/lang/Integer.intValue:()I
8: istore_1      

intValue()被使用(同样,其他包装类型也是类似的)。这就是所有自动装箱和拆箱的本质。

您可以在JLS §5.1.7JLS §5.1.8中了解有关装箱和拆箱转换的内容。


事实上,我是Java初学者(我不理解字节码),所以当我开始阅读自动装箱的章节时,它只给出了一些自动装箱/拆箱限制的示例,而不是所有规则,然后我尝试解析所有情况,并想确认我的对这个概念的理解是否正确。 - Naruto Biju Mode
@NarutoBijuMode 我认为你的主要误解在于如何进行(取消)装箱。正如我在答案中指出的那样,它只涉及到两个特定方法的调用; valueOf()intValue()(分别)。 - arshajii
谢谢您的帮助:),我认为我是一个愚蠢的人,问了一个我现在无法理解的解释(我应该学习更多关于Java)。谢谢。 - Naruto Biju Mode
我认为隐式装箱/拆箱很昂贵,从未真正推荐过。我想Joshua Bloch可能会这么说吧?那么现在使用隐式转换而不必显式调用valueOf()intValue()是可接受的吗? - tmn

4

可以通过使用javac的开关-XD-printflat来消除这种混淆,这在像这样的情况下非常有用。因此,要解开装箱和拆箱的谜团,您可以编写一个简单的程序,如下所示:

import java.util.*;

public class Boxing{
  public static void main(String[] args){
    Double d1 = 10.123;
    Float  f1 = 12.12f;
    Long   l1 = 1234L;
    Integer i1 = 55555;
    Short   s1 = 2345;
    Byte    b1 = 89;

    double d2 = d1;
    float  f2 = f1;
    long   l2 = l1;
    int    i2 = i1;
    short  s2 = s1;
    byte   b2 = b1;
  }
} 

现在我们将上述文件编译为:

javac -XD-printflat -d src/ Boxing.java

该命令的输出是一个Java文件,其中所有语法糖(泛型、增强的for循环和在这种情况下的装箱拆箱等)被删除。以下是输出内容:

import java.util.*;

public class Boxing {

    public Boxing() {
        super();
    }

    public static void main(String[] args) {
        Double d1 = Double.valueOf(10.123);
        Float f1 = Float.valueOf(12.12F);
        Long l1 = Long.valueOf(1234L);
        Integer i1 = Integer.valueOf(55555);
        Short s1 = Short.valueOf(2345);
        Byte b1 = Byte.valueOf(89);
        double d2 = d1.doubleValue();
        float f2 = f1.floatValue();
        long l2 = l1.longValue();
        int i2 = i1.intValue();
        short s2 = s1.shortValue();
        byte b2 = b1.byteValue();
    }
}

这是Java进行装箱拆箱的方式。使用valueOf和***Value方法。

0

自动装箱和自动拆箱

自动装箱是指当我们尝试将原始数据赋值给对象类型时,它会自动转换为对象类型。这个过程称为自动装箱。而当对象类型转换为原始类型时,它被称为拆箱。请从以下示例中尝试理解。

class Demo{
public static void main(String args[]){
    int x=100;

    //Integer iob=x; //Illegal jdk1.4
    Integer iob=Integer.valueOf(x); //Legal at JDK1.4 =>Boxing

    Integer iob2=x; //Legal JDK1.5 - Autoboxing
    System.out.println(iob2);
}

}

自动装箱的另一个示例

class Demo{
public static void main(String args[]){
    Integer iob=new Integer(100);    
    int x;
    x=iob; //Legal => auto unboxing
    System.out.println(x);
}

}

自动拆箱示例

class Demo{
public static void main(String args[]){
    Integer iob=new Integer(100);
    int x=iob; //Auto unboxing ==>Assignment

}

}

感谢您。。

0

考虑以下代码作为自动拆箱的示例:

System.out.println('b'+ new Integer(63));

以下是上述代码的编译过程:
步骤1:实例化对象整数63,然后自动解包为int 63。
new Integer(63)

步骤2:字符'b'被转换为数值,即98

步骤3:两个值相加:98+63

步骤4:输出为161


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接