【问题标题】:Difference between a protected and package-private (no modifier) constructor?受保护和包私有(无修饰符)构造函数之间的区别?
【发布时间】:2019-02-21 01:30:48
【问题描述】:

Possible Duplicate

那里给出的最佳答案指出可以使用super() 调用受保护的构造函数,但不能使用new 实例化?为什么这样?如果这是原因,那么包私有(无修饰符)和受保护的构造函数之间有什么区别? 以下代码对我没有任何影响。

package pack1;

public class Rr {
    protected Rr() {
        System.out.println("Rello");
    }
}

package pack2;

class Tt extends pack1.Rr {
    Tt() {
        super(); //works 
        pack1.Rr r = new pack1.Rr(); //error
    }
}

class Uu {
    public static void main(String args[]) {
        Tt t = new Tt();
    }
}

【问题讨论】:

  • 访问修饰符写在构造函数前面没有特殊含义。因此,它们的正常定义适用(参见here)。简而言之:default 和 protected 之间的区别在于 protected 授予子类的访问权限。
  • “为什么会这样?” => 最佳答案还引用了 Java 语言规范的相关部分。
  • @Turing85 这里的特殊情况是两个访问(super() 和 new ...)都来自一个子类。这就是问题所在。
  • 奇怪的是,我刚试了一下,只有当两个类在不同的包中时调用才无效。可以在您链接的重复问题中找到解释。

标签: java


【解决方案1】:

“默认构造函数”是 Java 生成的构造函数,如果您自己不提供的话;它始终与类本身具有相同的访问权限,并且没有参数。即当你定义

class YourClass {}

系统使之

class YourClass {
    YourClass() {} // default constructor
}

如果您创建任何其他构造函数——即使是没有参数的public——它不是默认构造函数。上面的第二个代码段会阻止系统生成一个,因为您已经声明了它。

如果您创建 protected 构造函数,情况也是如此。比较:

public class Class1 {}

public class Class2 {
    protected Class2() {}
}

您可以实例化 Class1,但不能实例化 Class2:

// invokes autogenerated default constructor
Class1 c1 = new Class1(); 
// compile error: default constructor is not generated because you declared a protected one
Class2 c2 = new Class2();

正如@Turing85 在评论中所说,protected 修饰符仅意味着声明的构造函数只能在适当的访问范围内调用 - 从同一包中的子类或类。

为了完整起见:您可以从子类中显式调用super() 和构造函数:

class Class3 extends Class2() {
    public Class3() {
        // call to protected constructor of Class2
        super(); 

        // explicit call to constructor by creating new instance
        Class2 o = new Class2(); 
    }
}

编辑:我刚刚尝试过,最后一个示例仅在两个类在同一个包中时才有效。我不知道,也想知道。

【讨论】:

  • 这里的特殊情况是两个访问(super() 和new ...)都来自一个子类。这就是问题所在。
  • 您确定默认构造函数是始终 public吗?我记得,它应该具有与类相同的访问权限。
  • 我的记忆没那么错:JLS-8.8.9-100-A
  • @CarlosHeuberger 不错的收获。
【解决方案2】:

protected 允许访问子类,除了同一包中的类。 当您直接调用构造函数时,例如new A() 您可以将其视为调用 static 方法。还没有该类的实例,因此不能应用继承规则。因此,静态访问规则适用:如果构造函数是受保护的,那么它必须在同一个包中才能被访问。

一旦您在构造函数代码中并调用super(),那么您已经有了一个实例,因此如果您在子类中并调用受保护的构造函数,它将起作用。

【讨论】:

  • 不幸的是,这是一个错误的答案。参考我的回答。
  • 我看不出我的回答到底有什么错误。除了使用非标准术语“通过子类方式”之外,您的答案也与我的不同
  • 其实不是static或instantiated的问题,而是你访问方法的方式(构造函数或普通方法在同一个规则中,但在你的答案中它们是不同的)。跨度>
  • 是的。查看用于实现构造函数的 invokespecial 字节码 (docs.oracle.com/javase/specs/jvms/se7/html/…)。查看注释,实际上要调用的方法(即构造函数)是静态解析的
  • 我没有看到任何句子说构造函数是静态解析的。但是我发现了这个(docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.9):“它们只能在未初始化的类实例上调用”,uninitialized 这里的意思是instantiated but not initialized yet。
【解决方案3】:

protected 方法可以通过其子类对象访问。

注意,这里的“子类”很重要,意思是“通过子类的方式”,而不是“子类中的任何地方”。

让我们看一个例子。

以下是 2 个类:

package one;
public class A {
    protected A() {} // protected constructor
    public A(int i) {} // public constructor

    protected void foo() {} // protected method
}

package two;
import one.A;
public class B extends A {
    public void bar() {
        this.foo(); // correct

        A a = new A(0); // call A's public constructor to construct an A instance.
        a.foo(); // compiler error: the method foo from the type A is not visible.
    }
}

允许第一次调用,但第二次不允许,为什么?

就像我说的,第一个是通过“子类方式”访问,其中this是A的子类实例;但是第二个只是“在一个子类中”(这里B对A没有意义,它只是一个普通的类,所以它不能访问A的受保护方法)。


另一个例子是Object.clone。这是根类Object 的受保护方法。您可能会说它可以从任何类创建的任何对象中访问,但答案是大写“否”:只有那些使用“子类方式”的代码才能访问它。

以下说明了这一点:

this.clone(); // ok

Object o = new Object();
o.clone(); // compiler error: the method clone from the type Object is not visible.

【讨论】:

  • 我认为你的解释是正确的!你能也向我解释一下这段代码吗? pasted.co/4d591cbf
  • @KarlaCarr 不要混淆protected 和private。 private 只能在其自身内部访问,而在您的代码中,main 是其自身。
  • 您可以阅读许多类(例如String)中方法equals 的实现来理解这一点。
  • 我正在创建它的一个实例,然后访问它。这不应该是有效的。例如,现在这应该可以工作pasted.co/f098e7b1,但是这个 pasted.co/4d591cbf 不应该工作,因为 f 类是 f 类,当我在 main() 中实例化它时,它会像对待任何其他普通类一样对待它不是?
猜你喜欢
  • 2010-12-03
  • 2020-02-21
  • 2013-07-24
  • 1970-01-01
  • 2019-02-22
  • 2010-10-27
  • 2012-04-03
  • 2010-12-16
  • 1970-01-01
相关资源
最近更新 更多