入门篇-其之三-基本数据类型及其转换
本文最后更新于:2025年4月6日
Java的数据类型分为基本数据类型和引用数据类型,具体分类如下图:

对于初学者而言,认为字符串类型String也属于基本数据类型,事实上String属于类,即引用数据类型。从String的源码中,我们就可以看出其使用的class关键字进行修饰:

观察上述结构图我们可以发现Java的八种基本类型又可以细分成四类:整数类型、浮点类型、字符类型和布尔类型。本文将会对这四大类型一一进行讲解。
一、整数类型
整数类型,简称整型。Java中存储整型由四个类型组成:**byte、short、int、long**。其中int类型最常用。这四个数据类型的对照表如下所示:
| 计算机存储大小 | 存储范围(使用数学开闭区间表示) | 默认值 | |
|---|---|---|---|
| byte | 8位,1字节 | [-128, 127] | 0 | 
| short | 16位,2字节 | [-216-1,216-1-1] | 0 | 
| int(默认) | 32位,4字节 | [-232-1,232-1-1] | 0 | 
| long | 64位,8字节 | [-264-1,264-1-1] | 0L | 
在定义这四个类型的变量时,需要注意定义的整数不要超过其存储范围(尤其是byte类型,因为它的存储范围最小)。
例如:我想定义一个byte、short、int类型的变量并赋值,其代码如下:
| 1 |  | 
运行结果:

在使用long类型的时候需要注意:**long类型的数据后面需要加上L或l**(不推荐小写l,因为小写l很有可能和数字1或者大写字母I混淆)。示例代码如下:
| 1 |  | 
运行结果:

细心的小伙伴会发现,为什么变量num1的值88后面没有加上后缀L呢?
由于整型的默认使用的int类型,而long类型的范围比int大,因此数字88会由int类型自动提升为long类型,这种现象称作自动类型提升(本文后面会讲到自动类型提升)。因此long num1 = 88;并不会报语法错误。
而变量num2的值6666666666666666666已经超出了int类型的最大范围,但是这个数字在long范围内,此时就必须要加上后缀L。
以下是对定义long类型变量的总结说明:
| 说明 | 案例 | 
|---|---|
| 在 int范围内的数字,可以用L或l表示long类型,也可以不使用后缀。 | long num1 = 32;long num2 = 43L; | 
| 如果表示的数字在 int范围之外,但是在long的范围之内,则必须使用L或l作为后缀。 | long num = 66666666666666L; | 
如果你并不能确定所定义的整数是否在int范围,我个人的建议就是**只要定义long类型的整数,就在数字后面加个后缀L**。
二、浮点类型
浮点类型,其实就是我们说的小数类型。浮点类型主要由float和double类型组成。其中,**float类型的数值后必须要加f或F为后缀**,二者对照表如下所示:
| 计算机存储位数 | 存储范围(使用数学开闭区间表示) | 数字后缀 | 默认值 | 精度 | |
|---|---|---|---|---|---|
| float | 32位,4字节 | [-2128,2128] | f或F(必须写后缀) | 0.0f或0.0F | 7位小数 | 
| double | 64位,8字节 | [-21024,21024] | d或D(非强制要求,一般不写后缀) | 0.0 | 15位小数 | 
示例代码如下:
| 1 |  | 
运行结果为:

为什么d2的输出结果是8.0?由于8默认为int类型,给变量d2赋值时,int类型的数值会向范围更大的double转换(自动类型提升,在后面文章会讲到),而double是浮点类型,后面需要跟随小数点,默认会在后面加上.0(一位小数),即输出结果为8.0。同理,f3的输出结果为6.0。
从输出结果中我们还能看出,10 / 3得到的是无限循环小数,但是float类型变量f4输出结果保留了7位小数,而double类型变量d3输出结果保留了15位小数。由这两个输出结果可以印证两个浮点类型的精度大小。
在日常使用过程中,使用double的次数要比float多,个人总结有如下三点:
- float类型数值需要在必须其后面加上- f和- F,而- double不需要在值后面加后缀符。
- double存储范围比- float的大,并且浮点类型数值默认类型就是- double。
- double的精度要比- float的高,表示的数值更加准确。
三、字符类型
字符类型,即char类型,用来存储单个字符,使用单引号和单个字符表示,因此在单引号中写多个字符是错误写法:
| 1 |  | 
char是一个单一的16位的Unicode字符,它的存储范围是[0,65535],即'\u0000'到'\uffff'。
这里会有小伙伴问:char不是只能表示单个字符吗?这就要说到Unicode字符表了,这个表存储了所有的字符(各种符号、中文英文等各种字符),Unicode字符表中的每个字符默认使用的是以\u和十六进制数组合表示,也就是说\u0000就是一个Unicode值,这个Unicode值对应着字符表中的一个字符。
Unicode字符表中存储了所有的可用的字符,\u0000其实表示的时候Unicode字符表中第一个字符,编写测试代码如下:
| 1 |  | 
运行结果如下:

代码中我写了三个输出语句,其中第一个直接输出这个字符,但是从运行结果中我们发现这个语句确实输出了,但是控制台无法显示这个字符。
为了进一步验证输出的字符是否是Unicode字符表第一个字符,这里我使用了一个if判断。如果我们定义变量和\u0000相等时,输出ch1是Unicode字符表中的第一个字符,此时也就说明了第一个字符确实在计算机中存在,只是无法正常显示;相反,\u0000并不是Unicode字符表中的第一个字符。运行结果正如我们所料,输出的内容是ch1是Unicode字符表中的第一个字符。
四、布尔类型
boolean类型,即布尔类型,它只有两个值:true(真)和false(假)。通常用于条件表达式的判断(条件表达式后续文章会讲到),例如:我们都知道20 > 30是假,即判断结果为false。
| 1 |  | 
运行结果:

