注解的原理-类的常量池

上次讲了注解的定义和自定义注解,
但是留了个问题没有进一步说明,就是注解所设定的数据是存在什么地方的?
这里需要引入一个新东西,类的常量池。

class的结构

对于Java新手来说这部分可能不是很友好,
class文件是java文件编译后的字节码,对于一个class文件来说规定的结构可以理解为一张表,
下面是class文件结构的规定,


image

如果第一次接触的话可以先忽略具体的各个项目,
总的说就是Java编译后的字节码按照表的规定非常严格的以表的结构构成。

对于我们要关注的问题"注解的数据存储在哪里"来说,
只需要关注表里面的 constant_pool 这个部分,
这个称作常量池的东西,保存了一系列的数据,分为四种

  • Literal,字面量
  • Symbolic References,符号引用
  • Others,其他
  • constant pool,常量
    注解的数据就存在 constant pool这里。

常量池

用比较直观的方式来理解常量池的话,最简单便捷的方式就是看字节码,
javap 是一个查看字节码的命令,之前多次用过它来理解Java的字节码,
这里我们用 javap来看常量池的话可以执行

javap -p Student.Class

输出

Classfile /Users/zhenghui/StudioProjects/MyProject/AnnotationDemo/Student.class
  Last modified 2018-7-28; size 925 bytes
  MD5 checksum 6ec1e13999388ff134142418179a88d8
  Compiled from "Student.java"
public class Student
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref          #15.#34        // java/lang/Object."<init>":()V
   #2 = Fieldref           #4.#35         // Student.name:Ljava/lang/String;
   #3 = Fieldref           #4.#36         // Student.age:I
   #4 = Class              #37            // Student
   #5 = Methodref          #4.#38         // Student."<init>":(Ljava/lang/String;I)V
   #6 = Fieldref           #39.#40        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = Class              #41            // java/lang/StringBuilder
   #8 = Methodref          #7.#34         // java/lang/StringBuilder."<init>":()V
   #9 = String             #42            // name:
  #10 = Methodref          #7.#43         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #11 = String             #44            //  age:
  #12 = Methodref          #7.#45         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  #13 = Methodref          #7.#46         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #14 = Methodref          #47.#48        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #15 = Class              #49            // java/lang/Object
  #16 = Utf8               name
  #17 = Utf8               Ljava/lang/String;
  #18 = Utf8               age
  #19 = Utf8               I
  #20 = Utf8               <init>
  #21 = Utf8               (Ljava/lang/String;I)V
  #22 = Utf8               Code
  #23 = Utf8               LineNumberTable
  #24 = Utf8               createStudent
  #25 = Utf8               (Ljava/lang/String;I)LStudent;
  #26 = Utf8               RuntimeVisibleAnnotations
  #27 = Utf8               LSexual;
  #28 = Utf8               value
  #29 = Utf8               male
  #30 = Utf8               displayInfo
  .... //省略部分内容
  {
  java.lang.String name;
    descriptor: Ljava/lang/String;
    flags:

  int age;
    descriptor: I
    flags:
    .... //省略部分内容
    public void displayInfo();
      descriptor: ()V
      flags: ACC_PUBLIC
      Code:
        stack=3, locals=1, args_size=1
           0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: new           #7                  // class java/lang/StringBuilder
           6: dup
           7: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
          10: ldc           #9                  // String name:
          12: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          15: aload_0
          16: getfield      #2                  // Field name:Ljava/lang/String;
          19: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          22: ldc           #11                 // String  age:
          24: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          27: aload_0
          28: getfield      #3                  // Field age:I
          31: invokevirtual #12                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
          34: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          37: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          40: return
        LineNumberTable:
          line 16: 0
          line 17: 40
  }

内容比较长,就只截取其中一部分。
感兴趣的话可以自己写个简单的类编译一下,然后查看完整的字节码,跟上面的大同小异。
上面的字节码是从上一个文章中的例子里编译来的,
在 Constant pool 这部分保存了我们注解的内容,关注
#24 - #29 的内容,
这里就是注解所携带的信息存放的地方了。
这里用了一个RuntimeVisibleAnnotations作为标注,对应注解中的RUNTIME标记。
可能跟你一开始理解的不同,现在应该明白,注解的信息并不保存在方法的执行栈中,而是在一个叫常量池的地方独立保存起来。

关于class的文件结构可以说很长的篇幅,
比如魔数,比如最大最小版本,
可能做过gradle插件的同学会遇到"major.minor version 52.0"这么个问题,
原因是在低版本的java上使用了高版本的插件导致的,这个 version 52就定义在class文件的 major version 字段。
如果打算进阶资深Java开发的话可以仔细弄清楚这一块的知识哦。


公众号.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352