封装、继承、多态是面向对象编程中三个比较重要的概念,理解这3个概念对领会JAVA语言至关重要,而搞懂方法的覆盖又是理解继承的关键部分,这里主要看一下静态方法的覆盖。
首先看一段代码:
java 代码
- class Super{
- static String greeting(){
- return "Good night";
- }
-
- String name(){
- return "Richard";
- }
- }
-
- class Sub extends Super{
- static String greeting(){
- return "Hello";
- }
-
- String name(){
- return "Dick";
- }
- }
-
- class Test{
- public static void main(String[] args){
- Super s = new Sub();
- System.out.println(s.greeting()+","+s.name());
- }
- }
运行Test 类的结果为:Good night,Dick
也许你会感到迷惑,这里调用的到底是Super类的方法还是Sub子类的方法?让我们首先判断调用的是哪个类的name()方法。两个类中的name()方法都不是静态方法而是实例方法,因为Sub 类继承了Super 类而且有一个和它父类同样标识的name()方法,所以Sub 类中的name()方法覆盖了Super 类中的name()方法,那么前面提到的变量s 又是Sub 类的一个实例,这样一来s.name()的返回值就是Dick 了。 至此我们解决了问题的一半,现在我们需要判断被调用的greeting()方法究竟是Super类的还是Sub 类的。需要注意的是,两个类中的greeting()方法都是静态方法,也称为类方法。尽管事实上Sub 类的greeting()方法具有相同的返回类型、相同的方法名以及相同的方法参数,然而它并不覆盖Super 类的greeting()方法。由于变量s 被强制转换为Super 型并且Sub 类的greeting()方法没有覆盖Super 类的greeting()方法,因此s.greeting()的返回值为Goodnight 。还是很迷惑?请记住这条规则:“实例方法被覆盖,静态方法被隐藏” 。假如你就是刚才大喊不能覆盖静态方法的读者之一,那么你完全正确。现在你可能会问:“隐藏和覆盖有什么区别”?你也许还未理解这点,然而实际上我们刚刚在这个Super/Sub 类的例子中已经解释了两者的不同。使用类的全局名可以访问被隐藏的方法,即使变量s 是Sub 类的一个实例,而且Sub 类的greeting()方法隐藏了Super 类的同名方法,我们仍旧能够将s 强制转换为Super 型以便访问被隐藏的greeting()方法。与被隐藏的方法不同,对被覆盖的方法而言,除了覆盖它们的类之外其他任何类都无法访问它们,这就是为何变量s 调用的是Sub 类的而非Super 类的name()方法。 本单元简要解释了Java 语言中一个不时引起混淆的问题,也许对你来说理解隐藏静态方法和覆盖实例方法的区别的最佳方式就是自己创建几个类似于Sub/Super 的类再重复一次规则“实例方法被覆盖而静态方法被隐藏”,被覆盖的方法只有覆盖它们的类才能访问它们,而访问被隐藏的方法的途径是提供该方法的全局名。 现在你终于明白标题里问题的答案了吧?什么时候“被覆盖的”方法并非真地被覆盖了呢?答案就是“永远不会”。另外,还有几点要注意:
1. 试图用子类的静态方法隐藏父类中同样标识的实例方法是不合法的,编译器将会报 错; 2. 试图用子类的实例方法覆盖父类中同样标识的静态方法也是不合法的,编译器同样 会报错; 3. 静态方法和最终方法(带关键字final 的方法)不能被覆盖; 4. 实例方法能够被覆盖; 5. 抽象方法必须在具体类中被覆盖
|