【问题标题】:Where do private fields of super class live when accessed from sub class using public getter? [duplicate]使用公共 getter 从子类访问时,超类的私有字段在哪里? [复制]
【发布时间】:2018-09-05 03:34:12
【问题描述】:

不要说重复这个问题地址,该字段在程序中的位置不是它是否被继承,或者它是否被继承但不可访问。

请继续阅读了解

在下面的示例中,我们看到狗对象可以访问动物私有字段“name”抛出使用公共getter“getName”但不是私有字段不被继承所以字段名称所在的位置

通过搜索,我意识到只有一个对象是从子类创建的,而不是两个一个到 sub 和一个到 super 来保存私有字段。

立即关注>>我们都知道任何类中的公共设置器都可以设置其私有字段,但我们知道该字段的位置!在类的对象中

所以我的摘要问题如下

访问的私有字段在子类创建的对象中的位置!

它在哪里!

public class Animal {
    private String name ;
    public Animal (String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return this.name;
    }
}

public class Dog extends Animal {

    public Dog(String name) {
        super(name);
    }
}

public class Hello {
    public static void main(String[] args) {
        Dog mmms = new Dog("mmkk");
        System.out.println(mmms.getName());
    }
}

编辑:我认为这个问题可以表述为

如果父类的私有字段不被继承,它在哪里?

问题名称已更改,以便获得更多理解

子类对象如何从公共 getter 访问其父类的私有成员?

使用公共 getter 从子类访问时,超类的私有字段在哪里?

【问题讨论】:

  • 你在Dog类中调用super(name);时设置了Animal类中的字段
  • 我知道我做到了,但是在我的对象中它不是继承还是它在哪里?
  • 我的问题集中在它在哪里而不是我是如何做到的,但最好解释一下
  • 子类的对象包含超类的私有字段。所以从某种意义上说它们是继承的,你只是不能在子类中访问它们。
  • 不,如果你读得好,你看我说的是不一样的,如果它是继承的,这是一个愚蠢的问题

标签: java


【解决方案1】:

每当创建子类的实例时,即Dog 在这种情况下的实例,所有父类的构造函数都会按顺序调用,分配存储完整链状态所需的空间。在您的情况下,超级构造函数被显式调用,但即使没有显式调用,只要有默认构造函数,就会进行隐式调用(如果没有,则代码不会编译)。

有关父实例状态的信息可用并与子实例一起存储。此信息的存储方式取决于 Oracle JRE 与 OpenJRE 不同的 JVM 实现。但是,没关系,重要的是子级可能无法访问父级的字段或方法(在私有字段或方法的情况下),但所有字段都存在并分配了一个值,至少是一个默认值。所有方法也都存在并基于vtable 调度:

实例方法被分派到与定义类对应的实例部分。因此,如果在您的情况下,Animal 类定义了 getNamesetNameDog 的无父路径重新定义它们,则 JRE 将执行代码,其状态由对应于 Animal 的部分维护,从而设置或检索存储在那里的name。同时,所有 Java 方法(包括私有方法)都是虚拟的,因此可以在子类中被覆盖。有关此类覆盖的信息将保存在负责调度的vtable 中。因此,如果您的类 Dog 重新定义 getNamesetName 以执行其他操作(它将无法从该类定义中访问字段 name),那么在 Dog 的实例上调用这些方法实际上会调用Dog 中定义的方法,而不是Animal。在这种情况下,您可能会失去对 Animal 中字段 name 的任何访问权限(除非在 Animal 中定义了其他 getter/setter),即使您在构造函数中放置的值仍将被存储。

正如其他地方所指出的,人们仍然可以通过反射或字节码操作来访问私有信息,但这会很少或出于非常特殊的原因进行(例如,私有字段上的 @Inject 注释可能会导致设置一个值通过一些注入框架)。尽管可以这样做,但通过反射访问私有隐藏(私有、受保护、包可见)方法或字段应该是一个例外,而不是常见做法,因为它显然违反了最初的设计决策。

进一步阅读:

【讨论】:

  • 感谢您的回复,它清除了一些东西
  • 不是私有方法不是虚拟的
  • 所有 Java 方法都是虚拟的、私有的和非私有的
  • 我读到的所有资源都说在 Java 中,所有非静态方法默认都是“虚拟函数”。只有用关键字 final 标记的不能被覆盖的方法,以及不能被继承的私有方法是非虚拟的。你确定吗!
  • Static 和 final 不是虚拟的,因为它们都不能在子类中被覆盖。正如我上面答案中的最后一个链接所示,私人似乎依赖于 JVM 实现。但是,这对您最初的问题没有影响,因为这仅仅是运行时优化而不是行为改变。
【解决方案2】:

只有一个 Dog 实例,它还包含 name 实例变量,因为 Dog Animal 并且尽管该字段未正确继承自它存在于实例中的父类。

令您不安的是name 不能直接从Dog 代码访问,但它就在那里。它只是为了保持封装。

但是,您可以使用 Animal 的公共或受保护方法来读取或修改此值,就像 getName() 所做的那样,或者您可以使用 reflection 来实现此目标:

Dog dog= new Dog("mmmk");
Field name = Animal.class.getDeclaredField("name");
name.setAccessible(true);
name.set(dog, "xxxxx");
System.err.println(dog.getName());

现在可以看到name的值已经修改为“xxxxx”了

【讨论】:

  • 感谢解释 有没有一篇文章解释了虚拟机是如何做到这一点的更多
  • @Sam 我会尝试找到一个,但我可以看出该字段是继承的。它和其他领域一样存在。除非您使用反射,否则无法访问它。
  • @Sam 实际上我已经阅读了作为重复的链接并且有一点细微差别:该字段是 NOT 从父类继承的,但它 IS 在实例中存在(虽然隐藏)。
  • 谢谢,我会尝试进一步了解如何
猜你喜欢
  • 1970-01-01
  • 2016-03-27
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-30
  • 2020-06-20
  • 1970-01-01
相关资源
最近更新 更多