3.5 基本类型的类型转换

3.5 基本类型的类型转换

Java程序中,不同的基本类型的值经常需要进行相互转换。Java语言所提供的7种数值类型之间可以相互转换,有两种类型转换方式:自动类型转换强制类型转换

3.5.1 自动类型转换

Java所有的数值型变量可以相互转换,如果系统支持把某种基本类型的值直接赋给另一种基本类型的变量,则这种方式被称为自动类型转换。
当把一个表数范围小的数值或变量直接赋给另一个表数范围大的变量时,系统将可以进行自动类型转换;
否则就需要强制转换。
表数范围小的可以向表数范围大的进行自动类型转换,就如同有两瓶水,当把小瓶里的水倒入大瓶中时不会有任何问题。Java支持自动类型转换的类型如图3.10所示。
这里有一张图片

1
2
3
        char

byte→short→int→long→float→double

图3.10中所示的箭头左边的数值类型可以自动类型转换为箭头右边的数值类型。

程序示例 自动类型转换

下面程序示范了自动类型转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AutoConversion {
public static void main(String[] args) {
int a = 6;
// int可以自动转换为float类型
float f = a;
// 下面将输出6.0
System.out.println(f);
// 定义一个byte类型的整数变量
byte b = 9;
// 下面代码将出错,byte型不能自动类型转换为char型
// char c = b;
// 下面是byte型变量可以自动类型转换为double型
double d = b;
// 下面将输出9.0
System.out.println(d);
}
}

基本类型转字符串: 连接一个空字符串

不仅如此,当把任何基本类型的值和字符串值进行连接运算时,基本类型的值将自动类型转换为字符串类型,虽然字符串类型不是基本类型,而是引用类型。因此,如果希望把基本类型的值转换为对应的字符串时,可以把基本类型的值和一个空字符串进行连接

+不仅可作为加法运算符使用,还可作为字符串连接运算符使用。

程序示例 字符串和数值的连接运算

看如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PrimitiveAndString
{
public static void main(String[] args)
{
// 下面代码是错的,因为5是一个整数,不能直接赋给一个字符串
// String str1 = 5;
// 一个基本类型值和字符串进行连接运算时,基本类型值自动转换为字符串
String str2 = 3.5f + "";
// 下面输出3.5
System.out.println(str2);
// 下面语句输出7Hello!
System.out.println(3 + 4 + "Hello!");
// 下面语句输出Hello!34,因为Hello! + 3会把3当成字符串处理,
// 而后再把4当成字符串处理
System.out.println("Hello!" + 3 + 4);
}
}

上面程序中有一个“3+4+"Helo!"”表达式,这个表达式先执行“3+4”运算,这是执行两个整数之间的加法,得到7,然后进行“7+"Helo!"”运算,此时会把7当成字符串进行处理,从而得到7Helo!
反之,对于“"Helo!"+3+4”表达式,先进行“"Hell!"+3”运算,得到一个Hello!3字符串,再和4进行连接运算,4也被转换成字符串进行处理。

3.5.2 强制类型转换

这里有一张图片
如果希望把图3.10中箭头右边的类型转换为左边的类型,则必须进行强制类型转换。
强制类型转换的语法格式是:(targetType)value,强制类型转换的运算符是圆括号()。当进行强制类型转换时,类似于把一个大瓶子里的水倒入一个小瓶子,如果大瓶子里的水不多还好,但如果大瓶子里的水很多,将会引起溢岀,从而造成数据丢失。
这种转换也被称为“缩小转换(Narrow Conversion)”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class NarrowConversion {
public static void main(String[] args) {
int iValue = 233;
// 强制把一个int类型的值转换为byte类型的值
byte bValue = (byte) iValue;
// 将输出-23
System.out.println(bValue);
double dValue = 3.98;
// 强制把一个double类型的值转换为int
int tol = (int) dValue;
// 将输出3
System.out.println(tol);
}
}

在上面程序中,把一个浮点数强制类型转换为整数时,Java将直接截断浮点数的小数部分。除此之外,上面程序还把223强制类型转换为byte类型的整数,从而变成了23,这就是典型的溢出。图3.11示范了这个转换过程。
这里有一张图片
从图3.11可以看出,32位int类型的233在内存中如图3.11上面所示,强制类型转换为8位的byte类型,则需要截断前面的24位,只保留右边8位,最左边的1是一个符号位,此处表明这是一个负数,负数在计算机里是以补码形式存在的,因此还需要换算成原码

补码转原码

将补码减1得到反码形式,再将反码取反就可以得到原码。

原码 加权展开 得到十进制

最后的二进制原码为100101,这个byte类型的值为-(16+4+2+1),也就是-23

