在Java和C#中,int和Integer有什么区别?

269

我在阅读更多Joel关于软件的内容时,看到Joel Spolsky提到了一种特定类型的程序员需要知道Java/C#(面向对象编程语言)中intInteger之间的区别。

那么,这两者有什么区别呢?


5
C#没有整数类型。 - Judah Gabriel Himango
26个回答

251
在Java中,'int'类型是一个原始类型,而'Integer'类型是一个对象。
在C#中,'int'类型与System.Int32相同,并且是一个值类型(更像Java的'int')。整数(就像任何其他值类型一样)可以被装箱("包装")成一个对象。
对象和原始数据类型之间的差异有点超出了这个问题的范围,但总结一下: 对象提供了多态性的功能,通过引用传递(或更准确地说是通过值传递引用),并从heap分配。相反,原始数据类型是不可变类型,通过值传递,并且通常从stack中分配。

63
在我看来,“对象是通过引用传递”的说法令人困惑且不正确。更准确的说法是“对象引用是按值传递的”。(此外,原始类型并不总是从栈分配——考虑对象内的原始字段...) - Jon Skeet
5
至少在C#中,int是一种语言关键字,相当于Int32 CLR(实际上是CTS)类型。 - Scott Dorman
6
抱歉,英语中的“pass something by reference”和“pass a reference to something by value”并不是等效的陈述,在编程语言的上下文中也没有相同的含义。 - Alan Krueger
8
这可能准确地描述了Java如何处理此问题,但对于C#来说就是完全错误的。 - Joey
9
为什么这个回答会被投票赞成?这个回答是错误的。它对于Java来说不完全正确,对于C#更是相去甚远。读到这个回答的人会比之前更加对这个话题一无所知。 - Thom Smith
显示剩余5条评论

153

在Java中,int是一种基本类型,而Integer是一个对象。这意味着,如果您创建了一个新的Integer:

Integer i = new Integer(6);

您可以在i上调用一些方法:

String s = i.toString();//sets s the string representation of i

当使用 int 时:

int i = 6;

您不能对其调用任何方法,因为它只是一个基本类型。 因此:

String s = i.toString();//will not work!!!

会产生错误,因为int不是一个对象。

int是Java中很少有的原始类型之一(与char和其他一些类型一起)。我不确定百分之百,但我认为Integer对象基本上只有一个int属性和大量与该属性交互的方法(例如toString()方法)。因此,Integer是使用int的一种高级方式(就像String可能是使用一组字符的高级方式一样)。

我知道Java不是C,但由于我从未在C中编程过,这是我能找到的最接近答案的方式。

Integer对象javadoc

Integer对象与int原始类型的比较


在C#中,int是Int32的同义词,请参见https://dev59.com/B3VD5IYBdhLWcg3wKoaH - ripper234
我不懂Java,但是在Java中没有Integer类型,而是有Int32和Int64等类型,它们都属于结构体,也就是值类型。在C#中,原始类型指的是由CLR团队在FCL(Framework Class Library)中定义的类型,因此它们被称为原始类型。在这种情况下,即使Date对象也被称为原始类型。 - Tarik

40

我将在上面提供的精彩回答基础上添加一些内容,讨论装箱和拆箱,以及这如何适用于Java(虽然C#也有)。我仅使用Java术语,因为我对此更加熟悉。

正如答案所提到的,int只是一个数字(称为未包装类型),而Integer是一个对象(其中包含数字,因此是包装类型)。在Java术语中,这意味着(除了不能在int上调用方法之外),您不能将int或其他非对象类型存储在集合(ListMap等)中。为了存储它们,您必须先将它们装箱成相应的包装类型。

从Java 5开始,有一种称为自动装箱和自动拆箱的东西,允许在幕后完成装箱/拆箱。对比一下:Java 5版本:

Deque<Integer> queue;

void add(int n) {
    queue.add(n);
}

int remove() {
    return queue.remove();
}

Java 1.4或更早版本(也没有泛型):

Deque queue;

void add(int n) {
    queue.add(Integer.valueOf(n));
}

int remove() {
    return ((Integer) queue.remove()).intValue();
}

需要注意的是,尽管Java 5版本中代码很简短,但两个版本都生成相同的字节码。因此,虽然自动装箱和自动拆箱非常方便,因为您可以写更少的代码,但这些操作实际上会在幕后发生,并带有相同的运行时成本,因此您仍然必须意识到它们的存在。

希望这可以帮助到您!


2
Deque在Java 1.5或1.4中不存在,它是在1.6中添加的。 - Leo Izen

29

由于其他帖子中的一些描述与 C# 有关的技术细节不太准确,因此我在这里发帖。

正确的说法:intSystem.Int32 的别名。
错误的说法:float 不是 System.Float 的别名,而是 System.Single 的别名。

在 C# 编程语言中,int 是保留关键字,是 System.Int32 值类型的别名。

然而,floatFloat 并不相同,正确的系统类型是 System.Single。有一些类似的类型也有保留关键字,但这些关键字并不直接匹配类型名称。

在 C# 中,“int”和“System.Int32”,以及其他一些关键字/系统类型对之间没有区别,除了定义枚举时。在枚举中,您可以指定要使用的存储大小,在这种情况下,只能使用保留关键字,而不能使用系统运行时类型名称。

int 变量中的值是存储在堆栈、内存中还是作为引用堆对象存储取决于上下文和如何使用它。

在方法中的声明:

int i;

定义一个类型为 System.Int32 的变量 i,它可以在寄存器或堆栈上运行,具体取决于编译器的优化。同样的声明语句如果出现在一个类型(结构体或类)中,则会定义成员字段。如果出现在方法参数列表中,则会定义一个参数,其存储选项与局部变量相同。(请注意,如果你开始涉及迭代器方法,这段话将不再适用,这是完全不同的情况)

要获取一个堆对象,可以使用装箱操作:

object o = i;

这将在堆上创建一个内容盒子复制i。在IL中,您可以直接访问堆对象上的方法,但在C#中,您需要将其强制转换回int,这将创建另一个副本。因此,在C#中很难更改堆上的对象,而不创建新的包装了新int值的盒子副本。(呃,这一段并不那么容易阅读。)


21

关于Java 1.5和自动装箱,在比较整数对象时会出现一个重要的“怪异”情况。

在Java中,值为-128到127的整数对象是不可变的(也就是说,对于一个特定的整数值,比如23,在你程序中实例化的所有整数值为23的Integer对象指向完全相同的对象)。

例如,下面的代码返回true:

Integer i1 = new Integer(127);
Integer i2 = new Integer(127);
System.out.println(i1 == i2); //  true

虽然这会返回false:

Integer i1 = new Integer(128);
Integer i2 = new Integer(128);
System.out.println(i1 == i2); //  false

== 操作符是按引用比较(变量是否指向同一对象)。

这个结果可能因所使用的JVM而异。Java 1.5中自动装箱的规范要求整数(-128到127)始终装箱到相同的包装器对象。

解决方案?=)在比较Integer对象时,应始终使用Integer.equals()方法。

