【问题标题】:Java protected modifier not working as expectedJava protected 修饰符未按预期工作
【发布时间】:2011-01-23 02:50:00
【问题描述】:

我有以下两个文件:

水果.java:

package superClass;

public class Fruit { 
 protected static void printName() {
  System.out.println("My name is Khan");
 }

}

Apple.java:

package food;

import superClass.*;

public class Apple  {
 public static void main(String[] args) {
  int i, j;
  for(i = 0; i < 5; i++) {
   for(j = 0; j < i; j++) {
    System.out.print("*");
   }
   System.out.println();
  }
  printName(); // Call inherited member - NO ERROR, expected

  Fruit f = new Fruit();
  f.printName(); // Call instantiated member - ERROR, expected
 }

}

正如预期的那样,我无法从 Apple 类中访问受保护的方法 printName,因为它们位于不同的包中。我收到以下错误:

printName() has protected access in superClass.Fruit

完全正确。但是,如果我按如下方式从类 superClass 继承,则不会出现任何错误!

package food;

import superClass.*;

public class Apple extends Fruit {
 public static void main(String[] args) {
  int i, j;
  for(i = 0; i < 5; i++) {
   for(j = 0; j < i; j++) {
   System.out.print("*");
   }
   System.out.println();
 }
  printName(); // Call inherited member - NO ERROR, expected

  Fruit f = new Fruit();
  f.printName(); // Call instantiated member - NO ERROR, WHAT????
 }
}

为什么它允许我通过引用访问不同包中另一个类的受保护成员?这应该是非法访问吧?

我很困惑!有人请帮忙。

代码是使用 Java 1.6.0_18 编译的。

【问题讨论】:

  • 感谢 Peter 格式化代码
  • calvinkrishy 的答案似乎是最正确的(其他人没有提到静态修饰符!)。另见docs.oracle.com/javase/tutorial/java/IandI/override.html 简而言之,不要对静态方法进行实例访问。这是 Findbugs 发出的警告之一……

标签: java access-modifiers protected


【解决方案1】:

您观察到的行为是因为 printName 是静态的。 protected 修饰符在子类中提供该方法的可见性,但如果该方法是非静态的,则只能通过子类的实例调用该方法(您提出的问题“如果我从类 superClass 继承如下,我不会收到任何错误!”)。 JLS section 6.6.2.1 简洁地定义了这一点。

【讨论】:

  • 我想知道您为什么还没有投票,因为您的消息是唯一提到该方法的静态性质的消息,因此它可能是最正确的。 f.printName() 实际上是 Fruit.printName() 并忽略受保护的修饰符。 Findbugs 对实例访问静态方法发出警告,可能正是因为这种混淆。
【解决方案2】:

protected 修饰符允许在继承的类中访问。如果您只需要限制对某个包中的类的访问,则需要一个“包”修饰符,它只是一个空修饰符,即编写如下方法:

static void printName()

并且它将具有包可见性。

【讨论】:

  • protected 修饰符允许在继承类中直接访问,因为继承后它们成为子类的成员。我们不允许通过超类的对象访问它们,对吧?
  • @PuneCoder 我们不允许从包子类层次结构之外访问它们。
【解决方案3】:

这是protected 访问的定义:protected 成员可以从子类访问,而不管它们在不同的包中。

这是对access modifiers of Java的引用。

注意:在 Java 中,约定是使用小写的包名,即superClass 不遵守此约定。您可能会为此感到困扰——尤其是如果您在文件和目录名称区分大小写的 *nix 平台上进行开发。

【讨论】:

    【解决方案4】:

    超类的受保护成员可以从子类访问,即使超类在另一个包中,但我们可以通过引用使用受保护成员的那个类来使用,因为此时该类是受保护成员的所有者。 假设

    中的受保护成员
    com.pack1 class A { protected int i;}
    com.pack2 class D { System.out.println(i); System.out.println(a1.i); }
    

    i 完美但a1.i 不完美,请给出错误。 在 D 类中,我们可以通过 d 的引用而不是 A 类的引用来使用受保护的成员

    【讨论】:

      【解决方案5】:

      超类的protected 成员可以从子类访问,即使超类在另一个包中。请参阅本页的第二部分和随附的表格/图表:

      http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html

      【讨论】:

      • 是的,没错。我认为我所说的只是混乱。我想我必须在某处放一个括号...... :)
      【解决方案6】:

      事情是这样的,当你说的时候

      printName(); // Call inherited member - NO ERROR, expected
      Fruit f = new Fruit();
      f.printName(); // Call instantiated member - NO ERROR, WHAT????
      

      记住 printName 是一个静态方法,所以即使编译器接受了表单

      f.printName();
      

      真正的形式永远是

      Fruit.printName(); // protected accesor allows the use of the method
      Apple.printName(); // or just
      printName(); // at the end everything is the same to this call
      

      所以最后你继承了静态方法,你只是对静态方法如何工作感到困惑,它们是类方法而不是实例,即使编译器没有抱怨编写表单

      f.printName();
      

      真正发生在里面的是类访问。但是确定编译器会给出类似这样的警告:

      应以静态方式访问 Fruit 类型的静态方法 printName()

      【讨论】:

        【解决方案7】:

        您是否将protected 与默认(又名包)修饰符混淆了?后者限制对同一包的成员的访问。

        Protected 限制对从相关类继承的包和类成员的访问。

        详情请见http://java.sun.com/docs/books/jls/second_edition/html/names.doc.html#62587

        【讨论】:

          【解决方案8】:

          是的,很高兴您没有收到任何错误...这可能违反了 Java 语言规范或错误...无论如何,这是正确的。这就是受保护的访问修饰符的定义。受保护类似于类成员从外部或其他类的可访问性的中间级别。我想这是在私人和公共之间。受保护的类成员是可访问的(从一个类的子类)和(从其他类,只要它们属于同一个包)。只是一个想法!

          【讨论】:

            猜你喜欢
            • 2014-10-20
            • 1970-01-01
            • 1970-01-01
            • 2011-02-28
            • 1970-01-01
            相关资源
            最近更新 更多