为什么人们仍然在Java中使用基元类型?

为什么人们仍然在Java中使用基元类型?,java,primitive,primitive-types,autoboxing,jdk1.5,Java,Primitive,Primitive Types,Autoboxing,Jdk1.5,自Java5以来,我们对原语类型进行了装箱/拆箱,以便将int包装为Java.lang.Integer,依此类推 我最近看到很多新的Java项目(如果不是6版的话,肯定需要至少5版的JRE),它们使用int而不是Java.lang.Integer,不过使用后者要方便得多,因为它有一些辅助方法可以转换为long值等 为什么一些仍然在Java中使用原语类型?有什么切实的好处吗?盒装类型的性能较差,需要更多内存。首先是习惯。如果你已经用Java编写了八年的代码,你会积累大量的惰性。如果没有令人信服的理

自Java5以来,我们对原语类型进行了装箱/拆箱,以便将
int
包装为
Java.lang.Integer
,依此类推

我最近看到很多新的Java项目(如果不是6版的话,肯定需要至少5版的JRE),它们使用
int
而不是
Java.lang.Integer
,不过使用后者要方便得多,因为它有一些辅助方法可以转换为
long
值等


为什么一些仍然在Java中使用原语类型?有什么切实的好处吗?

盒装类型的性能较差,需要更多内存。

首先是习惯。如果你已经用Java编写了八年的代码,你会积累大量的惰性。如果没有令人信服的理由,为什么要改变?使用盒装原语并没有任何额外的优势

另一个原因是断言
null
不是有效选项。将两个数字或一个循环变量的和声明为
Integer
,将是毫无意义和误导的


它也有性能方面的问题,虽然性能差异在很多情况下并不重要(尽管当性能差异很大时,它非常糟糕),但没有人喜欢编写代码,这些代码可以以我们已经习惯的更快的方式轻松编写。

对象比原始类型更重,因此,基本类型比包装器类的实例更有效

基元类型非常简单:例如,int是32位的,在内存中正好占用32位,可以直接操作。整数对象是一个完整的对象,它(像任何对象一样)必须存储在堆上,并且只能通过对它的引用(指针)进行访问。它很可能还占用超过32位(4字节)的内存

这就是说,Java在原语和非原语类型之间有区别这一事实也是Java编程语言时代的标志。较新的编程语言没有这种区别;这种语言的编译器足够聪明,可以自己判断您是使用简单值还是更复杂的对象

例如,在Scala中没有基元类型;整数有一个类Int,而Int是一个实对象(您可以在其上使用方法等)。编译器编译代码时,会在后台使用基本Int,因此使用Int与在Java中使用基本Int一样有效

基本类型:

int x = 1000;
int y = 1000;
现在评估:

x == y
x == y
这是真的。毫不奇怪。现在,请尝试装箱类型:

Integer x = 1000;
Integer y = 1000;
现在评估:

x == y
x == y
它是
false
。可能取决于运行时。这就足够了吗?

在Joshua Bloch的第5项:“避免创建不必要的对象”中,他发布了以下代码示例:

public static void main(String[] args) {
    Long sum = 0L; // uses Long, not long
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}
结果:

false
false
true
false
编辑为什么(3)返回
true
和(4)返回
false


因为它们是两个不同的对象。JVM缓存了最接近零[-128;127]的256个整数,因此它们为这些整数返回相同的对象。但是,超出该范围后,它们不会被缓存,因此会创建一个新对象。为了使事情更加复杂,JLS要求至少缓存256个flyweights。JVM实现者可以根据需要添加更多,这意味着这可以运行在一个系统上,其中最近的1024个被缓存,并且所有这些都返回true#笨拙的

基本类型的速度要快得多:

int i;
i++;

整数(所有数字和字符串)是一种不可变的类型:一旦创建,它就不能更改。如果
i
是整数,那么
i++
将创建一个新的整数对象-在内存和处理器方面要昂贵得多。

除了其他人所说的以外,原始局部变量不是从堆中分配的,而是在堆栈中分配的。但是对象是从堆中分配的,因此必须进行垃圾收集。

你真的能想象

  for (int i=0; i<10000; i++) {
      do something
  }

for(inti=0;i很难知道在幕后进行了什么样的优化

对于本地使用,当编译器有足够的信息进行优化时,排除空值的可能性,我希望性能相同或相似

然而,原语数组显然来自装箱原语的集合。考虑到集合中很少有优化是可能的,这是有道理的

此外,
Integer
int
相比,具有更高的逻辑开销:现在您必须担心
int a=b+c;
是否引发异常


我会尽可能多地使用原语,并依赖工厂方法和自动装箱,在需要时为我提供语义更强大的装箱类型。

自动拆箱可能导致难以识别NPE

Integer in = null;
...
...
int i = in; // NPE at runtime

在大多数情况下,
中对
的空赋值比上面要明显得多。

顺便说一句,Smalltalk只有对象(没有原语),但是他们优化了小整数(不使用所有32位,只有27位或更多),不分配任何堆空间,只使用特殊的位模式。还有其他常见对象(true、false、null)在这里有特殊的位模式


因此,至少在64位JVM(具有64位指针命名空间)上,应该可以完全没有整数、字符、字节、短、布尔、浮点(和小长)对象(除了由显式
new…()
创建的对象),只有特殊的位模式,普通运算符可以非常有效地对其进行操作。

我不敢相信没有人提到我认为最重要的原因: “int”比“Integer”更容易输入。我认为人们低估了简洁语法的重要性。性能并不是避免使用它们的真正原因,因为大多数的ti
Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.
int loops = 100000000;

long start = System.currentTimeMillis();
for (Long l = new Long(0); l<loops;l++) {
    //System.out.println("Long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start));

start = System.currentTimeMillis();
for (long l = 0; l<loops;l++) {
    //System.out.println("long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));
Integer loop1 = new Integer(0);
for (loop1.lessThan(1000)) {
   ...
}
Integer loop1 = new Integer(1000);
for (loop1.greaterThan(0)) {
   ...
}
public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}
long bigNumber = Integer.MAX_VALUE + 2;
long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now (it is '2L').
Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling