我正在审核我的OOP,我对对象有一点疑问。好吧,我有一个超类Ship
,它也是一个抽象类。 Ship有一些子类,即Submarine
,Destroyer
等等。
我知道我无法创建Ship类的对象,因为它是抽象的。但为什么以下仍然有效?
Ship s1 = new Submarine("ship 1");
答案 0 :(得分:2)
行:Ship s1 = new Submarine("ship 1");
您不是创建抽象类Ship
的对象,它仅仅是指向子类Submarine
的对象的引用。你正在做的是创建子类的对象。
答案 1 :(得分:1)
这是由于多态性。
父类的引用可以保存子类对象。但是使用此引用可以调用在子类中正确覆盖的方法。
答案 2 :(得分:1)
您应该区分运行时类型和编译时类型。
假设您有以下声明:
Ship s1 = new Submarine("ship 1");
s1
的编译时类型是分配规则左侧的类型,例如编译时类型为Ship
。您可以将s1
分配给任何继承Ship
。
s1
的运行时类型是赋值语句的右侧,例如运行时类型s1
是Ship
的具体实现(在您的情况下为Submarine
)。
更多信息:
答案 3 :(得分:1)
简单来说,抽象类意味着您无法创建此类的新实例
但是,仍然可以将对象声明为Ship
(编译时类型 vs 运行时类型)以使用多态 ,允许你做一些有趣的事情,比如Ship
数组,其中包含Submarine
和Destroyer
(还有更多)。
Ship[] army = new Ship[2];
army[0] = new Submarine("0");
army[1] = new Destroyer("1");
for( Ship s : army ) {
s.fire();
}
在此示例中,我们可以在两个对象上调用fire()
,因为fire()
是Ship
的方法。由于船的攻击取决于船的类型(潜艇不会像驱逐舰一样射击),因此您将fire()
方法设为Ship
摘要并在Submarine
和{{1}中实施}。
这些技巧在OOP中是必不可少的,并允许您实现强大的设计模式。如果您是初学者,请查看strategy pattern和template method pattern,几年前他们改变了我对OOP的看法;)
希望这个小样本可以帮助你:)
答案 4 :(得分:0)
仅仅因为您无法实例化抽象类,并不意味着您无法拥有该类型的对象。您还可以拥有某个界面的对象,例如List<String> strings = new ArrayList<>()
您声明一个方法摘要,因为默认实现没有意义。
考虑一个名为shape
的类,其中包含子类circle
和square
。
shape
有一个区域,但circle
区域的计算方式与square
的区域不同。
您可以在抽象getArea
类中创建一个名为shape
的抽象方法,让circle
和square
提供它们的实现。
答案 5 :(得分:0)
s1是一艘船,您可以使用抽象类中定义的所有方法,但只能使用这些方法。 如果您的Submarine类中有其他方法(例如,dive,periscopeUp,...),则无法调用它们,因为它们未在您的抽象Ship类中定义。
使用它对于集合来说非常方便,例如,您现在可以在列表中存储不同类型的Ship。
List<Ship> ships = new ArrayList<Ship>();
ships.add(new Submarine("sub 1"));
ships.add(new Destroyer("des 1"));
如果您要定义“List<Submarine> subs
”,则只能在该列表中存储潜艇类。