-
Notifications
You must be signed in to change notification settings - Fork 3.7k
2020 01 18 似乎你总也记不住,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 二进制求和
2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^7
= 2^(n+1) - 1
= 127
-128 二进制求和
2^8
= 128
好了,现在看懂逻辑就很清晰了,为什么是负数到-128,因为1代表负数的符号位,也就整整好好是2的8次方,-128。
因为java语言与一些其他语言byte的取值范围不同,所以在有时候处理一些文件时候需要进行进制转换。也就是 -128127 与 0255 的转换处理;
比如我们现在将一个java中byte=120,转换成 0~255取值范围的数值;
一般可以进行与运算;
120 & 0x0FF
同时还可以进行增位运算;(也就是将8个字节长度的内容,放到16个长度中,进行转换)
byte[] val = {-120};
BigInteger bigInteger = new BigInteger(1, val);
//有符号
System.out.println(bigInteger.byteValue());
//无符号(增位)
String str_hex = bigInteger.toString(16);
System.out.println(Integer.parseInt(str_hex, 16)); // 136
java的类文件都会被编译成class文件,那么class文件需要经过jvm的解析、验证,加载等处理才可以被虚拟机的指令执行操作。
如果下是一段class文件的byte数组,将内容解析出对应的结果;
public class ClassReaderTest {
//取部分字节码:java.lang.String
private static byte[] classData = {
-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,
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,
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,
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,
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,
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};
public static void main(String[] args) {
//classData是我们的字节码,第一是-54,因为byte取值范围是-128~+127,所以如果想看到和其他虚拟机一样的值,需要进行与运算。
System.out.println("* byte字节码与运算原值(-54)换行后(-54 & 0x0FF):" + (-54 & 0x0FF));
//校验魔数
readAndCheckMagic();
//校验版本号
readAndCheckVersion();
//接下来会依次读取[可以参照java版本虚拟机代码];constantPool、accessFlags、thisClassIdx、supperClassIdx、interfaces、fields、methods、attributes
}
/**
* 校验魔数
* <p>
* 很多文件格式都会规定满足该格式的文件必须以某几个固定字节开头,这几个字节主要起到标识作用,叫作魔数(magic number)。
* 例如;
* PDF文件以4字节“%PDF”(0x25、0x50、0x44、0x46)开头,
* ZIP文件以2字节“PK”(0x50、0x4B)开头
* class文件以4字节“0xCAFEBABE”开头
*/
private static void readAndCheckMagic() {
System.out.println("\r\n------------ 校验魔数 ------------");
//从class字节码中读取前四位
byte[] magic_byte = new byte[4];
System.arraycopy(classData, 0, magic_byte, 0, 4);
//将4位byte字节转成16进制字符串
String magic_hex_str = new BigInteger(1, magic_byte).toString(16);
System.out.println("magic_hex_str:" + magic_hex_str);
//byte_magic_str 是16进制的字符串,cafebabe,因为java中没有无符号整型,所以如果想要无符号只能放到更高位中
long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16);
System.out.println("magic_unsigned_int32:" + magic_unsigned_int32);
//魔数比对,一种通过字符串比对,另外一种使用假设的无符号16进制比较。如果使用无符号比较需要将0xCAFEBABE & 0x0FFFFFFFFL与运算
System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL));
if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) {
System.out.println("class字节码魔数无符号16进制数值一致校验通过");
} else {
System.out.println("class字节码魔数无符号16进制数值一致校验拒绝");
}
}
/**
* 校验版本号
* <p>
* 魔数之后是class文件的次版本号和主版本号,都是u2类型。假设某class文件的主版本号是M,次版本号是m,那么完整的版本号可以
* 表示成“M.m”的形式。次版本号只在J2SE 1.2之前用过,从1.2开始基本上就没有什么用了(都是0)。主版本号在J2SE 1.2之前是45,
* 从1.2开始,每次有大版本的Java版本发布,都会加1{45、46、47、48、49、50、51、52}
*/
private static void readAndCheckVersion() {
System.out.println("\r\n------------ 校验版本号 ------------");
//从class字节码第4位开始读取,读取2位
byte[] minor_byte = new byte[2];
System.arraycopy(classData, 4, minor_byte, 0, 2);
//将2位byte字节转成16进制字符串
String minor_hex_str = new BigInteger(1, minor_byte).toString(16);
System.out.println("minor_hex_str:" + minor_hex_str);
//minor_unsigned_int32 转成无符号16进制
int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16);
System.out.println("minor_unsigned_int32:" + minor_unsigned_int32);
//从class字节码第6位开始读取,读取2位
byte[] major_byte = new byte[2];
System.arraycopy(classData, 6, major_byte, 0, 2);
//将2位byte字节转成16进制字符串
String major_hex_str = new BigInteger(1, major_byte).toString(16);
System.out.println("major_hex_str:" + major_hex_str);
//major_unsigned_int32 转成无符号16进制
int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16);
System.out.println("major_unsigned_int32:" + major_unsigned_int32);
System.out.println("版本号:" + major_unsigned_int32 + "." + minor_unsigned_int32);
}
}
测试结果:
* byte字节码与运算原值(-54)换行后(-54 & 0x0FF):202
------------ 校验魔数 ------------
magic_hex_str:cafebabe
magic_unsigned_int32:3405691582
0xCAFEBABE & 0x0FFFFFFFFL:3405691582
class字节码魔数无符号16进制数值一致校验通过
------------ 校验版本号 ------------
minor_hex_str:0
minor_unsigned_int32:0
major_hex_str:34
major_unsigned_int32:52
版本号:52.0
Process finished with exit code 0
- 关于byte在文章;《用java实现jvm虚拟机》中讲过,但是没有单独拿出来分析,现在单独分析下也增强记忆。
- 任何一个可能不起眼的知识点,不是他不重要,而是你还没有用到。就像有句话说,不是读书没用,而是你没用。国语博大精深!
- 认认真真对待每一个知识点,不断的夯实自己的地基,这就像是盖房子在打地基一样。越深越稳,最终所服能于你的上层架构才会更加精进。
小傅哥(微信:fustack),公众号:bugstack虫洞栈
| bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
🌏 知识星球:码农会锁
实战项目:「DDD+RPC分布式抽奖系统
」、专属小册、问题解答、简历指导、架构图稿、视频课程
🐲 头条
-
💥
🎁 Lottery 抽奖系统
- 基于领域驱动设计的四层架构的互联网分布式开发实践 -
小傅哥的《重学 Java 设计模式》
- 全书彩印、重绘类图、添加内容 -
⭐小傅哥的《Java 面经手册》
- 全书5章29节,417页11.5万字,完稿&发版 -
小傅哥的《手撸 Spring》
- 通过带着读者手写简化版 Spring 框架,了解 Spring 核心原理 -
🌈小傅哥的《SpringBoot 中间件设计和开发》
- 小册16个中间件开发30个代码库
⛳ 目录
💋 精选
🐾 友链
建立本开源项目的初衷是基于个人学习与工作中对 Java 相关技术栈的总结记录,在这里也希望能帮助一些在学习 Java 过程中遇到问题的小伙伴,如果您需要转载本仓库的一些文章到自己的博客,请按照以下格式注明出处,谢谢合作。
作者:小傅哥
链接:https://bugstack.cn
来源:bugstack虫洞栈
2021年10月24日,小傅哥
的文章全部开源到代码库 CodeGuide
中,与同好同行,一起进步,共同维护。
这里我提供 3 种方式:
-
提出
Issue
:在 Issue 中指出你觉得需要改进/完善的地方(能够独立解决的话,可以在提出 Issue 后再提交PR
)。 -
处理
Issue
: 帮忙处理一些待处理的Issue
。 -
提交
PR
: 对于错别字/笔误这类问题可以直接提交PR
,无需提交Issue
确认。
详细参考:CodeGuide 贡献指南 - 非常感谢你的支持,这里会留下你的足迹
- 加群交流 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!由于微信群人满 100 之后无法加入,请扫描下方二维码先添加作者 “小傅哥” 微信(fustack),备注:加群。
- 公众号(bugstack虫洞栈) - 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。
感谢以下人员对本仓库做出的贡献或者对小傅哥的赞赏,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。