为什么这两个代码示例产生不同的输出?

时间:2010-09-09 19:47:28

标签: java static

样本1:

 class Animal {
     public static void saySomething() { System.out.print(" Gurrr!"); 
   }
 }
 class Cow extends Animal {
    public static void saySomething() { 
     System.out.print(" Moo!"); 
    }
    public static void main(String [] args) {
         Animal [] animals = {new Animal(), new Cow()};
         for( Animal a : animals) {
           a.saySomething();
         }
         new Cow().saySomething();
    }
 }

输出是:

 Gurrr! Gurrr! Moo!

样本2:

 class Animal {
     public void saySomething() { System.out.print(" Gurrr!"); 
   }
 }
 class Cow extends Animal {
    public void saySomething() { 
     System.out.print(" Moo!"); 
    }
    public static void main(String [] args) {
         Animal [] animals = {new Animal(), new Cow()};
         for( Animal a : animals) {
           a.saySomething();
         }
         new Cow().saySomething();
    }
 }

输出:

 Gurrr! Moo! Moo!

我只是不明白为什么使saySomething非静态导致第二次调用saySomething调用Cow版本而不是Animal版本。我的理解是Gurrr! Moo! Moo!在任何一种情况下都是输出。

5 个答案:

答案 0 :(得分:7)

当您在动物身上调用saySomething()时,动物的实际类型不计算在内,因为saySomething()是静态的。

Animal cow = new Cow();
cow.saySomething(); 

相同
Animal.saySomething();

JLS示例:

  

当计算目标引用然后因为调用模式是静态而被丢弃时,不会检查引用以查看它是否为null:

class Test {
  static void mountain() {
      System.out.println("Monadnock");
  }
  static Test favorite(){
       System.out.print("Mount ");
       return null;
   }
   public static void main(String[] args) {
       favorite().mountain();
   }
     

}

     

打印:
  Mount Monadnock
  这里的收藏返回null,但没有抛出NullPointerException。


资源:

关于同一主题:

答案 1 :(得分:3)

你不能在子类中使用相同的签名覆盖静态方法,只需隐藏它们。

  

对于类方法,运行时系统调用在其上调用方法的编译时类型的引用中定义的方法。对于实例方法,运行时系统调用在调用该方法的运行时类型的引用中定义的方法。

http://life.csu.edu.au/java-tut/java/javaOO/override.html

答案 2 :(得分:3)

一些已知的重写“PITFALLS”

  • 静态方法无法覆盖
  • 私有方法无法覆盖

这解释了输出。

答案 3 :(得分:1)

静态方法与“类”绑定,而不是对象的“实例”。 因为你指的是“Animal”并且调用静态方法saySomething()。除非你指的是牛,否则它总是会调用“动物”。

答案 4 :(得分:1)

静态方法在编译时绑定到它们的类,不能以多态方式使用。当您在Animal上声明一个“静态”方法时,它将永远绑定到Animal类,并且无法覆盖。静态方法绑定到Class对象,而不是Class的实例。

常规方法在运行时绑定,因此JVM可以查看对“saySomething”的调用,并尝试确定是否传递了Animal的子类,如果是,则覆盖saySomething()方法。常规方法绑定到对象的实例,而不是绑定到Class本身。

这也是你永远不能这样做的原因:

class Animal
{
   public abstract static void saySomething();
}

由于“静态”意味着“在编译时绑定”,因此静态和抽象的东西永远不会有意义。