【问题标题】:What's the penalty for Synthetic methods?合成方法的惩罚是什么?
【发布时间】:2011-07-30 07:12:58
【问题描述】:

在 Eclipse 下开发 Java 应用程序时,我收到有关“通过合成方法访问的方法/值”的警告。解决方案只是将私有访问修饰符更改为默认级别。

这让我想知道:使用合成方法的惩罚是什么?有一些?我假设编译器/Eclipse 会发出警告,但它是如此相关或可以安全地忽略?

我没有在这里看到这个信息,所以我在问。

【问题讨论】:

    标签: java jvm java-synthetic-methods


    【解决方案1】:

    我很确定惩罚只不过是一个额外的方法调用。换句话说,与几乎所有用例完全无关。

    如果它处于一个特别热门的路径中,您可能会担心,但您应该首先使用分析器确定这实际上是您的问题。

    我只是关闭警告。

    【讨论】:

    • 由于合成方法总是微不足道的,它应该是内联的。它只会在几乎没有优化的 JVM 中产生影响,例如一些J2ME。我不喜欢合成方法,因为它会污染堆栈跟踪中的调用堆栈。 ;)
    【解决方案2】:

    Eclipse 警告您,您可能会泄露您认为是私密的信息。合成访问器可以被恶意代码利用,如下所示。

    如果您的代码需要在安全的 VM 中运行,使用内部类可能是不明智的。如果您可以使用反射并且可以完全访问所有内容,那么合成访问器不太可能产生可衡量的差异。


    例如,考虑这个类:

    public class Foo {
      private Object baz = "Hello";
      private class Bar {
        private Bar() {
          System.out.println(baz);
        }
      }
    }
    

    Foo 的签名实际上是:

    public class Foo extends java.lang.Object{
        public Foo();
        static java.lang.Object access$000(Foo);
    }
    

    access$000 是自动生成的,让单独的类Bar 访问baz,并将用Synthetic 属性进行标记。生成的精确名称取决于实现。常规编译器不允许您针对此方法进行编译,但您可以使用 ASM(或类似方法)生成自己的类,如下所示:

    import org.objectweb.asm.*;
    public class FooSpyMaker implements Opcodes {
      public static byte[] dump() throws Exception {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "Spy", null, "java/lang/Object",null);
        MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        ctor.visitCode();
        ctor.visitVarInsn(ALOAD, 0);
        ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        ctor.visitInsn(RETURN);
        ctor.visitMaxs(1, 1);
        ctor.visitEnd();
        MethodVisitor getBaz = cw.visitMethod(ACC_PUBLIC, "getBaz",
            "(LFoo;)Ljava/lang/Object;", null, null);
        getBaz.visitCode();
        getBaz.visitVarInsn(ALOAD, 1);
        getBaz.visitMethodInsn(INVOKESTATIC, "Foo", "access$000",
            "(LFoo;)Ljava/lang/Object;");
        getBaz.visitInsn(ARETURN);
        getBaz.visitMaxs(1, 2);
        getBaz.visitEnd();
        cw.visitEnd();
        return cw.toByteArray();
      }
    }
    

    这将创建一个名为 Spy 的简单类,它允许您调用 access$000

    public class Spy extends java.lang.Object{
        public Spy();
        public java.lang.Object getBaz(Foo);
    }
    

    使用它,您可以检查baz 的值,而无需反射或任何暴露它的方法。

    public class Test {
      public static void main(String[] args) {
        Foo foo = new Foo();
        Spy spy = new Spy();
        System.out.println(spy.getBaz(foo));
      }
    }
    

    Spy 实现要求它与Foo 在同一个包中,并且Foo 不在sealed JAR 中。

    【讨论】:

    • +0:我认为使用反射访问/修改私有字段要容易得多。所以我根本没有看到访问者增加了问题。 ;)
    • @Peter Lawrey - 我试图在我的公开发言中说明这一点 - 这些东西只能在某些(异常)条件下被视为漏洞利用:1)您使用的安全管理器禁止反射(或至少setAccessible 2)攻击者可以运行自己的代码 3)包没有密封。我们正在讨论编写安全的类似小程序的框架。 之所以举这个例子,是因为一旦你开始思考,就很难不去尝试。
    • @McDowell 我如何在Foo 中看到access$000?当我尝试在Class&lt;Foo&gt; 中打印所有方法时,它不会打印access$000
    • @Jatin 请参阅javap 工具。
    猜你喜欢
    • 2019-10-18
    • 2016-12-29
    • 2014-05-31
    • 1970-01-01
    • 2014-12-25
    • 1970-01-01
    • 2019-07-10
    • 1970-01-01
    • 2018-11-15
    相关资源
    最近更新 更多