JAVA基本数据类型及其包装类

本文深入探讨了Java中基本数据类型在计算机中的表示方式、取值范围、存储结构及转换规则,包括整数型、小数型、布尔型等,并详细解析了包装类及其自动装拆箱机制,最后介绍了不同类型间的转换规则与比较方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、背景

JAVA基本数据类型几乎每天都会用到,但是你真的了解其在计算机中是如何表示的吗?每一种基本数据类型的取值范围究竟是如何计算得到的?为什么float型所占字节数为4,但比同样占4个字节的int型的取值范围更为广阔?为什么float型和double型变量会有精度丢失的问题?这些问题都值得我们去细细探究一下。

二、JAVA基本数据类型

基本数据类型

最小值

最大值

所占字节

byte

2^7=-128

2^7-1=127

1

short

2^15=-32768

2^15-1=32767

2

int

2^31=0x80000000

2^31-1=0x7fffffff

4

long

2^63

2^63-1

8

float

 

 

4

double

 

 

8

char

0

2^16-1=65535

2

boolean

 

 

1

这八种基本类型可以分为以下几类:

1. boolean型:boolean

2. 数值型:

    A. 整数:按取值范围由小到大排列,byte,short,int,long

    B. 小数:按取值范围由小到大排列,float,double

三、数据在计算机中的存储结构

任何数据,在计算机中都是按照二进制进行存储的。基于此,我们可以根据不同基本类型所占字节数的多少,来计算得出其取值范围。

此外,数据有原码、反码、补码三种形式。

原码顾名思义,不再赘述。

反码就是原码按位取反,例如,原码为10101010,反码为01010101。

补码为原码取反码+1,例如,原码为10101010,补码为01010110。

说了这么多,本质上是为了说明,正数在计算机中用原码表示,而负数用其补码表示(第一位符号位为1,标识负数)。

这也就能解释为什么占用4个字节的int型变量的取值范围是-2^31 -- 2^31-1。这里先卖个关子,下边章节中进行具体计算。

四、基本数据类型的取值范围计算

 

 

4.1 boolean型

boolean型就不加以赘述了,只有true和false两种取值。

4.2 数值型

数值型又分为整数型和小数型两大类。

4.2.1 整数型

我们以int型为例。int型占用4个字节,共32位。第一位为符号位,0-正数,1-负数。

因此,最大的正数为0x7fffffff,即除了高位为0外,其他位均为1,对应十进制数为2^31-1;

最小的负数为0x80000000,其最高位为1,绝对值为0x80000000的补码即80000000。对应十进制数为2^31。

同理,可以得到short型和long型的取值范围,所不同的均为所占用字节数不同。

4.2.2 小数型:

小数型的数据存储方式与整数型不同,因此不能完全套用上文中提到的整数型的方式来进行取值范围计算。

小数型数据的存储结构分为符号位+指数位+尾数位。

符号位决定该小数是正数还是负数,指数位决定了其取值范围,而尾数位决定了其精度,即保留小数点后多少位。

float型的存储方式如下:

符号位

1位

指数位

8位

尾数位

23位

double型的存储方式:

符号位

1位

指数位

11位

尾数位

52位

从二者的尾数位的不同上看,你就可以理解为什么说double是双精度浮点数,其精度为什么要高于float。

由于8个指数位决定了其取值范围,且表征的是以2为底的指数,因此其取值范围要远远大于同样是8个位的整数。

五、基本数据类型的包装类及自动装拆箱

上文中说到了8种基本数据类型,在JAVA中,还有8种与之对应的包装类。具体关联关系如下表:

 

基本数据类型

包装类

byte

Byte

char

Character

short

Short

int

Integer

long

Long

float

Float

double

Double

boolean

Boolean

那么很多人看到这里要问,既然JAVA有了这些基本类型,为什么还要引入包装类的概念呢?为什么要引入自动拆装箱呢?原因很简单,那就是JAVA是面向对象的语言,很多地方要用到的不是基本数据类型,而是其包装类。如果没有自动拆装箱,势必涉及从基本数据类型创建其包装类的实例,代码不够简洁。

例如,如果我们不使用自动拆装箱,那么就不可避免地引入:Integer i = new Integer(1);

但是如果我们借助于自动拆装箱,可以使用:Integer i = 1;相比较而言,简洁了很多。

自动拆装箱是由JAVA编译器提供的功能,以int与Integer的互转为例,可以通过调用Integer.valueOf实现int到Integer的转换,实现装箱;通过调用Integer.intValue实现Integer到int的转换,即拆箱。这一切都是由JAVA编译器提供的,对开发者完全透明。

 

六、类型间的转换

1. 将取值范围小的类型向取值范围大的类型转换,必须使用强制类型转换,有可能会损失精度或数据溢出

例如将double型向float型转换,极有可能会损失精度。

2. 浮点数到整数的转换其实是舍弃小数部分,而不是四舍五入;

3. 包装类有帮助进行类型转换的方法,例如Integer.longValue, shortValue,floatValue,doubleValue。

七、==与equals方法

基本数据类型比较是否相等时,常用==进行比较。

比如:

int i = 0;
int j = 0;
System.out.println(i == j); // true

但是基本数据类型的包装类的实例其实是个对象,对象间==的语义是两者是否是同一个引用。因此不能简单用==来进行比较。

Double i = 1.0;
Double j = 1.0;
System.out.println(i==j); // false

我们之所以没有用Integer来举例,是因为Integer.valueOf方法中对-128--127之间的数进行了cache,因此valueOf函数的返回值是同一个引用。

而equals方法的出现就是用来解决上述问题的,即两个同等类型的包装类的对象是否在取值上相等。

 public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

首先判断是否为同类型,如果不同,则直接就是不相等。如果是同类型,则取intValue,看是否在取值上相等。

个人认为equals方法算是JAVA中比较重要的方法,其最常见的使用场景是在HashMap.get(Object key)中。通过key的hash值找到HashMap中对应该hash值的链表,然后遍历,通过key.equals(链表中取到的key),来最终定位到期望的key,之后取出对应的value。

最容易出错的场景就是在很多初学者非常容易忘记equals方法的实现,尤其是实现中首先要判断是否为同类型。即使语义上的取值相同,但类型不同,也会被equals方法判定为false,从而在HashMap中获取不到期望的key-value。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值