下面这行代码的输出是什么?

public static void main(String[] args) {System.out.println(0xffffffff);
}

下面两行代码的输出相同吗?

public static void main(String[] args) {byte b=-1;System.out.println((int)(char)b);System.out.println((int)(char)(b & 0xff));
}

请尝试在Eclipse中运行上面的两个代码片段,如果你对输出结果感到很惊讶,请继续往下读...

正如你所看到的: 第1个代码片段的运行结果是:-1 第2个代码片段的运行结果是:65535和255

上面的两个代码片段来源于《Java解惑》的第6个小问题“多重转型”,原题目内容如下:

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

上面的代码中连续进行了3次类型转换,最后的结果会回到-1吗?答案当然是不会,它输出的结果是65535。下面我为大家整理了相关的基础知识,相信大家读完后应该就知道其中的原因了。

一、Java中如何编码负数?

Java采用”2的补码“(Two's Complement)编码负数,它是一种数值的编码方法,要分二步完成:第一步,每一个二进制位都取相反值,0变成1,1变成0。比如,+8的二进制编码是00001000,取反后就是11110111。第二步,将上一步得到的值加1。11110111就变成11111000。所以,00001000的2的补码就是11111000。也就是说,-8在计算机(8位机)中就是用11111000表示。关于“2的补码”的详细信息,请参考阮一峰的博文《关于2的补码》,博文地址附在本文的参考部分。

二、什么是符号扩展(Sign Extension)?

符号扩展(Sign Extension)用于在数值类型转换时扩展二进制位的长度,以保证转换后的数值和原数值的符号(正或负)和大小相同,一般用于较窄的类型(如byte)向较宽的类型(如int)转换。扩展二进制位长度指的是,在原数值的二进制位左边补齐若干个符号位(0表示正,1表示负)。

举例来说,如果用6个bit表示十进制数10,二进制码为"00 1010",如果将它进行符号扩展为16bits长度,结果是"0000 0000 0000 1010",即在左边补上10个0(因为10是正数,符号为0),符号扩展前后数值的大小和符号都保持不变;如果用10bits表示十进制数-15,使用“2的补码”编码后,二进制码为"11 1111 0001",如果将它进行符号扩展为16bits,结果是"1111 1111 1111 0001",即在左边补上6个1(因为-15是负数,符号为1),符号扩展前后数值的大小和符号都保持不变。

三、Java类型转换规则

1. Java中整型字面量

Java中int型字面量的书写方式有以下几种:

- 十进制方式,直接书写十进制数字

- 八进制方式,格式以0打头,例如012表示十进制10

- 十六进制方式,格式为0x打头,例如0xff表示十进制255

需要注意的是,在Java中012和0xff返回的都是int型数据,即长度是32位。

2. Java的数值类型转换规则

这个规则是《Java解惑》总结的:如果最初的数值类型是有符号的,那么就执行符号扩展;如果是char类型,那么不管它要被转换成什么类型,都执行零扩展。还有另外一条规则也需要记住,如果目标类型的长度小于源类型的长度,则直接截取目标类型的长度。例如将int型转换成byte型,直接截取int型的右边8位。

四、解析“多重转型”问题

连续三次类型转换的表达式如下:

(int)(char)(byte)-1

1. int(32位) -> byte(8位)

-1是int型的字面量,根据“2的补码”编码规则,编码结果为0xffffffff,即32位全部置1.转换成byte类型时,直接截取最后8位,所以byte结果为0xff,对应的十进制值是-1.

2. byte(8位) -> char(16位)

由于byte是有符号类型,所以在转换成char型(16位)时需要进行符号扩展,即在0xff左边连续补上8个1(1是0xff的符号位),结果是0xffff。由于char是无符号类型,所以0xffff表示的十进制数是65535。

3. char(16位) -> int(32位)

由于char是无符号类型,转换成int型时进行零扩展,即在0xffff左边连续补上16个0,结果是0x0000ffff,对应的十进制数是65535。

五、几个转型的例子

在进行类型转换时,一定要了解表达式的含义,不能光靠感觉。最好的方法是将你的意图明确表达出来。

在将一个char型数值c转型为一个宽度更宽的类型时,并且不希望有符号扩展,可以如下编码:

int i = c & 0xffff;

上文曾提到过,0xffff是int型字面量,所以在进行&操作之前,编译器会自动将c转型成int型,即在c的二进制编码前添加16个0,然后再和0xffff进行&操作,所表达的意图是强制将前16置0,后16位保持不变。虽然这个操作不是必须的,但是明确表达了不进行符号扩展的意图。

如果需要符号扩展,则可以如下编码:

int i = (short)c; //Cast causes sign extension

首先将c转换成short类型,它和char是 等宽度的,并且是有符号类型,再将short类型转换成int类型时,会自动进行符号扩展,即如果short为负数,则在左边补上16个1,否则补上16个0.

如果在将一个byte数值b转型为一个char时,并且不希望有符号扩展,那么必须使用一个位掩码来限制它:

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

(b & 0xff)的结果是32位的int类型,前24被强制置0,后8位保持不变,然后转换成char型时,直接截取后16位。这样不管b是正数还是负数,转换成char时,都相当于是在左边补上8个0,即进行零扩展而不是符号扩展。

如果需要符号扩展,则编码如下:

char c = (char)b; //Sign extension is performed

此时为了明确表达需要符号扩展的意图,注释是必须的。

六、小结

实际上在数值类型转换时,只有当遇到负数时才会出现问题,根本原因就是Java中的负数不是采用直观的方式进行编码,而是采用“2的补码”方式,这样的好处是加法和减法操作可以同时使用加法电路完成,但是在开发时却会遇到很多奇怪的问题,例如(byte)128的结果是-128,即一个大的正数,截断后却变成了负数。3.2节中引用了一些转型规则,应用这些规则可以很容地解决常见的转型问题。

七、参考引用

1. 阮一峰-关于2的补码  http://www.ruanyifeng.com/blog/2009/08/twos_complement.html

2. wikipedia-Sign extension http://en.wikipedia.org/wiki/Sign_extension

3. Joshua Bloch, 陈昊鹏译 - 《Java解惑》

【java】java负数相关推荐

  1. Java | Java 基础语法(上)

    Java | Java 基础语法 I.注释.标识符.关键字 1.1 注释 1.2 标识符 1.3 关键字 II.数据类型 2.1 强弱类型语言 2.1.1 强类型语言 2.1.2 弱类型语言 2.2 ...

  2. [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[JDBC](1)

    MIS信息管理系统实战开发之使用MySQL实现保存 开发背景 ID.姓名.年龄为公共信息,而学生有成绩,工人有工资 定义一个抽象类Person(ID.姓名.年龄),学生是其子类,有成绩,工人是其子类有 ...

  3. 零基础学JAVA]Java SE基础部分-01. Java发展及JDK配置

    1.课程名称:Java发展及JDK配置 本季介绍了JAVA的发展过程,包括JDK的发展历程,path路径的配置和classpath的配置及作用.并简单讲解了一个简单的JAVA程序,并通过此程序讲解了J ...

  4. Java - Java集合中的安全失败Fail Safe机制 (CopyOnWriteArrayList)

    文章目录 Pre 概述 fail-safe的容器-CopyOnWriteArrayList add remove函数 例子 缺陷 使用场景 Pre Java - Java集合中的快速失败Fail Fa ...

  5. Java - Java集合中的快速失败Fail Fast 机制

    文章目录 什么是 fail-fast 源码解读 Itr 为什么对集合的结构进行修改会发生并发修改异常-源码分析 修改方法之 remove 修改方法之 add 案例分享 [案例一] [案例二] [案例三 ...

  6. java比python难_python java JAVA比python难多少?

    python和java哪个好? 学python好还是java好一次一次的失望后来渐渐放弃,哪怕后来有人提起你的名字小编脸上也不会泛起红晕. Python Python是一种面向对象的解释型计算机程序设 ...

  7. [Jmeter]通过批处理调用java,java从CSV动态读取登录的用户名和密码,并将其作为参数组合成字符串,写入外部.bat文件,然后通过Java执行这个外部批处理文件...

    问题1:怎样通过批处理调用java代码? 问题2:怎样通过java从CSV文件获取到用户名和密码存入变量? 问题3:怎样将获取到的用户名和密码组合成字符串,写入外部批处理文件? 问题4:怎样在批处理文 ...

  8. java java se_Java 8 SE可选,严格的方法

    java java se 大约两周前,Stephen Colebourne提出了使用Optional的实用方法 . 如果您阅读了它,您可能会从我以前的建议中猜到我不同意. 总览 我必须以免责声明开头, ...

  9. java java编码_Java编码约定被认为是有害的

    java java编码 在Oracle网站上有Java编程语言的官方代码约定指南. 您可能希望这份超过20页的文档将是有关Java语言的最佳实践,提示和技巧的最完整,最全面和最权威的来源. 但是一旦您 ...

  10. java java se_Java SE 11:推动Java前进

    java java se 介绍 在我看来,这篇文章提出了Java语言应该如何发展成为一种首选语言. 它还提供了一些我喜欢但是有时(可能永远不会)成为Java的一部分的功能,出于某些原因,我将对此加以解 ...

最新文章

  1. 股市币市:数据分析与交易所最新公告(20190228)
  2. Flink在快手实时多维分析场景的应用
  3. linux 文件缓冲区,如何查看 Linux 当前缓存了哪些大文件?
  4. 个人作业-Alpha项目测试
  5. 百度正在加速小程序的开放趋势
  6. CVPR论文 | 所见所想所找:基于生成模型的跨模态检索
  7. 让iis7.5显示php错误的详细信息~
  8. VScode设置中文
  9. 去掉DataGridView最后一行的空白行,删除空白行
  10. obs可以推到中转服务器吗,能否使用OBS(Open Broadcaster Software)、FMLE(Flash Media Live Encoder)等第三方软件进行推流?...
  11. Win10怎么备份系统
  12. php memcache数据类型,php读取memcache二进制数据
  13. 10个办公必备神器PPT网站,免费!!!
  14. 神奇的css之 -webkit-background-clip: text; 五彩斑斓的文字效果
  15. MOVE-CORRESPONDING
  16. 十大运动蓝牙耳机品牌排行榜,六款值得买的运动耳机推荐
  17. Java学习——类和对象(上)
  18. Java 金额信息存储、精度运算及显示的处理
  19. docker 安装kafka
  20. 分子对接教程 | (6) AutoDock对接操作与对接结果解读

热门文章

  1. 多线程+Race Condition现象及产生的原因
  2. 一个阿里云语音AI都无法处理的英语单词
  3. 《测绘管理与法律法规》的各种【时限】
  4. 分享:用Datacamp进行R语言,python学习
  5. 【安全知识分享】安全意识和素养专题培训课件(附下载)
  6. 亿级流量架构演进实战 | 架构演进重构消息PUSH系统 05
  7. 不同坐标系下角速度_星空的经纬线:浅谈赤道坐标系
  8. 横河液位计EJA118E-JMSCJ-917DA-WA22B1SW00-BA21NS21
  9. Java定义交通工具
  10. Kotlin学习与实践 (三)fun 函数