Java 类型转换与强制类型转换

对于大多数编程语言,把一种类型的值赋给另外类型的变量是很常见的操作。Java是强类型语言,不是所有的类型都可以随便自动转换。

把 int 类型的值赋给 long 类型的变量,总是可行的。但是,反过来,将 long 类型 赋值给 int 类型,你必须使用强制类型转换。

Java 的类型转换分两种:

  • 自动类型转换 – 没有任何的表现形式,Java自动处理
  • 强制类型转换 – 需要使用类型转换语法

Java的自动转换

Java的自动转换,需要满足下面的两个条件:

  • 这2种类型是兼容的
  • 目的类型数的范围比来源类型的大

Java 中8种基本数据类型 中,对基本数据类型分了组。
数字类型,包括整数integer和浮点floating-point类型都是彼此兼容的,但是,数字类型和字符类型char或布尔类型bollean是不兼容的。字符类型char和布尔类型bollean也是互相不兼容的。

public static void main(String[] args) {
    int w = 3;
    long width;
    width = w;
    System.out.println("int " + w + " long " + width );
}

将 int 值 赋值给 long 值,不需要特殊处理。

类型的强制转换

有时候,你需要将 int 型的值赋给一个 byte 型的变量,你将怎么办?这种转换不会自动进行,因为 byte 型的变化范围比 int 型的要小。取值范围可以参考Java 中8种基本数据类型

强制类型转换只不过是一种显式的类型变换。它的通用格式如下:

(target_type)value;

其中,目标类型(target-type)指定了要将指定值转换成的类型。

public static void main(String[] args) {
    long height = 34L;
    int h;
    h = (int) height;
    System.out.println("int " + h + " long " + height );
}

将 long 赋值给 int,并不能自动发生,需要 (int) height将至转换。

下面有两段程序,注意观察它们的区别:

public static void main(String[] args) {
    int key = 29;
    byte bit = (byte)key;
    System.out.println("int " + key + " byte " + bit);
}

public static void main(String[] args) {
    int key = 285;
    byte bit = (byte)key;
    System.out.println("int " + key + " byte " + bit);
}

查看结果,简直是个神奇的现象,不同的 int 值,竟然转换以后,能得到相同的 byte 值。

在将 int 型强制转换成 byte 型时,如果整数的值超出了 byte 型的取值范围–128~127,它的值将会因为对 byte 型值域取模(整数除以byte得到的余数)而减少。

将 285 转换为 byte 时,其结果是 285 除以 256 的余数 29,这里 256 是byte类型的变化范围,从 –128~127 一共 256 个变化。

当把浮点值赋给整数类型时一种不同的类型转换发生了:截断truncation。整数没有小数部分,所以,当把浮点值赋给整数类型时,它的小数部分会被舍去。

public static void main(String[] args) {
    double money = 3.5d;
    int m = (int) money;
    System.out.println("double " + money + " int "+m);
}

将浮点数值 3.5 赋给一个整数,其结果值只是3,0.5被丢弃了。当然,如果浮点值太大而不能适合目标整数类型,那么它的值将会因为对目标类型值域取模而减少。

强制类型转换,可能会丢失数据,导致数据错乱。
public static void main(String[] args) {
    boolean flag = true;
    int value = (int)flag;
}

上面的代码是不正确的,强制类型转换并不能在不兼容的数据类型之间发生。

如果强制转换,将会出现错误:

java.lang.ClassCastException

自动转换或者强制类型转换,都必须保证类型是兼容的。

类型提升

类型转换发生在简单赋值(=)阶段,如果是表达式赋值,还有另外一种类型转换。

public static void main(String[] args) {
    byte b = 3;
    char c = 'a';
    int d = b*c;
    System.out.println("d is " + d);
}

中间项结果 b*c 很容易超过它的任何一个 byte 型操作数的范围。为处理这种问题,当计算表达式时,Java 自动提升 各个byte型或short型的操作数到int型。这意味着子表达式b*c使用整数而不是字节型或字符来执行。虽然结果已经超出了 byte 类型的取值范围,但是结果是正确的。

对于 char 类型的 ‘a’ ,对应的ACCSII 码值为 97,所以表达式的值为 3×97。

public static void main(String[] args) {
    byte b = 6;
    byte c = 8;
    byte d = b + c ;
}

上面的代码想要计算两个 byte 值之间的和,但是代码不能通过编译。因为Java自动提升了类型。

表达式 b + c 求值的时候,操作数被自动地提升为int型,计算结果也被提升为int型。这样,表达式的结果现在是int型,不强制转换它就不能被赋为byte型。

public static void main(String[] args) {
    byte b = 6;
    byte c = 8;
    byte d = (byte) (b + c);
    System.out.println(d);
}

使用强制类型转换,代码正常编译,结果正确。

类型提升的约定

Java定义了若干适用于表达式的类型提升规则:

  • 所有的byte型和short型的值被提升到 int型
  • 如果一个操作数是long型,整个表达式将被提升到long型
  • 如果一个操作数是float型,整个表达式将被提升到float型
  • 如果一个操作数是double型,计算结果就是double型
public static void main(String[] args) {
    byte b = 2;
    char c = 'c';
    short s = 3;
    int i = 50000;
    float f = 8.23f;
    double d = 33.14;
    double result = (f * b) + (i / c) - (d * s);
    System.out.println((f * b) + " + " + (i / c) + " - " + (d * s));
    System.out.println(result);
}

这个例子有点复杂:

第一个子表达式 f*b 中,变量b被提升为float类型,该子表达式的结果当然是float类型。

第二个子表达式 i/c中,变量c被提升为int类型,该子表达式的结果当然是int类型。

第三个子表达式 d*s 中,变量s被提升为double类型,该子表达式的结果当然也是double类型。

最后,考虑三个中间值,float类型,int类型,和double类型。float类型加int类型的结果是float类型。然后float类型减去提升为double类型的double类型,该表达式的最后结果是double类型。

相关推荐