Java学习之路04---类型转换

架构图

前言

我们再进行Java程序编写时,常常会遇到运算的问题,例如常见的整数加减、浮点数的乘除、甚至是字元类型的运算。但很多时候运算的操作数不一定都是相同数据类型的

这时候我们就需要仰赖型别转换(Casting)将双方数据类型转换成统一类型,才可以继续运算。当我们说到型别转换,主要就是自动型别转换强制型别转换两种

布林类型数据不能进行型别转换

自动类型转换

自动型别转换顾名思义就是不需要使用者进行手动添加操作,编译器自行就会进行处理的型别转换,所以又称为隐式型别转换。出现条件为转换後变数类型的存储范围大於转换前的存储范围时,例如:

整数之间
若是相同数据类型之间的自动型别转换,通常不会造成数据丢失或发生错误

byte b = 1;
short s = 256;
int i = 65538;
long l = 458294l;

l = i; // 自动型别转换
i = s; // 自动型别转换
s = b; // 自动型别转换

上述例子中,编译器会自动将语句进行转换,例如l = i,经过编译器处理会变成l = (long)i
整数与浮点数
浮点数类型的存储范围较整数来得广,所以整数赋值给浮点数也是自动型别转换的一种

int i = 123456789;
float f = 12.3f;
f = i; // 自动型别转换

不过整数转浮点数的过程中很有可能发生赋值数据不一致问题。例如我们将上述范例执行一次,发现虽然整数可以自动转换成浮点类型,但精准度已经丢失

字元
字元类型数据也可以与浮点和整数之间进行转换,不过short与char之间不能进行自动型别转换,因为根据顺序转换表,char类型是直接转换成int

int i = 10;
char c = 'A';
float f = 12.3f;

i = c; // 自动型别转换
f = c; // 自动型别转换

强制类型转换

/* 等号左侧变数范围较大 */
int a = 65;
char ch = (char)65536;
ch = (char)a;

/* 特殊计算用途 */
float height = 12.5f;
float width = 2.0f;
int area = (int)(height*width); // 注意乘法运算要使用括号,不然最後结果还是float类型

我们可以从上述例子看到,等号右侧的变数存储范围较等号左侧大时,编译器就会发出警告,"这个操作可能会造成赋值错误"

由此可知只要数据范围是由大转小,就需要使用强制类型转换。因此我们又称强制类型转换为Narrow Casting

另一种使用场景是进行运算时有特殊需求,需要将计算结果转换成特定变数类型时,这时我们也要使用强制类型转换,不然编译器会觉得我们的操作很危险

float f1 = 123456789f;
int a = (int)f1; // (1)

float f2 = 12.95f 
a = (int)f2; // (2) 

不意外,第一种状况我们在自动类型转换中也看到过,整数与浮点数之间的运算会存在一些误差

第二种状况也很好理解,单存就是整数类型不能表示小数,因此造成转换後的误差

float f1 = 999999999999f;
float f2 = 99999999999f;

int a = (int)f1;
System.out.println("int a = "+a);


a = (int)f2;
System.out.println("int a = "+a);


a = (int)2147483648F;
System.out.println("int a = "+a);


a = (int)99999999999l;
System.out.println("int a = "+a);

既然如此我们将浮点数的变数值推至整数的极限,看看赋值後打印结果如何

可以从执行结果发现,若是相同变数族群(例如都是整数类型),进行强制转换时发生溢位,结果会依照最高位形式呈现负数(最高位为1代表负数),且会随者溢位长度而改变

但假如是浮点数转整数的状况,不管赋值的浮点数超出范围多大,转换後的结果均相同,整数经过转换後,都会卡在最大值,也就是2147483647

类型转换表


以上是变数类型之间的类型转换顺序表,类型转换的目的是将多个不同类型的变数转换成一个统一的变数类型,通常都是变数之间储存范围最长的那个

黑色实心箭头代表类型转换行为是编译器自动产生的,并且不用考虑数据丢失问题

int a=65;
byte b = 127;
char ch='o';
a = b + ch; 

例如上面这个案例,b和ch都会转换成int类型,然後才会操作进而赋值

黑色虚线箭头则表示虽然编译器会自动进行类型转换,但可能会有精度丢失问题。自动转换下通常是int转float与long转float, double时会发生问题

int i = 123456789;
long l = 123456789l;
float f = 0.0f;
double d = 0.0;

f = i; // 可能发生精度丢失
f = l; // 可能发生精度丢失

d = i; // 不会发生精度丢失
d = l; // 可能发生精度丢失

红色箭头代表需要使用强制类型转换,而且可能造成精度丢失。垂直方向视为同数据类型之间的转换,水平方向代表跨数据类型之间的转换

double d = 0d;
float f = 10.0f;
int i = 20;
f += (float)i; // 强制将i转换成float类型再进行相加
d = f+i;

上述的例子中,f += (float)i;这条语句若是没有加上强制转型,编译器将会报错

因为float类型与int类型之间进行运算时会自动转型成double类型(大家统一的浮点格式,且不失精度),但最终赋值对象是float类型,由小转大会发生错误,所以需要提前进行强制转换

从上图中我们可以看出变数之间运算时的转型顺序,特别要注意运算完成後进行赋值时的双方变数范围,以此决定是否要进行强制类型转换

陷阱

关於後缀和强制转换的区别

float f1 = (float)1.23456; // (1)
float f2 = 1.23456f; // (2)

上述两种状况是不一样的。第一种状况1.23456是一个double类型,经过强制转换变成一个浮点类型,结果可能造成精度丢失;第二种状况1.23456在宣告时就已经是一个float类型了

整数的预设类型与转换

int number = 2147483648 // 出错
long l = 2147483648; // 出错
long l = 2147483648L;

由於整数常数的预设类型为int,因此不进行任何後缀修饰时,通常是表示为int类型

一个整数常数若是超过int上下限时,编译器就会报错,这跟等号左侧的变数范围无关,重点在於我们需要告诉编译器,现在使用的这个常数为long类型,所以不用担心赋值的范围超过int上下限

便数类型小於预设类型

short s1 = 12;
short s2 = 34;
short s3 = s1 + s2; // 出错
short s3 = (short)(s1 + s2);

若是变数类型都是不大於int的运算元,则编译器会自动将变数类型提升至int类型进行计算,因次上述例子的运算结果需要进行强制类型转换

操作运算转换

short s = 12;
long l = 34;
int i = s + l; // 出错
int i = s + (int)l;

上述例子中i为int类型,但运算结果为long类型,因此需要在short碰到long之前先进行强制类型转换,让结果变成int类型变数


<<:  Z Ringtones - An intriguing assortment of telephone ringtones

>>:  React-依视窗大小改变DOM

第 2 集:认识 Bootstrap 5 世界

此篇会分享 Bootstrap 5 环境设置,示范 VSCode、CodePen 两种不同环境的设置...

LineBot - 图文选单

昨天已经把 LineBot 设定好了,今天要做一些简单的指令,包含一图文选单,做完之後大概如下图: ...

EP 05 - [TDD] HashID 计算

Youtube 频道:https://www.youtube.com/c/kaochenlong ...

DAY27-JAVA的集合物件

集合物件(collection)是指一群相关联的资料,集合在一起组成的一个物件。在集合物件里的资料称...

[Day 5] 就决定是你了!艺文资讯整合平台

我最後选择了什麽主题 我後来用了第三种-Open API的方式 因为这样就不用自己想资料内容了~ 这...