JavaRush /Java 博客 /Random-ZH /Java 中的自动装箱和拆箱
Viacheslav
第 3 级

Java 中的自动装箱和拆箱

已在 Random-ZH 群组中发布
<h2>简介</h2>一门编程语言,就像人们所说的语言一样,生活和变化,新的现象出现在其中,使语言更方便使用。众所周知,语言应该方便地表达我们的想法。
Java 中的自动装箱和拆箱 - 1
所以,在Java SE 5中,引入了装箱/拆箱机制。Oracle 的一个单独的教程专门介绍了这种表达思想的方式的特性:自动装箱和拆箱。<h2>自动装箱 Boxing</h2>让我们看一个自动装箱 Boxing 的例子。首先,让我们看看它是如何工作的。让我们使用站点compilejava.net并创建一个类:
public class App {
    public static void main(String[] args) {
        Integer portNumber = 8080;
        if (args.length != 0) {
            portNumber = Integer.valueOf(args[0]);
        }
        System.out.println("Port number is: " + portNumber);
    }
}
简单的代码。我们可以指定输入参数并更改端口值。正如我们所见,因为 String我们从参数中读取端口值,我们Integer通过获取它来获取它Integer.valueOf。因此,我们被迫不将其指定为原始类型,而是指定为对象类型Integer。一方面,我们有一个对象变量,默认值是一个基元。它有效。但我们不相信魔法,不是吗?正如他们所说,让我们看看“幕后”。单击“Download ZIP”从compilejava.net 下载源代码。之后,将下载的存档解压到一个目录中并转到该目录。现在让我们这样做:javap -c -p App.class其中 App.class 是您的类的已编译类文件。我们会看到这样的内容:
Java 中的自动装箱和拆箱 - 2
这也是臭名昭著的“字节码”。但现在对我们来说重要的是我们所看到的。首先,8080原语被放置在方法执行堆栈上,然后执行Integer.valueOf。这就是拳击的“魔力”。里面的魔法看起来像这样:
Java 中的自动装箱和拆箱 - 3
也就是说,本质上,将根据数字的值从缓存中取出Integer或获取一个新的Integer(缓存只不过是一个整数数组)。如此幸运的人自然Integer不只一人。有相关原始类型及其包装器(代表 OOP 世界中的原始类型的类)的完整列表。该列表在 Oracle 教程的最底部给出:“自动装箱和拆箱”。值得注意的是,在不连接任何第三方库的情况下,由原语组成的数组没有“包装器”。那些。Arrays.asList不会让int[]我们ListInteger的。<h2>拆箱</h2>装箱的逆过程称为拆箱拆箱。我们看一个拆包的例子:
public class App {

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Please, enter params");
            return;
        }
      	int value = Math.abs(Integer.valueOf(args[0]));
        System.out.println("Absolute value is: " + value);
    }

}
Math.abs仅接受原语。该怎么办?对于这种情况,包装类有一个特殊的方法,它返回一个原语。例如,这是intValueInteger方法。如果我们看一下字节码,它是这样的:
Java 中的自动装箱和拆箱 - 4
显然,没有魔法。一切都在 Java 中。它只是“自行”工作。为了我们的方便。<h2>耙子</h2>
Java 中的自动装箱和拆箱 - 5
任何工具,如果使用不当,都会成为对抗自身的强大武器。Java中的自动装箱/拆箱机制也不例外。第一个明显的比较是通过==. 我认为这已经很清楚了,但我们再看一下:
public static void main(String[] args) {
    Integer inCacheValue = 127;
    Integer inCacheValue2 = 127;
    Integer notInCache = 128; // new Integer(129)
    Integer notInCache2 = 128; // new Integer(129)
    System.out.println(inCacheValue == inCacheValue2); //true
    System.out.println(notInCache == notInCache2); //false
}
在第一种情况下,值是从Integer值缓存中获取的(参见上面装箱的解释),在第二种情况下,每次都会创建一个新对象。但这里值得预订。此行为取决于缓存上限 ( java.lang.Integer.IntegerCache.high )。此外,此限制可能会因其他设置而改变。您可以在 stackoverflow 上阅读有关此主题的讨论:Integer 缓存有多大? 当然,对象需要使用 equals 进行比较: System.out.println(notInCache.equals(notInCache2)); 与相同机制相关的第二个问题是性能。Java中的任何装箱都相当于创建一个新对象。如果该数字不包含在缓存值中(即-128到127),那么每次都会创建一个新对象。如果突然在循环中进行打包(即装箱),这会导致不必要的对象大量增加,并消耗垃圾收集器工作的资源。因此,不要太草率了。第三种同样痛苦的耙子也源于相同的机制:
public static void check(Integer value) {
    if (value <= 0) {
        throw new IllegalStateException("Value is too small");
    }
}
在这段代码中,这个人显然试图不去克服这个错误。但没有检查null. 如果涉及到输入null,那么我们将得到一个不可理解的错误,而不是一个可以理解的错误NullPointerException。因为为了比较,Java会尝试执行value.intValue并崩溃,因为...... value将要null。<h2>结论</h2>装箱/拆箱机制允许程序员编写更少的代码,有时甚至不需要考虑从基元到对象的相互转换。但这并不意味着您应该忘记它是如何工作的。否则,您可能会犯一个可能不会立即出现的错误。我们不应该依赖系统中不完全受我们控制的部分(例如整数边界)。但不要忘记包装类的所有优点(例如Integer)。通常,这些包装类有一组附加的静态方法,可以让您的生活更美好,让您的代码更具表现力。这是一个追赶的例子:
public static void main(String[] args) {
    int first = 1;
    int second = 5;
    System.out.println(Integer.max(first, second));
    System.out.println(Character.toLowerCase('S'));
}
一切的正确结论是,没有魔法,有某种实现。并不是一切都会如我们所期望的那样。例如,没有打包: System.out.println("The number is " + 8); 上面的例子会被编译器优化成一行。也就是说,就好像你写了“数字是 8”。在下面的示例中,也不会出现任何包装:
public static void main(String[] args) {
    System.out.println("The number is " + Math.abs(-2));
}
println当我们将一个对象作为输入并需要以某种方式连接线路 时,会发生什么情况呢?线条……是的,这就是为什么没有这样的包装。有Integer静态方法,但其中一些是package. 也就是说,我们不能使用它们,但在 Java 本身中它们可以被主动使用。这里的情况正是如此。将调用 getChars 方法,该方法根据数字创建字符数组。再说一遍,没有魔法,只有 Java)。因此,在任何不清楚的情况下,你只需要看看实施情况,至少有些东西会就位。#维亚切斯拉夫
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION