这是我今天在工作中碰到的问题,是关于继承和多态的。同事对项目中的一项基础功能进行了重构,可是当我们从CVS服务器上更新了项目源代码并编译了之后,发现这项功能已不能正常工作了。先撇开这个同事所犯的错误(对代码进行重构后没有测试他的新代码就上传到了CVS服务器上)不说,在这里我就说说这个问题所带出来的JAVA konwhow.
由于我们的项目比较复杂,我在这里采取比较简单的例子来讲解这个问题。首先,我们有2个类:supper.TestSupper.java 和sub.TestSub.java。他们的代码是这样的:
package supper;
public class TestSupper {
String getString()
{
return "This is supper class.";
}
}
-----------------------------------
package sub;
import supper.TestSupper;
public class TestSub extends TestSupper {
public String getString(){
return "This is sub class.";
}
}
十分简单,第一眼看上去,你会觉得TestSub继承了TestSupper并且重写getString()方法。
现在我们写一个测试程序:
package supper;
import sub.TestSub;
public class Test {
public static void main(String[] args) {
TestSupper test = new TestSub();
System.out.println(test.getString());
}
}
请注意啊!测试程序和TestSupper在同一个包里。从理论上说,这个测试程序应该输出“This is sub class”,因为test的实体是一个TestSub对象而不是TestSupper。所以,当我们调用test.getString()时,真正被调用的应该是TestSub里的getString()。可是事实如何呢?输出是"This is supper class."!为什么会这样的?
原因很简单,因为在TestSupper里方法getString()的标签(signature)是"default",是默认的不用写出来。这导致这个方法只能在这个包里面可见。TestSub虽然继承了TestSupper,却没有办法“看见”getString()方法,因为TestSub在另外一个包里。所以当我们在测试程序里调用test.getString(),程序首先会寻找TestSub中是否重写了这个方法,在这里请一定要注意标签是default的,当然是没有发现。结果程序就会调用父类的相应方法,故父类中的结果就被输出了。
解决的办法很简单,但凡是要被重写的方法一定不能定义成“default”,最少要定义成"protected”.
如果你是在使用Eclipse的话,你可以在eclipse中进行设置,把这种情况视为Error就可以避免这种错误的产生。
方法是:window ->preferences -> style ->methods overridden but not pachage visible这项选为Error。我用的是eclipse3.0。
后话
别看这是个很小的问题,而且很简单,一看就明白,可是当系统出现了问题,而你要在几百个类中寻找到问题所在的时候,这种不易察觉的错误绝对是致命的。寻找这个错误花了我们2个人天!可怕吧!
分享到:
相关推荐
### Java.lang.Object 类详解 #### 一、概述 在Java编程语言中,`java.lang.Object`类是所有类的根类。这意味着每一个类都直接或间接地继承...此外,了解如何适当地重写这些方法对于实现定制化的行为也是必不可少的。
封装和多态也是OOP的核心概念,JavaScript通过闭包实现封装,通过接口和方法重写实现多态。 对于Web开发者而言,操作DOM(文档对象模型)是必须掌握的技能。DOM是HTML和XML文档的编程接口,JavaScript通过DOM API...
override是方法重写,指的是派生类重新定义基类中的方法。 F. 字符串相关函数:strcpy和memcpy是C语言中用于字符串操作的函数,strcpy用于复制字符串,而memcpy则可以用于复制任意类型的数据。编写拷贝字符串的函数...
覆写基类的方法是实现多态的关键。通过覆写,子类可以提供针对特定上下文的实现,从而增强程序的灵活性和可扩展性。 #### 17. 使用委托进行回调 委托是一种引用方法的类型,可用于实现回调机制。通过委托,可以将...
如果比较的是对象,"=="只是检查两个引用是否指向同一个实例,而equals()默认行为也是这样,但可以通过重写来实现特定的比较逻辑。 5. **数组与集合的区别**:数组长度固定,一旦创建不能更改,适合存储固定数量的...
当一个基类中的成员函数被声明为`virtual`时,其派生类可以重写这个函数来提供不同的实现。这样,在通过基类指针或引用调用该成员函数时,实际执行的是指向的对象所属类型的函数版本,从而实现了动态绑定。 - **...
9. **多态与虚函数**:如果要实现多态,记得声明至少一个虚函数。不声明虚函数会导致无法调用派生类的重写方法。 10. **异常处理**:不妥善处理异常可能导致程序崩溃。考虑使用`try-catch`来捕获和处理异常。 11. ...
Java中的`equals`方法是用于对象比较的核心方法,其正确实现至关重要。Java规范中定义了`equals`方法应遵循的五项基本原则:自反性、对称性、传递性、一致性以及非空性。然而,即使遵循这些原则,重写`equals`方法时...
1. **封装**:将数据和操作数据的方法捆绑在一起,对外隐藏内部实现细节。 2. **继承**:支持类之间的继承关系,可以重用和扩展已有类的功能。 3. **多态**:通过继承和虚函数机制,同一个接口可以表示多种不同的...
虚函数允许子类重写父类的行为,当使用指针或引用调用虚函数时,实际上调用的是指向或引用的派生类对象的方法,而非基类的方法。 ### 9. 抽象类与纯虚函数 抽象类是一种包含至少一个纯虚函数的类,不能实例化。...
通过这种方式,可以通过指向基类的指针调用派生类中的重写版本,实现了运行时多态。 ### 内存管理 #### 1. 动态内存分配 题目中的一个例子是在主函数中使用`new`操作符动态创建了一个`Child`类的对象,并通过一个...
- **多态**:通过虚拟方法实现多态性,允许子类重写父类的方法。 **3.6 私有方法** - **封装**:通过私有方法来隐藏对象内部的实现细节。 **3.7 类接口** - **接口定义**:类似于签名,用于定义类提供的公共方法...
- **一般虚函数**: 可以提供默认实现,派生类可以选择重写。 - **非虚函数**: 接口和实现都必须继承,不能被覆盖。 #### 七、C++程序设计的原则 1. **尽可能使用const**: 限制变量的修改,增加程序的可读性和安全...
C++的多态机制主要通过虚函数实现,它允许子类重写父类的方法,使得指向子类对象的父类指针能够调用子类的实现。虚函数必须在基类中声明,并且在派生类中可选地重新定义。这样,即使在不知道对象实际类型的情况下,...
- **内存管理**:多态数组在内存分配和释放时需要特别小心,以避免内存泄漏。 **实践建议**:避免在数组中使用多态性,而是考虑使用标准容器(如`std::vector`)来管理对象集合。 ##### 4. 避免无用的缺省构造...
当一个类的成员函数被声明为`virtual`时,该函数可以在派生类中重写,从而实现运行时的多态。这意呸着在运行时,可以通过基类指针或引用调用派生类的成员函数,而不是编译时确定的函数版本。 - **虚函数**:在基类...
这是实现多态和回调功能的强大工具。 #### vector `vector<bool>`是一个特例,它的行为不同于其他类型的`vector`。为了节省空间,它使用位级存储,但这导致了一些非直观的行为,比如迭代器的行为。 #### iterator...
现在,如果我们想定义一个新的类C,它继承自B,并且想使用A中的方法m而不是B中重写的版本,我们可以使用`super()`来实现这一点,因为`super()`会遵循Python的MRO来找到并调用正确的方法。例如: ```python class A:...
- **普通虚拟函数**:允许子类重写父类的方法,实现多态。 - **纯虚函数**:必须在派生类中实现,否则派生类也将成为抽象类。 - **重载函数**:允许在同一作用域内定义多个同名函数,但参数列表必须不同。 - **...
- **多态**:允许子类对象被当作父类对象使用,即同一接口可以有不同的实现。 #### 4. 子类析构时是否要调用父类的析构函数? - **答案**:是的,子类析构时通常需要调用父类的析构函数。这通常通过将父类的析构...