帮助理解受保护方法的问题
我正在阅读2005年4月的Sybex完整Java 2认证研究指南 (ISBN0782144195)。 本书适用于想要通过java认证的java开发人员。
在关于访问修饰符(以及其他修饰符)的章节之后,我发现了以下问题(#17):
对或错:如果类Y扩展了类X,那么这两个类在不同的包中,而类X有一个名为abby()的受保护方法,那么Y的任何实例都可以调用Y的任何其他实例的abby()方法。
这个问题让我有点困惑。
据我所知,你可以在同一个类(或子类)的任何变量上调用protected方法。 您不能在变量上调用它,层次结构中的变量比您更高(例如,您实现的接口)。
例如,您不能仅仅因为inheritance它而克隆任何对象。
但问题没有说明变量类型,只涉及实例类型。
我有点困惑,回答“真实”。
书中的答案是
假。 从不同包中的超类inheritance受保护方法的对象可以在自身上调用该方法,但不能在同一类的其他实例上调用该方法。
这里没有关于变量类型的内容,只有实例类型。
这很奇怪,我不明白。
谁能解释一下这里发生了什么?
对或错:如果类Y扩展了类X,那么这两个类在不同的包中,而类X有一个名为abby()的受保护方法,那么Y的任何实例都可以调用Y的任何其他实例的abby()方法。
“False。从不同包中的超类inheritance受保护方法的对象可以在自身上调用该方法,但不能在同一类的其他实例上调用”。
让我们写下来,就像BalusC所做的那样,并向Y添加一个调用Y的任何其他实例的abby()的方法:
package one; public class X { protected void abby() { } } package other; import one.X; public class Y extends X { public void callAbbyOf(Y anyOther) { anyOther.abby(); } }
Y可以调用它具有引用的任何Y实例的abby()方法。 所以书中的答案显然是错误的。 Java没有特定于实例的范围(与例如具有实例私有范围的Scala不同)。
如果我们试图变得仁慈,也许这个问题意味着说“Y的任何其他实例”,它可以访问碰巧在内存中的任何Y实例的方法 – 这是不可能的,因为Java没有直接的内存访问。 但在这种情况下,问题的措辞非常糟糕,您甚至可以回答:“错误。您无法在不同JVM上的实例上调用方法,或者在垃圾收集的实例上调用方法,或者JVM上的实例无法调用一年前等“
来自Java语言规范 :
6.6.2.1访问受保护的成员
设C是声明受保护成员m的类。 仅允许在C的子类S的主体内进行访问。此外,如果Id表示实例字段或实例方法,则:
- 如果访问是通过限定名称Q.Id,其中Q是ExpressionName,则当且仅当表达式Q的类型是S或S的子类时才允许访问。
- 如果访问是通过字段访问表达式E.Id,其中E是主表达式,或者是通过方法调用表达式E.Id(…),其中E是主表达式,那么只允许访问如果E的类型是S或S的子类。
因此受保护的成员可以在S的所有实例中访问,并且您的书中的答案是错误的。
这个问题似乎措辞严厉 – 并询问一个非常罕见的边缘情况(我甚至不确定在SCJP测试中有所涉及)。 措辞的方式使你的答案正确,给定的答案不正确。 编写类似的结构并轻松运行它certificate了这一点……
package inside; public class Base { private String name; public Base(String name) { this.name = name; } public void setName(String name) { this.name = name; } public String getName() { return name; } protected String abby(String name) { String old = this.name; this.name = name; return old; } } package outside; import inside.Base; public class Another extends Base { public Another(String name) { super(name); } public String setAnother(Another another, String hack) { return another.abby(hack); } public static void doCrazyStuff() { Another one = new Another("one"); Another two = new Another("two"); one.abby("Hi one"); two.abby("Hi two"); one.setAnother(two, "Hi two from one"); System.out.println("one = " + one.getName()); System.out.println("two = " + two.getName()); } public static void main(String[] args) { Another.doCrazyStuff(); } }
因为变量类型在这里是无关紧要的,直到它在问题的上下文中“理智”。 由于方法abby()
属于X
(并且Y
inheritance它),因此声明引用Y实例的类型变量无关紧要:它可以是X
或Y
要abby()
可访问,我们可以通过两个变量调用它:
X myY1 = new Y(); myY1.abby(); Y myY2 = new Y(); myY2.abby();
对或错:如果类Y扩展了类X,那么这两个类在不同的包中,而类X有一个名为abby()的受保护方法,那么Y的任何实例都可以调用Y的任何其他实例的abby()方法。
让我们描绘一下。
X级:
package one; public class X { protected void abby() {} }
Y级:
package other; public class Y extends X {}
测试用例:
public static void main(String[] args) { Y y1 = new Y(); Y y2 = new Y(); Y y3 = new Y(); // ... }
现在重读一下这个问题:你可以y1
在y2
, y3
等上调用abby()
吗? 在y1
上调用abby()
还会调用y2
, y3
等吗?
对于未来的问题,尝试抓住笔和纸并按字面解释问题。 这类模拟问题有很多漏洞。
我几乎可以肯定这个问题意味着:
“Y的任何实例都可以调用X ”(不是Y)的任何其他实例的abbey()方法。
在那种情况下,它确实会失败。 借用上面另一个答案的例子,以下内容:
package one; public class X { protected void abby() { } } package other; import one.X; public class Y extends X { public void callAbbyOf(X anyOther) { anyOther.abby(); } }
将无法编译。
Java语言规范解释了原因: http : //java.sun.com/docs/books/jls/third_edition/html/names.html#6.6.2
6.6.2.1访问受保护的成员
设C是声明受保护成员m的类。 仅允许在C的子类S的主体内进行访问。此外,如果Id表示实例字段或实例方法,则:如果访问是通过限定名称Q.Id,其中Q是ExpressionName,则访问当且仅当表达式Q的类型是S或S的子类时才被允许。如果访问是通过字段访问表达式E.Id,其中E是主表达式,或者是通过方法调用表达式E.Id( ……),其中E是主表达式, 当且仅当E的类型是S或S的子类时才允许访问 (强调我的)。