Java的基本数据类型转换

01、自动类型转换

自动类型转换(自动类型提升)是 Java 编译器在不需要显式转换的情况下,将一种基本数据类型自动转换为另一种基本数据类型的过程。这种转换通常发生在表达式求值期间,当不同类型的数据需要相互兼容时。自动类型转换遵循以下规则:

  • 如果任一操作数是double类型,其他操作数将被转换为double类型。
  • 否则,如果任一操作数是float类型,其他操作数将被转换为float类型。
  • 否则,如果任一操作数是long类型,其他操作数将被转换为long类型。
  • 否则,所有操作数将被转换为int类型。

需要注意的是,自动类型转换只发生在兼容类型之间。例如,从较小的数据类型(如int)到较大的数据类型(如longdouble)的转换是安全的,因为较大的数据类型可以容纳较小数据类型的所有可能值。

byte -> short -> int -> long -> float -> double
char -> int -> long -> float -> double

下面是一个简单的示例,演示了自动类型转换:

int intValue = 5;
double doubleValue = 2.5;

// 自动类型转换:intValue 被转换为 double 类型
double result = intValue * doubleValue;
System.out.println("结果: " + result); // 输出:结果: 12.5

在这个示例中,我们有一个 int 类型的变量intValue和一个double类型的变量doubleValue。当我们将它们相乘时,根据自动类型转换的规则,intValue 将被转换为double类型,以便将两个double类型的操作数相乘。最终结果将是一个double类型的值:12.5。

再来举个例子,顾客到超市购物,购买牙膏 2 盒,面巾纸 4 盒。其中牙膏的价格是 10.9 元,面巾纸的价格是 5.8 元,求商品总价格。实现代码如下:

float price1 = 10.9f; // 定义牙膏的价格,单精度浮点型float
double price2 = 5.8; // 定义面巾纸的价格,双精度浮点型double
int num1 = 2; // 定义牙膏的数量,整型 int
int num2 = 4; // 定义面巾纸的数量
double res = price1 * num1 + price2 * num2; // 计算总价
System.out.println("一共付给收银员" + res + "元"); // 输出总价

上述代码中首先定义了一个float类型的变量存储牙膏的价格,然后定义了一个double类型的变量存储面巾纸的价格,再定义两个int类型的变量存储物品的数量,最后进行了乘运算以及和运算之后,将结果储存在一个double类型的变量中进行输出。

一共付给收银员44.99999923706055元

从执行结果看出,floatintdouble三种数据类型参与运算,最后输出的结果为double类型的数据。这种转换一般称为“表达式中类型的自动提升”。

自动类型提升有好处,但它也会引起令人疑惑的编译错误。例如,下面看起来正确的程序却会引起问题:

byte b = 50;

b = b * 2; // Type mismatch: cannot convert from int to byte

如上所示,第二行会报“类型不匹配:无法从int转换为byte”错误。

该程序试图将一个完全合法的byte型的值 50*2 存储给一个byte型的变量。但是当表达式求值的时候,操作数被自动的提升为int型,计算结果也被提升为int型。这样表达式的结果现在是int型,不强制转换它就不能被赋为byte型。

所以应该使用一个显示的强制类型转换,例如:

byte b = 50;
b = (byte)(b*2);

这样就能产生正确的值 100。

但如果是下面这样的代码,就不会报错:

byte b = 50;
b *= 2;

这是因为 b *= 2 等价于 b = (byte)(b*2),编译器会自动进行强制类型转换。

还有如下代码:

byte b = 50;

有人觉得这个地方的50应该是int类型,那么赋值给byte类型应该需要强制类型转换,写成byte b = (byte) 50;其实这样写也没有错,但是没有必要,因为编译器已经帮我们自动完成了类型转换。希望大家能够理解,自动类型转换并不局限于小类型到大类型,也可以是大类型到小类型,只要值在小类型的取值范围内,编译器就会自动帮我们做隐式转换。

