Java是一种面向对象语言,为了能将基本类型视为对象来处理,并能连接相关的方法,Java为每个基本数据类型都提供了包装类,这样便可以把这些基本类型转化为对象来处理。
基本数据类型 | 包装类 | 基本数据类型 | 包装类 |
boolean | Boolean | int | Integer |
byte | Byte | long | Long |
char | Character | float | Float |
short | Short | double | Double |
Java int与integer的区别
int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别:
int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象
1.Java 中的数据类型分为基本数据类型和引用数据类型
int 是前者而integer 是后者(也就是一个类);因此在类进行初始化时int类的变量初始为0.而Integer的变量则初始化为null.
2.初始化时:
int i =1;Integer i= new Integer(1);(要把integer 当做一个类看);但由于有了自动装箱和拆箱
使得对Integer类也可使用:Integer i= 1;
注意:Integer i=1编译时被翻译成Integer i=Integer.valueOf(i);
int 是基本数据类型(面向过程留下的痕迹,不过是对java的有益补充),Integer 是一个类,是int的扩展,定义了很多的转换方法
类似的还有:float Float;double Double等,而且还提供了处理 int 类型时非常有用的其他一些常量和方法
举个例子:当需要往ArrayList,HashMap中放东西时,像int,double这种内建类型是放不进去的,因为容器都是装 object的,这是就需要这些基本类型的包装类了。
Java中int和Integer关系是比较微妙的。关系如下:
1.int是基本的数据类型;
2.Integer是int的包装类;
3.int和Integer都可以表示某一个数值;
4.int和Integer不能够互用,因为他们两种不同的数据类型;
举例说明
ArrayList al=new ArrayList();
int n=40;
Integer nI=new Integer(n);
al.add(n);//不可以
al.add(nI);//可以
并且泛型定义时也不支持int: 如:List
Integer与int互转
int转Integer
int i=0;
Integer wrapperi=new Integer(i);
Integer转int
Integer wrapperi=new Integer(0);
int i=wrapperi;
JDK1.5以后的int转Integer
JDK1.5以后,Java为我们提供了更为丰富的转换方法。
其中最值得一提的就是自动装包/自动拆包(AutoBoxing/UnBoxing)。
此功能大大丰富了基本类型(primitive type)数据与它们的包装类(Wrapper Class)
的使用。
由于AutoBoxing的存在,以下代码在JDK1.5的环境下可以编译通过并运行。
int i = 0;
Integer wrapperi = i;
JDK1.5为Integer增加了一个全新的方法:
public static Integer valueOf(int i)
此方法与new Integer(i)的不同处在于:
valueOf(i)方法返回一个表示指定的int值的Integer实例,new Integer(i)产生一个新的Integer对象。
JDK API文档中对这个新的valueOf方法有明确的解释:
如果不需要新的 Integer 实例,则通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能通过缓存经常请求的值而显著提高空间和时间性能。
但这个解释有点晦涩难懂。为什么该方法有可能通过缓存经常请求的值而显著提高性能?
通过反编译工具查看valueOf方法。
/*
* 返回一个表示指定的 int 值的 Integer 实例。如果不需要新的 Integer 实例,则
* 通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能通过
* 缓存经常请求的值而显著提高空间和时间性能。
* @param i an int
value.
* @return a Integer instance representing i.
* @since 1.5
*/
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
可以看到对于范围在-128到127的整数,valueOf方法做了特殊处理。
采用IntegerCache.cache[i + offset]这个方法。
从名字,我们可以猜出这是某种缓存机制。
进一步跟踪IntegerCache这个类,此类代码如下
/*
* IntegerCache内部类
* 其中cache[]数组用于存放从-128到127一共256个整数
*/
private static class IntegerCache {
private IntegerCache(){}
static final Integer cache[] = new Integer[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
}
这就是valueOf方法真正的优化方法,当-128=127 或 i<-128 时,返回的是Integer类对象。
再举一个经常被提到的例子
Integer i=100;//编译时被翻译成Integer i=Integer.valueOf(100);
Integer j=100;
//print true
System.out.println(i==j);
此时的 i=IntegerCache.cache[i + 128] = IntegerCache.cache[228],
同样j = IntegerCache.cache[j + 128] = IntgerCache.cache[228]
因此 Integer引用i中存储的是cache数组第228号元素的地址。同理j也是同一个cache数组的第228号元素的地址(因为cache是Integer的static数组,只有一个)。
i==j比较的是引用地址,因此返回true。
Integer i=200;
Integer j=200;
//print false
System.out.println(i==j);
此时的 i=new Integer(200); 同样j=new Integer(200) 。
两次都在堆中开辟了Integer的对象。
i 和 j 中存储的堆的对象地址是完全不同的。i==j 自然返回false。
引入缓存机制的作用何在?
接着上面的例子,假如我们在编程时大量需要值为100(100的范围在-128到127之间)的Integer对象。如果只能通过new来创建,需要在堆中开辟大量值一样的Integer对象。 这是相当不划算的,IntegerCache.cache很好的起到了缓存的作用。
当我们需要Integer i = 100的时候,直接从cache中取出第[100+128]号元素的地址赋值给引用i,再次需要Integer j = 100时,还是直接去这个地址赋值给j。是不是省去了在堆中不停的创建对象的代价了(空间,时间上的消耗都很大)。 这就是valueOf方法真正的提高性能之处。
正如JDK API文档对valueOf(int i)方法的描述,该方法有可能通过缓存经常请求的值而显著提高空间和时间性能。
结论
valueOf(int i)的优化只针对于范围在-128到127的整数。
JDK1.5以后的Integer转int
由于UnBoxing的存在,以下代码在JDK1.5的环境下可以编译通过并运行。
Integer wrapperi = new Integer(0);
int i = wrapperi;
附:AutoBoxing与UnBoxing带来的转变
在JDK1.5之前,我们总是对集合不能存放基本类型而耿耿于怀。
以下代码在JDK1.5中成为了可能,试想下在JDK1.5之前该如何实现这段代码?
int x = 1;
Collection collection = new ArrayList();
collection.add(x);//AutoBoxing,自动转换成Integer.
Integer y = new Integer(2);
collection.add(y + 2); //y + 2为UnBoxing,自动转换成int。之后再次转换为Integer。
此特性同样适用于Map
Map map = new HashMap();
int x = 1;
Integer y = new Integer(2);
int z = 3;
map.put(x,y + z);//x自动转换成Integer。y+z自动转换成int。之后再次转换为Integer。
Integer k=100;
Integer k2=100;
Integer k3=324;
Integer k4=324;
Integer s=new Integer(90);
Integer s3=new Integer(90);
int k5=100;
int k6=324;
int k7=324;
System.out.println("k==k2 "+(k==k2));
System.out.println("k3==k4 "+(k3==k4));
System.out.println("k==k5 "+(k==k5));
System.out.println("k6==k7 "+(k6==k7));
System.out.println("s==s3 "+(s==s3));
System.out.println("k3==k7 "+(k3==k7));
k2=k2.valueOf(100);
System.out.println(k2==k);
k=k.valueOf(324);
System.out.println("k==k3 "+(k==k3));
System.out.println("k==k6 "+(k==k6));
输出结果:
k==k2 true
k3==k4 false
k==k5 true
k6==k7 true
s==s3 false
k==s false
k3==k7 true
true
k==k3 false
k==k6 true
总而言之:如果我们定义一个int类型的数,只是用来进行一些加减乘除的运算or作为参数进行传递,那么就可以直接声明为int基本数据类型,但如果要像对象一样来进行处理,那么就要用Integer来声明一个对象,因为java是面向对象的语言,因此当声明为对象时能够提供很多对象间转换的方式,与一些常用的方法。自认为java作为一们面向对象的语言,我们在声明一个变量时最好声明为对象格式,这样更有利于你对面向对象的理解。