【java解惑】多重强转引发的问题

如下代码:

public class Example006 {

	public static void main(String[] args) {
		System.out.println("(byte) -1 = " + (byte) -1);
		// System.out.println("(char) -1 = " + (char) -1);
		// System.out.println("(char)((byte) -1) = " + (char) ((byte) -1));
		System.out.println("(int)((char)((byte) -1)) = "
				+ (int) ((char) ((byte) -1)));
	}
}

输出结果:

(byte) -1 = -1
(int)((char)((byte) -1)) = 65535

原因分析:

该程序的行为紧密依赖于转型的符号扩展行为。Java 使用了基于 2 的补码的二进制运算,因此 int 类型的数值-1 的所有 32 位都是置位的(补码:正数同原码,负数符号位不变,其他位取反后加以,所以-1的所有32位都为1)。从 int 到 byte 的转型是很简单的,它执行了一个窄化原始类型转化 直接将除低 8 位之外的所有位全部砍掉。这样做留下的是一个8位都被置位了的 byte,它仍旧表示-1(8个1)。

从 byte 到 char 的转型稍微麻烦一点,因为 byte 是一个有符号类型,而 char是一个无符号类型。在将一个整数类型转换成另一个宽度更宽的整数类型时, 通常是可以保持其数值的,但是却不可能将一个负的 byte 数值表示成一个 char。因此, 从 byte 到 char 的转换被认为不是一个拓宽原始类型转换,而是一个拓宽并窄化原始类型的转换:byte 被转换成了 int,而这个 int 又被转换成了 char。

有一条很简单的规则能够描述从较窄的整型转换成较宽的整型时的符号扩展行为:如果最初的数值类型是有符号的,那么就执行符号扩展;如果它是 char,那么不管它将要被转换成什么类型,都执行

零扩展。 

    因为 byte 是一个有符号的类型,所以在将 byte 数值-1 转换成char 时,会发生符号扩展。作为结果的 char 数值的16 个位就都被置位了,因此它等于 2^16-1,即 65535。从 char 到 int 的转型也是一个拓宽原始类型转换,所以这条规则告诉我们,它将执行零扩展而不是符号扩展。作为结果的 int 数值也就成了65535,这正是程序打印出的结果。

如果你在将一个 char 数值 c 转型为一个宽度更宽的类型,并且你不希望有符号扩展,那么为清晰表达意图,可以考虑使用一个位掩码,即使它并不是必需的:

 int i = c & 0xffff;

或者, 书写一句注释来描述转换的行为:

 int i = c; //不会执行符号扩展

如果你在将一个 char 数值 c 转型为一个宽度更宽的整型,并且你希望有符号扩展,那么就先将 char 转型为一个 short,它与 char 具有同样的宽度,但是它是有符号的。在给出了这种细微的代码之后,你应该也为它书写一句注释:

 int i = (short) c; //转型将引起符号扩展

如果你在将一个 byte 数值b 转型为一个 char,并且你不希望有符号扩展,那么你必须使用一个位掩码来限制它。这是一种通用做法,所以不需要任何注释:

 char c = (char) (b & 0xff);

      


更多参考资料:

1、关于Java符号扩展问题的解释:http://bbs.csdn.net/topics/310238373

2、补码至少介绍:http://baike.so.com/doc/4026477.html

时间: 2024-10-02 06:50:40

【java解惑】多重强转引发的问题的相关文章

《Java解惑》读书笔记

 摘选自<Java解惑>一书,之前整理了部分,一直没看完,最近为了督促自己每天读点这本书,决定一天至少更新一个谜题的内容,欢迎讨论. 欢迎关注技术博客http://blog.sina.com.cn/u/1822488043 Java解惑读书笔记 谜题1:奇数性 取余操作的定义: ( a / b ) * b + ( a % b ) = a 其中(a/b)是java运算的结果,也就是a/b是一个整数,比如3/2=1. 所以当取余操作返回一个非零结果的时候,它与左操作数具有相同符号. 请测试你的

java解惑之常常忘记的事

java解惑之常常忘记的事 2012-10-17 18:38:57|  分类: JAVA |  标签:基础知识  软件开发  |举报|字号 订阅 针对刚接触java的菜鸟来说,java基础知识都是我们必须认真学习的,但是在工作过几年时间的老鸟来说,有时候也会对java的基础知识产生疑问,对于这种不确定,并且很容易混淆的知识点,java解惑已经为大家进行了很好的总结,现在借用一个作者的总结,进行一下罗列,希望能对你有所帮助. 1. 奇偶判断 不要使用 i % 2 == 1 来判断是否是奇数,因为i

【java解惑】try-finally语句执行问题

如下所示代码: public class Example039 { public static void main(String[] args) { Example039 example039 = new Example039(); System.out.println(example039.output1()); example039.output2(); } boolean output1() { try { // ... return true; } finally { return fa

【java解惑】final域变量初始化顺序

如下所示代码: public class Example049 { private final int overtime; public static final Example049 INSTANCE = new Example049();//1 private static final int CURRENT_YEAR = Calendar.getInstance().get( Calendar.YEAR);//2 private Example049() { overtime = CURR

【java解惑】前缀自增自减和后缀自增自减问题

    如下代码: public class Example025 { public static void main(String[] args) { int ape = 100; int it = 100; int ape_it = 100; for (int i = 0; i < 100; i++) { ape--; it = it--; ape_it = --ape_it; } System.out.println("ape = " + ape); System.out.

Java:对象的强、软、弱和虚引用(转)

1.对象的强.软.弱和虚引用 在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对象处于可触及(reachable)状态,程序才能使用它.从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期.这4种级别由高到低依次为:强引用.软引用.弱引用和虚引用.图1为对象应用类层次. 图1 ⑴强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空

《Java 解惑》笔记(一)

<Java 解惑>里都是一些编程时容易忽略的细节,却也蛮有意思的,所以将里面的内容稍作整理,简略地概括一下: 1.奇数性 在编程的时候经常会遇到要判断传进来的参数是否为奇数,而且容易惯性地认为判断余数是否为1即可,如下代码: public static boolean isOdd ( int i ) { return i % 2 == 1 } 这段程序在四分之一的时间里返回的都是错误的答案 因为在所有的 int 数值中,有一半都是负数,而 isOdd 方法对于对所有负奇数的判断都会失败.在任何

Java解惑七:更多类之谜

谜题66 继承的问题. 对于实例方法:命名相同时,子类会覆写父类的方法,且访问权限至少和父类一样大. 对于域:命名相同时,子类会隐藏父类的域,且访问权限任意. 谜题67 不要重用库中的类名. 谜题68 命名的问题. 类名应该以大写字母开头,形式为:MixedCase. 变量以小写字母开头,形式为:mixedCase. 常量以大写字母开头,形式为:MIXED_CASE. 单个大写字母,只能用于类型参数,形式为:Map<K, V>. 包名应该都是小写,形式为:lower.case. 当一个变量和一

【java解惑】重载构造函数

如下所示代码: public class Example046 { private Example046(Object o) { System.out.println("Object"); } private Example046(double[] dArray) {//2 System.out.println("double array"); } private Example046(String str) {//3 System.out.println(&quo