Java 基本类型与引用类型

版权声明:本文为博主原创文章,欢迎转载,转载请注明出处。觉得此文有用的,不嫌麻烦的,就留个言呐,或者点个赞呐,要是嫌麻烦呢,也麻烦点个赞嘛 https://blog.csdn.net/qq_40147863/article/details/88855226

Java 基本类型与引用类型

一、基本数据类型

java 中一共分为 8 种基本数据类型:byte、short、int、long、float、double、char、boolean,
其中 byte、short、int、long 是整型。float、double 是浮点型,char 是字符型,boolean 是布尔型。

二、引用类型

java 为每种基本类型都提供了对应的封装类型,分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean。引用类型是一种对象类型,它的值是指向内存空间的引用,就是地址。

三、基本类型与引用类型的区别

1. 默认值

整型 byte、short、int、long 的默认值都为 0,浮点型 float、double 的默认值为 0.0,boolean 默认值为 false,char 默认值为空。对应的包装类型默认值都为 null。

2. 内存分配

基本数据类型的变量是存储在栈内存中,而引用类型变量存储在栈内存中,保存的是实际对象在堆内存中的地址,实际对象中保存这内容。

3. 自动装箱、自动拆箱

Java 从 jdk1.5 开始引入自动装箱和拆箱,使得基本数据类型与引用类型之间相互转换变得简单。

自动装箱: java 自动将原始类型转化为引用类型的过程,自动装箱时编译器会调用 valueOf 方法,将原始类型转化为对象类型。

自动拆箱: java 自动将引用类型转化为原始类型的过程,自动拆箱时编译器会调用 intValue(),doubleValue() 这类的方法将对象转换成原始类型值。

自动装箱主要发生在两种情况:一种是赋值时,一种是方法调用时。

a.赋值

Integer a = 3; //自动装箱
int b = a; //自动拆箱

b.方法调用

public Integer query(Integer a){
   return a;
}
query(3); //自动装箱
int result = query(3); //自动拆箱

4. 自动装箱、拆箱带来的问题

(1)程序的性能
由于装箱会隐式地创建对象创建,因此千万不要在一个循环中进行自动装箱的操作,下面就是一个循环中进行自动装箱的例子,会额外创建多余的对象,增加 GC 的压力,影响程序的性能:

Integer sum = 0;
 for(int i=0; i<1000; i++){
   sum+=i;
}

(2)空指针异常
注意拆箱过程中可能产生的空指针异常,一个简单的例子:

Object obj = null;
int i = (Integer)obj;

(3)对象相等比较时
先来看一个常见的例子:

Integer a = 120;
int b = 120;
Integer c = 120;
Integer d = new Integer(120);
System.out.println(a == b);   //true    t1
System.out.println(a == c);   //true    t2
System.out.println(a == d);   //false   t3

Integer e = 128;
Integer f = 128;
System.out.println(e == f);   //false    t4

返回结果是不是出乎大家的意料,解释一下每种结果的原因:
我们先反编译一下生成字节码:

Integer a = Integer.valueOf(120);
int b = 120;
Integer c = Integer.valueOf(120);
Integer d = new Integer(120);
System.out.println(a.intValue() == b);
System.out.println(a == c);
System.out.println(a == d);

Integer e = Integer.valueOf(127);
Integer f = Integer.valueOf(127);
System.out.println(e == f);

Integer e1 = Integer.valueOf(128);
Integer f1 = Integer.valueOf(128);
System.out.println(e1 == f1);

可以看到变量 a、c 在初始化的时候编译器调用了 valueOf 进行自动装箱,在 a==b 时对变量 a 调用了 intValue() 方法进行了自动拆箱操作,这就很好解释 t1~t4 的结果了。

t1 产生的原因是编译器编译时会调用 intValue() 自动的将 a 进行了拆箱,结果肯定是 true;
t2 跟 t4 的结果比较难理解:这是因为初始化时,编译器会调用装箱类的 valueOf() 方法,查看 jdk 的源码:

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

发现 jdk 对 -128 ~ 127 之间的值做了缓存,对于 -128~127 之间的值会取缓存中的引用,通过缓存经常请求的值而显著提高空间和时间性能。

这就能解释 t2 结果返回 true,而 t4 由于 128 不在缓存区间内,编译器调用 valueOf 方法会重新创建新的对象,两个不同的对象返回 false。

t3 结果无论如何都不会相等的,因为 new Integer(120) 构造器会创建新的对象。

Byte、Short、Integer、Long、Char 这几个装箱类的 valueOf() 方法都会做缓存,而 Float、Double 则不会,原因也很简单,因为 byte、Short、integer、long、char 在某个范围内的整数个数是有限的,但是 float、double 这两个浮点数却不是。

猜你喜欢

转载自blog.csdn.net/qq_40147863/article/details/88855226