Java字节

原码 反码 补码

原码:第一位表示符号,其余位表示数值。比如8位2进制:

[+1]原 = 0000 0001

[-1]原 = 1000 0001

第一位是符号位,8位2进制的取值范围:

[11111111, 01111111] = [-127, 127]

反码:正数的反码是等于原码,负数的反码是在其原码的基础上,符号位不变,其余位取反:

[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

补码:正数的补码等于原码,负数的补码是在其原码的基础上,符号位不变,其余位取反,然后加1:

[+1] = [00000001]原 = [00000001]反 = [00000001]补

[-1] = [10000001]原 = [11111110]反 = [11111111]补

在Java中,存储的数值都是有符号的,同时也是使用补码存储的。

public static void main(String[] args) {
    int a = -1;
    System.out.println(Integer.toBinaryString(a));
}

输出:

11111111111111111111111111111111

Primitive Data Types:

byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive). The byte data type can be useful for saving memory in large arrays, where the memory savings actually matters. They can also be used in place of int where their limits help to clarify your code; the fact that a variable's range is limited can serve as a form of documentation.

short: The short data type is a 16-bit signed two's complement integer. It has a minimum value of -32,768 and a maximum value of 32,767 (inclusive). As with byte, the same guidelines apply: you can use a short to save memory in large arrays, in situations where the memory savings actually matters.

int: By default, the int data type is a 32-bit signed two's complement integer, which has a minimum value of -231 and a maximum value of 231-1. In Java SE 8 and later, you can use the int data type to represent an unsigned 32-bit integer, which has a minimum value of 0 and a maximum value of 232-1. Use the Integer class to use int data type as an unsigned integer. See the section The Number Classes for more information. Static methods like compareUnsigned, divideUnsigned etc have been added to the Integer class to support the arithmetic operations for unsigned integers.

long: The long data type is a 64-bit two's complement integer. The signed long has a minimum value of -263 and a maximum value of 263-1. In Java SE 8 and later, you can use the long data type to represent an unsigned 64-bit long, which has a minimum value of 0 and a maximum value of 264-1. Use this data type when you need a range of values wider than those provided by int. The Long class also contains methods like compareUnsigned, divideUnsigned etc to support arithmetic operations for unsigned long.

float: The float data type is a single-precision 32-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. As with the recommendations for byte and short, use a float (instead of double) if you need to save memory in large arrays of floating point numbers. This data type should never be used for precise values, such as currency. For that, you will need to use the java.math.BigDecimal class instead. Numbers and Strings covers BigDecimal and other useful classes provided by the Java platform.

double: The double data type is a double-precision 64-bit IEEE 754 floating point. Its range of values is beyond the scope of this discussion, but is specified in the Floating-Point Types, Formats, and Values section of the Java Language Specification. For decimal values, this data type is generally the default choice. As mentioned above, this data type should never be used for precise values, such as currency.

boolean: The boolean data type has only two possible values: true and false. Use this data type for simple flags that track true/false conditions. This data type represents one bit of information, but its "size" isn't something that's precisely defined.

char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).

有符号 无符号

上文可以看出,Java 中的 byte 是 1 字节,short 是 2 字节,int 是 4 字节,long 是 8 字节。他们都是有符号的数值。

类型 最小值 最大值
byte -2^7 2^7-1
short -2^15 2^15-1
int -2^31 2^31-1
long -2^63 2^63-1

发现byte类型跟上文所说的取值范围[-127, 127]不太一样,这是因为使用了补码的缘故。查看原码, 反码, 补码 详解了解。

C 语言中的整数类型都提供了对应的"无符号"版本,第一位不再表示符号位。比如C语言中的无符号类型byte,其取值范围为[0, 256]。

所以,当C程序向Java程序通过网络传递了一个无符号数时,我们需要怎么存他呢?

答案就是:使用比要用的无符号类型更大的有符号类型。

比如:使用 short 来处理无符号的字节,使用 long 来处理无符号整数等。下面看一个例子,使用int(4字节)存储一个无符号byte(1字节):

public static void main(String[] args) {
    int a = 250;// 无符号byte的取值范围[0, 256]
    byte b = (byte) a; // 强制转换,直接截取int低8位
    // b 相当于C后台发来的无符号数
    int  c = b & 0xff; 
    System.out.println(c);// 250
}

上面的程序中,b 当做C后台发来的无符号数,最后我们使用int存下了这个无符号的byte。为什么需要 b & 0xff这步操作呢?

public static void main(String[] args) {
    int a = 250;// 无符号byte的取值范围[0, 256]
    byte b = (byte) a; // 强制转换,直接截取int低8位
    // b 相当于C后台发来的无符号数
    int  c = b; 
    System.out.println(c);// -6
}

可以看到,无符号byte直接转为int,丢失了原本的数值。为什么执行 b & 0xff这步操作就可以了呢? 看下文

符号位扩展

上文中,b直接转换为 int,丢失了无符号byte原本的数值。是因为,byte在向int转换的过程中,发生了符号位扩展:

[250]无符号 = 1111 1010

转为4字节int:

1111 1111 1111 1111 1111 1111 1111 1010 = [-6]补

在Java中,当较窄的整型扩展为较宽的整型时,发生符号位扩展:

对于正数而言,将需要扩展的高位全部赋为0;

