了解Java中的inheritance和抽象类

好吧所以我通过googleing寻找文档,但是我没有找到任何真正描述我想要回答的内容,所以我在这里问你们。

所以我得到了inheritance,以及它是如何工作的。 我遇到问题的有时候我看到一个最初定义为一种类型的对象,并设置为不同的类型,我不明白究竟发生了什么。 这是一个例子:

说我有一个类动物,并且类猫和狗延伸动物。 猫,动物和狗都有方法说(),对于猫打印“喵”和狗打印“woof”和动物“不能说话”。

好吧,所以最后这是我的问题。 如果制作一只猫(c)然后运行Animal a = c,究竟会发生什么? 如果我运行a.speak()会发生什么? 哪种说法叫做? 当我改变那样的类型时究竟发生了什么? 我有没有真正的理由使用它?

就抽象方法而言,我的问题是拥有它们究竟是什么意思? 在示例中,我已经看到它们已被放入超类中,并且其下的类定义了确切的行为。 通过在超类中放置一个抽象方法,需要它下面的所有类来实现它吗?

感谢你的帮助!

如果制作一只猫(c)然后运行Animal a = c,究竟会发生什么? 如果我运行a.speak()会发生什么? 哪种说法叫做? 当我改变那样的类型时究竟发生了什么? 我有没有真正的理由使用它?

始终是真实类的方法,例如在这种情况下,cat的speak()方法。

就抽象方法而言,我的问题是拥有它们究竟是什么意思?

例如,他们确保每只动物都有一个方法walk() ,你可以召唤每只动物。 这是一个保证,说“每个Animal对象都有这种方法,你不必关心它”。

在示例中,我已经看到它们已被放入超类中,并且其下的类定义了确切的行为。 通过在超类中放置一个抽象方法,需要它下面的所有类来实现它吗?

实现它或者是抽象的,是的。

Cat c = new Cat(); 动物a = c; a.speak()将打印喵喵。

请检查java多态性 。

关于抽象类:

当抽象类被子类化时,子类通常为其父类中的所有抽象方法提供实现。 但是,如果没有,则子类也必须声明为abstract。

JLS,第5.2节解释了为什么Cat可以赋予Animal 。 (注意, Animal不能隐式赋值给Cat因为Cat是“更具特异性”类型; CatAnimalAnimal的子类型是Cat的超类型)

将编译时引用类型S(源)的值分配给编译时引用类型T(目标)的变量,如下所示:

  • 如果S是类类型:
    • 如果T是类类型,则S必须与T相同,或者S必须是T的子类,否则会发生编译时错误。
    • 如果T是接口类型,则S必须实现接口T,否则会发生编译时错误。
    • 如果T是数组类型,则发生编译时错误。
  • 如果S是接口类型:
    • 如果T是类类型,则T必须是Object ,否则会发生编译时错误。
    • 如果T是接口类型,则T必须是与S相同的接口或S的超接口,否则会发生编译时错误。
    • 如果T是数组类型,则发生编译时错误。
  • 如果S是数组类型SC [],即SC类型的组件数组:

[为简洁省略]

说我有一个类动物,并且类猫和狗延伸动物。 猫,动物和狗都有方法说(),对于猫打印“喵”和狗打印“woof”和动物“不能说话”。

好吧,所以最后这是我的问题。 如果制作一只猫( c )然后运行Animal a = c;究竟会发生什么? ? 如果我运行a.speak();会发生什么? ? 哪个speak()方法被调用? 当我改变那样的类型时究竟发生了什么? 我有没有真正的理由使用它?

Java中的对象确切地知道它们被创建的类型; 它实际上是一个隐藏字段(可以使用Object.getClass()方法检索)。 此外,所有非静态方法解析都从最具体的类的方法定义开始,并向最通用的类​​( Object )进行; 因为Java中只有单一的实现inheritance,所以这是一个简单的搜索。 Cat知道它是Animal的子类型,它是Object的子类型,并且c知道它是一个Cat ,与变量的类型无关。

执行赋值时, 编译器会检查所分配的值的已知类型是指定的类型还是其子类型之一 。 如果是,则分配有效。 如果不是,你将需要一个显式的强制转换(在运行时进行正确的类型检查;强制转换不能破坏Java的类型系统,它们只会让它变得丑陋)。 它不会改变方法查找仍然是动态完成的事实,并且对象仍然知道它实际是什么类型; 所有程序正在做的是忽略一些信息。 如果您了解C ++,那么将Java视为仅具有虚拟方法(和静态方法),并且处理查找到vtable非常简单,因为inheritance钻石或其他恶意案例没有问题。

在使用接口的实现时,它几乎是相同的,只是进行了更复杂的查找(即,首先查找vtable中的索引,然后再继续进行)。 但是,拥有一个实现接口的对象意味着必须有一些完全实现接口的类,这一点再次相对简单。 请记住,所有非常复杂的东西都是在编译时完成的; 在运行时,事情在所有情况下都相对简单。

那么,你会利用这些吗? 那么,你应该 (实际上你会发现在实际代码中很难避免)。 根据接口或超类定义的合同来考虑这种方式是好的方式,其中子类型在没有调用者必须知道细节的情况下遵守合同。 Java库非常重要,特别是因为合同类型细节及其实现的可见性可能不同。 所有客户端代码都知道对象服从合同,它是给定类型的。

根据您的背景,C ++或Java的东西会变得非常混乱。

在C ++中,存在虚函数的概念,在运行时查找它以决定函数属于哪个实际类。 还有一些非虚函数将根据变量类型调用。

在Java中,虽然所有方法本质上都是虚拟的,这意味着Java方法总是在运行时查找,这个过程称为Runtime Polymorphism

这样做的好处就是这样

 class Animal{ public void type(){ System.out.println("animal"); } } class Dog extends Animal{ public void type(){ System.out.println("dog"); } } class Cat extends Animal{ public void type(){ System.out.println("cat"); } } public class Driver{ public static void main(String[] args){ Animal[] animals = new Animal[3]; animals[0] = new Animal(); animals[1] = new Dog(); animals[2] = new Cat(); for(Animal animal: animals){ animal.type(); } } } 

这将输出

 animal dog cat 

据我所知,您使用接口来解耦代码。 您希望针对接口而不是实现进行编程: “编程到接口”是什么意思? 。 您将使用抽象类来实现functionlity,这对于所有实现类来说都是微不足道的,因此您不需要在所有实现类中编写它。