Java基础类型存储与运算知识
最近发现一个问题,面试者、甚至组员中,在被问及一些计算机基础类型存储知识的时候,也许是大家目前的工作都是比较偏业务,以至于基础知识不太扎实,感觉有必要在这里在夯实一下,以被后人和自己牢记——基础知识或许不是你现在工作最重要的,但却是你未来进步的基石。
1. Java中的原码、反码和补码
1.1 原码
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制:
[+1]原 = 0000 0001
[-1]原 = 1000 0001
第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111] 即 [-127 , 127]
1.2 反码
反码的表示方法是:
正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反
1.3 补码
补码的表示方法是:
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
1.4 负数运算的问题
将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
计算十进制的表达式: 1-1=0
1 – 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
如果用原码表示, 让符号位也参与计算, 对于减法来说, 结果是不正确的.
为了解决原码做减法的问题, 出现了反码:
计算十进制的表达式: 1-1=0
1 – 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
用反码计算减法, 结果的真值部分是正确的, 但是会有[0000 0000]原和[1000 0000]原两个编码表示0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1 – 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128。
1.5 补码出现的原因
使用补码, 一是为了防止0有2个编码,其次就是为了把减法运算用加法运算表示出来,以达到简化电路的作用(因为有了负数的概念,减法可以换算为加法) 而且还能够多表示一个最低数.
这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-2^31, 2^31-1] 因为第一位表示的是符号位, 而使用补码表示时又可以多保存一个最小值.
2. 基础数据所占的内存
注意:对于boolean类型,单个时候占用4个字节,在数组中时占用1个字节。
3. 大尾端和小尾端
尾端是计算机中数据存储的一种模式。首先由Danny Cohen(1980引入计算机科学界。
应用于大于1个字节的数字的高位和低位在内存中的存放顺序。
大尾端,指的是数字的高位存储在低地址,数字的低位存放在高地址,主要在solaris,Macintosh ,05年之前的mac中使用。
小尾端和大尾端相反,主要在windows,linux中使用。
代码示例:
public class EndianTest { public static void main(String[] args) { byte[] payload = toArray(-1991249); int number = fromArray(payload); System.out.println(number); //-1991249 System.out.println("按照大尾端来从byte[4]中解出int"); int result = 0xFF & payload[0]; result <<= 8; result += 0xFF & payload[1]; result <<= 8; result += 0xFF & payload[2]; result <<= 8; result += 0xFF & payload[3]; System.out.println(result); //-1991249 } public static int fromArray(byte[] payload){ ByteBuffer buffer = ByteBuffer.wrap(payload); buffer.order(ByteOrder.BIG_ENDIAN); return buffer.getInt(); } public static byte[] toArray(int value){ ByteBuffer buffer = ByteBuffer.allocate(4); buffer.order(ByteOrder.BIG_ENDIAN); buffer.putInt(value); buffer.flip(); return buffer.array(); } } |
4. 符号位扩展与进制转换
直接上代码,运行后可以自己想想结果:
代码示例:
public class SignedUnsignedTest { public static void main(String[] args) { byte x = 127; System.out.println("打印一个字节127:" + x); //127 System.out.println("打印二进制,是正数,高位被扩展为符号位0,Integer.toBinaryString(127):" + Integer.toBinaryString(x)); //1111111(7个1) System.out.println("打印十六进制,是正数,高位被扩展为符号位0,Integer.toHexString(127):" + Integer.toHexString(x)); //7f x += 1; System.out.println("已经溢出了!((byte)127)+1:" + x); System.out.println("高位被扩展成符号位1,Integer.toBinaryString(127+1):" + Integer.toBinaryString(x)); //11111111111111111111111110000000 System.out.println("高位被扩展成符号位1,Integer.toHexString(127+1):" + Integer.toHexString(x)); //ffffff80 System.out.println("保证了高位不被扩展成符号位1,Integer.toHexString((127+1) & 0xff):" + Integer.toHexString(x & 0xff)); //80 System.out.println("0x80:" + 0x80); // 128,java这种字面常亮,不遵循符号位扩展原则,高位全部补0 System.out.println("0x81:" + 0x81); // 129 System.out.println("0x82:" + 0x82); // 130 System.out.println("(byte)0x80:" + (byte)0x80); // -128 System.out.println("(byte)0x81:" + (byte)0x81); // -127 System.out.println("(byte)0x82:" + (byte)0x82); // -126 System.out.println("0x8000:" + 0x8000); // 32768 System.out.println("(short)0x8000:" + (short)0x8000); // -32768 System.out.println("0x80000000:" + 0x80000000); // -2147483648 System.out.println("0x80000000L:" + 0x80000000L); // 2147483648 System.out.println("高位被扩展为了fff...f,所以结果不对,Long.toHexString(0x1000000L + 0xcafebabe):" + Long.toHexString(0x1000000L + 0xcafebabe)); // ffffffffcbfebabe System.out.println("高位没有杯扩展为了fff...f,所以结果对,Long.toHexString(0x1000000L + 0xcafebabe):" + Long.toHexString(0x1000000L + 0xcafebabeL)); // cbfebabe System.out.println("如果是窄类型扩充为宽类型,符号位扩展,但是!!如果是char则扩展0,(int)(char)(byte)-1:" + (int)(char)(byte)-1); //65535 System.out.println("如果是窄类型扩充为宽类型,符号位扩展,(int)(char)(byte)-1:" + (int)(short)(byte)-1); //-1 char c = 'x'; int y = c & 0xffff; //等同于int y = c; 如果是char,则零扩展 System.out.println("char c = 'x';c:" + c); System.out.println("int y = c & 0xffff;y:" + y); byte b = -1; System.out.println("有符号位扩展,(short)b" + (short)b); // -1 System.out.println("无符号位扩展,(short)(b & 0xff)" + (short)(b & 0xff)); //255 System.out.println("符号位扩展后提升为int,高位全部补充了1,而右边的int高位是0,自然不相等,(byte)0x90 == 0x90:" + ((byte)0x90 == 0x90)); // false System.out.println("符号位扩展后提升为int,用掩码将其保留原值高位都是0,而右边的int高位是0,二者相等,(byte)0x90 == 0x90:" + (((byte)0x90 & 0xff) == 0x90)); // true byte[] byteArray = new byte[4]; byteArray[0] = -1; byteArray[1] = 127; byteArray[2] = 3; byteArray[3] = -128; System.out.println(byteArrayToHexString(byteArray)); //ff7f0380 } /** * 将字节数组输出为十六进制字符串 * @param byteArray * @return */ public static String byteArrayToHexString(byte[] byteArray) { String res = ""; for (byte b : byteArray) { String hex = Integer.toHexString(b & 0xff); hex = (hex.length() == 1) ? "0" + hex : hex; res += hex.toLowerCase(); } return res; } } |
旭总,那个boolean类型的上下矛盾了吧。 图片里说是1位,我理解跟bitmap一样,是1byte,为啥下面注释说单个4字节,数组中占1字节。
你好,java中的boolean在虚拟机规范里面是当做int来对待的,所以是4byte,而boolean数组可以当做1个byte认识。