受保护的构造函数和可访问性

Posted

技术标签:

【中文标题】受保护的构造函数和可访问性【英文标题】:Protected constructor and accessibility 【发布时间】:2011-07-06 06:20:23 【问题描述】:

如果子类在不同的包中,为什么我们不能用受保护的构造函数实例化一个类?如果可以访问受保护的变量和方法,为什么同样的规则不适用于受保护的构造函数?

包装1:

package pack1;

public class A 
    private int a;
    protected int b;
    public int c;

    protected A()     
        a = 10;
        b = 20;
        c = 30;
    

包装2:

package pack2;

import pack1.A;

class B extends A 
    public void test() 
        A obj = new A(); // gives compilation error; why?
        //System.out.println("print private not possible :" + a);
        System.out.println("print protected possible :" + b);
        System.out.println("print public possible :" + c);
    


class C 
    public static void main(String args[]) 
        A a = new A(); // gives compilation error; why?
        B b = new B();
        b.test();
    

【问题讨论】:

请努力清楚地构建您的问题,正确格式化并适当标记。这些你都没有做过。 很清楚.. 父 A 和子 B 位于两个不同的包中(分别是 pack1 和 pack2)。为什么在创建构造函数受保护的类(父 A)的对象时编译器会出错? 我相信不是从实际实例访问b,如果它有obj.b(假设obj已创建或成功传递)它也会给出编译错误 【参考方案1】:

根据 Java 规范 (https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.2.2)

6.6.2.2。对 protected 构造函数的合格访问

C 成为声明protected 构造函数的类,并让S 成为在其声明中使用protected 构造函数的最内层类。那么:

如果访问是通过超类构造函数调用super(...) 或合格的超类构造函数调用E.super(...),其中EPrimary 表达式,则允许访问。 p>

如果访问是通过匿名类实例创建表达式new C(...)...,或者合格的匿名类实例创建表达式E.new C(...)...,其中E是一个Primary表达式,那么访问是允许。

如果访问是通过一个简单的类实例创建表达式new C(...),或者一个合格的类实例创建表达式E.new C(...),其中E是一个Primary表达式,或者一个方法引用表达式C :: new,其中C 是一个ClassType,则不允许访问。 可以通过类实例创建表达式访问protected 构造函数(即不声明匿名类)或仅在定义它的包中的方法引用表达式。

在您的情况下,通过super() 的调用,从B 的构造函数访问A 的受保护构造函数是合法的。但是,使用new 访问是不合法的。

【讨论】:

【参考方案2】:

JLS 6.6.7 回答了您的问题。如果子类涉及其父类的实现,则子类只能访问其父类的受保护成员。因此,如果父构造函数受保护并且位于不同的包中,则不能在子类中实例化父对象...

6.6.7 示例:受保护的字段、方法和构造函数考虑 这个例子,其中分包 声明:

package points;
public class Point 
    protected int x, y;
    void warp(threePoint.Point3d a) 
        if (a.z > 0)        // compile-time error: cannot access a.z
            a.delta(this);
    

并且三点包声明:

package threePoint;
import points.Point;
public class Point3d extends Point 
    protected int z;
    public void delta(Point p) 
        p.x += this.x;      // compile-time error: cannot access p.x
        p.y += this.y;      // compile-time error: cannot access p.y
    
    public void delta3d(Point3d q) 
        q.x += this.x;
        q.y += this.y;
        q.z += this.z;
    

它定义了一个 Point3d 类。一种 编译时错误发生在 方法 delta 这里:它无法访问 它的受保护成员 x 和 y 参数 p,因为虽然 Point3d (引用的类 字段 x 和 y 出现)是 点(x 和 y 所在的类 声明),它不参与 点的实现(类型 参数 p)。方法 delta3d 可以访问受保护的成员 它的参数 q,因为类 Point3d 是 Point 的子类,是 参与实施 点 3d。方法 delta 可以尝试 将(§5.5,§15.16)其参数转换为 是一个 Point3d,但这个演员会 失败,导致异常,如果 运行时的 p 类不是 点 3d。

编译时错误也发生在 方法warp:它无法访问 其参数 a 的受保护成员 z, 因为虽然类点( 引用字段的类 z 发生)参与 Point3d 的实现(类型 参数 a),它不是 Point3d 的子类(在 其中 z 被声明)。

【讨论】:

构造函数也是类的成员,不是吗? 其实不是。构造函数不是成员。这些不是在这种情况下适用的规则。真正适用的规则在 JLS 6.6.2.2 中 - 请参阅@mazaneicha 的答案。【参考方案3】:

我同意以前的海报,不知道你为什么要这样做(在扩展类中以这种方式实例化父级)但你甚至可以这样做:

public void test() 
    A obj = new A(); // no compilation error; why? you use anonymous class 'override'
    ...

【讨论】:

请注意,obj 不是A 类的(直接)实例,它是A 的匿名子类的实例【参考方案4】:

为什么类中需要A obj=new A();,而b类的对象本身就是class A的对象

在 c 类中它给出错误,因为您正在访问作为构造函数的类 A 的受保护属性。

在这种情况下,要获取 A 类的对象,您必须在 A 类中使用此函数

static A getInstance()

   A obj = new A(); // create obj of type A.
   return obj; // returns that object by this method. No need to use 'New' kind of instantiation.

【讨论】:

A 类的构造函数受到保护。所以你不能在A类之外的任何地方创建A类的对象。所以在类中添加一个静态函数,这将给出A类的对象。 仅供参考,我们可以在同一个包中的任何位置创建 A 类对象!! 1.b 类的对象本身是 A 类的对象,2.在 c 类中它给出错误是什么意思,因为您正在访问作为构造函数的 A 类的受保护属性..您只写了这个。 .!!! 受保护的属性不能从类本身和子类中访问。但是您正在 C 类中创建 A 类的对象。并且类与 A 类无关。每当您创建类的对象时,它都会调用该类的构造函数。对JAVA了解不多,但是懂OOP。 用于装饰器并防止直接实例化。【参考方案5】:

如果可以访问受保护的变量和方法,为什么同样的规则不适用于受保护的构造函数?

只有当另一个包中的子类扩展包含受保护变量和方法的类时,才能访问受保护的变量和方法。 您可以访问子类 B 中的变量“b”,因为您扩展了类 A。您将无法访问类 C 中的变量“b”它没有扩展 A 类。

访问子类中受保护的构造函数的唯一方法是使用父类引用变量和子类对象。

package pack2;

import pack1.A;

class B extends A 
    public void test() 
        A obj = new B(); // will execute protected constructor
        System.out.println("print protected possible :" + b);
    

【讨论】:

以上是关于受保护的构造函数和可访问性的主要内容,如果未能解决你的问题,请参考以下文章

在类的构造函数上使用受保护的访问修饰符

特征可以具有具有私有和受保护可见性的属性和方法吗?特质可以有构造函数、析构函数和类常量吗?

C++:为啥我的 DerivedClass 的构造函数无法访问 BaseClass 的受保护字段?

内部接口 *比内部受保护的构造函数更不可访问?

为啥我不能访问派生构造函数的成员初始化列表中继承的受保护字段?

无法从派生类构造函数参数访问受保护的基类成员[重复]