目录
在写代码的过程中,我们难免会遇到数据类型的转换,相信每一位写过代码的人都会遇到,数据类型转换虽说是很基础很基础的一个知识点,但是它出现的代码中的频率非常之高,并且在很多笔试题里面都会有这类题,确实不难,但如果大家如果不小心,就很容易弄错,对此,我给大家总结了一下关于基本数据类型转换的问题。比较全面白话,保证大家一看就能懂。也是希望各位小伙伴能够解决自己的疑问。
在Java中,数据类型转换分为基本数据类型转换和引用数据类型转换,其中基本数据类型转换又分为自动数据类型转换和强制类型转换,(引用数据类型转换又分为向上转型和向下转型,这个先作为了解,后面会给大家做详细介绍)。
数据类型转换包括以下两点:
- 基本数据类型转换
- 自动数据类型转换
- 强制数据类型转换
- 引用数据类型转换
- 向上转型
- 向下转型
下面我们来看基本数据类型转换。
自动数据类型转换
要弄清楚自动数据类型转换,首先要了解什么是自动数据类型转换,在Java中,数据类型包含8中,按照它们的容量大小依次是byte、short(char)、int、long、float、double和boolean,在数据类型转换中不牵扯到boolean类型,所以我们所说的数据类型转换只针对前面7种类型,自动数据类型转换就是从容量小的向容量大的转换。此过程不需要我们做任何事情,也不会有任何问题。
public class Test {
public static void main(String[] args) {
int i = 10;
long j = i; //这里就是一个典型的自动类型转换,因为i变量是int类型数据,把int类型数据赋给long类型变量j,小容量向大容量转换。
System.out.println(j);
}
}
下图是上面代码的结果展示:可见自动类型转换直接赋值即可,代码不会报错。
在学习数据类型转换之前,我们要知道在Java中有一条很重要的结论,那就是在任何情况下,整数型的字面量默认都被当作int类型处理。如果希望该字面量被当作long类型对待,需要在该字面量后面添加L或者是小写的l,但是由于小写的l和数字1比较像,所以我们一般建议都加上一个大写的L。请看下面的案例,说说哪些存在数据类型转换,若存在,指出是什么数据类型转换。
int a = 100;
//这个不存在类型转换,因为Java默认会把100当成int类型,int类型赋给int类型的a,这中间不存在数据类型转换。
long b = 200;
//存在自动数据类型转换,因为200默认是int类型,int类型赋给long类型的b,是小容量转向大容量,属于自动数据类型转换。
long c = 300L;
//不存在,因为300后面跟了一个大写的L,此时300就是一个long类型的数据,long类型数据赋给long类型的c,不存在数据类型转换。
分析下面程序,都没问题还是存在问题呢?若存在问题请指出问题所在。
long d = 2147483647 //提示:2147483647是int类型的最大值
//这句代码没问题,因为2147483647属于int类型的范围内,默认为int类型数据,int类型数据向long类型的d转换属于自动类型转换,没问题。
long e = 2147483648
/*这里可能大家会认为2147483648是一个int类型数据,向long类型变量e转换(自动类型转换)也没问题,对,2147483648确实默认情况下是一个int类型数据,可正是因为它默认是一个int类型,而它本身又超出了int类型的范围,所以它在还没有赋值之前就已经报错了。
那么怎么解决呢?其实只要在2147483648后面加一个大写的L就行。
*/
强制数据类型转换
在上一小节中,我们学习了自动数据类型转换,自动类型转换在Java中允许直接将小容量的数据赋值给大容量的类型,那么我们下面要讲的强制数据类型转换是否也可以直接赋值?首先,强制数据类型转换就是将大容量的数据向小容量的数据转换,话不多说,我们来看一个案例:
long x = 100L;
int y = x;
如上图所示,这个代码报错了,首先不要急,我们来分析一下:首先上面的第一行不存在数据类型转换,100后面加了一个大写的L,是long类型,赋值给long类型的x,没有问题。然后第二行的x是long类型数据,long类型数据赋值给int类型的变量y,小容量向大容量转换,属于强制类型转换,我们上面还是和自动类型转换一样,采用直接赋值的方式,但是它报错了这说明强制类型转换不能像自动类型转换那样单纯的赋值那么我们就需要对强制转换做一些措施,它才能够帮我们去完成转换。在Java中,我们只需要在大容量字面量前面加上一对小括号,小括号里面写上小容量变量的数据类型即可。以下是正确代码:
long x = 100L;
int y = (int) x; //强转
需要大家注意的是虽然加上强制类型转换符之后能编译并且能够成功,但是这样可能会损失精度,就好比一个大瓶子里面的水往小瓶子里倒,如果大瓶子里面的水的体积没有超过小瓶子的体积时,小瓶子能装下水,但是如果大瓶子里面的水超过了小瓶子的体积,这个时候小瓶子就不能装下大瓶子的水,导致有一部分水会溢出。强制类型转换就好比这倒水,当发生强制类型转换时,大容量数据类型就是大瓶子,里面的数据就是大瓶子里面的水,强转这个过程就相当于是倒水的过程,要是大容量里面的数据没有超过这个小容量数据类型能够存储的范围还好,要是超过了就会导致有一部分数据会丢失(即造成精度损失)。那么强制数据类型转换时精度到底是怎样损失的呢?下面我们从底层来简单解释一下它的基本原理:
long类型数据有8个字节,一个字节中有8个bit位,所以100的long类型表示就会有64个二进制数,每8个占1字节,同理,int类型占4个字节,所以100的int类型表示就会有32个二进制数,也是每8个占1字节,当long类型的100转换成int类型的100时,它会截取long类型前面的4个字节,这样就刚好符合int类型是4个字节的标准。由于100还比较小,在long类型表示中前面7个字节都是用二进制数0来占位的,所以前4个字节截了也就截了,把0截取了没什么影响,不会影响到100的正确表示,所以此时的强转不会影响精度。
100的long类型表示:00000000 00000000 00000000 00000000 00000000 00000000 00000000 01100100
100的int类型表示: 00000000 00000000 00000000 01100100
上面的例子是不会损失精度的,下面我们再来看一个会损失精度的例子:
byte b = (byte) 300;
上面这行代码中300默认是int类型,现在要强转到byte类型,首先我们来分析一下300的表示:
同理上面的例子:由于int是4字节,每个字节有8个bit位,所以300的int表示如下,然后int类型的300要向byte类型强转,此时由于byte类型值占1个字节,所以它会把int类型的前3个字节都截断,只保留后面1个字节,在这波操作之后由于截取的300的int类型表示中前3个字节是非零的(在第3个字节的最后一位是二进制数1),所以就改变了其大小,所以就导致了精度的损失。
300的int表示: 00000000 00000000 00000001 00101100
300的byte表示: 00101100
那么损失精度之后的结果会是多少呢?还会是300吗?
如上图,在经过我们运行之后,发现答案竟然是44,300直接变成了44,这精度损失的够大的呀!那这精度就有的聊了。下面我对这个结果做一个详细的解释:可能大家会以为所谓的精度损失就是四舍五入这样的比较小的损失,但是其实在程序中并不是的,因为数据在程序中都是按照二进制存储的,如上面的300经过截取之后的二进制变成了00101100,至于怎么截取的,上面已经做的详细的说明,那么也就很好解释了,00101100这个二进制数转换成十进制数就是44,所以这就是这个结果的由来。
byte、short类型不超范围也能直接赋值
在上面一节的学习中,我们说强转必须要做特殊处理,那么只要是强转都要特殊处理吗?有没有特例呢?其实是有的,在Java中,设置者为了方便我们程序员编程,给我们指定了这样一条规则:如果有比byte、short类型的大的类型的数据向byte、short类型强转时只要大容量的那个数值不超过byte或者short类型的范围时,可以直接赋值但要注意这条规则只针对byte和short类型有效。如下图中,前面的强转都没有报错,只有第7行说从long类型转为int类型可能会有精度损失,说明除了第7行,其他都是没问题的,这就证实了这条规则。
char类型数据的类型转换
上面我们只是说了4种整数型的类型转换,这一小节种我们来聊聊char类型的数据类型转换。char虽说是字符型,但是因为有些字符有对应的ASCII码值(如字符a对应的数值为97),且char类型也有其自己的数值范围(0~65535),所以我们有必要探讨一下char字符的数据类型转换。
char c1 = 'a';
//能成功,输出字符a
char c2 = 97;
//能成功,输出字符a,因为a对于的ASCII码值就是97
char c3 = 65535;
//也能成功,只不过不能识别这个字符
char c4 = 65536
//编译报错,因为超出了char类型的范围(0~65535),会提示int类型无法转向char类型,可能会有精度损失
总结:
- 要注意:char类型和short类型一样,也是有2个字节。而给它赋值的时候的数值默认都是int类型,所以上面的赋值都属于强转。
- 和byte、short一样,只要在它的范围之内,强转不需要进行强转操作。
- 如果赋的整型数值超过了它的范围,就必须给它加强转操作,可虽然这样能够编译并且运行通过,但无实际意义。
不同的数据类型做混合运算
在实际开发中,难免会有不同类型的数据做混合运算,这个时候它们之间又会遵循什么样的规则呢?得到的结果会是一个什么样的数据类型?其实在Java中,不同数据类型的数据做混合运算,哪个数据的数据类型的容量大,得到的结果的数据类型就是哪个数据类型。比如int、byte、short三种类型的数据做混合运算,由于这里面int类型所对应的容量最大,所以最后得到的结果的数据类型就是int类型。我这里就不进行演示了,但是有一个特例需要大家知道:当byte、short、char三种类型的数据做混合运算时,是把它们所有的数据先转换成int类型,然后再做运算,也就是说它们三种数据类型做混合运算之后得到的结果的数据类型是int类型。如下图所示:按照普遍结论,该运算结果的类型应该是short类型(byte占1字节,short占2字节),我们假设前面short、byte运算的结果的类型为short类型,所以第5行代码我们用short类型来接收,但是发现报错了,并且还说的是从int类型转为short类型可能会有精度损失,这说明此结果的数据类型并不是short类型,而是int类型,我们要想得到此结果,就必须对第5行代码做强转操作。
以下是正确的代码:
不知道这里大家是否会有一个小小的疑问,byte类型的变量b和short类型的变量s经过自动类型转换之后的数据类型都是int类型,然后int类型的变量b加上int类型的变量s所得的结果为2,我们前面说在不超过short类型的范围时,可以直接强转,不需要进行强转操作,这里的2明显没有超过short类型的范围,但是为什么还是需要进行强转操作呢?相信很多小伙伴都有这样的问题,其实在我们的编译阶段只能帮助我们识别语法上的错误,编译器只能看见res变量后的值是b + s,它也知道b的值是1,s的值也是1,但编译器没有运算的功能,并不知道1+1是多少,可能就超出了short的取值范围了呢?编译器为了严谨,所以才会给我们报错提示。例如下面的代码案例,很多小伙伴容易犯的错,以为能编译成功。实际上就是因为编译器不具有运算的功能导致报错。
总结
以上就是关于基本数据类型转换的全部知识点,下面我对以上知识点进行一个总结:
- 八种基本数据类型中,除boolean类型不能转换之外,剩下的七种类型之间都可以进行转换。
- 如果整数型字面量没有超出byte、short、char的取值范围,可以直接将其赋值给byte、short、char类型的变量。
- 小容量向大容量转换称为自动类型转换,容量从下到大的排序为:byte→short(char)→int→long→float→double。其中short和char都占用2字节,但是char可以表示更大的正整数。
- 大容量转换成小容量,称为强制类型转换,编写时必须添加强制类型转换符,但运行时可能会出现精度的损失,这是无法避免的,谨慎使用。
- byte、short、char类型做混合运算时,先各自转换为int类型,然后再做运算。
- 多种数据做混合运算时,各自先转换为容量最大的那一种再做运算。