关于Java溢出

回顾一下上课的小实验:

首先写下如下代码:

首先定义了一个Demo类,然后定义了byte类型的a变量,初始化a的变量值为127,(已知byte类型的范围为-128-127),没有越线,然后输出a。

编译没错,得出答案为127,完美。

然后我们给a赋值为a+1;

但是,error。

他告诉我们cannot convert from int to byte 翻译为不能从int转换为字节

然后实验继续:

我们把它强制转换:

还好编译通过了,值为-128。

what’s the fuck!!!!

然后度娘走起:

原文问题是:

原 Java 从流中读取byte的奇怪现象,出现负值,详解

解答如下:

其实一点都不奇怪,下面我们来详细分析

首先看下面一段代码,代码的意思是将128写入到文件中,再从文件中读出一个byte输出

    File f = new File(“f1”);

    FileOutputStream fos = new FileOutputStream(f);

    fos.write(128);

    fos.close();

    FileInputStream fis = new FileInputStream(f);

    int a = fis.read();

    System.out.println(a);

输出结果为:

128

这符合我们的直观感觉,写入128,读出来也是128

下面我们修改一下代码,将数据度入到byte数组里,看看输出会是什么样子,代码如下

    File f = new File(“f1”);

    FileOutputStream fos = new FileOutputStream(f);

    fos.write(128);

    fos.close();

    FileInputStream fis = new FileInputStream(f);

byte[] b = new byte[1];

    fis.read(b, 0, 1);

    System.out.println(b[0]);

输出结果为

-128

好了,“奇怪”的现象出现了,这是为什么呢?

首先,我们需要了解一些基础知识,在Java中,byte的范围为 -128 ~ 127,并没有128这个数值;其次,读者需要了解一下负数在Java中如何表示(即原码、反码、补码的相关知识,请读者自行谷歌)。

下面,我们来分析一下出现 -128的原因:

首先,fos.write(128)表示写入 “128” 所代表的字节,即 1000 0000,也就是说,文件中当前包含一个字节,8位依次为 1 0 0 0 0 0 0 0

当我们调用fis.read()时,该方法会返回一个无符号byte,也就说直接将1000 0000翻译为128,大家可以去看该方法的说明,返回值的范围为0 ~ 255;所以我们得到了 128 的输出;

当我们调用fis.read(b, 0, 1)时,相当于把文件中第一个字节读入到了b[0]中,此时b[0]的二进制表示为1000 0000,根据负数在Java中的表示方法,我们可以计算出,该二进制序列的数值为 -128,也就是说这个过程并没有将其转化为无符号数值的过程,所以我们得到了 -128 的输出。

那么,我们如何解决这个问题呢?

首先,我们需要看一下fos.write(int a)这个方法,它的功能是写入a代表的byte值,也就是说,该函数实际上是将a 的后八位写入到文件中 , 所以作者建议在使用该方法时,为了避免产生歧义,a 的范围最好在 0 ~ 255 之间,初学者最好在 0 ~ 127 之间。(具体原因可以结合负数的表示规则自己想想哦~)。

回归正题,我们如何解决上文中的问题呢,即 “写入128、读出-128” 的问题,我们可以用下面的代码来解决。

    File f = new File(“f1”);

    FileOutputStream fos = new FileOutputStream(f);

    fos.write(128);

    fos.close();

    FileInputStream fis = new FileInputStream(f);

byte[] b = new byte[1];

    fis.read(b, 0, 1);

    System.out.println(b[0] & 0xff);

也就是说,用b[0]& 0xff的方法得到b[0]的无符号值。

插入一个小问题,就是,b[0] | 0xff得到的是一个负值,而& 得到的却是一个正值。这里根据作者的猜测,可能是如下原因,主要和& 运算的步骤有关:

进行 b[0]& 0xff运算时的步骤如下:

  1. 因为0xff默认应该用int来保存,所以首先将 b[0] 强制转化为 int 类型表示,b[0] 的数值为-128,所以其二进制表示为11111111 11111111 11111111 10000000
  2. 0xff的二进制表示为 00000000 00000000 00000000 11111111
  3. 这两个数做& 运算后,得到 00000000 00000000 00000000 10000000,其表示的值为128

“或 运算”的过程类似,读者可以自己推理一下

感觉还是有点不是很懂:

于是google走起:

发现一个帖子有作者回答如下:

To try to briefly address your query about -128, the fundamental idea behind generating a two’s complement number is to take the unsigned form of the number, invert all of the bits and add one. So unsigned 128 is 10000000. Inverted, it’s 01111111, and adding one gets 10000000 again. So in a two’s complement system, 10000000 is unambiguously -128 and not +128. Numbers greater than or equal to +128 simply cannot be represented in 8 bits using a two’s complement system because they would be ambiguous with the forms of negative numbers

翻译一下:

nice!!!

这才是想要的答案!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值