char 类型比较特殊,char 自动转换成 int、long、float 和 double,但 byte 和 short 不能自动转换为 char,而且 char 也不能自动转换为 byte 或 short。

02、强制类型转换

强制类型转换是 Java 中将一种数据类型显式转换为另一种数据类型的过程。与自动类型转换不同,强制类型转换需要程序员显式地指定要执行的转换。强制类型转换在以下情况中可能需要:

  • 将较大的数据类型转换为较小的数据类型。
  • 将浮点数转换为整数。
  • 将字符类型转换为数值类型。

需要注意的是,强制类型转换可能会导致数据丢失或精度降低,因为目标类型可能无法容纳原始类型的所有可能值。因此,在进行强制类型转换时,需要确保转换后的值仍然在目标类型的范围内。

double -> float -> long -> int -> char -> short -> byte

以下是一个简单的示例,演示了强制类型转换:

double doubleValue = 42.8;

// 强制类型转换:将 double 类型转换为 int 类型
int intValue = (int) doubleValue;
System.out.println("整数值: " + intValue); // 输出:整数值: 42

在这个示例中,我们有一个double类型的变量doubleValue。我们希望将其转换为int类型的变量intValue。为此,我们使用强制类型转换语法,即在要转换的变量之前加上目标类型的括号(如 (int))。

需要注意的是,将doubleValue 转换为int类型时,小数部分将被截断。因此,输出结果将是:Integer value: 42。在这种情况下,精度丢失是可以接受的,但在其他情况下,我们可能需要更加小心地处理类型转换以避免数据丢失。

顾客到超市购物,购买牙膏 2 盒,面巾纸 4 盒。其中牙膏的价格是 10.9 元,面巾纸的价格是 5.8 元,求商品总价格,在计算总价时采用 int 类型的数据进行存储。实现代码如下:

float price1 = 10.9f;
double price2 = 5.8;
int num1 = 2;
int num2 = 4;
int res2 = (int) (price1 * num1 + price2 * num2);
System.out.println("一共付给收银员" + res2 + "元");

在上述实例中,有double类型、float类型和int类型的数据参与运算,其运算结果默认为double类型,题目要求的结果为int类型,因为int类型的取值范围要小于double类型的取值范围,所以需要进行强制类型转换。

一共付给收银员44元

小结

考虑下面这几个算式的结果:

int a = 1500000000, b = 1500000000;
int sum = a + b;
long sum1 = a + b;
long sum2 = (long)a + b;
long sum3 = (long)(a + b);

输出结果:

-1294967296
-1294967296
3000000000
-1294967296

因为int 类型的取值范围是 -2147483648~2147483647,而 a 和 b 的值都是1500000000,和超出了int类型的取值范围,所以会出现溢出的情况。

因为 sum2 的计算过程是先将 a 转换为long类型(强制类型转换),然后再和 b 相加(隐式类型转换),此时的结果也是long型,而 3000000000 并没有超出long型的取值范围。

  • int sum = a + b,a 和 b 都是int类型,所以a+b的结果也是int类型,但是 a+b 的结果超出了int类型的取值范围,所以会出现溢出的情况。
  • long sum1 = a + b,a 和 b 都是int类型,于是 a+b 的和也是int类型,但超出了int的取值范围,所以会出现溢出的情况;如果a+b的和没有超出 int 取值范围,其实会将a+b的结果隐式转换为long类型。
  • long sum2 = (long)a + b,a 是int类型,但是 (long)a 将 a 强转为了long类型,然后再和 b 相加,此时 b 将隐式提升为 long 型,于是等式右边的结果也是long型,而 3000000000 并没有超出long型的取值范围。
  • long sum3 = (long)(a + b),a 和 b 都是int类型,a+b 的结果也是 int类型,但超出了int的取值范围,所以会出现溢出的情况;即便是外面有一层long的强转,但还没有来得及强转,a+b 的结果已经溢出了,所以强转也没用。