A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 路边小色狼 于 2013-9-13 16:37 编辑

毕老师第7天第11个视频中,为了父类中方法不让子类覆写老师用final,那可用static吗?
  做了个试验
  1. class Fu
  2. {
  3. public static void show()
  4.     {
  5. System.out.println("父");
  6.     }
  7. }

  8. class Zi extends Fu
  9. {
  10. public static void show()
  11.      {
  12. System.out.println("子");
  13.      }
  14. }
  15. class Demo
  16. {
  17. public static void main(String[] args)</P>
  18. <P>Fu f=new Zi();
  19. f.show();                //----------------------------------结果是父,没有复写
  20.                           //-----------------------如果不加static结果是子,那可以用static吗?
  21. }
  22. }
复制代码

评分

参与人数 1技术分 +1 收起 理由
EYE_SEE_YOU + 1

查看全部评分

6 个回复

倒序浏览
不管加不加static,父类的方法都会被子类覆盖了,所不同的是静态方法在类加载完成就存在了。你这样  的结果是“父”的原因是,你父类中的这个show()方法是静态方法,这个类一加载,这个方法就存在了,调用它的时候,也不需要必须new一个父类的对象,所以,你即使这样声明 Fu f = null;然后f.show()也不会报空指针异常,即使你这样写Fu f=new Zi();结果一样是 父;如果不加static,则就必须这样写了Fu f=new Zi();他再去调用show()方法,就是调用的子类中的方法,因为子类的方法将父类中的覆盖了。  可能我说的有点啰嗦了,但是意思应该表达清楚了吧。


评分

参与人数 1技术分 +1 收起 理由
EYE_SEE_YOU + 1

查看全部评分

回复 使用道具 举报
顶起  黑马程序员 我要的就是这种效果 !!!!
回复 使用道具 举报
白磊 发表于 2013-9-13 16:33
不管加不加static,父类的方法都会被子类覆盖了,所不同的是静态方法在类加载完成就存在了。你这样  的结果 ...

谢谢,有点乱,不过大致懂了,我再想想
回复 使用道具 举报
本帖最后由 张晓辉 于 2013-9-13 16:50 编辑

方法的覆盖和静态方法的隐藏下面的程序对巴辛吉小鬣狗和其它狗之间的行为差异进行了建模。如果你不知道
什么是巴辛吉小鬣狗,那么我告诉你,这是一种产自非洲的小型卷尾狗,它们从
来都不叫唤。那么,这个程序将打印出什么呢?
class Dog {
public static void bark() {
System.out.print("woof ");
}
}
class Basenji extends Dog {
public static void bark() { }
}
public class Bark {
public static void main(String args[]) {
Dog woofer = new Dog();
Dog nipper = new Basenji();
woofer.bark();
nipper.bark();
}
}
随意地看一看,好像该程序应该只打印一个woof。毕竟,Basenji 扩展自Dog,
并且它的bark 方法定义为什么也不做。main 方法调用了bark 方法,第一次是
在Dog 类型的woofer 上调用,第二次是在Basenji 类型的nipper 上调用。巴辛
吉小鬣狗并不会叫唤,但是很显然,这一只会。如果你运行该程序,就会发现它
打印的是woof woof。这只可怜的小家伙到底出什么问题了?
问题在于bark 是一个静态方法,而对静态方法的调用不存在任何动态的分派机
制[JLS 15.12.4.4]。当一个程序调用了一个静态方法时,要被调用的方法都是
在编译时刻被选定的,而这种选定是基于修饰符的编译期类型而做出的,修饰符
的编译期类型就是我们给出的方法调用表达式中圆点左边部分的名字。在本案
中,两个方法调用的修饰符分别是变量woofer 和nipper,它们都被声明为Dog
类型。因为它们具有相同的编译期类型,所以编译器使得它们调用的是相同的方
法:Dog.bark。这也就解释了为什么程序打印出woof woof。尽管nipper 的运
行期类型是Basenji,但是编译器只会考虑其编译器类型。
要订正这个程序,直接从两个bark 方法定义中移除掉static 修饰符即可。这样,
Basenji 中的bark 方法将覆写而不是隐藏Dog 中的bark 方法,而该程序也将会
打印出woof,而不是woof woof。通过覆写,你可以获得动态的分派;而通过隐
藏,你却得不到这种特性。
当你调用了一个静态方法时,通常都是用一个类而不是表达式来标识它:例如,
Dog.bark 或Basenji.bark。当你在阅读一个Java 程序时,你会期望类被用作为
静态方法的修饰符,这些静态方法都是被静态分派的,而表达式被用作为实例方
法的修饰符,这些实例方法都是被动态分派的。通过耦合类和变量的不同的命名
规范,我们可以提供一个很强的可视化线索,用来表明一个给定的方法调用是动
态的还是静态的。本谜题的程序使用了一个表达式作为静态方法调用的修饰符,
这就误导了我们。千万不要用一个表达式来标识一个静态方法调用。
覆写的使用与上述的混乱局面搅到了一起。Basenji 中的bark 方法与Dog 中的
bark 方法具有相同的方法签名,这正是覆写的惯用方式,预示着要进行动态的
分派。然而在本案中,该方法被声明为是static 的,而静态方法是不能被覆写
的;它们只能被隐藏,而这仅仅是因为你没有表达出你应该表达的意思。为了避
免这样的混乱,千万不要隐藏静态方法。即便在子类中重用了超类中的静态方法
的名称,也不会给你带来任何新的东西,但是却会丧失很多东西。
对语言设计者的教训是:对类和实例方法的调用彼此之间看起来应该具有明显的
差异。第一种实现此目标的方式是不允许使用表达式作为静态方法的修饰符;第
二种区分静态方法和实例方法调用的方式是使用不同的操作符,就像C++那样;
第三种方式是通过完全抛弃静态方法这一概念来解决此问题,就像Smalltalk
那样。
总之,要用类名来修饰静态方法的调用,或者当你在静态方法所属的类中去调用
它们时,压根不去修饰这些方法,但是千万不要用一个表达式去修饰它们。还有
就是要避免隐藏静态方法。所有这些原则合起来就可以帮助我们去消除那些容易
令人误解的覆写,这些覆写需要对静态方法进行动态分派。

回复 使用道具 举报
final修饰的方法,称为最终方法。最终方法不可被子类重新定义,子类不能出现和父类final方法名相同的方法,即不可被覆盖,但注意覆盖与重载的区别。不能被覆盖并不是不能被重载,你还可以定义和父类final方法名相同但参数表不同的方法
static修饰的方法,子类可以出现和父类final方法名相同的方法,但这不是覆盖(本人亲自试过,在eclipse理加@Override会报错),只能称之为隐藏;
对引用调用静态方法等于对引用的类型调用静态方法,所以你虽然是new的Zi类,但用的是父类接收,所以调的是Fu类的静态方法
不加static就是覆盖,所以会调用Zi类自己的方法
回复 使用道具 举报
static关键字并不能达到禁止子类覆写父类中的方法,它只是共享数据的关键字,如果在父类中定义了带static关键字的函数,而子类继承父类,子类仍然可以覆写父类中的这个函数,只父类中的函数带上final关键字才可以防止子类覆写父类中的方法
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马