问题

下面程序会打印什么呢?

public class Multicast {
    public static void main(String[] args) {
        System.out.println((int)(char)(byte)-1);
    }
}

解答

该程序打印的是 65535,为什么呢?

首先列几条基础知识:

  1. int 有符号,32位;char无符号,16位;byte有符号,8位。
  2. 整数的默认类型为int,即问题中的-1为int类型。
  3. 不同基本类型之间的强制转换时,我们这样称呼这种转换:

    窄化 <————————————> 拓宽
    byte,short,char—> int —> long—> float —> double

我们来逐步分析这个程序:

System.out.println((int)(char)(byte)-1);

1. 从 -1 开始

-1 是一个 int 类型的字面量,在 Java 中占 4 个字节(32 位),二进制补码表示为:

11111111 11111111 11111111 11111111

2. 强制转换为 (byte)-1

byte 类型占 1 个字节(8 位),转换时截取低 8 位:

11111111

这仍然是 -1(在 byte 类型中)。

3. 强制转换为 (char)(byte)-1

char 在 Java 中是 无符号 16 位 整数类型。
现在要把 byte 类型的 -1(值为 11111111)转换为 char

首先,byte 类型在转换为 char(或 int 等)时会先进行 符号扩展 到 int,然后再截取低 16 位赋给 char

byte-1(二进制 11111111)符号扩展到 int

11111111 11111111 11111111 11111111

然后转换为 char 时取低 16 位:

11111111 11111111

对于 char(无符号)来说,这个二进制值表示 65535(因为 0xFFFF 的无符号值是 65535)。

4. 强制转换为 (int)(char)...

char65535\uFFFF)转换为 int 时,因为 char 是无符号的,所以直接零扩展为 int

00000000 00000000 11111111 11111111

65535

5. 最终输出

System.out.println(65535);

Java 类型转换系统

1. 拓宽基本类型转换 (Widening Primitive Conversion)

定义

将小范围类型转换为大范围类型,不会丢失信息。

转换规则(按顺序)

byte → short → int → long → float → double
      ↓
      char → int → long → float → double

特点

  • 不会丢失数值信息
  • 可能损失精度(long → float, int → float)
  • 自动进行,不需要显式转换

示例

int i = 100;
long l = i;        // 自动拓宽
double d = l;      // 自动拓宽
float f = i;       // 自动拓宽,可能损失精度

2. 窄化基本类型转换 (Narrowing Primitive Conversion)

定义

将大范围类型转换为小范围类型,可能丢失信息。

转换路径

  • double → float
  • float → long, int, short, char, byte
  • long → int, short, char, byte
  • int → short, char, byte
  • short → char, byte
  • char → byte, short

特点

  • 可能丢失信息和精度
  • 必须显式强制转换
  • 使用截断规则

示例

double d = 123.456;
int i = (int)d;     // 结果为 123(截断小数部分)
long l = 300;
byte b = (byte)l;   // 结果为 44(300 % 256)

3. 符号扩展 vs 零扩展

符号扩展 (Sign Extension)

  • 用于有符号类型的拓宽转换
  • 用原数值的符号位填充高位
  • 保持数值的符号和大小不变
byte b = -1;        // 二进制: 11111111
short s = b;        // 符号扩展: 11111111 11111111 (-1)
int i = b;          // 符号扩展: 11111111 11111111 11111111 11111111 (-1)

零扩展 (Zero Extension)

  • 用于无符号类型的拓宽转换
  • 0填充高位
  • 对于正数,值与符号扩展相同;对于负数,结果不同
char c = '\uFFFF';  // 二进制: 11111111 11111111 (无符号 65535)
int i = c;          // 零扩展: 00000000 00000000 11111111 11111111 (65535)

4. 特殊转换规则

byte → char 的特殊处理

byte b = -1;                    // 11111111
char c = (char)b;               // 实际过程:
                                // 1. byte → int: 符号扩展 → 11111111...11111111
                                // 2. int → char: 截取低16位 → 11111111 11111111
                                // 结果: '\uFFFF' (65535)

char → 数值类型的转换

char c = 'A';                   // 65
int i = c;                      // 65 (零扩展)
byte b = (byte)c;               // 65 (窄化)

5. 实际应用示例

原问题的完整分析

System.out.println((int)(char)(byte)-1);
// 步骤分解:
// 1. -1 (int):    11111111 11111111 11111111 11111111
// 2. (byte)-1:    11111111                         (截取低8位)
// 3. (char)(byte)-1: 
//    - byte → int: 11111111 11111111 11111111 11111111 (符号扩展)
//    - int → char: 11111111 11111111                 (截取低16位)
//    结果: '\uFFFF' (65535)
// 4. (int)(char): 00000000 00000000 11111111 11111111 (零扩展)
// 最终输出: 65535

常见陷阱

byte b = -1;
char c = (char)b;           // 结果是 65535,不是 -1
int i = c;                  // 结果是 65535
int j = (short)b;           // 结果是 -1 (符号扩展)

6. 总结表

转换类型是否显式信息丢失扩展方式
拓宽转换自动可能损失精度符号/零扩展
窄化转换必须显式可能丢失信息截断

理解这些规则对于处理二进制数据、网络编程和类型安全至关重要。