对于负数而言,将需要扩展的高位全部赋为1。

观察下面的代码:

System.out.println((int)(char)(byte)-1); //65535

为什么没有输出-1呢?

因为如果最初的类型是char,那么不管他将要被提升成什么类型,都执行0扩展,即需要扩展的高位全部赋0。

byte是有符号的类型,所以在将byte数值-1(二进制为:11111111)提升到char时,会发生符号位扩展,又符号位为1,所以就补8个1,最后为16个1;然后从char到int的提升时,由于是
char型提升到其他类型,所以采用零扩展而不是符号扩展,结果int数值就成了65535。

总结:

窄的整型转换成较宽的整型时符号扩展规则:如果最初的数值类型是有符号的,那么就执行符号扩展(即如果符号位
为1,则扩展为1,如果为零,则扩展为0);如果它是char,那么不管它将要被提升成什么类型,都执行零扩展。

回顾上文提到的0xff问题:int c = b & 0xff;

对于0xff,是Java中的字面常量,本身是个int值。0xff 表示为 11111111 ,Java对于这种字面常量,不把他前面的1看做符号位,当发生符号位扩展时,扩展成的是"000...ff"。

当 执行 b & 0xff 时,b发生符号位扩展:

1111 1111 1111 1111 1111 1111 1111 1010
&
0000 0000 0000 0000 0000 0000 1111 1111
=
0000 0000 0000 0000 0000 0000 1111 1010
= [250]补

逻辑右移 算数右移

Java中有三种移位操作:左移、算数右移、逻辑右移

注意: short, byte,char 在移位之前首先将数据转换为int,然后再移位

算数右移:>>,有符号的移位操作,右移之后的空位用符号位补充,如果是正数用 0 补充,负数用1补充。

-4>>1

[-4]原= 10000000 00000000 00000000 00000100
[-4]补= 11111111 11111111 11111111 11111100
0 向右移出 1 位后 11111111 11111111 11111111 11111110 = [-2]补

逻辑右移:>>>,不管正数、负数,左端都用0补充。

-1>>>1
[-1]原= 10000000 00000000 00000000 00000001
[-1]补= 11111111 11111111 11111111 11111111
1 向右移出1位 01111111 11111111 11111111 11111111 = [2^31-1]补

算数左移:<<,左移后右端用0补充。

字节序

字节顺序是指占用内存多于一个字节类型的数据在内存中的存放顺序,有小端、大端两种顺序。

  • 小端字节序(little endian):低字节数据存放在内存低地址处,高字节数据存放在内存高地址处;

  • 大端字节序(bigendian):高字节数据存放在低地址处,低字节数据存放在高地址处。

int value = 0x01020304;采用不同的字节序,在内存中的存储情况如下:

小端字节序:

内存地址编号 字节内容
0x00001000 04
0x00001001 03
0x00001002 02
0x00001003 01

大端字节序:

内存地址编号 字节内容
0x00001000 01
0x00001001 02
0x00001002 03
0x00001003 04

显然大字节序,比较符合人类思维习惯。

JAVA字节序都为大端字节序,所谓JAVA字节序,是指在JAVA虚拟机中多字节类型数据的存放顺序。

java.nio包中提供了 ByteOrder.nativeOrder()方法来查看主机的字节序。

还可以指定ByteBuffer读写操作时的字节序:byteBuffer.order(ByteOrder.LITTLE_ENDIAN)

例子

byte 数组 转为 int (默认为大端字节序)

// 第一种方法
public int convertByteToInt(byte[] b){           
    int value= 0;
    for(int i=0; i<b.length; i++)
       value = (value << 8) | b[i];     
    return value;       
}
// 第二种方法
public static int byteArrayToInt(byte[] b) {  
    return   b[3] & 0xFF |  
            (b[2] & 0xFF) << 8 |  
            (b[1] & 0xFF) << 16 |  
            (b[0] & 0xFF) << 24;  
}
// 第三种方法
ByteBuffer.wrap(byteBarray).getInt();

int 转为 byte 数组 (默认为大端字节序)

// 第一种方法
private byte[] intToByteArray ( final int i ) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(bos);
    dos.writeInt(i);
    dos.flush();
    return bos.toByteArray();
}
// 第二种方法
public byte[] intToBytes( final int i ) {
    ByteBuffer bb = ByteBuffer.allocate(4);
    bb.putInt(i);
    return bb.array();
}
// 第三种方法
public static byte[] intToByteArray(int a) {
    return new byte[] {
        (byte) ((a >> 24) & 0xFF),
        (byte) ((a >> 16) & 0xFF),
        (byte) ((a >> 8) & 0xFF),
        (byte) (a & 0xFF)
    };
}

从流中读取指定长度的整数:

 public static int ReceiveIntegerR(InputStream input, int siz) throws IOException {
    int n = 0;
    for (int i = 0; i < siz; i++) {
        int b = input.read();
        if (b < 0)
            throw new IOException();
        n = b | (n << 8);
    }
    return n;
}

向流中写入指定长度的整数:

public static void SendInteger(OutputStream output, int val, int siz) throws IOException {
    byte[] buf = new byte[siz];
    while (siz-- > 0) {
        buf[siz] = (byte) (val & 0xff);
        val >>= 8;
    }
    output.write(buf);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容