我有代码:
class Father{
String name="father";
void f(){System.out.print("father class");}
}
class Son extends Father{
String name = "son";
void f(){System.out.print("son class");}
void f2(){}
}
public class Test {
public static void main(String[] args) {
Father s = new Son();
System.out.println(s.name);// outputs father
s.f();// outputs "son class"
s.f2();// s does not have f2
}
}
我的问题是,做父亲s =新父亲()或父亲s =新儿子()或儿子s =新儿子()之间的区别是什么?
同样,为什么示例中的s.f2会导致错误?必须父亲实施f2()?
答案 0 :(得分:1)
您正在处理的是引用类型(变量类型)和对象类型(实际引用的是什么)。 Java编译器需要某种保证,即被引用的对象可以运行您正在调用的方法。为此,它会查看引用类型。执行时,方法运行是对象类型。
简单地说:
Father f = new Father(); //Treated as a Father, behaves like a Father
Son s = new Son(); //Treated as a Son, behaves like a Son
Father q = new Son(); //Treated as a Father, behaves like a Son (sounds like my own father)
如果您通过说(Son)q
向儿子施放 q,编译器会将其视为Son
,除非该对象不是实际上一个儿子,在这种情况下,你会得到ClassCastException
。
答案 1 :(得分:1)
让我们采用一个更简单的概念,因为您的层次结构意味着所有Son
都是Father
s,但并非所有Father
都是Son
s(不是非常正确。)
让我们看一下抽象类Number
及其任何一个孩子 - 为简洁起见,我们可以使用Integer
,Float
和BigInteger
。
假设我们声明:
Number num = Float.NaN;
我们现在有一个Float
实例,由Float
引用。我们可以对该实例执行任何操作,但仅在Number
的上下文中执行。
Float
有一个有用的方法isNan
,它可以让我们看看我们的浮动 是否是一个数字。在Number
的上下文中...该方法不存在。
这样做有一些优点 - 如果你不需要子引用的特殊性,你可以通过它的父类(或接口)引用所有内容。如果您希望与孩子的API脱钩,这也会使您与孩子的API脱钩(参见developing to an interface)。
答案 2 :(得分:1)
我认为用动物例子来解释更容易:
class Animal {
void printName() {
System.out.println("Animal");
}
}
class Dog extends Animal{
@Override
void printName() {
System.out.println("Dog");
}
}
class Cat extends Animal{
@Override
void printName() {
System.out.println("Cat");
}
void meow() {
System.out.println("meow");
}
}
扩展类时,子类可以覆盖父方法,并且可以拥有自己的方法。在我的Animal示例中,通用Animal对象只能提供其名称,但Cat对象可以给出其名称和喵喵声。显然,喵喵方法是特定于猫的,因为我们知道狗不能做喵喵和动物一般。
当你这样做时
Animal animal = new Cat();
您实际上是创建Cat的实例,但将其用作一般动物。因此,您的动物实例仅具有Animal类中可用的方法,但Cat类重写的方法的执行将委派给Cat类。 如果你想执行Cat的特定方法,那么你需要将你的Animal投射到猫
(Cat) animal.meow();
在您调用f2()方法的示例中,您需要先将父对象强制转换为子对象
(Son)s.f2();
答案 3 :(得分:0)
s.f2()是语法错误,因为你告诉JVM s是父亲,而不是儿子。
在代码中,它无法在父类
中找到f2方法class Father{
String name="father";
void f(){System.out.print("father class");}
}
但这并不意味着代码错了,只是JVM不喜欢它。
如果将s.f2()更改为
(Son)s.f2();
它会起作用
答案 4 :(得分:0)
好的,我知道这里有什么混乱。
在java中,您可以覆盖方法,但不能覆盖类变量
记住这条规则
所以当你做到了
父亲s =新儿子();
对象" s"是父亲类型
正如我们所说,其中的变量不会被方法覆盖 所以最终的结果是一个对象,它包含来自Father类的成员变量(" name"变量)和来自子类的方法(因为父只有1个方法而且子被覆盖了它)。
以及为什么f2不起作用
这是因为对象" s"是父类型不是儿子(父亲对象有1个方法,它被子类覆盖除了它将保持为父对象)而父亲没有f2方法,这就是为什么你得到编译错误