我有以下两个班级:
class Animal {
public static void staticMethod(int i) {
System.out.println("Animal : static -- " + i);
}
public void instanceMethod(int i) {
System.out.println("Animal : instance -- " + i);
}
}
class Cat extends Animal {
public static void staticMethod(int i) {
System.out.println("Cat : static -- " + i);
}
public void instanceMethod(int i) {
System.out.println("Cat : instance -- " + i);
}
public static void main(String[] args) {
Cat myCat = new Cat();
myCat.staticMethod(1); // Cat : static -- 1
myCat.instanceMethod(2); // Cat : instance -- 2
System.out.println("");
Animal myAnimal = myCat;
Animal.staticMethod(3); // Animal : static -- 3
myAnimal.staticMethod(4); // Animal : static -- 4 [ ? ]
System.out.println("");
myAnimal.instanceMethod(5); // Cat : instance -- 5
}
}
当我运行Cat时,我得到了以下结果:
Cat : static -- 1
Cat : instance -- 2
Animal : static -- 3
Animal : static -- 4
Cat : instance -- 5
我能理解1,2,3和5,但为什么#4不是:“Cat:static - 4”? 我的理解是这样的:
myAnimal = myCat意味着“myAnimal”现在与“myCat”完全相同,因此无论“myAnimal”在哪里,你都可以用“myCat”替换它并获得相同的结果,因为myAnimal中的所有内容都与所有内容相同在myCat中,因此“myAnimal.staticMethod(4)”应与“myCat.staticMethod(4)”相同,输出应为:“Cat:static - 4”,类似于“myCat.staticMethod(1)”上方。
但情况似乎并非如此,为什么?
答案 0 :(得分:3)
您将myAnimal
声明为Animal
。因此,也可以从该类调用静态方法。
您永远不应该从实例调用静态方法(或访问静态字段)以防止这种混淆。
答案 1 :(得分:2)
原因是Java根据引用变量本身的类型解析静态方法,而不是像运行时那样以多态方式解析静态方法。
要进行扩展,当您执行Animal myAnimal = myCat
时,您将为动物参考分配Cat引用。这是可以接受的,因为Cat
也是Animal
,因此Animal
可以做的任何事情,Cat
都可以做。
此外,如果您通过myAnimal
引用调用实例(即非静态)方法,并且该方法在Cat
中被覆盖,则该方法的Cat
版本被称为,因为这就是为什么首先覆盖该方法的原因。另一方面,静态方法永远不会被覆盖。这就是为什么它们是“静态的”,就像“非动态”一样。这意味着编译器可以解析静态方法,而不必依赖于运行时环境。
答案 2 :(得分:2)
来自Oracle docs:
<强> 8.4.8.2。隐藏(按类方法)
如果一个C类声明或继承静态方法m,那么就说m 隐藏任何方法m',其中m的签名是子签名 (§8.4.2)m'的签名,在超类和 C的超级接口,否则C中的代码可以访问。
例8.4.8.2-1。隐藏类方法的调用
可以使用a调用隐藏的类(静态)方法 引用的类型是实际包含的类 声明方法。在这方面,隐藏了静态方法 与覆盖实例方法不同。例子:
class Super {
static String greeting() { return "Goodnight"; }
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());
}
}
产生输出:
晚安,迪克因为问候语的调用使用了s的类型,即Super, 在编译时弄清楚要调用哪个类方法,而 名称的调用使用s的类,即Sub,来弄清楚, 在运行时,调用哪个实例方法。
答案 3 :(得分:1)
静态意味着:静态解析调用(在您的情况下,它是根据变量的声明类型解析的,并且变量是编译的-time实体)。
您期望的结果将要求解析动态(多态,基于引用实例的实际类型,并且实例是运行时实体)。
答案 4 :(得分:1)
当你设置myAnimal = myCat时,指针myAnimal指向一个cat对象,但是当你尝试通过myAnimal指针访问静态方法时,它会从myAnimal被声明为的类中访问静态方法。