(转) Java中的负数及基本类型的转型详解
(转) https://my.oschina.net/joymufeng/blog/139952
面这行代码的输出是什么?
下面两行代码的输出相同吗?
请尝试在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
(转) Java中的负数及基本类型的转型详解的更多相关文章
- java中包的命令行(cmd)操作详解
一.什么是包? 为了更好地组织类,防止在一个空间下出现类重名,Java提供了包机制.包是类的容器,用于分隔类名空间(类型于C++中的命名空间).如果没有指定包名,所有的示例都属于一个默认的无名包(又称 ...
- Java中对象的深复制和浅复制详解
1.浅复制与深复制概念 ⑴浅复制(浅克隆) 被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象. ⑵ ...
- Java中String做为synchronized同步锁使用详解
Java中使用String作同步锁 在Java中String是一种特殊的类型存在,在jdk中String在创建后是共享常量池的,即使在jdk1.8之后实现有所不同,但是功能还是差不多的. 借助这个特点 ...
- Java中使用Scanner类进行键盘的输入详解
我们在使用Java写代码时,经常会遇到从键盘输入字符串等操作,这时候我们需要用到的是我们的Scanner类来实现获取用户从键盘上的输入操作. Scanner类是一个基于正则表达式的文本扫描器,它可以从 ...
- java中四种访问修饰符区别及详解全过程
客户端程序员:即在其应用中使用数据类型的类消费者,他的目标是收集各种用来实现快速应用开发的类. 类创建者:即创建新数据类型的程序员,目标是构建类. 访问控制存在的原因:a.让客户端程序员无法触及他们不 ...
- (转)java中静态代码块的用法 static用法详解
一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来 ...
- java中静态代码块的用法 static用法详解(转)
(一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序 ...
- 关于Java中的程序,进程和线程的详解...
程序:一段静态的代码,一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体,是应用软件执行的蓝本. 进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个 ...
- 【转】java中静态代码块的用法 static用法详解
原文网址:http://www.cnblogs.com/panjun-Donet/archive/2010/08/10/1796209.html (一)java 静态代码块 静态方法区别一般情况下,如 ...
随机推荐
- leetcode 148排序链表
优先队列容器,使用小顶堆排序:timeO(nlogn) spaceO(n) /** * Definition for singly-linked list. * struct ListNode { * ...
- HSV颜色识别-HSV基本颜色分量范围
原文地址:https://blog.csdn.net/taily_duan/article/details/51506776 一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本色中对 ...
- flutter stack嵌套,appbar透明,Container设置背景图片并且图片在appbar之下
stack嵌套 一般情况下 stack是无法嵌套,出现stack嵌套,布局就会出乱 解决方式:就是第二个stack需要确定宽高 appbar透明 AppBar( backgroundColor: Co ...
- apache通过rewrite限制某个目录
1.<IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{HTTP_URI} ^.*/tmp/* [NC] RewriteRule ...
- 十一:jinja2模板传参
从后台传参到模板,模板再渲染到前端 传参的时候,可以在html后面加上关键字传参,在模板里面用{{ 参数 }}使用即可,可以传多个参数 也可以使用**传参,取值的时候就直接取内容
- Delphi中的Free和Nil和freeandnil函数
Delphi中的Free和Nil 在Delphi中释放对象资源时一般用Obj.Free(Obj为一个实例名),不过程Delphi中还有一个FreeAndNil(对象名)函数,那么用哪个好呢?Free和 ...
- 【PyTorch】PyTorch使用LMDB数据库加速文件读取
PyTorch使用LMDB数据库加速文件读取 原始文档:https://www.yuque.com/lart/ugkv9f/hbnym1 对于数据库的了解较少,文章中大部分的介绍主要来自于各种博客和L ...
- 【VS开发】【电子电路技术】RJ45以太网传输线研究
RJ45以太网传输线研究 最近研究远距离差分视频传输方案,理所当然想到了LVDS协议.至于选用cameralink传输线,还是选用其他方案,本人更倾向于廉价的RJ45以太网线来实现LVDS差分信号的传 ...
- 向tabcontrol中添加form
昨天花了一天的时间去找一个错误,关系是这样的,我添加一个tabcontrol就叫tc1好了,然后在tc1中再动态添加一个父窗体l叫form1,要把form1添加进tabcontrol就要先新建一个ta ...
- 在kali2.0中使用msf图形界面可能会遇到的问题
kali版本:Linux kali 4.9.0-kali3-amd64 #1 SMP Debian 4.9.18-1kali1 (2017-04-04) x86_64 GNU/Linux 编写日期:2 ...