查看原文
其他

Java中的变量隐藏和阴影

影宸风洛 SpringForAll社区 2021-05-27

让我们来看看Java中变量阴影和隐藏的基本实践,它们的工作原理以及一些如何更好地使用它们的建议。

Java允许我们在需要时声明变量。我们可以将所有变量按照使用范围分为三类:

  1. 实例变量:在类中定义并具有对象级范围。

  2. 类变量:在类中定义使用static关键字。它们具有对同一类的所有对象通用的类级别范围。

  3. 本地变量:在方法内或任何条件块中定义。它们具有块级范围,只能在定义它们的块中访问。

1 什么是阴影变量

当我们在闭包范围中定义变量时,会产生阴影变量,变量名与我们已经在外部范围中曾定义的变量名相同。

换句话说,当局部变量与其中一个实例变量具有相同的名称时,局部变量会影响方法块内的实例变量。

在下面的示例中,有一个名为x的实例变量,在内部方法printLocalVariable()中,我们使用局部变量x对其进行遮蔽。

  1. class Parent {

  2. // Declaring instance variable with name `x`

  3. String x = "Parent`s Instance Variable";

  4. public void printInstanceVariable() {

  5. System.out.println(x);

  6. }

  7. public void printLocalVariable() {

  8. // Shadowing instance variable `x` with a local variable with the same name

  9. String x = "Local Variable";

  10. System.out.println(x);

  11. // If we still want to access the instance variable, we do that by using `this.x`

  12. System.out.println(this.x);

  13. }

  14. }

2 什么是变量隐藏

当我们在子类中定义一个与父类名称相同的变量时,会发生变量隐藏。子类可以声明一个与其父类的继承变量同名的变量,从而隐藏继承的变量。

换句话说,当子类和父类都具有相同名称的变量时,子类'变量隐藏父类'变量。

在下面的示例中,我们将在子类中隐藏由其父类定义名为x的变量。

  1. class Child extends Parent {

  2. // Hiding the Parent class's variable `x` by defining a variable in the child class with the same name.

  3. String x = "Child`s Instance Variable";

  4. @Override

  5. public void printInstanceVariable() {

  6. System.out.print(x);

  7. // If we still want to access the variable from the super class, we do that by using `super.x`

  8. System.out.print(", " + super.x + "\n");

  9. }

  10. }

3 变量隐藏与方法重写的区别

虽然变量隐藏看起来像覆盖变量(类似于方法重写),但事实并非如此。覆盖仅适用于隐藏方法适用于变量的方法。

在方法重写的情况下,重写方法完全替换继承的方法,因此当我们尝试通过保持子对象从父级引用访问该方法时,将调用子类中的方法。您可以阅读 Everything About Method Overloading vs Method Overriding, Why We Should Follow Method Overriding Rules, 和How Does the JVM Handle Method Overloading and Overriding Internally。

但是在变量隐藏中,子类隐藏了继承的变量而不是替换它们,因此当我们尝试通过保存子对象从父引用访问变量时,它将从父类访问。

  1. 当子类中的实例变量与超类中的实例变量具有相同名称时,则从引用类型中选择实例变量。

  2. public static void main(String[] args) throws Exception {

  3. Parent parent = new Parent();

  4. parent.printInstanceVariable(); // Output - "Parent's Instance Variable"

  5. System.out.println(parent.x); // Output - "Parent's Instance Variable"

  6. Child child = new Child();

  7. child.printInstanceVariable();// Output - "Child's Instance Variable, Parent's Instance Variable"

  8. System.out.println(child.x);// Output - "Child's Instance Variable"

  9. parent = child; // Or parent = new Child();

  10. parent.printInstanceVariable();// Output - "Child's Instance Variable, Parent's Instance Variable"

  11. System.out.println(parent.x);// Output - Parent's Instance Variable

  12. // Accessing child's variable from parent's reference by type casting

  13. System.out.println(((Child) parent).x);// Output - "Child's Instance Variable"

  14. }

在上面的例子中,当我们在保存子对象时调用重写方法printInstanceVariable()onparent时,我们可以看到输出为:

  1. Child's Instance Variable, Parent's Instance Variable

因为在子类中,该方法是打印子类的x变量和super.x.

但是当我们调用System.out.printlnparent.variable)时;在保存子对象的同一父引用上,它打印Parent的实例变量,因为新的Child()对象保留父对象x以及子对象x并隐藏父对象x。因此,在这种情况下,x是从作为引用类型的类中选择的。

但是如果当我们使用父引用,仍想要访问子类的变量,也可以通过使用(Childparent(.variable)来实现。

当我们的变量是私有的或在另一个包中并且具有默认访问权限时,这些变量在该类之外是不可见的,并且子类不能访问它们。请不要混淆它们 - 这就是为什么我们应该始终坚持通用指南来创建POJO并使用私有访问声明我们的变量(并且还提供适当的get / set方法来访问它们)。

你想了解更多关于变量隐藏的内容吗?可以继续阅读这篇文章: Why Instance Variable Of Super Class Is Not Overridden In SubClass,在这篇文章中我已经讨论过为什么变量不遵循覆盖,为什么变量隐藏设计不同于方法覆盖以及为什么实例变量是从引用类型而不是对象中选择的?

您可以在GitHub存储库找到完整的代码,欢迎随时提供宝贵的意见。

原文链接:https://dzone.com/articles/variable-shadowing-and-hiding-in-java

作者:Naresh Joshi

译者:Emma


推荐:Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现

上一篇:JVM中的压缩OOP





 关注公众号

点击原文阅读更多


    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存