Oop 面向对象编程练习

Oop 面向对象编程练习,oop,object,interface,static,Oop,Object,Interface,Static,我正在复习考试,参加了这次复习练习。我有答案,但我这辈子都不能把脑袋绕在这上面。有人能解释一下发生了什么事吗 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() { //imple

我正在复习考试,参加了这次复习练习。我有答案,但我这辈子都不能把脑袋绕在这上面。有人能解释一下发生了什么事吗

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=新的B()

I1 i2=新的A()

a1=新的A()

A a2=新的B()

A b1=(A)新的B()

在不编写任何代码的情况下,用文字解释以下各项的效果: 下面的方法调用。只需说“这将调用f” 方法在“etc”中实现

b1.f()

b1.g()

i1.f()

a1.g()

i2.f()

a2.g()

g
方法分析 请注意,
f
是一个实例方法,而
g
是一个静态方法。静态方法与
类关联

因此,因为您将
b1
a1
a2
声明为
A
,调用
b1.g()
a1.g()
a2.g()
都将调用
A.g()

下面是关于Java静态方法的另一个好问题:

f
方法分析 Java实例方法默认为
virtual
方法,可以被子类覆盖。因此
f()
实际执行的操作取决于实例是什么(也就是说,取决于
new
关键字后面的类型)

b1
B
的一个实例,因此
b1.f()
将调用
B
上定义的
f
方法,即
B.f()

i1
也是
B
的一个实例,因此
i1.f()
也将调用
B
上定义的
f
方法,即
B.f()

i2
A
的一个实例,因此
i2.f()
将调用
A
上定义的
f
方法,即
A.f()

那么
a2.f()
呢?如上所述,
a2
B
的一个实例(您使用
new
关键字创建它),因此
a2.f()
将调用
B.f()

更多关于方法重写的信息 你可能想知道他们为什么这样工作?为什么
a2.f()
在Java中不被设计为调用
A.f()
?原因如下:

假设您有一个模块,该模块将根据记录列表执行复杂的计算。这些记录可以从数据库中检索,也可以从第三方RESTAPI提供的web api中检索。如何使您的模块尽可能可扩展

这是您的模块代码:

public class MyModule {
    public void doComplexWork() {
        
        // Where to get the records?
        List<Record> records = ???;
        
        // Do complex computation 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{
专用IRecordRetriever\u recordRetriever;
//从构造函数注入IRecordRetriever
公共MyModule(IRecordRetriever检索器){
_recordRetriever=检索器;
}
公共无效文档复合工作(){
//在哪里可以找到记录?
//从IRecordRetriever
列表记录=\u recordRetriever.retrieveRecords();
//在这里做复杂的计算
}
}
现在我们的
MyModule
可以扩展了。因为无论在哪里检索记录,都不需要更改
MyModule
中的代码。若现在您从数据库检索记录,但后来您决定更改为从RESTAPI检索记录,
MyModule
中的任何内容都不需要更改!您只需更改传递给
MyModule
构造函数的运行时
IRecordRetriever
实例

为什么我们可以实现这种可扩展性?这是因为方法重写。我们将私有字段
\u recordRetriever
声明为
IRecordRetriever
,因此实际行为取决于实际运行时实例(由
new
关键字创建)。因此,要更改记录检索行为,我们只需要更改传递给
MyModule
构造函数的运行时
IRecordRetriever
实例。如果方法重写不可用,即:

IRecordRetriever retriever=新数据库RecordRetriever(); retriever.retrieveRecords()

如果对
retriever.retrieveRecords()
的调用不是调用
DatabaseRecordRetriever
上定义的
retrieveRecords()
方法,而是调用
ireRecordRetriever
上定义的
retrieveRecords()
方法,则我们无法实现可扩展性。(当然,
IRecordRetriever
是一个接口,因此您不能调用
IRecordRetriever.retrieveRecords()
,但这也适用于虚拟方法)

我上面提到的也适用于其他面向对象编程语言


我通过构造函数将
IRecordRetriever
传递给
MyModule
的方式被调用。您可以阅读更多关于面向对象编程、方法重写和GoF设计模式的资料。

要了解这一点,您只需在每个实现中放入一条打印语句,比如“方法f在a中实现”,您可以使用它来了解发生了什么。
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
    }
}