五、数字的进制表示(了解)
在中学期间我们学过数字有二进制、八进制、十进制和十六进制。
- 二进制数字是由0、1组成,满二进一。
- 八进制数字是由0~7组成,满八进一。
- 十六进制是由0~9、A、B、C、D、E、F组成,满十六进一
日常我们表示数字都是采用十进制,Java程序表示数字亦是如此。那么,如何表示二进制、八进制、十六进制的数字?
以十进制的数字22为例,转换为各个进制的数字如下:
| 二进制 | 八进制 | 十六进制 | 
|---|---|---|
| 10110 | 26 | 16 | 
在Java中,表示二进制数字,需要在数字前面加上0B或0b;如果表示八进制数字,需要在数字前面加上0即可;如果是十六进制的数字,需要在数字前面加上0X或者0x,以下是示例代码:
| 1 |  | 
运行结果:

从运行结果我们可以看出:输出的数字无论是哪一种进制,默认都会转换为十进制的数字22。
如果我想直接将十进制数字22转换为各个进制并进行输出。
例如:我想定义的变量是int类型,可以使用int的包装类Integer,在Integer类中有和进制转换相关的方法:
- toBinaryString(num):将十进制数字转换为二进制数字并表示。
- toOctalString(num):将十进制数字转换为八进制数字并表示。
- toHexString(num):将十进制数字转换为十六进制数字并表示。
| 1 |  | 
运行结果:

六、原码、反码、补码(了解)
原码、反码、补码是计算机中表示数值的一种方式,主要应用于计算机的加减运算。
原码是最基本的表示方法, 直接将数值以二进制的形式表示,原码就是符号位加上真值的绝对值,即第一位表示正负号(0为整数,1为负数),其他位表示值。
例如:127的原码是01111111,-127的原码是11111111。
原码的优点就是直观,容易理解。
反码:正数的反码就是其原码本身,负数的反码在其原码的基础上保持符号位不变,其他位取反。
例如:-127的反码是10000000,127的反码是01111111。
补码:正数的补码就是其原码本身,负数的补码需要在反码的基础上加1。
例如:-127的补码就是10000001。
想深入了解此方面的内容的小伙伴,详见这篇文章:《原码、反码、补码的基本概念》,我个人觉得写的很棒!
七、自动类型提升
前面我们已经讲过了8种基本数据类型,按照数据存储范围来比较:double > float > long > int > short 、char > byte
自动类型提升是指小范围的数据类型向大范围的数据类型进行转换。
boolean类型不能进行自动类型提升或强制类型转换。
例如:short的存储范围比int小,因此,short类型的值赋值给int类型的变量时,short类型的值自动转换为int类型,代码如下:
| 1 |  | 
运行结果:

上述案例可以看出,s1赋值给i1的时候并没有报错,原因就在于s1自动转换为int类型的值赋给i1。
自动类型提升可能存在的特殊情况:
情况一:当byte、short、char三者互相参与运算时,默认转为int类型。
| 1 |  | 
当我们解除一个错误写法的注释(例如byte num3 = num1 + num2;)。我们可以执行javac命令查询错误信息:

情况二:整数类型向浮点类型转换时,默认后面会带.0。
| 1 |  | 
运行结果:

情况三:**char类型向更高数据范围(例如:int、long等)转换时,以数字的形式输出。**
| 1 |  | 

八、强制类型转换
和自动类型提升相比,强制类型转换正好相反,由大范围的数据类型向小范围的数据类型进行转换,转换格式如下:
| 1 |  | 
如果我想将long类型的数据转换为byte、short、int类型的数据,由于long是大范围的数据类型,向这三个小范围数据类型转换时需要进行强制类型转换。以下是示例代码:
| 1 |  | 
运行结果:

当然,强制类型转换也会存在如下的情况:
情况一:浮点类型转换成整数类型时,会出现精度损失,即小数点会被截断(不会四舍五入),只保留整数部分。
| 1 |  | 
运行结果:

情况二:要转换的数字超出目标类型的范围,Java会自动对整数进行溢出处理,不会得到预期的值。
例如:定义一个int类型的变量130,将其转换成byte类型,而byte类型的存储范围是[-128,127],130很明显超出了这个范围,强制转换的结果不会符合我们的预期,示例代码如下:
| 1 |  | 

很明显,输出结果并不符合我们的预期,而是得到了值-126,接下来我们从底层角度进行分析:
由于int为4字节32位,每一位是由二进制的0和1表示,因此130转换成二进制数(32位)为:

int类型强制转换成byte类型以后,只保留后八位,结果如下:

得到的10000010是源码,8位的byte第一位是符号位,0表示正号,1表示负号。很明显这个数是负数,表示负数需要先将原码转换成反码,反码变成补码,补码再转换成十进制数字以后就是byte类型的结果。首先我们先将其转换成反码(符号位除外):

将反码加1之后,就得到补码:

将11111110转换成十进制数为(第1位是符号位,是负数):
$$
-(1\times2^{6}+1\times2^{5}+1\times2^{4}+1\times2^{3}+1\times2^{2}+1\times2^{1}+0\times2^{0})=-126
$$
因此强制类型转换得到的结果是-126。
情况三:**byte、short、char进行运算时,会被提升为int类型,然后再进行计算**。要想转换成小范围数据类型,需要进行强制类型转换。
以下写法无法通过编译而报错:
| 1 |  | 
无法通过编译,因为进行加减法运算时,变量会自动提升为int类型,得到的结果也是int类型,和左侧原有的数据类型不匹配而报错:

正确的写法是:将得到的结果进行强制类型转换:
| 1 |  | 
运行结果符合预期:
