Java中对字节的详解
郝伟 2021/06/04
在信息安全领域,对字节的操作非常频繁,所以本文对字节操作的相关内容进行详细介绍。
1字节等于8个位,是计算机中数据的最基础的组成单元。在Java中,没有无符号数,所以1个字节的表示范围是 -128 ~ 127。
二进制有初始化有三种方法:
byte b = 100;byte b = 0xff;byte b = 0b10001000;注意:使用任何超出字节值域(即-128~127)的赋值都会会报 java.lang.Error: cannot convert from int to byte 错误。
节点变量支持常规的四则运算,但是需要注意一点就是返回的数据类型都是 Integer 所以需要进行转换。
先看这样的一个示例,以下代码实现两个字节的四则运算:
public static void test1() { byte a = 0x7f; byte b = 0x3; var c1 = a + b; var c2 = a - b; var c3 = a * b; var c4 = a / b; System.out.println("c1 = " + c1 + ", " + getClassName(c1)); System.out.println("c2 = " + c2 + ", " + getClassName(c2)); System.out.println("c3 = " + c3 + ", " + getClassName(c3)); System.out.println("c4 = " + c4 + ", " + getClassName(c4)); } public static String getClassName(Object o) { return o.getClass().getSimpleName(); }
输出结果为:
c1 = 130, Integer
c2 = 124, Integer
c3 = 381, Integer
c4 = 42, Integer
由此可见,字节运算的底层实现是将字节型转为整型,再进行运算,然后返回一个整型。这样在理解上就比较容易,对结果也好评估。
或运算是最基本的逻辑运算之一,可以实现字节中每个位与位的运算,请看以下示例:
public static void test3() {
System.out.println("或运算");
System.out.println(0b0 | 0b0);
System.out.println(0b1 | 0b0);
System.out.println(0b1 | 0b1);
System.out.println(0b01010101 | 0b10101010);
}
输出结果如下,输出分别是由低到高取得每一位的值,结果与预期一致。
或运算
0
1
1
255
与运算是最基本的逻辑运算之一,可以实现字节中每个位与位的运算,请看以下示例:
public static void test2() {
System.out.println("变量与常量异或");
byte b = (byte) 0b11111111;
System.out.println(b & 0b00000001);
System.out.println(b & 0b00000010);
System.out.println(b & 0b00000100);
System.out.println(b & 0b00001000);
System.out.println(b & 0b00010000);
System.out.println(b & 0b00100000);
System.out.println(b & 0b01000000);
System.out.println(b & 0b10000000);
}
输出结果如下,输出分别是由低到高取得每一位的值,结果与预期一致。
变量与常量异或
1
2
4
8
16
32
64
128
与运算是最基本的逻辑运算之一,可以实现字节中每个位与位的运算,请看以下示例:
public static void test4() { System.out.println("非运算"); byte b1 = (byte) 0b00000000; byte b2 = (byte) 0b11111111; byte b3 = (byte) 0b01010101; System.out.println(byteToString(b1) + " --取反--> " + byteToString((byte)~b1)); System.out.println(byteToString(b2) + " --取反--> " + byteToString((byte)~b2)); System.out.println(byteToString(b3) + " --取反--> " + byteToString((byte)~b3)); }
输出结果如下,输出分别是由低到高取得每一位的值,结果与预期一致。
非运算
00000000 --取反--> 11111111
11111111 --取反--> 00000000
01010101 --取反--> 10101010
异或顾名思义,两个位不相同时为1,相同时为0,请看下面的示例。
public static void test5() { System.out.println("异或运算"); byte b1 = (byte) 0b00000000; byte b2 = (byte) 0b11111111; byte b3 = (byte) 0b01010101; xor(b1, b2); xor(b2, b3); xor(b3, b1); } public static void xor(byte b1, byte b2) { var bstr1 = byteToString(b1); var bstr2 = byteToString(b2); var bstr3 = byteToString((byte)(b1^b2)); System.out.printf("%s ^ %s --> %s\n", bstr1, bstr2, bstr3); }
输出结果为:
异或运算
00000000 & 11111111 --> 11111111
11111111 & 01010101 --> 10101010
01010101 & 00000000 --> 01010101
实际上,与异或相对应的还是同或运行,即相同时为1,不同时为0,但是在Java中没有实现。我们可以对异或取反即可。
public static String byteToString(byte b) { int datalen = 8; String[] data = new String[datalen]; for (int i = 0, w = 1; i < datalen; i++, w *= 2) data[datalen - i - 1] = (b & w) > 0 ? "1" : "0"; return String.join("", data); } public static String intToString(int b) { int datalen = 32; String[] data = new String[datalen]; for (int i = 0, w = 1; i < datalen; i++, w *= 2) data[datalen - i - 1] = (b & w) > 0 ? "1" : "0"; return String.join("", data); }
public class ByteOperationDemo { public static void main(String[] args) { test0(); test1(); test2(); test3(); test4(); test5(); } public static void test5() { System.out.println("异或运算"); byte b1 = (byte) 0b00000000; byte b2 = (byte) 0b11111111; byte b3 = (byte) 0b01010101; xor(b1, b2); xor(b2, b3); xor(b3, b1); } public static void xor(byte b1, byte b2) { var bstr1 = byteToString(b1); var bstr2 = byteToString(b2); var bstr3 = byteToString((byte)(b1^b2)); System.out.printf("%s ^ %s --> %s\n", bstr1, bstr2, bstr3); } public static void test4() { System.out.println("非运算"); byte b1 = (byte) 0b00000000; byte b2 = (byte) 0b11111111; byte b3 = (byte) 0b01010101; System.out.println(byteToString(b1) + " --取反--> " + byteToString((byte)~b1)); System.out.println(byteToString(b2) + " --取反--> " + byteToString((byte)~b2)); System.out.println(byteToString(b3) + " --取反--> " + byteToString((byte)~b3)); } public static void test3() { System.out.println("或运算"); System.out.println(0b0 | 0b0); System.out.println(0b1 | 0b0); System.out.println(0b1 | 0b1); System.out.println(0b01010101 | 0b10101010); } public static void test2() { System.out.println("变量与常量异或"); byte b = (byte) 0b11111111; System.out.println(b & 0b00000001); System.out.println(b & 0b00000010); System.out.println(b & 0b00000100); System.out.println(b & 0b00001000); System.out.println(b & 0b00010000); System.out.println(b & 0b00100000); System.out.println(b & 0b01000000); System.out.println(b & 0b10000000); } public static void test1() { byte a = 0x7f; byte b = 0x3; var c1 = a + b; var c2 = a - b; var c3 = a * b; var c4 = a / b; System.out.println("c1 = " + c1 + ", " + getClassName(c1)); System.out.println("c2 = " + c2 + ", " + getClassName(c2)); System.out.println("c3 = " + c3 + ", " + getClassName(c3)); System.out.println("c4 = " + c4 + ", " + getClassName(c4)); } public static void test0() { for (int i = 0; i < 256; i++) System.out.println((i & 0xff) + ": " + byteToString((byte) i)); } public static String getClassName(Object o) { return o.getClass().getSimpleName(); } public static void showType(Object o) { //System.out.println(o instanceof Integer); //System.out.println(o.getClass().getName()); System.out.println(o.getClass().getSimpleName()); } public static String byteToString(byte b) { int datalen = 8; String[] data = new String[datalen]; for (int i = 0, w = 1; i < datalen; i++, w *= 2) data[datalen - i - 1] = (b & w) > 0 ? "1" : "0"; return String.join("", data); } public static String intToString(int b) { int datalen = 32; String[] data = new String[datalen]; for (int i = 0, w = 1; i < datalen; i++, w *= 2) data[datalen - i - 1] = (b & w) > 0 ? "1" : "0"; return String.join("", data); } }