美文网首页
Java编译器自动生成access$xxx方法探究

Java编译器自动生成access$xxx方法探究

作者: Liuqc | 来源:发表于2020-04-05 17:25 被阅读0次

    简介

    Java编译器在编译过程中自动生成accessxxx方法,主要是为了内部类与外部类私有成员的访问,又不愿破坏类的封装性,所以就在被调用类中生成package权限静态的accessxxx方法

    实例

    接下来,我们就看看那些情况会生成accessxxx方法,还有就是accessxxx方法有啥特点

    情况一

    私有内部类访问外部类私有成员变量,源代码如下

    public class OutClass extends Base {
        private String test = "test";
        String test2 = "test";
        private String test3 = "test";
    
        private class InnerClass {
            private InnerClass() {
            }
    
            public void innerTest() {
                String test = OutClass.this.test;
                String test2 = OutClass.this.test2;
                String test3 = OutClass.this.test3;
                String baseTest = OutClass.this.baseTest;
                outTest();
                outTest2();
                outTest3();
                baseTest();
            }
        }
    
        private void outTest() {
        }
    
        void outTest2() {
        }
    
        public void outTest3() {
        }
    }
    
    

    首先看一下编译后外部类字节码,通过javap -c查看

    public class OutClass extends Base {
      java.lang.String test2;
    
      public sort.OutClass();
        Code:
           0: aload_0
           1: invokespecial #4                  // Method sort/Base."<init>":()V
           4: aload_0
           5: ldc           #5                  // String test
           7: putfield      #3                  // Field test:Ljava/lang/String;
          10: aload_0
          11: ldc           #5                  // String test
          13: putfield      #6                  // Field test2:Ljava/lang/String;
          16: aload_0
          17: ldc           #5                  // String test
          19: putfield      #2                  // Field test3:Ljava/lang/String;
          22: return
    
      void outTest2();
        Code:
           0: return
    
      public void outTest3();
        Code:
           0: return
    
      static java.lang.String access$000(sort.OutClass);
       flags: ACC_STATIC, ACC_SYNTHETIC
        Code:
           0: aload_0
           1: getfield      #3                  // Field test:Ljava/lang/String;
           4: areturn
    
      static java.lang.String access$100(sort.OutClass);
       flags: ACC_STATIC, ACC_SYNTHETIC
        Code:
           0: aload_0
           1: getfield      #2                  // Field test3:Ljava/lang/String;
           4: areturn
    
      static void access$200(sort.OutClass);
       flags: ACC_STATIC, ACC_SYNTHETIC
        Code:
           0: aload_0
           1: invokespecial #1                  // Method outTest:()V
           4: return
    }
    
    

    看到外部类的中多了access000和access100方法以及没有内部类,而在目录中会多一个OutClassInnerClass.class文件,接下来看OutClassInnerClass.class

    class OutClass$InnerClass {
        private OutClass$InnerClass(OutClass var1) {
            this.this$0 = var1;
        }
    
        public void innerTest() {
            String test = OutClass.access$000(this.this$0);
            String test2 = this.this$0.test2;
            String test3 = OutClass.access$100(this.this$0);
            String baseTest = this.this$0.baseTest;
            OutClass.access$200(this.this$0);
            this.this$0.outTest2();
            this.this$0.outTest3();
            this.this$0.baseTest();
        }
    }
    
    

    类的访问权限会被由private修改成default,test方法中调用外部类的私有成员变成对了OutClass.accessxxx(this.this0)方法的调用

    情况二

    外部类调用内部类私有成员

    public class sort.OutClass {
      public sort.OutClass();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public void outTest();
        Code:
           0: new           #2                  // class sort/OutClass$InnerClass
           3: dup
           4: aload_0
           5: aconst_null
           6: invokespecial #3                  // Method sort/OutClass$InnerClass."<init>":(Lsort/OutClass;Lsort/OutClass$1;)V
           9: astore_1
          10: aload_1
          11: invokestatic  #4                  // Method sort/OutClass$InnerClass.access$100:(Lsort/OutClass$InnerClass;)Ljava/lang/String;
          14: astore_2
          15: aload_1
          16: getfield      #5                  // Field sort/OutClass$InnerClass.test2:Ljava/lang/String;
          19: astore_3
          20: aload_1
          21: invokestatic  #6                  // Method sort/OutClass$InnerClass.access$200:(Lsort/OutClass$InnerClass;)Ljava/lang/String;
          24: astore        4
          26: aload_1
          27: getfield      #7                  // Field sort/OutClass$InnerClass.baseTest:Ljava/lang/String;
          30: astore        5
          32: aload_1
          33: invokevirtual #8                  // Method sort/OutClass$InnerClass.innerTest:()V
          36: aload_1
          37: invokestatic  #9                  // Method sort/OutClass$InnerClass.access$300:(Lsort/OutClass$InnerClass;)V
          40: aload_1
          41: invokevirtual #10                 // Method sort/OutClass$InnerClass.innerTest3:()V
          44: aload_1
          45: invokevirtual #11                 // Method sort/OutClass$InnerClass.baseTest:()V
          48: return
    }
    

    外部类调用内部类私有成员内部类也会生成access$xxx方法

    总结

    accessxxx方法只有在内部类、外部类访问对方私有成员时java编译器才会生成,accessxxx方法flag是ACC_STATIC、 ACC_SYNTHETIC,ACC_STATIC是指静态方法,ACC_SYNTHETIC是指此方法是编译器生成的。调用asscess方法要比直接使用变量消耗性能,大量的access方法会对性能有一些影响。另外,在Android中art以及dalvik虚拟机执行的是dex文件,dex中方法数不能超过64K,编译器多生成的access方法,意味着可能要多分几个dex包,意味着会增加包的体积,同样也会增加虚拟机加载dex时间,造成冷启动启动时间延长。<P>
    如何减少access方法生成呢?主要有两个方案。

    • 方案一:开发者开发时把访问权限改成default,这个显然有点难度
    • 方案二:通过asm查找并删除flags标记为ACC_STATIC、 ACC_SYNTHETIC,前缀为access的方法

    相关文章

      网友评论

          本文标题:Java编译器自动生成access$xxx方法探究

          本文链接:https://www.haomeiwen.com/subject/fgzuphtx.html