在Java中重写成员变量(变量隐藏)

在Java中重写成员变量(变量隐藏),java,overriding,Java,Overriding,我正在研究JAVA中重写成员函数,并考虑尝试重写成员变量 所以,我定义了类 public class A{ public int intVal = 1; public void identifyClass() { System.out.println("I am class A"); } } public class B extends A { public int intVal = 2; public void identify

我正在研究JAVA中重写成员函数,并考虑尝试重写成员变量

所以,我定义了类

public class A{
    public int intVal = 1;
    public void identifyClass()
    {
        System.out.println("I am class A");
    }
}

public class B extends A
{
    public int intVal = 2;
    public void identifyClass()
    {
        System.out.println("I am class B");
    }
}

public class mainClass
{
    public static void main(String [] args)
    {
        A a = new A();
        B b = new B();
        A aRef;
        aRef = a;
        System.out.println(aRef.intVal);
        aRef.identifyClass();
        aRef = b;
        System.out.println(aRef.intVal);
        aRef.identifyClass();
    }
}
输出为:

1
I am class A
1
I am class B

我不明白为什么aRef设置为b时intVal仍然属于A类?

变量在Java中不是多态的;它们不会相互重写。

当您在子类中创建同名变量时,这称为隐藏。生成的子类现在实际上将同时具有两个属性。您可以使用
super.var
((超类)this.var
从超类访问该超类。变量甚至不必是同一类型的;它们只是两个共享名称的变量,很像两个重载方法。

变量在编译时解析,方法在运行时解析。aRef的类型为A,因此aRef.Intvalue在编译时解析为1。

根据Java规范,扩展时,实例变量不会被子类从超类重写

因此,子类中的变量只能被视为具有相同名称的变量


另外,当在创建B实例的过程中调用A的构造函数时,变量(intVal)被初始化,因此输出。

好吧,我希望你得到答案。如果没有,您可以尝试在调试模式下查看。子类B可以访问两个intVal。它们不是多态的,因此不会被覆盖


如果你使用B的参考,你会得到B的intVal。如果您使用A的引用,您将获得A的intVal。就这么简单。

Java具有封装特性,这意味着它紧密地绑定了对象的属性和行为。因此,只有通过类引用,我们才能调用它的行为来更改它的属性

在继承中,只有方法重写,所以它只能影响它的属性

来自JLS Java SE 7版本§15.11.1:

由于缺少对字段访问的动态查找,程序可以通过简单的实现高效运行。后期绑定和重写的功能是可用的,但只有在使用实例方法时才可用

Oliver Charlesworth和Marko Topolnik的回答是正确的,我想进一步阐述一下问题的原因:

在Java中,是根据引用的类型而不是实际对象的类型来访问的。出于同样的原因,如果在类
B
中有
someOtherMethodInB()
,则在运行
aRef=B
后,您将无法从
aRef
访问它。标识符(即类、变量等名称)在编译时解析,因此编译器依赖引用类型来完成此操作

现在在您的示例中,当运行
System.out.println(aRef.intVal)时
它打印
A
中定义的
intVal
的值,因为这是用于访问它的引用类型。编译器看到
aRef
属于
A
类型,这就是它将访问的
intVal
。不要忘记在
B
的实例中有两个字段。JLS还有一个类似于您的示例,“15.11.1-1.字段访问的静态绑定”,如果您想看一下的话


但为什么方法的行为会有所不同呢?答案是,对于方法,Java使用后期绑定。这意味着在编译时,它会在运行时查找最合适的方法。搜索涉及在某些类中重写方法的情况。

Java中的字段没有多态性。

变量
决策在编译时发生,因此总是基类变量(不是子类的继承变量)将被访问

因此,无论何时发生向上投射,请始终记住

1) 将访问基类变量

2) 将调用子类方法(如果发生重写,则调用重写方法,否则将调用继承自父类的方法)。

重写Java中的概念
Parent parent=new Parent();
parent.behaviour();
函数将根据对象类型进行重写,变量将根据引用类型进行访问

  • 重写函数:在这种情况下,假设父类和子类都具有相同的函数名和自己的定义。但执行哪个函数取决于对象类型,而不是运行时的引用类型
  • 例如:

    Parent parent=new Child();
    parent.behaviour();
    
    Parent parent=new Child();
    System.out.println(parent.state);
    
    这里的
    parent
    是父类的引用,但保存了子类的对象,所以在这种情况下将调用子类函数

    Child child=new Child();
    child.behaviour();
    
    这里
    child
    持有child类的对象,因此将调用child类函数

    Parent parent=new Parent();
    parent.behaviour();
    
    此处
    parent
    保存父类的对象,因此将调用父类函数

    Parent parent=new Parent();
    parent.behaviour();
    
  • 重写变量:Java支持重载变量。但实际上这是两个同名的不同变量,一个在父类中,一个在子类中。这两个变量可以是相同的数据类型,也可以是不同的数据类型
  • 尝试访问变量时,它取决于引用类型对象,而不是对象类型

    例如:

    Parent parent=new Child();
    parent.behaviour();
    
    Parent parent=new Child();
    System.out.println(parent.state);
    
    引用类型是父类,因此访问的是父类变量,而不是子类变量

    Child child=new Child();
    System.out.println(child.state);
    
    Parent parent=new Parent();
    System.out.println(parent.state);
    
    这里的引用类型是Child,因此访问的是Child类变量,而不是父类变量

    Child child=new Child();
    System.out.println(child.state);
    
    Parent parent=new Parent();
    System.out.println(parent.state);
    
    这里的引用类型是Parent,因此可以访问Parent类变量。

    我希望这有助于:

    public class B extends A {
    //  public int intVal = 2;
    
        public B() {
            super();
            super.intVal = 2;
        }
    
        public void identifyClass() {
            System.out.println("I am class B");
        }
    }
    

    所以重写基类的变量是不可能的,但基类变量值可以从继承类的构造函数中设置(更改)。

    这称为变量隐藏。分配
    aRef=b时
    aRef
    有两个intVal,一个命名为just
    intVal
    ,另一个隐藏在
    A.intVal
    下(请参见调试器屏幕截图),因为您的变量类型为
    class A
    ,即使您只打印
    intVal
    java int