原创

Java基本类型及其包装类


1.Java基本类型及其包装类

基本类型,或者叫做内置类型,是Java中不同于类的特殊类型,是我们编程中使用最频繁的类型。Java是一种强类型语言,第一次声明变量必须说明数据类型,第一次变量赋值称为变量的初始化。Java中共有8种基本类型,可分为三类:

  • 数值类型:byte,short,int,long,float,double
  • 布尔类型:boolean
  • 字符类型:char

其中数值类型又可分为整数类型byteshortintlong和浮点数类型floatdouble

除此之外其它的类型都可算为引用类型(如类、数组等);因为基本类型不具有自己的属性和方法,为了方便操作,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)

FloatDouble的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"表示E之前的数字要乘以10的多少倍。比如3.14E3就是3.14×1000=31403.14E-3就是3.14/1000=0.00314

对于基本类型的取值范围,我们不用强记,在它们对应的包装类中可以直接获取到,如:

int maxValue = Integer.MAX_VALUE; //int最大值
int minValue = Integer.MIN_VALUE; //int最小值

2.数据类型之间的转换

数据类型之间的转换分为两种情况:

1)自动转换,比如当一个较“小”数据与一个较"大"的数据进行运算时,系统会自动把"小"数据转换成"大"数据,然后再进行运算;又或者在调用方法时,实际的参数比方法声明的参数"小"时,系统也会自动把"小"数据转换成"大"数据,然后再进行方法的调用,这里说的"大""小"指的是按取值范围区分的大小类型,数据类型由"小""大"排序:(byteshortchar)< 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==i2true呢,看以下源码我们就知道了:

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对象。

上面i1i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1i2指向的是同一个对象,而i3i4则指向新创建的不同对象。

示例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()方法返回值始终是两个常量TRUEFALSE的引用,所以这里输出都是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方法)。

正文到此结束