转型被用来将一个数值从一种类型转换到另一种类型。下面的程序连续使用了三个转型。那么它到底会打印出什么呢?
public class Multicast{
public static void main (String[] args){
System.out.println((int)(char)(byte) -1);
}
}
无论你怎样分析这个程序,都会感到很迷惑。它以int数值-1开始,然后从int转型为byte,之后转型为char,最后转型回int。第一个转型将数值从32位窄化到了8位,第二个转型将数值从8位拓宽到了16位,最后一个转型又将数值从16位拓宽回了32位。这个数值最终是回到了起点吗?如果你运行该程序,你就会发现不是。它打印出来的是65535,但是这是为什么呢?
该程序的行为紧密依赖于转型的符号扩展行为。Java使用了基于2的补码的二进制运算,因此int类型的数值-1的所有32位都是置位的。从int到byte的转型是很简单的,它执行了一个窄化原始类型转化(narrowing primitive conversion),直接将除低8位之外的所有位全部砍掉。这样做留下的是一个8位都被置位了的byte,它仍旧表示-1。
从byte到char的转型稍微麻烦一点,因为byte是一个有符号类型,而char是一个无符号类型。在将一个整数类型转换成另一个宽度更宽的整数类型时,通常是可以保持其数值的,但是却不可能将一个负的byte数值表示成一个char。因此,从byte到char的转换被认为不是一个拓宽原始类型的转换,而是一个拓宽并窄化原始类型的转换(widening and narrowing primitive conversion):byte被转换成了int,而这个int又被转换成了char。
所有这些听起来有点复杂,幸运的是,有一条很简单的规则能够描述从较窄的整型转换成较宽的整型时的符号扩展行为:如果最初的数值类型是有符号的,那么就执行符号扩展;如果它是char,那么不管它将要被转换成什么类型,都执行零扩展。了解这条规则可以使我们很容易地解决这个谜题。
因为byte是一个有符号的类型,所以在将byte数值-1转换成char时,会发生符号扩展。作为结果的char数值的16个位就都被置位了,因此它等于216-1,即65535。从char到int的转型也是一个拓宽原始类型转换,所以这条规则告诉我们,它将执行零扩展而不是符号扩展。作为结果的int数值也就成了65535,这正是程序打印出的结果。
尽管这条简单的规则描述了在有符号和无符号整型之间进行拓宽原始类型时的符号扩展行为,你最好还是不要编写出依赖于它的程序。如果你正在执行一个转型到char或从char转型的拓宽原始类型转换,并且这个char是仅有的无符号整型,那么你最好将你的意图明确地表达出来。
如果你在将一个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);
这个教训很简单:如果你通过观察不能确定程序将要做什么,那么它做的就很有可能不是你想要的。要为明白清晰地表达你的意图而努力。尽管有这么一条简单的规则,描述了涉及有符号和无符号整型拓宽转换的符号扩展行为,但是大
分享到:
相关推荐
将正数转换成负数,负数转换成正数, int main(int argc, char* argv[]) { float k; c.f=-10; k=0-c.f; printf("k=%f\n",k); printf("Hello World!\n"); return 0; }
它可以用来表示一个 Unicode 字符,数值范围是0到65535。由于它是无符号的,所以只能用来表示非负数。 - **数值范围**:0 至 65535 - **字节占用**:2个字节 2. **示例**:在实际编程中,`char` 主要用于处理...
这个过程涉及到将数字转换成符合人类阅读习惯的货币格式,包括添加逗号分隔符,处理负数,以及在必要时添加前导零。本文将深入探讨如何实现这一功能,并结合标签"源码"和"工具",给出一个实用的解决方案。 首先,...
任意输入一个十进制整数,包括正数负数,通过程序实现可以输出相应的二进制编码
对于字符转换,Java中的每个字符都有一个对应的Unicode编码,这兼容ASCII编码。可以使用`Integer.toString()`方法将字符的Unicode值转换为特定进制的字符串。例如,字符'a'的ASCII值是97,可以通过`Integer....
4. 数学运算:还可以通过一些数学运算来间接检测负数,比如 `num * -1 > 0`,这会将负数转换为正数,然后比较结果是否大于0。 在实际应用中,负数测试常常用于以下几个场景: - 账户余额检查:在银行系统或财务...
在Java编程语言中,判断一个数字是正数、负数还是零是一项基本操作。这个任务通常涉及使用条件语句,如if-else结构。在给定的【标题】"java代码-判断正负数"和【描述】中,我们可以推测这是一个简单的Java程序,用于...
在本文中,我们将深入探讨如何创建一个简易的Java计算器程序,该程序具备基本的数学运算功能,如加、减、乘、除,还包括求平方根、取倒数、求余数以及清除操作。这个计算器的设计旨在提供用户友好的界面和简单易用的...
`System.currentTimeMillis()` 方法返回的就是这样的一个long类型的数值。然而,当你需要将这个long类型的时间戳转换为int类型时,可能会遇到数据溢出的问题,因为int类型的存储空间只有32位(4个字节),而long类型...
* 负数的补码转换为十进制数可以通过将补码转换为原码,然后将原码转换为十进制数。 二、char类型 * char类型是Java中的基本数据类型,用于表示单个字符。 * char类型占用2个字节,范围从0到65535。 * char类型...
通过上述分析和代码示例,我们可以看到,在C# WinForms开发中,对`TextBox`输入负数的验证是一个相对复杂的任务,涉及到多种情况的判断和处理。开发者需要根据实际需求仔细设计验证逻辑,并注意提升用户体验和程序的...
在编程领域,阶乘是一个常见的数学概念,通常用于计算组合数和概率问题。在Java中,我们可以使用循环或递归的方式来实现求解任意正整数的阶乘。本篇文章将详细探讨如何用Java来计算阶乘。 首先,我们需要理解阶乘的...
- 二进制转换:通过计算机内的算法,可以将一个数值在不同进制间进行转换,例如将十进制转换为二进制,或者相反。 - 数据类型范围:每种数据类型有其对应的取值范围。 - 转码:涉及到字符编码的转换,如UTF-8编码...
对于负数,我们需要先将其转换为绝对值,然后再进行进制转换,并在结果前加上负号。 例如,将十进制数-123.45转换为二进制,我们先转换正数部分123为二进制,然后处理小数部分0.45。正数部分的123转换为二进制是...
1. **直接取负**:一种简单的办法是先生成一个0到最大正数的随机数,然后对其取负,这样就得到了一个负数。例如,如果要生成-100到0的随机负数,可以先生成0到100的随机数,然后加上负号。 ```易语言 .随机数(100) ...
本项目以Java语言作为主要开发工具,搭配Microsoft SQL Server(MSSQL)数据库,构建了一个完整的图书库存管理解决方案。 1. **Java技术栈**:Java是一种广泛使用的面向对象的编程语言,以其“一次编写,到处运行”...
实现了 有理数(可以负数,可以小数点)之间的加减法(允许包含括号的加减乘除) 允许负数带括号 利用BigDecimal 大数据类 来保证精度 例如Double 2-1.1=0.89999,在大数据类就无异常 例如 9+(-9)=0 9--6=15 9*...
此方法进一步处理上一个方法得到的结果,以便更好地表示汉字的英文字符形式。具体实现如下: - **参数**: - `str`: 上一步处理后得到的字符串。 - **返回值**: - 返回经过优化的英文字符表示的字符串。 - **实现...
这个技巧在处理大量数据时非常实用,能够避免手动修改每一个负数,极大地提高了工作效率。记住,转换前最好备份原始数据,以防不测。同时,如果需要将正数转换为负数,只需在步骤2中输入1,而不是-1,然后按照同样的...