Java基本类型及其包装类
1.Java基本类型及其包装类
基本类型
,或者叫做内置类型
,是Java
中不同于类的特殊类型,是我们编程中使用最频繁的类型。Java
是一种强类型语言,第一次声明变量必须说明数据类型,第一次变量赋值称为变量的初始化。Java
中共有8种基本类型
,可分为三类:
- 数值类型:byte,short,int,long,float,double
- 布尔类型:boolean
- 字符类型:char
其中数值类型又可分为整数类型byte
、short
、int
、long
和浮点数类型float
、double
;
除此之外其它的类型都可算为引用类型
(如类、数组等);因为基本类型
不具有自己的属性和方法,为了方便操作,Java
提供了将基本类型封装后的包装类
:
基本类型 | 包装类 | 默认值 | 字节数 | 表示范围 |
---|---|---|---|---|
byte | Byte | 0 | 1byte | -128~127 |
short | Short | 0 | 2byte | -32768~32767 |
int | Integer | 0L或者0l | 4byte | -2147483648~2147483,47 |
long | Long | 0 | 8byte | -2^63 ~ 2^63-1 |
float | Float | 0.0F或者0.0f | 4byte | 1.4E-45~3.4028235E38 |
double | Double | 0.0 | 8byte | 4.9E-324~1.7976931348623157E308 |
boolean | Boolean | false | 1byte | true false |
char | Character | /u0000 | 2byte | \u0000(即为0) ~ \uffff(即为65535) |
Float
和Double
的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"
表示E之前的数字要乘以10的多少倍。比如3.14E3
就是3.14×1000=3140
,3.14E-3
就是3.14/1000=0.00314
。
对于基本类型
的取值范围,我们不用强记,在它们对应的包装类
中可以直接获取到,如:
int maxValue = Integer.MAX_VALUE; //int最大值
int minValue = Integer.MIN_VALUE; //int最小值
2.数据类型之间的转换
数据类型之间的转换分为两种情况:
1)自动转换
,比如当一个较“小”数据
与一个较"大"的数据
进行运算时,系统会自动把"小"数据
转换成"大"数据
,然后再进行运算;又或者在调用方法时,实际的参数比方法声明的参数"小"时,系统也会自动把"小"数据
转换成"大"数据
,然后再进行方法的调用,这里说的"大""小"
指的是按取值范围区分的大小类型,数据类型由"小"
到"大"
排序:(byte
、short
、char
)< int
< long
< float
< double
。
byte b=1;
int i=b; //自动转换为int
double d=b; //自动转换为double
2)强制转换
,将大类型转换为小类型时,可以使用强制转换,如:
int num = (int)3.14; //double强转为int,可想而知,结果会丢失精度。
3.装箱和拆箱
在我们使用基本类型
的包装类
时,有时肯定需要在他们之间进行转换,例如:
Integer i = Integer.valueOf(10); //将int转换为Integer
int i2 = i.intValue(); //将Integer转换为int
这种把基本数据类型
转换成包装类
的过程就叫装箱
,而把包装类
转换成基本数据类型
的过程自然就称为拆箱
了;从Java SE5
开始,为了减少开发人员的工作,Java
提供了自动拆箱
与自动装箱
功能,如果要生成一个数值为10的Integer
对象,只需要这样就可以了:
Integer i = 10; //系统自动调用Integer.valueOf()方法来进行装箱
int n = i; //系统自动调用Integer.intValue()方法进行拆箱
自动拆装箱大大节省了开发人员的精力,不用再关心什么时候需要拆箱和装箱。但是,在使用中还是有一些问题需要注意:
1)包装类型数值的比较
示例1:
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); //true
System.out.println(i3==i4); //false
这里我们普遍会认为两次判断结果都是false
,因为==
在比较对象时判断的是对象的引用,而不是判断对象的值;那么这里为什么i1==i2
为true
呢,看以下源码我们就知道了:
public static Integer valueOf(int i) {
//从下面IntegerCache实现中可得知IntegerCache.high默认为127
if(i >= -128 && i <= IntegerCache.high)
//也就是说当初始化赋值为-128~127时,会直接返回数组中对象的引用,不会创建新的对象
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
int h = 127;
if (integerCacheHighPropValue != null) {
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
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() {}
}
从源码可以看出,在通过valueOf()
方法创建Integer
对象时,如果赋值在-128~127
之间,就返回指向IntegerCache.cache
中已经存在的对象的引用;否则创建一个新的Integer
对象。
上面i1
和i2
的数值为100,因此会直接从cache
中取已经存在的对象,所以i1
和i2
指向的是同一个对象,而i3
和i4
则指向新创建的不同对象。
示例2:
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2); //false
System.out.println(i3==i4); //false
经过了上面Integer
数值的比较,这里大家会条件反射的认为i1==i2
应该输出true
,但Double
类的valueOf
方法与Integer
类的valueOf
方法实现并不相同,因为在限定范围内整数的个数是有限的,而浮点数却不是。
注意,Integer、Short、Byte、Character、Long这几个类的valueOf()方法的实现是类似的,都存在-128~127的缓存池。Double、Float的valueOf方法的实现是类似的,不存在缓存池。
示例3:
Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1==i2); //true
System.out.println(i3==i4); //true
因为Boolean
底层valueOf()
方法返回值始终是两个常量TRUE
和FALSE
的引用,所以这里输出都是true
:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
2)包装类型运算及比较时的拆装箱
Integer a = 1;
Integer b = 2;
Integer c = 3;
Long d = 3L;
Long e = 2L;
System.out.println(c==(a+b)); //true
System.out.println(c.equals(a+b)); //true
System.out.println(d==(a+b)); //true
System.out.println(d.equals(a+b)); //false
System.out.println(d.equals(a+e)); //true
这里需要注意的是,当 "=="运算符的两个操作数都是包装类型的引用,则比较指向的是否为同一个对象;而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装类型,equals()方法不会进行类型转换。
所以上面c==(a+b)
中,a+b
包含了算术运算,因此会触发自动拆箱
过程,它们比较的是数值是否相等;
而c.equals(a+b)
会先触发自动拆箱
过程,再触发自动装箱
过程,也就是说a+b
,会先各自调用intValue
方法,得到了加法运算后的数值之后,便调用Integer.valueOf
方法,再进行equals
比较。
同理对于后面的也是这样,不过要注意d.equals(a+b)
和d.equals(a+e)
输出的结果(如果数值是int
类型的,装箱过程调用的是Integer.valueOf
;如果是long
类型的,装箱调用的Long.valueOf
方法)。