我正在检查测试并参加了此次审核练习。我有答案,但不能为我的生活包围我的头。谁能解释一下发生了什么?
interface I1 {
public void f();
}
class A implements I1 {
public void f() {
//implementation
}
public static void g(){
//implementation
}
}
class B extends A {
public void f() {
//implementation
}
public static void g(){
//implementation
}
}
假设
I1 i1 = new B();
I1 i2 = new A();
a a1 = new A();
A a2 =新B();
A b1 =(A)新B();
无需编写任何代码,用文字解释每个代码的效果 以下方法调用。只需说出"这将调用f 在A"中实现的方法等
b1.f();
b1.g();
i1.f();
a1.g();
i2.f();
a2.g();
答案 0 :(得分:1)
g
方法请注意,f
是实例方法,而g
是静态方法。静态方法与class
相关联。
因此,因为您将b1
,a1
和a2
声明为A
,请致电b1.g()
,a1.g()
和a2.g()
将全部致电A.g()
。
这是关于Java静态方法的另一个好问题:Why doesn't the compiler complain when I try to override a static method?
f
方法 Java实例方法是默认的virtual
方法,可以被子类覆盖。那么f()
实际上做什么取决于实例是什么(即取决于new
关键字后面的类型)。
b1
是B
的一个实例,因此b1.f()
会调用f
上定义的B
方法,即B.f()
。
i1
也是B
的一个实例,因此i1.f()
也会调用f
上定义的B
方法,即B.f()
}。
i2
是A
的一个实例,因此i2.f()
会调用f
上定义的A
方法,即A.f()
。
那么a2.f()
呢?如上所述,a2
是B
的实例(您使用new
关键字创建),因此a2.f()
会调用B.f()
。
你可能想知道为什么他们这样工作?为什么a2.f()
不能用于在Java中调用A.f()
?这就是原因:
假设您有一个模块可以对记录列表进行复杂的计算。这些记录可以从数据库中检索,也可以从第三方rest api提供的web api中检索。如何使您的模块尽可能可扩展?
这是您的模块代码:
public class MyModule {
public void doComplexWork() {
// Where to get the records?
List<Record> records = ???;
// Do complex computation here
}
}
可扩展的方法是将接口(或抽象类)定义为记录检索的合同:
public interface IRecordRetriever {
List<Record> retrieveRecords();
}
然后实现两个IRecordRetriever
,一个用于从数据库中检索记录,另一个用于从rest api中检索记录。
public class DatabaseRecordRetriever implements IRecordRetriever {
@Override
public List<Record> retrieveRecords() {
// Connect database and fetch database records here
}
}
public class RestApiRecordRetriever implements IRecordRetriever {
@Override
public List<Record> retrieveRecords() {
// Access the rest api to fetch records here
}
}
现在让我们更改一下模块代码:
public class MyModule {
private IRecordRetriever _recordRetriever;
// Inject IRecordRetriever from contructor
public MyModule(IRecordRetriever retriever) {
_recordRetriever = retriever;
}
public void doComplexWork() {
// Where to get the records?
// From the IRecordRetriever
List<Record> records = _recordRetriever.retrieveRecords();
// Do complex computation here
}
}
现在我们的MyModule
现在可以扩展了。因为无论在哪里检索记录,都不需要更改MyModule
中的代码。如果现在您从数据库中检索记录,但稍后您决定更改以从rest api检索记录,则MyModule
中的任何内容都不需要更改!您只需要更改传递给IRecordRetriever
构造函数的运行时MyModule
实例。
为什么我们可以实现这种可扩展性?这是因为方法覆盖。我们将私有字段_recordRetriever
声明为IRecordRetriever
,因此实际行为取决于实际的运行时实例(由new
关键字创建)。因此,要更改记录检索行为,我们只需要更改传递给IRecordRetriever
构造函数的运行时MyModule
实例。如果方法覆盖不可用,那就是:
IRecordRetriever retrievever = new DatabaseRecordRetriever(); retriever.retrieveRecords();
如果对retriever.retrieveRecords()
的调用未调用retrieveRecords()
上定义的DatabaseRecordRetriever
方法,但调用retrieveRecords()
上定义的IRecordRetriever
方法,则我们不是能够实现可扩展性。 (当然,IRecordRetriever
是一个接口,因此您无法调用IRecordRetriever.retrieveRecords()
,但这也适用于虚方法。)
我上面提到的其他面向对象编程语言也适用。
我通过构造函数将IRecordRetriever
传递给MyModule
的方式称为Constructor Dependency Injection
。您可以阅读有关面向对象编程,方法覆盖和GoF设计模式的更多材料。