在Java中实例化对象的不同方式之间的差异

时间:2016-06-23 20:46:44

标签: java inheritance extends

我有代码:

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()?

5 个答案:

答案 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及其任何一个孩子 - 为简洁起见,我们可以使用IntegerFloatBigInteger

假设我们声明:

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方法,这就是为什么你得到编译错误