从图3.11很容易看出,当试图强制把表数范围大的类型转换为表数范围小的类型时,必须格外小,因为非常容易引起信息丢失。

程序示例 生成随机字符串

经常上网的读者可能会发现有些网页上会包含临时生成的验证字符串,那么这个随机字符串是如何生成的呢?可以先随机生成一个在指定范围内的int数字(如果希望生成小写字母,就在97~122之间),然后将其强制转换成char类型,再将多次生成的字符连缀起来即可。

下面程序示范了如何生成一个6位的随机字符串,这个程序中用到了后面的循环控制,不理解循环的读者可以参考后面章节的介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RandomStr {
public static void main(String[] args) {
// 定义一个空字符串
String result = "";
// 进行6次循环
for (int i = 0; i < 6; i++) {
// 生成一个97~122的int型的整数
int intVal = (int) (Math.random() * 26 + 97);
// 将intValue强制转换为char后连接到result后面
result = result + (char) intVal;
}
// 输出随机字符串
System.out.println(result);
}
}

小数直接量默认是double

下面一行容易出错的代码:

1
2
//直接把5.6赋值给f1oat类型变量将出现错误,因为5.6默认是 double类型
float a = 5.6;

上面代码中的5.6默认是一个double类型的浮点数,因此将5.6赋值给一个foat类型变量将导致错误,必须使用强制类型转换才可以,即将上面代码改为如下形式:

1
float a = (float) 5.6;

字符串数字转基本类型数字

在通常情况下,字符串不能直接转换为基本类型,但通过基本类型对应的包装类则可以实现把字符串转换成基本类型。例如,把字符串转换成int类型,则可通过如下代码实现:

1
2
3
String a ="45";
//使用 Integer的方法将一个字符串转换成int类型
int ivalue =Integer.parseInt(a);

Java为8种基本类型都提供了对应的包装类,8个包装类都提供了一个parseXxx(String str)静态方法用于将字符串转换成基本类型。关于包装类的介绍,请参考本书第6章

3.5.3 表达式类型的自动提升

一个算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升。Java定义了如下的自动提升规则:
所有的byte类型、short类型和char类型将被提升到int类型。
**整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型**。操作数的等级排列如图3.10所示:
这里有一张图片
位于箭头右边类型的等级高于位于箭头左边类型的等级。

程序示例

下面程序示范了一个典型的错误:

1
2
3
4
5
6
7
// 定义一个short类型变量
short sValue = 5;
// 下面代码将出错:
// 右边表达式中在最高等级操作数为2(int)
// 表达式中的sValue将自动提升到int类型,
// 则右边的表达式类型为int,将一个int类型赋给short类型的变量将发生错误。
sValue = sValue - 2;

上面的“sValue-2”表达式的类型将被提升到int类型,这样就把右边的int类型值赋给左边的short类型变量,从而引起错误。
下面代码是表达式类型自动提升的正确示例代码:

1
2
3
4
5
6
7
8
9
byte b = 40;
char c = 'a';
int i = 23;
double d = .314;
// 右边表达式中在最高等级操作数为d(double型)
// 则右边表达式的类型为double型,故赋给一个double型变量
double result = b + c + i * d;
// 将输出144.222
System.out.println(result);

整数除法会丢弃小数部分

必须指出,表达式的类型将严格保持和表达式中最高等级操作数相同的类型。下面代码中两个int类型整数进行除法运算,即使无法除尽,也将得到一个int类型结果:

1
2
3
4
5
int val = 3;
// 右边表达式中2个操作数都是int,故右边表达式的类型为int
// 因此,虽然23/3不能除尽,依然得到一个int整数
int intResult = 23 / val;
System.out.println(intResult); // 将输出7

从上面程序中可以看出,当两个整数进行除法运算时,如果不能整除,得到的结果将是把小数部分截断取整后的整数

数值和字符串的加法

如果表达式中包含了字符串,则又是另一番情形了。因为当把加号(+)放在字符串和基本类型值之间时,这个加号是一个字符串连接运算符,而不是进行加法运算。看如下代码:

1
2
3
4
// 输出字符串Hello!a7
System.out.println("Hello!" + 'a' + 7);
// 输出字符串104Hello!
System.out.println('a' + 7 + "Hello!");

对于第一个表达式“"Hello!" + 'a' + 7”,先进行“"Hello!"+a”运算,把a转换成字符串,拼接成字符串Hello!a,接着进行“"Hello!"+7”运算,这也是一个字符串连接运算,得到结果是Hello!a7
对于第二个表达式,先进行“’a’+7”加法运算,其中’a’自动提升到int类型,变成a对应的ASCII值97,“97+7”将得到104,然后进行“104+”Hello!“”运算,104会自动转换成字符串,将变成两个字符串的连接运算,从而得到104Hello!