首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往
23 篇文章
1
面经手册 · 第11篇《StringBuilder 比 String 快?空嘴白牙的,证据呢!》
2
面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》
3
面经手册 · 第8篇《LinkedList插入速度比ArrayList快?你确定吗?》
4
Stack有性能问题?推荐用ArrayDeque队列!队列是什么?什么是双端队列、延迟系列、阻塞队列,全是知识盲区!
5
面经手册 · 第9篇《队列是什么?什么是双端队列、延迟对列,全是知识盲区!》
6
面经手册 · 第7篇《ArrayList也这么多知识?一个指定位置插入就把谢飞机面晕了!》
7
面经手册 · 第7篇《ArrayList也这么多知识?一个指定位置插入就把谢飞机面晕了!》
8
面经手册 · 第6篇《带着面试题学习红黑树操作原理,解析什么时候染色、怎么进行旋转、与2-3树有什么关联》
9
面试28k职位,老乡面试官从HashCode到HashMap给我讲了一下午!
10
面经手册 · 第4篇《HashMap数据插入、查找、删除、遍历,源码分析》
11
面经手册 · 第3篇《HashMap核心知识,扰动函数、负载因子、扩容链表拆分深度学习(+实践验证)》
12
HashMap深度学习,扰动函数、负载因子,原理加实践,让懂了就是真的懂!
13
有点干货 | Jdk1.8新特性实战篇(41个案例)
14
有点干货 | JDK、CGLIB动态代理使用以及源码分析
15
似乎你总也记不住,byte取值范围是 -127~128 还是 -128~127
16
源码分析 | 像盗墓一样分析Spring是怎么初始化xml并注册bean的
17
面经手册 · 第16篇《码农会锁,ReentrantLock之公平锁讲解和实现》
18
面经手册 · 第17篇《码农会锁,ReentrantLock之AQS原理分析和实践使用》
19
面经手册 · 第18篇《AQS 共享锁,Semaphore、CountDownLatch,听说数据库连接池可以用到!》
20
面经手册 · 第14篇《volatile 怎么实现的内存可见?没有 volatile 一定不可见吗?》
21
面经手册 · 第19篇《Thread.start() ,它是怎么让线程启动的呢?》
22
面经手册 · 第21篇《手写线程池,对照学习ThreadPoolExecutor线程池实现原理!》
23
面经手册 · 第20篇《Thread 线程,状态转换、方法使用、原理分析》

似乎你总也记不住,byte取值范围是 -127~128 还是 -128~127

作者:付政委

博客:https://bugstack.cn

小傅哥 | https://bugstack.cn 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例、源码分析等。 你用剑?、我用刀?,好的代码都很烧,望你不吝出招!

一、前言介绍

无论在面试过程中还是平时的技术交流中,似乎有很多小伙伴始终记不住java中byte类型的取值范围是多少。究其原因大部分程序员对这个取值范围是不在意的,因为知道与不知道都不影响你完成工作。另外这种知识点压根不是让你死记硬背的,当然如果你是从其他文科专业转过来学编程开发的,还情有可原。但对一个理科生来说,就不太应该了。

二、取值范围计算

在java中,byte占1个字节,8比特位,可以想象成8个小块的数据区间,首位用0、1代表符号位。0[正]、1[负],那么绘制出一个表格如下;

byte

序号

8

7

6

5

4

3

2

1

2ⁿ

2^7

2^6

2^5

2^4

2^3

2^2

2^1

2^0

128

64

32

16

8

4

2

1

+127

0

1

1

1

1

1

1

1

-128

1

0

0

0

0

0

0

0

+127 二进制求和

代码语言:javascript
复制
1  2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^7
2= 2^(n+1) - 1
3= 127

-128 二进制求和

代码语言:javascript
复制
1  2^8
2= 128

好了,现在看懂逻辑就很清晰了,为什么是负数到-128,因为1代表负数的符号位,也就整整好好是2的8次方,-128。

三、进制数值转换

因为java语言与一些其他语言byte的取值范围不同,所以在有时候处理一些文件时候需要进行进制转换。也就是 -128~127 与 0~255 的转换处理;

比如我们现在将一个java中byte=120,转换成 0~255取值范围的数值;

一般可以进行与运算;

代码语言:javascript
复制
1120 & 0x0FF

同时还可以进行增位运算;(也就是将8个字节长度的内容,放到16个长度中,进行转换)

代码语言:javascript
复制
1byte[] val = {-120};
2BigInteger bigInteger = new BigInteger(1, val);
3//有符号
4System.out.println(bigInteger.byteValue());
5//无符号(增位)
6String str_hex = bigInteger.toString(16);
7System.out.println(Integer.parseInt(str_hex, 16)); // 136

四、解析一段class字节码

java的类文件都会被编译成class文件,那么class文件需要经过jvm的解析、验证,加载等处理才可以被虚拟机的指令执行操作。

如果下是一段class文件的byte数组,将内容解析出对应的结果;

