有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么内部类使私有方法可访问?

我不明白为什么会这样。尽管f()和g()是私有的,但它们在内部类中是可见的。他们是否因为是内部类而受到特殊对待

如果A和B不是静态类,它仍然是相同的

class NotPrivate {
    private static class A {
        private void f() {
            new B().g();
        }
    }

    private static class B {
        private void g() {
            new A().f();
        }
    }
}

共 (4) 个答案

  1. # 1 楼答案

    (编辑:在答案上展开以回答一些评论)

    编译器将内部类转换为顶级类。由于私有方法只对内部类可用,编译器必须添加具有包级访问权限的新“合成”方法,以便顶级类可以访问它

    类似这样(编译器添加了$1):

    class A 
    {
        private void f() 
        {
            final B b;
    
            b = new B();
    
            // call changed by the compiler
            b.$g();
        }
    
        // method generated by the compiler - visible by classes in the same package
        void $f()
        {
            f();
        }
    }
    
    class B
    {
        private void g() 
        {
            final A a;
    
            a = new A();
    
            // call changed by the compiler
            a.$f();
        }
    
        // method generated by the compiler - visible by classes in the same package
        void $g()
        {
            g();
        }
    }
    

    非静态类是相同的,但它们添加了对外部类的引用,以便可以对其调用方法

    Java这样做的原因是,他们不想要求VM更改来支持内部类,所以所有更改都必须在编译器级别进行

    编译器将内部类转换为顶级类(因此,在VM级别没有内部类)。然后,编译器还必须生成新的“转发”方法。它们是在包级别(非公共)创建的,以确保只有同一包中的类才能访问它们。编译器还将对私有方法的方法调用更新为生成的“转发”方法

    您可以避免让编译器生成方法my,并将方法声明为“package”(没有public、private和protected)。这样做的缺点是,包中的任何类都可以调用这些方法

    编辑:

    是的,您可以调用生成的(合成)方法,但不要这样做!:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    public class Main
    {
        public static void main(final String[] argv)
            throws Exception
        {
            final Class<?> clazz;
    
            clazz = Class.forName("NotPrivate$A");        
    
            for(final Method method : clazz.getDeclaredMethods())
            {
                if(method.isSynthetic())
                {
                    final Constructor constructor;
                    final Object instance;
    
                    constructor = clazz.getDeclaredConstructor(new Class[0]);
                    constructor.setAccessible(true);
                    instance = constructor.newInstance();
                    method.setAccessible(true);
                    method.invoke(null, instance);
                }
            }
        }
    }
    
  2. # 2 楼答案

    Java在特殊的访问器中编译,其中包含$。所以不能编写访问私有方法的Java。解释如下:

    http://www.retrologic.com/innerclasses.doc7.html

    There is one more category of compiler-generated members. A private member m of a class C may be used by another class D, if one class encloses the other, or if they are enclosed by a common class. Since the virtual machine does not know about this sort of grouping, the compiler creates a local protocol of access methods in C to allow D to read, write, or call the member m. These methods have names of the form access$0, access$1, etc. They are never public. Access methods are unique in that they may be added to enclosing classes, not just inner classes.

  3. # 3 楼答案

    我认为this quote可以很好地总结:

    。。。内部类可以访问声明类的所有成员,甚至是私有成员。事实上,内部阶级本身就是阶级的一员;因此,遵循面向对象工程的规则,它应该可以访问类的所有成员

    从那以后,由于两个内部类实际上只是包含类的一部分,它们也应该能够访问彼此的私有成员

  4. # 4 楼答案

    正如用户“一个花花公子”在接受答案的评论中所解释的:

    它可以编译,因为语言规范要求它以这种方式工作,即Java语言规范这样说:

    6.6.1确定可访问性(至少自JLS6起)

    “否则,如果该成员或构造函数被声明为私有,则当且仅当访问发生在包含该成员或构造函数声明的顶级类(§7.6)的主体内时,才允许访问。”

    也就是说,私人成员的“访问范围”是:在顶级班级成员的词汇范围内的任何地方

    这意味着:在最外层类的类主体中定义的所有私有成员都可以从该类主体中的任何其他地方访问

    例如,可以从外部类的方法或外部类的另一个内部类的任何方法访问内部类的私有方法