一个关于成员变量的多态问题

本文探讨了Java中成员变量的多态性问题,并通过一个具体示例解释了为何成员变量不具备多态性,以及如何区分编译时类型与运行时类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先看下面的代码:

class Fu{
    int i = 10;
}
class Zi extends Fu{
    int i = 20;
}
class Test{
    public static void main(String[] args)
    {
        Fu test = new Zi();
        System.out.println(test.i);
    }
}

这是一个很常规的多态,父类引用指向子类对象,引用变量所调用的方法总是表现出子类方法的行为特征,按照这种思想,思维惯性,上面的代码应该输出的是20,但是这段代码的输出其实是10,在这里先给出结论:

成员变量不具备多态性,通过引用变量来访问其包含的实例变量,系统总是试图访问它编译时类型所定义的成员变量,而不是运行时类型所定义的成员变量

什么是编译时类型和运行时类型?
Java中的许多对象(一般都是具有父子类关系的父类对象)在运行时都会出现两种类型:编译时类型和运行时类型,例如:Person person
= new Student();这行代码将会生成一个person变量,该变量的编译时类型是Person,运行时类型是Student。
Java的引用变量有两个类型,一个是编译时类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,会出现所谓的多态。因为子类其实是一种特殊的父类,因此java允许把一个子类对象直接赋值给一个父类引用变量,无须任何类型转换,或者被称为向上转型,由系统自动完成。

依据上面的结论,在这段代码中Fu test = new Zi();执行完后形成了多态,编译时类型和运行时类型不一致,所以System.out.println(test.i);这一句输出的是test编译时类型所定义的成员变量,也就是10。

### C++ 静态成员变量和静态成员函数在继承与多态中的行为 #### 静态成员变量的行为 静态成员变量属于整个类而非某个特定的对象实例。这意味着无论创建多少个该类的对象,静态成员变量只有一份副本存在于内存中。当涉及到继承时,派生类并不会获得自己独立的一份父类的静态成员变量;相反,它们共享同一份。 如果希望派生类拥有自己的版本而不是覆盖基类的数据,则需要重新声明并定义一个新的静态成员变量于派生类内[^1]: ```cpp class Base { protected: static int sharedData; }; int Base::sharedData = 0; class Derived : public Base { private: static int derivedSharedData; // 新的静态成员变量 }; int Derived::derivedSharedData = 1; ``` 需要注意的是,在这种情况下虽然`Base`和`Derived`都有各自的静态数据成员,但是这并不构成真正的重载或隐藏关系——两个不同的名称代表完全分离的存在实体。 #### 静态成员函数的行为 对于静态成员函数而言,其主要特点是无法访问非静态成员(因为没有隐式的this指针),因此也不能直接操作对象的状态。然而,静态成员函数是可以被继承下来的,并且可以通过作用域解析运算符来区分不同层次上的实现[^2]。 关于多态方面,由于静态成员函数不具备关联到具体对象的能力,也就不存在所谓的运行时期间决定调用哪一个版本的情况。也就是说,即使存在相同签名的静态成员函数分别位于基类和派生类之中,也不会发生类似于虚拟机制下的动态绑定现象。取而代之的是编译器会在编译阶段就确定好要执行的具体函数地址[^3]。 下面给出一段简单的代码示例展示这一点: ```cpp #include <iostream> using namespace std; class Animal { public: static void makeSound() { cout << "Animal sound" << endl; } }; class Dog : public Animal { public: static void makeSound() { cout << "Dog barks" << endl; } }; void callMakeSound(Animal* animal) { animal->makeSound(); // 编译错误:尝试通过对象指针调用静态成员函数 } int main(){ Animal a; Dog d; Animal::makeSound(); Dog::makeSound(); return 0; } ``` 上述例子试图经由指向基类型的指针去间接调用静态成员函数将会引发编译期错误,这是因为静态成员函数本质上不属于任何具体的对象实例,故不应如此使用。 #### 注意事项 - **不可作为虚函数**:正如之前提到过的那样,静态成员函数不能成为虚函数的一部分参与多态性处理。 - **初始化顺序依赖问题**:全局/命名空间级别的静态对象之间可能存在复杂的相互依存关系,尤其是在跨文件的情况下容易引起难以调试的问题。建议尽可能减少对外部链接符号的依赖程度以降低风险。 - **线程安全考虑**:考虑到并发环境下多个线程可能同时修改同一个静态资源的情形,应当采取适当措施保障同步控制,比如利用互斥锁等手段保护临界区内的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值