9

我正在阅读2005 年 4 月的 Sybex Complete Java 2 Certification Study Guide (ISBN0782144195)。本书适用于希望通过 java 认证的 java 开发人员。

在关于访问修饰符(以及其他修饰符)的一章之后,我发现了以下问题(#17):

对或错:如果类 Y 扩展类 X,两个类在不同的包中,并且类 X 有一个名为 abby() 的受保护方法,那么 Y 的任何实例都可以调用 Y 的任何其他实例的 abby() 方法。

这个问题让我有点困惑。

据我所知,您可以在同一类(或子类)的任何变量上调用受保护的方法。您不能在层次结构中比您更高的变量上调用它(例如,您实现的接口)。

例如,您不能仅仅因为您继承它而克隆任何对象。

但是这些问题没有提到变量类型,只提到了实例类型。

我有点困惑,回答“是”。

书中的答案是

错误的。从不同包中的超类继承受保护方法的对象可以在其自身上调用该方法,但不能在同一类的其他实例上调用。

这里没有关于变量类型的内容,只有实例类型。

这很奇怪,我不明白。

谁能解释这里发生了什么?

4

6 回答 6

2

对或错:如果类 Y 扩展类 X,两个类在不同的包中,并且类 X 有一个名为 abby() 的受保护方法,那么 Y 的任何实例都可以调用 Y 的任何其他实例的 abby() 方法。

“错误。从不同包中的超类继承受保护方法的对象可以在其自身上调用该方法,但不能在同一类的其他实例上调用该方法”。

让我们把它写下来,就像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 上死掉的实例的方法。一年前等等。”

于 2010-02-11T13:59:27.067 回答
1

来自Java 语言规范

6.6.2.1 访问受保护成员

令 C 为声明受保护成员 m 的类。仅允许在 C 的子类 S 的主体内进行访问。此外,如果 Id 表示实例字段或实例方法,则:

  • 如果通过限定名称 Q.Id 进行访问,其中 Q 是 ExpressionName,则当且仅当表达式 Q 的类型是 S 或 S 的子类时才允许访问。
  • 如果通过字段访问表达式 E.Id(其中 E 是 Primary 表达式)或通过方法调用表达式 E.Id(. . .)(其中 E 是 Primary 表达式)进行访问,则当且仅如果 E 的类型是 S 或 S 的子类。

所以受保护的成员在 S 的所有实例中都可以访问,而你书中的答案是错误的。

于 2010-02-11T19:37:43.287 回答
0

我几乎可以肯定这个问题的意思是:

“Y 的任何实例都可以调用X的任何其他实例的 abbey() 方法”(不是 Y)。

在那种情况下,它确实会失败。借用上面另一个答案的例子,如下:

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 是 Primary 表达式,则当且仅当 E 的类型是 S 或 S 的子类时才允许访问。(强调我的)。

于 2010-03-09T03:34:01.737 回答
0

对或错:如果类 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调用abby(),y2y3吗?call abby()ony1还会调用y2,y3等的吗?

对于未来的问题,请尝试拿起笔和纸并逐字解释问题。这类模拟问题有很多漏洞。

于 2010-02-11T13:27:55.150 回答
0

这个问题的措辞似乎很糟糕 - 并询问了一个非常罕见的边缘案例(我什至不确定 SCJP 测试是否涵盖了这个案例)。它的措辞方式使您的答案正确,而给定的答案不正确。编写一个类似的结构并轻松运行它证明了这一点......

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();
    }
}
于 2010-02-11T13:36:05.710 回答
0

因为变量类型在这里是无关紧要的,直到它在问题的上下文中是“理智的”。由于方法abby()属于X(并Y继承它),因此声明引用 Y 实例的类型变量无关紧要:它可以是XY。可以abby()访问,我们可以通过两个变量调用它:

X myY1 = new Y();
myY1.abby();

Y myY2 = new Y();
myY2.abby();
于 2010-02-11T13:36:58.360 回答