System.out.println(i1.equals(i2)); //  true

更多信息请查看java.net,示例请参考bexhuff.com


3
使用new 关键字创建的对象在与==比较时,结果总是false。Andreas误将Integer.valueOf(int)与new Integer(int)混淆了。 - McDowell
1
注意:默认值 127 是从 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); 获取的。 - KNU
1
@andnil - 我曾在Stellent与Bex一起工作。他是一个非常好的资源,可以引用bexhuff.com!+1 - bcar
https://dev59.com/cmEi5IYBdhLWcg3w4PdB - Ciro Santilli OurBigBook.com

20

在Java中,JVM有两种基本类型:1)原始类型和2)引用类型。int是原始类型,而Integer是类类型(这是引用类型的一种)。

原始值不与其他原始值共享状态。变量的类型为原始类型时,它始终保存该类型的原始值。

int aNumber = 4;
int anotherNum = aNumber;
aNumber += 6;
System.out.println(anotherNum); // Prints 4

对象是动态创建的类实例或数组。引用值(通常只是引用)是指向这些对象和特殊的null引用的指针,该引用不引用任何对象。同一对象可能有许多引用。

Integer aNumber = Integer.valueOf(4);
Integer anotherNumber = aNumber; // anotherNumber references the 
                                 // same object as aNumber

在Java中,所有东西都是按值传递的。对于对象来说,传递的值是指向该对象的引用。因此,在Java中,int和Integer之间的另一个区别在于它们在方法调用中的传递方式。例如,在...中。
public int add(int a, int b) {
    return a + b;
}
final int two = 2;
int sum = add(1, two);

变量two以原始整数类型2传递。而在

public int add(Integer a, Integer b) {
    return a.intValue() + b.intValue();
}
final Integer two = Integer.valueOf(2);
int sum = add(Integer.valueOf(1), two);

变量two作为一个引用传递给一个持有整数值2的对象。

@WolfmanDragon: 按引用传递的方式可以这样实现:

public void increment(int x) {
  x = x + 1;
}
int a = 1;
increment(a);
// a is now 2

当调用increment时,它会传递一个指向变量a的引用(指针)。而increment函数直接修改变量a。
对于对象类型,它将按以下方式工作:
public void increment(Integer x) {
  x = Integer.valueOf(x.intValue() + 1);
}
Integer a = Integer.valueOf(1);
increment(a);
// a is now 2

现在你看到区别了吗?


根据您的定义,不存在按引用传递。任何被传递的东西都必须有一个值(即使是 null 也是一个值),即使只是指针的值,否则它就是一个空集合。按计算机科学的术语,按引用传递是传递引用(指针)的值。我有点困惑。 - WolfmanDragon

11
在C#中,int仅是System.Int32的别名,string是System.String的别名,double是System.Double的别名等等。
个人而言,我更喜欢使用int、string和double等,因为它们不需要使用“using System;”语句 :) 虽然这听起来很傻,但确实如此...

2
需要补充的是,C# 的 int/Int32 与 Java 的 Integer 不同 - Judah Gabriel Himango

10

使用包装类有许多原因:

  1. 我们可以获得额外的行为(例如我们可以使用方法)
  2. 我们可以存储空值,而在基本类型中我们不能
  3. 集合支持存储对象而不是基本类型。

8
`int` 用于声明原始变量。
e.g. int i=10;

Integer用于创建整型数据类型的引用变量。

Integer a = new Integer();

8

你是否曾经编程过?那么 (int) 是你可以为变量设置的原始类型之一(就像 char,float 等)。

但是 Integer 是一个包装类,你可以使用它对 int 变量执行一些函数操作(例如将其转换为字符串或反之亦然...),但请注意包装类中的方法是静态的,因此你可以随时使用它们而无需创建 Integer 类的实例。 简而言之:

int x;
Integer y; 

x和y都是int类型的变量,但y被一个Integer类包装,并有多个你可以使用的方法。如果你需要调用Integer包装类的一些函数,你可以简单地这样做。

Integer.toString(x);

请注意,x和y都是正确的,但如果你只想将它们用作原始类型,请使用简单形式(用于定义x)。


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