面试官:详细说下基本数据类型与装箱拆箱

  基本数据类型,有存放在栈中的,也有存放堆中的,这取决去基本类型声明的位置。
一:在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因.
在方法中声明的变量可以是基本类型的变量,也可以是引用类型的变量。

  • 当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在方法栈中
  • 当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆类存中的。

二:在类中声明的变量是成员变量,也叫全局变量,放在堆中的(因为全局变量不会随着某个方法执行结束而销毁)。
同样在类中声明的变量即可是基本类型的变量 也可是引用类型的变量

  • 当声明的是基本类型的变量其变量名及其值放在堆内存中的
  • 引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中

比较一下八个基本数据类型的区别吧。
在这里插入图片描述

8 bit(位) = 1 Byte (字节)
整 型:byte,short,int,long
浮点型:float,double
逻辑型:boolean 字符型:char

我们先来看几个例子。

  • 奇怪的精度问题
public class demo {
   public static void main(String[] arg){
       System.out.println(2.00f-1.10f);
       System.out.println(2.00-1.10);
   }
   /*	输出:
   		0.9
   		0.8999999999999999
   */
}

你可能以为程序打印出来的都是0.9,但事实却不是,为什么会出现这种情况呢。
原来2.00和1.10在计算机里存储的时候,需要转换成二进制,浮点型默认是double类型的(即没有加f),从上面的列举的区别表格中知道,float占4个字节32位,double占8个字节64位,而1.10在转换成二进制的时候,是1.000110…,而double类型表示小数部分只有52位,当向后计算 52位后基数还不为0,那后面的部分只能舍弃,从这里可以看出float、double并不能准确表示每一位小数,对于有的小数只能无限趋向它,所以计算后的结果是0.8999999999999999。
当加上f之后,规定了类型为float精度,并没有double精度那么大,小数部分0.1二进制表示被舍去的比较多。

  • 长整除问题
public class demo {
   public static void main(String[] arg){
       final long a = 24 * 60 * 60 * 1000 * 1000;
       final long b = 24 * 60 * 60 * 1000;
       System.out.println(a/b);
   }//输出: 5
}

1000???错是5,这有是什么情况
24606010001000 的结果计算时为int类型,计算后再转成long型,明显结果超出了int类型的表达范围,在运算的过程中运算结果仍然为int型,超出范围就截取后64位作为运算的结果。因此,我们看到虽然定义了long型变量,但结果仍然是截取后的结果。其实 a = 500654080,b = 86400000 ,得到的结果也就是5。
当我们修改成这样:

public class demo {
    public static void main(String[] arg){
        final long a = 24l * 60 * 60 * 1000 * 1000;
        final long b = 24l * 60 * 60 * 1000;
        System.out.println(a/b);
    }//输出: 1000
}

24后面加上L后,结果就是我们所预期的结果了。
因此,我们中写代码中,如果对精度要求很高的,建议使用BigDecimal来运算,一般用来计算高精度的数据。

为什么需要包装类

因为基本数据类型不是对象,在有些时候使用对象是最方便的,比如List的add(Object e)方法,参数是Object类型,是引用数据类型,那我们想放入基本数据类型怎么办,为了方便操作基本数据类型,所以引入了包装类。

自动装箱和自动拆箱

    public static void main(String[] args) {
        Integer i = 10;//自动装箱
        int i2 = i;//自动拆箱
    }

自动装箱其实就是将基本数据类型转换成包装类,反之自动拆箱就是把包装类转换成基本数据类型。

我们看看下面几个输出

    public static void main(String[] args) {
        int i = 100;
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;
        System.out.println(i == i1);//  true
        System.out.println(i1 == i2);//  true
        System.out.println(i3 == i4);// false
    }

为什么一个true,一个false呢,我们分析下源码,查看编译后的字节码文件
在这里插入图片描述

从字节码文件中,我们可以得知,装箱调用的Integer.valueOf方法,拆箱调用Integer.intValue方法,我们快来看下这两个方法的源码和他的构造函数。

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

i与i1用==比较的时候,会自动将i1自动拆箱,比较的是基本数据类型。
这里没有放出IntegerCache的相关代码,从这个类中可以得知,IntegerCache.low为-128,IntegerCache.high为127,当在这个范围内的基本类型,转换成Integer的时候,就直接从缓存取,所以i1和i2是相等的,但是i3,i4都不在这个范围内,所以都新建了Integer对象,所以不相等。
其实Byte、Short、Integer、Long、Character的valueOf()方法实现机制和Integer类似,但是Float、Double中的valueOf(),永远返回新创建的对象,因为一个范围内的整数是有限的,但是小数却是无限的,无法保存在缓存中,Boolean中只有两个对象,要么是true的Boolean,要么是false的Boolean,只要boolean的值相同,Boolean就相等。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值