代码语言:javascript
复制
 1public class ClassReaderTest {
 2
 3    //取部分字节码:java.lang.String
 4    private static byte[] classData = {
 5            -54, -2, -70, -66, 0, 0, 0, 52, 2, 26, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0,
 6            59, 8, 0, 83, 8, 0, 86, 8, 0, 87, 8, 0, 110, 8, 0, -83, 8, 0, -77, 8, 0, -49, 8, 0, -47, 1, 0, 3, 40, 41, 73, 1,
 7            0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41,
 8            76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3,
 9            40, 41, 90, 1, 0, 4, 40, 41, 91, 66, 1, 0, 4, 40, 41, 91, 67, 1, 0, 4, 40, 67, 41, 67, 1, 0, 21, 40, 68, 41, 76,
10            106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 40, 73, 41, 67, 1, 0, 4};
11
12    public static void main(String[] args) {
13
14        //classData是我们的字节码,第一是-54,因为byte取值范围是-128~+127,所以如果想看到和其他虚拟机一样的值,需要进行与运算。
15        System.out.println("* byte字节码与运算原值(-54)换行后(-54 & 0x0FF):" + (-54 & 0x0FF));
16
17        //校验魔数
18        readAndCheckMagic();
19
20        //校验版本号
21        readAndCheckVersion();
22
23        //接下来会依次读取[可以参照java版本虚拟机代码];constantPool、accessFlags、thisClassIdx、supperClassIdx、interfaces、fields、methods、attributes
24    }
25
26    /**
27     * 校验魔数
28     * <p>
29     * 很多文件格式都会规定满足该格式的文件必须以某几个固定字节开头,这几个字节主要起到标识作用,叫作魔数(magic number)。
30     * 例如;
31     * PDF文件以4字节“%PDF”(0x25、0x50、0x44、0x46)开头,
32     * ZIP文件以2字节“PK”(0x50、0x4B)开头
33     * class文件以4字节“0xCAFEBABE”开头
34     */
35    private static void readAndCheckMagic() {
36        System.out.println("\r\n------------ 校验魔数 ------------");
37        //从class字节码中读取前四位
38        byte[] magic_byte = new byte[4];
39        System.arraycopy(classData, 0, magic_byte, 0, 4);
40
41        //将4位byte字节转成16进制字符串
42        String magic_hex_str = new BigInteger(1, magic_byte).toString(16);
43        System.out.println("magic_hex_str:" + magic_hex_str);
44
45        //byte_magic_str 是16进制的字符串,cafebabe,因为java中没有无符号整型,所以如果想要无符号只能放到更高位中
46        long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16);
47        System.out.println("magic_unsigned_int32:" + magic_unsigned_int32);
48
49        //魔数比对,一种通过字符串比对,另外一种使用假设的无符号16进制比较。如果使用无符号比较需要将0xCAFEBABE & 0x0FFFFFFFFL与运算
50        System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL));
51
52        if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) {
53            System.out.println("class字节码魔数无符号16进制数值一致校验通过");
54        } else {
55            System.out.println("class字节码魔数无符号16进制数值一致校验拒绝");
56        }
57
58    }
59
60    /**
61     * 校验版本号
62     * <p>
63     * 魔数之后是class文件的次版本号和主版本号,都是u2类型。假设某class文件的主版本号是M,次版本号是m,那么完整的版本号可以
64     * 表示成“M.m”的形式。次版本号只在J2SE 1.2之前用过,从1.2开始基本上就没有什么用了(都是0)。主版本号在J2SE 1.2之前是45,
65     * 从1.2开始,每次有大版本的Java版本发布,都会加1{45、46、47、48、49、50、51、52}
66     */
67    private static void readAndCheckVersion() {
68        System.out.println("\r\n------------ 校验版本号 ------------");
69
70        //从class字节码第4位开始读取,读取2位
71        byte[] minor_byte = new byte[2];
72        System.arraycopy(classData, 4, minor_byte, 0, 2);
73        //将2位byte字节转成16进制字符串
74        String minor_hex_str = new BigInteger(1, minor_byte).toString(16);
75        System.out.println("minor_hex_str:" + minor_hex_str);
76        //minor_unsigned_int32 转成无符号16进制
77        int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16);
78        System.out.println("minor_unsigned_int32:" + minor_unsigned_int32);
79
80        //从class字节码第6位开始读取,读取2位
81        byte[] major_byte = new byte[2];
82        System.arraycopy(classData, 6, major_byte, 0, 2);
83        //将2位byte字节转成16进制字符串
84        String major_hex_str = new BigInteger(1, major_byte).toString(16);
85        System.out.println("major_hex_str:" + major_hex_str);
86        //major_unsigned_int32 转成无符号16进制
87        int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16);
88        System.out.println("major_unsigned_int32:" + major_unsigned_int32);
89
90        System.out.println("版本号:" + major_unsigned_int32 + "." + minor_unsigned_int32);
91
92    }
93
94}

测试结果:

代码语言:javascript
复制
 1* byte字节码与运算原值(-54)换行后(-54 & 0x0FF):202
 2
 3------------ 校验魔数 ------------
 4magic_hex_str:cafebabe
 5magic_unsigned_int32:3405691582
 60xCAFEBABE & 0x0FFFFFFFFL:3405691582
 7class字节码魔数无符号16进制数值一致校验通过
 8
 9------------ 校验版本号 ------------
10minor_hex_str:0
11minor_unsigned_int32:0
12major_hex_str:34
13major_unsigned_int32:52
14版本号:52.0
15
16Process finished with exit code 0
17

五、综上总结

  • 关于byte在文章;《用java实现jvm虚拟机》中讲过,但是没有单独拿出来分析,现在单独分析下也增强记忆。
  • 任何一个可能不起眼的知识点,不是他不重要,而是你还没有用到。就像有句话说,不是读书没用,而是你没用。国语博大精深!
  • 认认真真对待每一个知识点,不断的夯实自己的地基,这就像是盖房子在打地基一样。越深越稳,最终所服能于你的上层架构才会更加精进。
下一篇
举报
领券