IDC

你管这破玩意叫 class?

作者:admin 2021-04-28 我要评论

我是一个 .java 文件,名叫 FlashObject.java,叫我小渣就行。 public classFlashObject{ privateString name ; private int age; public StringgetName(){ retu...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

 

我是一个 .java 文件,名叫 FlashObject.java,叫我小渣就行。

  1. public class FlashObject { 
  2.  
  3.     private String name
  4.     private int age; 
  5.      
  6.     public String getName() { 
  7.         return name
  8.     } 
  9.  
  10.     public int add(int a, int b) { 
  11.         return a + b; 
  12.     } 
  13.  

我马上就要被 JVM 虚拟机老大加载并运行了,此时老虚走了过来。

老虚:小渣呀,我马上就要把你载了,你先瘦身一下,别占太大地方。

小渣:好的,没问题,等我十秒钟。

public class FlashObject{private String name;private int age;public int add(int a,int b){return a+b;}

小渣:老虚,我瘦身好了,你看看。

老虚:...,你是不是有病。

小渣:怎么了,我把没用的空格和回车啥的都去掉了,瘦身了好多呢!

老虚:行吧,看你这智商,我就给你解释解释。你现在仍然是个文本文件,让你瘦身是让你定一个紧凑的数据结构来表示你这个 Java 文件里的信息,然后告诉我这个数据结构中每个字节都代表什么。

小渣:哦哦,这样啊。

老虚:对啊,这样一是方便我去加载,二是我这个虚拟机可不只是为你 Java 语言服务的,还有很多语言最终都可以转换为我虚拟机识别的,你得设计一个通用的格式。

小渣:嗯嗯,这回我明白啦!

1类信息

我的类名叫 FlashObject。

先找个地方把它存起来,放开头吧。

这里的一个小方格是 1 个字节,也就是 8 位。一个英文字母用 ASCII 码表示为 1 个字节,所以占一个方格,之后不再解释。

严谨的我又想到,这个类应该还有其父类。

虽然这个 .java 文件中没写,但也有其默认父类,Object。

当然,我们得记录下全类名

java/lang/Object

记在哪里呢?就紧跟在类名后面吧。

诶不对,我这个类名呀,父类名呀,都是变长的,这样紧挨着放,谁知道分界点在哪。

不行不行,得分别在前面加个长度,就用两字节表示吧。

除了父类之外,还有接口名呢!虽然我们这个类没写,但也得定义出来。

这个接口,和类名以及父类名稍有不同,因为可能有多个。

但这不是事儿,先占用两个字节,表示接口的数量即可,之后一个一个的接口名仍然像上面那样紧挨着排布。

嗯,完美。

2常量池

慢慢地,我发现需要字符串名字的地方越来越多。

除了刚刚的类名、父类名、接口名,还有属性名、方法名、属性的类名、方法的入参类型名、返回值类型名,等等等等。

一方面,要是每个都这么展开写下去,那文件格式会很乱,很多结构都是变长的。

另一方面,很多字符串都是重复的,比如属性 name 的类名 String,与方法 getName 的返回值类名 String,重复写两遍,就浪费了空间。

因此,我决定,之前的方案作废,设计一个新的结构来统一存储这些字符串,我给他起名为常量池。

每个字符串都有一个索引与之对应,这个是可以计算出来的,不需要额外的字段。

这样,刚刚的类、父类、接口,就都可以指向这个索引了,也因此可以将长度固定下来。

当然,现在这个常量池,仅仅存放了字符串。

不难想到,还可能有整型、浮点型的值作为常量,甚至还有可能是个引用类型,然后这个引用类型再次指向常量池中的一个索引,有点像指针的指针。

那这么多类型,必然就还需要一个记录类型信息的地方,看来我们得将之前的设计改改。

这样,我们的常量池,就不单单可以存储简单的字符串常量了,而是可以根据不同类型,存储与其相对应的数据结构的值。

当然,我们常量池的整体结构还是不变的,只不过里面是类型丰富的结构。

同样,我们的整个设计,也没有因为常量池的小改动,受到影响。

OK,总结一下我们目前的整体方案。

开头存常量池,之后需要的常量就全往这里放,用一个索引指向它即可。

紧接着存放类本身的相关信息,我们存放了当前类、父类以及接口的信息。

看来老虚要求的瘦身工作,已经初具规模啦。

3变量

现在类本身的信息,已经找到合适的位置存放起来了,接下来我们存变量。

变量也可能有多个,所以结构依然仿照我们之前的思路,开头存数量,后面紧跟着各个存放变量的数据结构。

至于变量用什么数据结构来存,是不是定长的,那就是我们接下来要设计的了。

我们把其中一个变量拿出来,看看它有什么?

  1. private String name

非常清晰,private 这部分是变量的标记,String 是变量类型,name 是变量名字。

先看标记部分

除了 private,还有 public、protected、static、final、volatile、transient 等,有的可以放在一起,比如

  1. public static final String name

有的不能放在一起,比如

public private String name; //错误

我们用位图的方式,每一个标记用一个位来表示(比如 public 在第一个位,private 在第二个位,static 在第四个位,final 在第五个位...),这样不论如何排列组合,最终的值都是不一样的。

我们把这些标记所对应的值,都设计并记录下来。

标记

public

0x0001

private

0x0002

protected

0x0004

static

0x0008

final

0x0010

volatile

0x0040

transient

0x0080

复合型的标记,就可以表现为将其相加,比如 public static,就是 0x0001 + 0x0008 = 0x0009。

而这样的赋值方式,不同排列组合后的和没有重复的,且也能根据值很方便地反推出标记。

不错不错,就这样了。

哦对了,类信息本身也有 public 呀 private 这些标记属性,刚刚记录类信息的时候忘了,先加上它,免得一会忘了!

再看类型部分

当前类型为 String,属于一个引用数据类型中的类类型。

  1. private String name

除此之外,还有八个基本数据类型,和引用类型中的数组类型。

为了占用更少的空间,我们将其用最少的符号来表示。

符号表示

类型

B

byte

C

char

D

double

F

float

I

int

J

long

S

short

Z

boolean

LClassName ;

[

数组

这里的基本数据类型,和数组类型,都只占用一个 char 来表示,就只占了 1 个字节。

如果是类,则占用了 L 和 ; 两个字节,再加上全类名所占的字节数。

比如这里的 String 类型,用符号表示,就是

  1. Ljava/lang/String; 

但注意,这里的符号,也都可以存放在常量池中,而我们的变量结构中的类型描述符部分,只需要一个常量池索引即可。

ok,第二部分也搞定了。

再看名字部分

名字部分没什么好说的,相信你直接能猜到了,直接上图。

OK,两字节的标记、两字节的类型描述符、两字节的变量名称,这个就是我们一个变量的数据结构。

把它放到我们最终的总视图里。

搞定!

4方法

方法也可能会有很多,我目前只有两个方法,我们拿 add 方法来分析。

  1. public int add(int a, int b) { 
  2.     return a + b; 

当然更准确地说,我还有个没写出来的构造方法。

总之,可能会有很多。

不过有了设计变量的经验,方法的数据结构很快就有了雏形。

标记部分,和变量标记部分的思路一样,值也差不多,我们也给他们赋上值就好了。

标记

public

0x0001

private

0x0002

protected

0x0004

static

0x0008

final

0x0010

volatile

0x0040

transient

0x0080

synchronized

0x0020

native

0x0100

abstract

0x0400



本文转载自网络,原文链接:https://mp.weixin.qq.com/s/pekAvJY84qSefHi69d3qgw

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • 你管这破玩意叫 class?

    你管这破玩意叫 class?

  • 从零搭建开发脚手架 Spring Boot文件上

    从零搭建开发脚手架 Spring Boot文件上

  • Kubernetes监控之优秀实践

    Kubernetes监控之优秀实践

  • C++为什么非要引入那几种类型转换?

    C++为什么非要引入那几种类型转换?