第二集-深度探秘Java的数据类型
在默认大家看完1-4章,有了一些基本概念之后,我就再就Java的数据类型问题给大家讲一讲。我们知道,Java有八种基本数据类型,int long float double byte short char boolean,这些数据类型规定了变量的数值形式和数据范围。我们也知道,每一个Java文件是由class组成的,每一种java class规定了一种Java类,约定了其属性和行为。我们来回顾一下,定义一个Java基本类型的变量我们怎么定义:
int a=1;//其中我们用标识符a来表示一个int类型的变量,并把它赋初值为1。
像a这种属于那种八个基本数据类型的我们把它叫做基本数据类型的变量。
但是像String这种就不一样了,我们定义String是这么定义的:
String s=”zyl”;//用标识符s来表示一个String类型的变量,并用”zyl”字符串来初始化它
像s这种的我们把它叫做引用类型的变量,在Java里面,除了那八个基本数据类型,绝大部分都属于引用数据类型,String属于jdk自带的类,像这种自定义的类A:
public class A{
private int a=1;
private double b=1.0;
public double sum(){
return a+b;
}
}
也属于引用类型,我们定义它的变量的时候也是这么定义A a我们就把a叫做一个A的一个引用,属于引用类型的变量。
因为Java秉承着万物皆对象的理念,jdk的设计者就想了一个办法,把所有基本类型和引用类型统一起来,它为每个基本数据类型编写了一个自己的引用数据类型。其中int对应Integer、float对应Float、long对应Long、short对应Short、double对应Double、byte对应Byte、boolean对应Boolean、char对应Character。
这样有什么好处呢?首先,将基本类型向引用类型转换了之后可以方便统一操作,这在后面要讲的泛型上面尤为体现。其次,我们可以在对应的引用类型上面进行一些针对数据的比较方便的一些操作,具体可以看JDK的API文档,比如将Integer转换成字符串、将字符串转换成Integer,我们给基本类型对应的引用类型起个名字,叫做包装类型。
怎么定义并使用这样的类型呢?很简单,跟基本类型类似:
Integer i=1;
int b=1;Integer i=b;//i就代表了由1初始化其元素的Integer类型的引用
为什么叫做初始化它不叫给它赋值呢,我们可以来看看Integer的源码:
实际上我们是将i初始化给了Integer类里面持有的value变量。
如果提前预习过,我们知道,对于引用数据类型,像这样A a;只是声明了一个A类型的引用,这个引用并没有对应的地址空间,让a跟对应的地址空间绑定了之后它才具有实际意义,分配地址空间的方法是这样的:
A a=new A();这样我们才能利用a访问A里面的属性,并且调用A里面的方法。同样的,Integer也一样,正常情况下我们初始化Integer是这样的:
Integer i=new Integer(1);
实际上在Java刚出来的时候也是这么做的,但是到了jdk 1.5的时候,我们这样写
Integer i=1;也能初始化了,是怎么做到的呢?这就涉及到Java的自动装箱机制了:
我们看到这样的代码:
下面我把它反编译了:
反编译之后,我们可以看到,编译器在看到那个等号的时候,其实是调用了Integer的静态方法valueOf,将1这个值传递进来,然后new Integer(1),实际上在这一句是从上层执行了对象创建的工作。而在赋值给int的时候,是调用了intValue的方法,返回的是自己持有的int类型的value,这就是传说中的自动装箱与拆箱。
在讲接下来的东西之前我先演示一下Integer和String相互转换的方法吧,这是一个非常常用的方法:
(在视频中讲)
下面说一些比较细节的东西:
也许有些朋友会说都会输出false,或者也有朋友会说都会输出true。但是事实上输出结果是:
true;flase
为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。
此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
而其中IntegerCache类的实现为:
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返
回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。
上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是
同一个对象,而i3和i4则是分别指向不同的对象。
这一集就讲到这里。