山东大学 软件学院 面向对象程序设计 最全·期末真题 OOP题型分类 简答题答案整理

面向对象程序设计,复习建议:

复习建议:

简答题每年考的大差不差,把本文(近6年)的简答题都背会,简答题没问题。

设计题:设计策略必考两道,设计原则必考一道

补全代码:必有一道 也是设计模式

一、简答题(30分/5*6分)

2022年

(1) 简述多态变量的四种形式

简单多态变量(Simple Polymorphic Variable)

  • 描述:可以接受不同类型的值。

  • 示例:Java 中的 Object 类型变量可以接受任何对象类型的值。

  • 优点:增强了变量的灵活性。

接收器变量(Receiver Variable)

  • 描述:方法内部的 this 变量,表示调用该方法的对象。

  • 示例:在一个实例方法中,this 代表当前对象。

  • 优点:简化了代码的编写和理解。

向下造型变量(Downcasting Variable)

  • 描述:将父类引用类型变量强制转换为子类引用类型。

  • 示例:将一个 Animal 类型的变量转换为 Dog 类型。

  • 优点:允许访问子类特有的方法和属性。

纯多态变量(Pure Polymorphic Variable)

  • 描述:通过接口或抽象类实现的多态性,变量可以指向实现接口或继承抽象类的任何对象。

  • 示例:一个 List 接口类型的变量可以指向 ArrayListLinkedList 等实现类的对象。

  • 优点:实现了接口和抽象类的多态性,增强了代码的扩展性

(2) 简述OOPL中的几种内存分配策略

  • 静态内存分配:在编译时确定内存分配,一般用于全局变量和静态变量。

  • 栈内存分配:在函数调用期间分配内存,用于局部变量,函数结束后自动释放。

  • 堆内存分配:动态分配内存,用于在运行时需要动态创建的对象,通过垃圾收集器或手动管理内存释放

(3) 简述什么是面向接口编程?请问在通过接口的形式调用对象时有什么优势?

封装是指将对象的状态(属性)私有化,并提供公共方法访问这些属性。封装的优点包括:

  • 保护对象的状态:防止外部对象直接访问和修改内部数据。

  • 提高代码可维护性:通过公共接口访问和修改数据,更易于维护和修改。

  • 提高代码的可读性:通过方法名来说明操作的含义,使代码更易读懂。

(4) 给出以下程序的输出结果

public class  Bird{
    public void fly(Bird p) {System.out.println(“Bird fly with Bird”);}
}
public class Eagle extends Bird {
    public void fly(Bird p) {System.out.println(“Eagle fly with Bird!”);}
    public void fly(Eagle e) { System.out.println(“Eagle fly with Eagle!”);}
}
Bird p1 = new Bird () ; 
Bird p2 = new Eagle () ; 
Eagle p3 = new Eagle () ; 
p2.fly(  p1  ) ;
p2.fly(  p2  ) ;
p2.fly(  p3  ) ;
p3.fly(  p1  ) ;
p3.fly(  p2  ) ;
p3.fly(  p3  ) ;

p2.fly(p1):

  • p2Eagle 类型的对象,实际调用的是 Eagle 类中的 fly(Bird p) 方法。

  • 输出: Eagle fly with Bird!

p2.fly(p2):

  • p2Eagle 类型的对象,实际调用的是 Eagle 类中的 fly(Bird p) 方法。

  • 输出: Eagle fly with Bird!

p2.fly(p3):

  • p2Eagle 类型的对象,实际调用的是 Eagle 类中的 fly(Bird p) 方法,因为 p2 被声明为 Bird 类型,虽然实际是 Eagle 类型。

  • 输出: Eagle fly with Bird!

p3.fly(p1):

  • p3Eagle 类型的对象,调用的是 Eagle 类中的 fly(Bird p) 方法。

  • 输出: Eagle fly with Bird!

p3.fly(p2):

  • p3Eagle 类型的对象,调用的是 Eagle 类中的 fly(Bird p) 方法。

  • 输出: Eagle fly with Bird!

p3.fly(p3):

  • p3Eagle 类型的对象,调用的是 Eagle 类中的 fly(Eagle e) 方法,因为 p3Eagle 类型。

  • 输出: Eagle fly with Eagle!

(5) 给出情景选择应该使用哪种合适的设计模式解决问题

选择题,选择设计模式,给了很多设计模式让你去选择

A 桥接模式 B 单例模式 C 。。。

(1) 毛笔有大中小三种类型,有12种颜料,请问应该选用哪种设计模式(桥接)

(2) 一个类现在有sort(),serach()方法需要被实现,但是我现在有BinarySearch类的binarySearch()方法和QuickSort类的qucikSort()方法,请问应该选用何种设计模式实现

(已有快速排序类和二分查找类,现希望利用这两个类实现通用接口排序和查找。)

(3) 当多个用户请求计算机执行打印操作时时,可以通过连接池相应用户的请求,请问连接池的实现应该采用什么模式?

(打印池是操作系统中一个应用程序,在局域网中所有用户可以通过打印池来打印,请问()模式适合该场景)

21年

1.重载、重定义、重置的区别,并使用C++或者JAVA举例说明(要求写出一个类的代码,然后根据代码说明)

重载(Overloading):在同一个类中定义多个同名方法,但参数列表不同。

重定义(Overriding):在子类中重新定义父类中的方法,提供新的实现。

重置(Resetting):将对象的状态恢复到默认状态或重新初始化对象的属性

// 父类
class Parent {
    public void show() {
        System.out.println("Parent show method");
    }
}
​
// 子类
class Child extends Parent {
    @Override
    public void show() {
        System.out.println("Child show method");
    }
}
​
// 示例类
public class Example {
    private int value;
// 构造函数
public Example(int value) {
    this.value = value;
}
​
// 重载方法 - 参数不同
public void display(int a) {
    System.out.println("Display with integer: " + a);
}
​
public void display(String a) {
    System.out.println("Display with string: " + a);
}
​
public void display(int a, String b) {
    System.out.println("Display with integer and string: " + a + ", " + b);
}
​
// 重置方法
public void reset() {
    this.value = 0;  // 重置对象状态
}
​
// 显示当前值
public void display() {
    System.out.println("Value: " + value);
}
​
// 主方法
public static void main(String[] args) {
    // 重载示例
    Example overloadExample = new Example(10);
    overloadExample.display(5);
    overloadExample.display("Hello");
    overloadExample.display(5, "World");
​
    // 重定义示例
    Parent parent = new Parent();
    parent.show();
    
    Child child = new Child();
    child.show();
​
    // 重置示例
    Example resetExample = new Example(10);
    resetExample.display();
    resetExample.reset();
    resetExample.display();
}
}

2.描述静态方法绑定,动态方法绑定,并从效率,忘了,忘了,三个方面对比两种绑定方式

静态方法绑定(Static Method Binding)

静态方法绑定是指在编译时就决定了调用哪个方法。静态方法、私有方法和 final 方法都是静态绑定的。静态绑定在编译时确定,执行速度较快,因为不需要在运行时进行查找。

动态方法绑定(Dynamic Method Binding)

动态方法绑定是指在运行时决定调用哪个方法。也称为晚绑定(Late Binding)。当一个方法被覆盖时,程序会在运行时根据对象的实际类型来调用相应的方法。这种机制允许子类覆盖父类的方法,实现多态。

执行速度

  • 静态方法绑定:编译时确定,执行速度快,因为不需要在运行时查找方法。

  • 动态方法绑定:运行时确定,执行速度相对较慢,因为需要在运行时查找实际调用的方法(面向对象编程导论+原书第3版-中文版)。

灵活性

  • 静态方法绑定:缺乏灵活性,无法实现多态行为。方法的调用在编译时就固定了。

  • 动态方法绑定:提供了高度的灵活性,可以实现多态。方法的调用根据实际对象的类型在运行时确定(面向对象编程导论+原书第3版-中文版)。

内存使用

  • 静态方法绑定:内存使用效率高,因为方法调用是直接的,不需要额外的开销。

  • 动态方法绑定:内存使用效率相对较低,因为需要额外的内存来支持动态方法查找和调用(面向对象编程导论+原书第3版-中文版)

3.替换原则,有哪几种空间分配方案(描述什么是替换原则?描述三种内存分配方式?

###

什么是替换原则?

替换原则(Liskov Substitution Principle, LSP)是面向对象设计中的基本原则。其核心思想是:在任何使用基类的地方都可以使用其子类而不影响程序的正确性和行为。这确保了子类可以扩展父类的功能而不破坏父类的功能。

三种内存分配方式
  1. 最小静态空间分配(Minimum Static Allocation)

    • 描述:编译时分配固定大小的内存。

    • 优点:简单,无碎片。

    • 缺点:不灵活,无法应对动态变化。

  2. 最大静态空间分配(Maximum Static Allocation)

    • 描述:编译时分配程序可能需要的最大内存。

    • 优点:保证足够的内存空间。

    • 缺点:可能浪费内存。

  3. 动态内存分配(Dynamic Memory Allocation)

    • 描述:运行时根据需要动态分配和释放内存。

    • 优点:灵活,内存利用高效。

    • 缺点:管理复杂,可能产生碎片和内存泄漏。

4.简要叙述代理模式的几种类型和用途

代理模式简介

代理模式(Proxy Pattern)为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。

代理模式的几种类型
  1. 远程代理(Remote Proxy)

    • 描述:为一个位于不同地址空间的对象提供本地代表,处理网络通信。

    • 用途:分布式系统,通过网络访问远程服务。

  2. 虚拟代理(Virtual Proxy)

    • 描述:在实际对象创建前,提供对它的访问,按需创建对象。

    • 用途:优化性能,延迟加载资源密集的对象。

  3. 保护代理(Protection Proxy)

    • 描述:控制对原始对象的访问,基于权限过滤请求。

    • 用途:安全控制,限制对敏感对象的访问权限。

  4. 智能代理(Smart Proxy)

    • 描述:在访问对象时,执行附加操作,如引用计数、日志记录。

    • 用途:增强功能,记录访问日志、性能监控。

用途
  1. 控制访问:通过在代理对象中实现访问控制逻辑,限制或控制对目标对象的访问。

  2. 延迟加载:虚拟代理用于在需要时才创建资源密集的对象,优化性能。

  3. 远程访问:远程代理使本地程序可以透明地调用远程对象的方法。

  4. 记录日志:智能代理在调用目标对象的方法前后执行日志记录、性能监控等操作。

20年

1.继承的八种形式、哪些好哪些不好

1. 特化子类化(Specialization Subclassing)
  • 描述:子类是父类的一种特定类型,继承父类的属性和方法,并增加新的属性和方法。

  • 优点:自然且符合直觉,易于理解和使用。缺点:容易导致类层次过深,增加复杂性。

2. 规范子类化(Specification Subclassing)
  • 描述:子类通过重新定义父类的方法来提供特定实现。

  • 优点:提高代码复用性,简化代码维护。缺点:可能导致子类和父类间的紧密耦合,难以独立修改。

3. 构造子类化(Construction Subclassing)
  • 描述:子类通过添加新功能来扩展父类,而不改变父类的现有功能。

  • 优点:易于扩展,不会破坏父类的功能。缺点:增加了系统的复杂性和维护难度。

4. 泛化子类化(Generalization Subclassing)
  • 描述:子类通过移除或简化父类的功能来创建。

  • 优点:简化子类,实现精简的功能。缺点:可能导致代码的冗余和重复。

5. 扩展子类化(Extension Subclassing)
  • 描述:子类在父类基础上增加新的属性和方法。

  • 优点:增加类的功能和灵活性。缺点:如果不慎,会导致子类过度复杂。

6. 限制子类化(Limitation Subclassing)
  • 描述:子类通过限制或去除父类的一些功能来实现。

  • 优点:提供更具体和精简的实现。缺点:可能导致违反替换原则,降低代码灵活性。

7. 变体子类化(Variant Subclassing)
  • 描述:子类通过改变父类的部分功能来创建。

  • 优点:提供了不同的实现方案,增加了多样性。缺点:增加了理解和维护的复杂性。

8. 结合子类化(Combination Subclassing)
  • 描述:子类通过组合多个父类的功能来创建。

  • 优点:实现了多个功能的组合,提高了代码复用性。缺点:可能导致类的层次结构过于复杂,难以管理和维护。

2.多态的四种形式

子类化多态(Subclass Polymorphism)

  • 描述:也称为运行时多态,通过继承和方法重写实现。子类可以替代父类进行操作。

  • 示例:一个父类 Animal 有一个方法 speak(),不同的子类如 DogCat 重写了 speak() 方法。

  • 优点:实现了动态绑定,增强了代码的灵活性。

参数多态(Parametric Polymorphism)

  • 描述:通过泛型实现的方法,使得方法或类可以操作任何类型的数据。

  • 示例:Java 中的泛型类 List<T> 可以存储任何类型的元素。

  • 优点:代码复用性高,类型安全。

重载多态(Overloading Polymorphism)

  • 描述:在同一个类中,多个同名方法通过不同的参数列表进行区分。

  • 示例:一个类中有多个 print 方法,分别接受 intStringdouble 类型的参数。

  • 优点:增强了方法的灵活性和可读性。

强制多态(Coercion Polymorphism)

  • 描述:通过自动类型转换来实现不同类型的兼容操作。

  • 示例:将一个 int 类型的数据赋值给一个 double 类型的变量。

  • 优点:简化了类型转换操作,提高了代码的可读性 。

3.多态变量的四种形式

简单多态变量(Simple Polymorphic Variable)

  • 描述:可以接受不同类型的值。

  • 示例:Java 中的 Object 类型变量可以接受任何对象类型的值。

  • 优点:增强了变量的灵活性。

接收器变量(Receiver Variable)

  • 描述:方法内部的 this 变量,表示调用该方法的对象。

  • 示例:在一个实例方法中,this 代表当前对象。

  • 优点:简化了代码的编写和理解。

向下造型变量(Downcasting Variable)

  • 描述:将父类引用类型变量强制转换为子类引用类型。

  • 示例:将一个 Animal 类型的变量转换为 Dog 类型。

  • 优点:允许访问子类特有的方法和属性。

纯多态变量(Pure Polymorphic Variable)

  • 描述:通过接口或抽象类实现的多态性,变量可以指向实现接口或继承抽象类的任何对象。

  • 示例:一个 List 接口类型的变量可以指向 ArrayListLinkedList 等实现类的对象。

  • 优点:实现了接口和抽象类的多态性,增强了代码的扩展性

4.三种内存分配方式(原题)

  1. 最小静态空间分配(Minimum Static Allocation)

    • 描述:编译时分配固定大小的内存。

    • 优点:简单,无碎片。

    • 缺点:不灵活,无法应对动态变化。

  2. 最大静态空间分配(Maximum Static Allocation)

    • 描述:编译时分配程序可能需要的最大内存。

    • 优点:保证足够的内存空间。

    • 缺点:可能浪费内存。

  3. 动态内存分配(Dynamic Memory Allocation)

    • 描述:运行时根据需要动态分配和释放内存。

    • 优点:灵活,内存利用高效。

    • 缺点:管理复杂,可能产生碎片和内存泄漏。

5.组合和继承优先用哪个好,为什么?(原题)

组合(Composition)与继承(Inheritance)

组合和继承是面向对象编程中的两种重要机制。组合通过包含其他对象来实现功能,而继承通过从父类派生子类来实现功能。

组合优先于继承的原因
  1. 松耦合

    • 组合:通过组合,可以在运行时动态改变组合对象的行为,增强了系统的灵活性和可扩展性。组合使得类之间的依赖关系更弱,便于独立修改和维护。

    • 继承:继承使得子类和父类之间的耦合度较高,子类依赖于父类的实现细节,修改父类可能导致子类发生错误。

  2. 复用性

    • 组合:可以在多个类中复用组件,实现代码复用,减少重复代码。组合可以实现更灵活的复用,因为不同类可以共享相同的组件。

    • 继承:继承的复用性较差,因为子类只能复用单一父类的代码,而且继承层次越深,越难以理解和维护。

  3. 灵活性

    • 组合:通过组合,可以在运行时动态改变对象的行为,提供了更大的灵活性。组合可以避免类层次过深的问题,简化类的设计。

    • 继承:继承是一种静态关系,编译时确定,无法在运行时改变对象的行为。继承层次过深会导致类的设计复杂,难以维护和扩展。

19年

1、参数数量相同,Java编译器绑定方法步骤;画出类层次图,举例说明(Dessert例子)

(1.重载解析的描述,然后让举例子说明执行结果,参照ppt dessert甜点的例子。)

2、解释替换原则;OOPC采用那几种内存分配方案?(我什么都不知道,只知道大纲上有:“3种内存分配方案: 最小静态空间分配 最大静态空间分配 动态内存分配”就写了,我也不知道对不对,反正ppt上也没有)

什么是替换原则?

替换原则(Liskov Substitution Principle, LSP)是面向对象设计中的基本原则。其核心思想是:在任何使用基类的地方都可以使用其子类而不影响程序的正确性和行为。这确保了子类可以扩展父类的功能而不破坏父类的功能。

三种内存分配方式
  1. 最小静态空间分配(Minimum Static Allocation)

    • 描述:编译时分配固定大小的内存。

    • 优点:简单,无碎片。

    • 缺点:不灵活,无法应对动态变化。

  2. 最大静态空间分配(Maximum Static Allocation)

    • 描述:编译时分配程序可能需要的最大内存。

    • 优点:保证足够的内存空间。

    • 缺点:可能浪费内存。

  3. 动态内存分配(Dynamic Memory Allocation)

    • 描述:运行时根据需要动态分配和释放内存。

    • 优点:灵活,内存利用高效。

    • 缺点:管理复杂,可能产生碎片和内存泄漏。

(2.可替换原则的描述,三种内存分配方式(最大,最小,动态)分别是什么。)

18年

一、什么是重载(overload )?什么是重写(override)?之间的区别是什么,请用面向对象的语言举例说明。

重载(Overloading):在同一个类中定义多个同名方法,但参数列表不同。

重定义(Overriding):在子类中重新定义父类中的方法,提供新的实现。

代码参考21年第1题答案

二、什么是静态绑定?什么是动态绑定?各有什么优缺点?

静态方法绑定(Static Method Binding)

静态方法绑定是指在编译时就决定了调用哪个方法。静态方法、私有方法和 final 方法都是静态绑定的。静态绑定在编译时确定,执行速度较快,因为不需要在运行时进行查找。

动态方法绑定(Dynamic Method Binding)

动态方法绑定是指在运行时决定调用哪个方法。也称为晚绑定(Late Binding)。当一个方法被覆盖时,程序会在运行时根据对象的实际类型来调用相应的方法。这种机制允许子类覆盖父类的方法,实现多态。

执行速度

  • 静态方法绑定:编译时确定,执行速度快,因为不需要在运行时查找方法。

  • 动态方法绑定:运行时确定,执行速度相对较慢,因为需要在运行时查找实际调用的方法(面向对象编程导论+原书第3版-中文版)。

灵活性

  • 静态方法绑定:缺乏灵活性,无法实现多态行为。方法的调用在编译时就固定了。

  • 动态方法绑定:提供了高度的灵活性,可以实现多态。方法的调用根据实际对象的类型在运行时确定(面向对象编程导论+原书第3版-中文版)。

内存使用

  • 静态方法绑定:内存使用效率高,因为方法调用是直接的,不需要额外的开销。

  • 动态方法绑定:内存使用效率相对较低,因为需要额外的内存来支持动态方法查找和调用(面向对象编程导论+原书第3版-中文版)

三、动物类Animal,其子类Cat;植物类Plant,其子类Tree,动物类和植物类有共同父类Object,各有对象aAnimal,aCat,aPlant,aTree,aObject,在某java片段中有如下几个方法:

void over(Object aObject,Plant aPlant);//方法一

void over(Animal aAnimal,Object aObject);//方法二

void over(Animal aAnimal,Plant aPlant);//方法三

void over(Cat aCat,Tree aTree);//方法四

请问以下几个方法调用会调用上述哪个方法,并写出分析过程:

over(aObject,aPlant);

over(aCat,aObject);

over(aObject,aObject);

over(aAnimal,aPlant);

over(aCat,aTree);

四、什么是单继承?什么是多重继承?各有什么优缺点?如果让你选择设计一种程序语言,你会选择哪种继承方式?为什么?

单继承

定义:单继承是指一个类只能继承一个父类。这是大多数面向对象编程语言(如Java、C#)的继承方式。

优点

  1. 简单明了:继承关系清晰,易于理解和维护。

  2. 避免歧义:没有名称冲突和继承路径歧义问题。

  3. 更高的可维护性:更容易跟踪类的来源和修改。

缺点

  1. 受限的复用性:无法直接复用多个类的功能,需要通过其他手段(如接口、组合)实现多重行为。

多重继承

定义:多重继承是指一个类可以同时继承多个父类。这在一些面向对象编程语言(如C++)中是允许的。

优点

  1. 高复用性:可以直接复用多个类的功能,减少代码重复。

  2. 更强的灵活性:可以自由组合多个类的行为和特性。

缺点

  1. 复杂性高:继承关系复杂,难以理解和维护。

  2. 名称冲突:容易产生方法或属性名称冲突问题,导致歧义。

  3. 菱形继承问题:多个父类继承自同一个祖先类时,可能会导致多次继承同一份数据,增加了管理复杂度 。

选择继承方式

选择单继承

  • 原因:单继承简单明了,避免了多重继承的复杂性和潜在问题。在设计新的编程语言时,为了保证代码的易读性和可维护性,单继承是一个更好的选择。

  • 实现方式:通过接口和组合来实现多重行为。接口提供了多态性,而组合则允许类包含其他类的实例,以复用功能。

17年

一、简述多态的含义以及四种表现形式。

答案参考22年第一题

二、代码复用的形式,各自的优缺点,应用场景,举例说明。

1. 继承(Inheritance)

描述:通过继承,子类可以复用父类的属性和方法。

优点:简化代码,减少重复。子类可以增强或修改父类的功能。

缺点:强耦合,子类依赖父类的实现。难以更改父类而不影响子类。

应用场景:当多个类具有相同的行为或属性时使用。

2. 组合(Composition)

描述:通过将一个对象包含在另一个对象中,实现代码复用。

优点:松耦合,易于维护和扩展。动态组合对象的行为。

缺点:需要更多的代码来委托方法调用。

应用场景:需要在运行时动态改变对象的行为。

3. 接口(Interfaces)

描述:通过定义接口,实现类必须实现这些接口的方法,从而实现代码复用。

优点:提供多态性和灵活性。接口实现是独立的,易于扩展。

缺点:增加了设计和实现的复杂性。

应用场景:需要定义一组行为,而不关心具体的实现。

4. 泛型(Generics)

描述:通过泛型,可以创建类型安全的集合类和方法,适用于多种数据类型。

优点:代码复用性高,类型安全。提高代码的灵活性和可维护性。

缺点:复杂性增加,泛型的使用需要更多的理解和学习。

应用场景:需要处理不同类型的数据,并保持类型安全。

三、8种继承方式的含义以及为什么不提倡其中的某几种。

答案参考20年第一题

四、写出执行结果

class List{
​
public void method1(){}
​
public void method2(){}
​
public int method3(int element){}
​
public void method4(int element){}
​
public int method5(){}
​
}
​
class Parent {public Parent() {
​
System.out.println("Parent");
​
}
​
}
​
class Child {
​
public Child(String s) {
​
System.out.println(s);
​
}
​
}
​
public class Test {
​
public static void main(String []args) {
​
Child c = new Child("Child");
​
}
​
}

五、写出运行结果并说明理由

public class Animal {
​
public void speak(Animal p) {
​
System.out.println("Animal Speak!");
​
}
​
}
​
public class Dog extends Animal {
​
public void speak(Animal p) {
​
System.out.println("汪!");
​
}
​
public void speak(Dog t) {
​
System.out.println("汪汪");
​
}
​
}
​
Animal p2 = new Animal(); //注意p1和p2的顺序,试卷上跟老师讲的顺序换了。
​
Animal p1 = new Dog();
​
Dog p3 = new Dog();p1.speak(p1);
​
p1.speak(p2);
​
p1.speak(p3);
​
p2.speak(p1);
​
p2.speak(p2);
​
p2.speak(p3);
​
p3.speak(p1);
​
p3.speak(p2);
​
p3.speak(p3);

答案:

汪!

汪!

汪!

Animal Speak!

Animal Speak!

Animal Speak!

汪!

汪!

汪汪

16年

  1. 比较重定义和改写的相同点和不同点,在方法绑定时候的区别。

    重定义(Redefinition)

    定义:重定义是指在子类中重新定义父类中已有的方法,但不改变其签名(即方法名称、参数类型和返回类型)。

    特点

    • 重定义的方法在子类中会覆盖父类的方法,但只在子类的上下文中有效。

    • 父类对象调用该方法时,仍然使用父类的实现。

    改写(Overriding)

    定义:改写是指子类提供对父类方法的具体实现。改写的方法在子类中与父类方法具有相同的签名。

    特点

    • 改写的方法在子类中覆盖父类的方法,且子类对象会使用改写后的实现。

    • 改写的方法需要使用 @Override 注解来标识。

    相同点和不同点

    相同点

    • 都是子类对父类方法的一种重新定义。

    • 都可以在子类中提供不同的实现。

    不同点

    • 重定义:不改变方法签名,仅在子类上下文中有效。

    • 改写:需要使用 @Override 注解,子类对象总是使用改写后的实现。

    方法绑定的区别

    静态绑定

    • 发生在编译期。

    • 用于静态方法、私有方法和 final 方法。

    • 无论对象的实际类型如何,方法调用都是根据引用类型决定的。

    动态绑定

    • 发生在运行期。

    • 用于实例方法。

    • 方法调用根据对象的实际类型决定。

  2. 用代码举例说明this的多态性。

this 的多态性示例

在面向对象编程中,this 关键字用于引用当前对象。当在子类中调用父类的方法时,this 会表现出多态性,指向实际调用方法的对象。下面通过一个示例代码来说明 this 的多态性。

示例代码
// 父类
class Animal {
 public void makeSound() {
     System.out.println("Animal makes a sound");
     callThis();
 }
​
 public void callThis() {
     System.out.println("Animal's callThis method");
 }
}
​
// 子类
class Dog extends Animal {
 @Override
 public void makeSound() {
     System.out.println("Dog barks");
     super.makeSound(); // 调用父类的方法
 }
​
 @Override
 public void callThis() {
     System.out.println("Dog's callThis method");
 }
}
​
// 测试类
public class Main {
 public static void main(String[] args) {
     Animal myDog = new Dog();
     myDog.makeSound();
 }
}
代码解释
  1. 类定义

    • Animal 类是父类,定义了 makeSoundcallThis 方法。

    • Dog 类继承自 Animal 类,重写了 makeSoundcallThis 方法。

  2. 方法调用

    • Dog 类的 makeSound 方法中,调用了 super.makeSound(),这会调用 Animal 类的 makeSound 方法。

    • Animal 类的 makeSound 方法调用了 callThis() 方法。

  3. 多态性表现

    • Animal 类的 makeSound 方法中,this.callThis() 实际上调用的是 Dog 类中的 callThis 方法,因为 this 指向的是 Dog 类的实例。

输出结果
Dog barks
Animal makes a sound
Dog's callThis method
输出解释
  • myDog.makeSound() 调用的是 Dog 类的 makeSound 方法,输出 Dog barks

  • super.makeSound() 调用的是 Animal 类的 makeSound 方法,输出 Animal makes a sound

  • Animal 类的 makeSound 方法中,callThis() 调用了 Dog 类的 callThis 方法,输出 Dog's callThis method

通过这个示例,可以看到 this 的多态性表现。在父类的方法中使用 this 调用的方法实际调用的是子类的实现,这说明了 this 的多态性。

3.用面向对象的语言说明替换。

替换原则(Liskov Substitution Principle)

定义

替换原则(Liskov Substitution Principle, LSP)是面向对象设计中的一个基本原则。其核心思想是:如果S是T的一个子类型,那么所有引用基类型T的程序都必须能够透明地使用类型S的对象而不改变其行为。

这意味着在软件系统中,使用基类的地方可以用其子类替换,且程序的功能和行为不受影响。替换原则确保了继承的正确性,使得子类可以扩展父类的功能而不破坏父类的功能。

示例代码

下面用Java代码展示替换原则的应用:

// 基类
class Bird {
 public void fly() {
     System.out.println("Bird is flying");
 }
}
​
// 子类
class Sparrow extends Bird {
 @Override
 public void fly() {
     System.out.println("Sparrow is flying");
 }
}
​
// 子类
class Penguin extends Bird {
 @Override
 public void fly() {
     // 企鹅不能飞,所以重写的方法可能不做任何事,或者抛出异常
     throw new UnsupportedOperationException("Penguin can't fly");
 }
}
​
// 测试替换原则
public class Main {
 public static void letBirdFly(Bird bird) {
     bird.fly();
 }
​
 public static void main(String[] args) {
     Bird myBird = new Sparrow();
     letBirdFly(myBird); // 输出:Sparrow is flying
​
     Bird anotherBird = new Penguin();
     try {
         letBirdFly(anotherBird); // 可能抛出异常
     } catch (UnsupportedOperationException e) {
         System.out.println(e.getMessage()); // 输出:Penguin can't fly
     }
 }
}
代码解释
  1. 基类和子类

    • Bird 是基类,定义了 fly 方法。

    • SparrowBird 的子类,重写了 fly 方法,实现自己的飞行行为。

    • Penguin 也是 Bird 的子类,但企鹅不能飞,所以 fly 方法抛出 UnsupportedOperationException 异常。

  2. 方法调用

    • letBirdFly 方法接受 Bird 类型的参数,并调用其 fly 方法。

    • main 方法中,letBirdFly 被调用两次,一次传入 Sparrow 对象,一次传入 Penguin 对象。

  3. 替换原则的应用

    • Sparrow 对象替换 Bird 对象,letBirdFly 方法正常工作,输出 Sparrow is flying

    • Penguin 对象替换 Bird 对象,letBirdFly 方法调用时抛出异常,捕获并处理异常,输出 Penguin can't fly

通过这个示例,可以看到替换原则的应用:在程序中使用基类引用 Bird 时,可以透明地替换为其子类 SparrowPenguin 对象,程序行为符合预期。这展示了继承的正确性和多态性的强大功能。

4.为什么用组合服复用而不是继承复用。

二者区分详见20年第5题答案

根据《面向对象编程导论》第14章,组合优于继承的主要原因在于组合提供了更大的灵活性、低耦合和更高的复用性 。

设计语言示例

如果设计一种新的编程语言,我会选择组合而非继承作为主要的代码复用方式。理由如下:

  1. 灵活性:组合可以在运行时动态改变对象的行为,提供更高的灵活性。

  2. 低耦合:减少类之间的依赖,提高代码的可维护性。

  3. 高复用性:通过组合多个组件,可以实现更高的代码复用。

5.Object Animal Plant。给出不同签名问执行哪一个方法并给出理由,参考课本91页dessert甜点的

例子。

二、设计题(60分/3*20分)

22年

(1) 轨迹球与鼠标类似,可以通过触摸旋转轨迹球来改变屏幕上光标的位置。有一个设计人员将轨迹球类继承自鼠标类,实现代码复用。此设计人员的设计是否合理?给出你的理由。如果不合理,请重新设计你的解决方案,给出UML类图并写出代码框架。

(问这个方法违反了哪个设计原则,应该怎么修改,画出修改后的类图并写出代码框架)

(2) 电视机工厂可以生产电视机。如海尔工厂可以生产海尔电视机,海信工厂可以生产海信电视机。现在需要对电视机工厂进行解耦,希望当生产一个新的品牌的电视机时,只需要新增一个生产它的工厂。请指明使用的设计模式,给出类图设计以及代码框架。

(请给出设计模式,满足在不修改上述类的前提下就可以增加生产电视机的工厂,并画出类图和写出代码框架。)

(如果后期还有新添加的品牌,只需要添加新的相应的工厂即可。应该使用哪一个模式,画出类图并写出代码框架。)

(3) 房地产公司想要对自己的业务进行优化。

房子有不同的类型,如别墅、公寓等。将来可能还会新增不同的类型。 销售人员销售房屋,当每销售一套房屋时,经理可以收到相关的消息。 针对以上两种情况,请你分别指出可能用到的设计模式,并给出相关的类图设计。

(如果后期还有新添加的品牌,只需要添加新的相应的工厂即可。应该使用哪一个模式,画出类图并写出代码框架。)

img

21年

1.FileName类来描述文件名,继承自String,问这样的设计是否合理,如果合理请说出理由,如果不合理违反了什么原则,并写出改进方案(原题)

.题目给出了一个UML图,里面描述了一个FileName类,继承String类。问这种方法违反了哪个设计原则,应该如何改进,画出改进后的类图

在这里插入图片描述

答案:

在这里插入图片描述

在这里插入图片描述

2.开关控制灯 (1)要求改成能控制灯管、灯泡,画UML图,并说明改进后的优点(要求改进以上结构,设计一个开关类,这个开关类可以实现对灯泡和灯管都进行开启和关闭的操作,画出类图) (2)要求还能控制电视、空调等电器,给出设计方案,画UML图,写出代码框架 (原题)(进一步拓展,要求这个开关类不仅能实现对电灯的控制,还能实现对电视,电冰箱,空调的控制,画出对应的类图,并写出关键代码,要求满足DIP原则)

在这里插入图片描述

类图大致如上,SwitchToggle里还有一个构造方法没画上

在这里插入图片描述

3.Shape类,能产生圆形、矩形、三角形三种形状,还希望能够给他们加不同的颜色和线框 (1)问用什么模式,画UML图,写代码框架 (2)产生各种不同的图形并把他们放入到一个集合中,再依次输出,写出代码片段,要求符合DIP原则

(3.有一个计算机程序,可以实现绘图功能。现有一个Shape类,要对圆,三角形,矩形实现填充颜色和描绘轮廓的方法

(1)使用什么模式实现?画出对应的类图,写出关键代码 (2)将圆,三角形,矩形(具体的图形类)放入一个链表中,并打印输出)

4.股票价格超过原来的5%就发送消息给所有股民(包括价格),股民手里没有股票的时候就删除他,问是用什么设计模式,画UML图,写出代码框架和发送信息的详细代码 (买股票,如果一个人买了一支股票,而这支股票的价格变动超过5%,就向所有股民发送消息,如果一个人将一支股票卖掉了,那么他不会收到消息。问用什么设计模式实现,要求画出类图和关键代码(发送消息的代码))

20年

1.

image-20240626191013117

(哪个以P开头的Food忘记怎么拼了,我也不认识那个单词)

有以下⽅法(我就⽤⾸字⺟代替了)

order(F,V)

order(P,F)

order(P,C)

order(R,V)

有相关的⼀系列对象,判断⼀下⽅法执⾏哪个,并说明理由

order(aF,aC)

order(aR,aF)

order(aF,aF)order(aR,aC)

2.写代码。

Sparrow有fly⽅法,Cat有run⽅法,这两个类不能改写,不能继承,

要求Sparrow和Cat对象可以放到⼀个列表中,并统⼀使⽤move⽅

法。

3.根据依赖导致原则进行重构,画类图

image-20240626191113366

a.重构灯泡开关,原来是一个开关管一个白炽灯,现在变成多种灯如日光灯、LED灯。

b.增加电器,同样用开关来管

(1.开关不仅能控制⽩炽灯,还能控制⽇光灯、LED灯等,进⾏重

构。画类图,说明设计优点。

2.再进⼀步扩展。开关不仅能控制灯,还能控制热⽔器、空调、电

视等,进⾏重构。画类图,说明设计优点。)

4.房地产卖房子,其中设计不同房型、不同客户装修不同等,问用什么设计模式,画类图,说设计模式的优点。

桥梁模式、观察者模式、简单工厂模式。

(针对以下场景,选择设计模式、画类图、说明优点。

1.公司要卖不同的房型

2.房屋卖出去,不同经理会收到消息

3.⽤户买房之后,⾃⼰要装修,刷墙、铺地板等)

5.电影院打折问题:策略模式,画类图、写代码、说优点。

(电影院售票系统

学⽣有学⽣票⼋折、⼉童票价减⼗、VIP会员减半还有积分制度。

系统可能还会采⽤更多的策略。

选择设计模式、画类图、写优点、写代码)

19年

1.【把继承改成组合的适配器模式?】Swan用fly()移动,Otrrich用run()移动,Penguin用swim()移动,这三个类不能修改、不能继承,问如何把三个类变成一个类加入链表且统一用move()移动

(设计模式适配器的题,大概类似 Sparrow类有方法fly使其移动,Penguin类有方法swim使其移动,不允许修改这两个类,将Sparrow和Penguin对象放入一个只能容纳一个类的列表list中,然后统一使用move方法使其移动,写出代码。)只是题目说了,不能用继承,而且给了三个不同的类。

2.观察者模式,画UML 图。

(老师笨死了,图片上就有observers= =,考试时还提醒我们划掉666)【观察者模式】:有数据类、界面类:表格、柱状图、饼状图。要求1、界面类间互相不知道对方存在;2、从一个界面修改了数据另外其他的界面同时修改

3.工厂模式,画UML图

【工厂方法模式】:ImageReader接口,要能生产:GifImageReader、JpegImageReader、TiffImageReader

4【策略模式】有三种书:计算机书九折、小说书满100减10、语言书优惠2元,有书店卖书 问:什么模式、画uml图、写代码

(书店卖书,有不同的销售策略,让分析用了什么设计原则,然后画UML图,写代码框架

只记得这些了 其他不记得了)

18年

1.有一个List类,代码如下:

class List{

public void method1(){}

public void method2(){}

public int method3(int element){}

public void method4(int element){}

public int method5(){}

}

(1)List有一个集合子类Set,有三个方法method2,method3,method5.请分别用继承和组合的方法写出Set的代码

(2)如果让你来选择,你会选择哪种方法来创建Set类,为什么?

2.商品在通过海关时,针对不同国家的有不同的税费收费标准,如果按照普通的设计思路,不符合开闭原则,请选择一种设计模式,并画出UML类图,并结合开闭原则谈谈这样设计有什么好处?

3.在公司中,员工报销需要上报领导审批,不同级别的领导审批权限不同,超过一定的金额就需要上报上级领导审批。在公司中有:主任,权限范围内审批金额<2000元(不包含2000);副董事,审批金额2000-10000(不包含10000);董事,审批金额10000-20000(不包含20000),当金额大于20000时,需要开会商议。请用责任链设计模式解决这个问题,画出类图,并写出主要代码框架。

4.浙江服装厂出售衣服,衣服包含衬衫,T恤,裤子;每件衣服的面料有纯棉,莱卡,亚麻;支付方式有银联支付,京东白条,货到付款。为了尽量减少类的数量,请选择一种设计模式,画出类图,并写出每个变化点一个子类的代码框架。

17年

六、Sparrow类有方法fly使其移动,Penguin类有方法swim使其移动,不允许修改这两个类,将

Sparrow和Penguin对象放入一个只能容纳一个类的列表list中,然后统一使用move方法使其移动,写

出代码。

七、食堂有红豆豆浆、黄豆豆浆、黑豆豆浆等五种豆浆(具体哪五种忘了),有小杯、中杯、大杯、超

大杯四种杯子规格,顾客可以根据需要自己添加盐或者糖两种调料,为了尽可能减少类的种类,采用哪

两种设计模式并画出类图。

八、不同继承层次object animal plant cat rose,方法重载,写出应该调用哪个方法。类似课本223页

dessert的例子。九、有power类,里面有getPower(long base, long exp)方法来计算base的exp次幂,现在一个系统中

有一个target接口,这个系统要用来计算一个数的平方,应该采用何种设计模式来复用power类的代

码?画出类图并写出代码框架。

三、补充代码(10/20分)

22年

装饰者模式的代码补全题目。题目的应用场景为页框、页面和叶底。

在这里插入图片描述

题目改编自下例:假设需要打印发票 sales ticket , 发票有抬头、正文和脚注。

发票问题,ppt原题改编
​
ppt题目:
​
假设你需要打印发票 sales ticket , 发票有抬头、正文和脚注,发票抬头可以是企事业单位,发票号等等,脚注也是一样,可能有很多不同种类的脚注需要打印。如果发票格式固定那也就没必要继续讨论了,现在的问题是,不同的客户需要的发票或者收据的抬头或脚注,他们需要的条目是不一样的,有的需要著明单位,有的只需要发票号,但是脚注需要开票人, 等等,对你来说跟现在的 Web 系统一样,客户的要求是动态;不过发票的正文是不会变化的,是固定的.
​
​
如果你的发票格式为:
SalesTicket Header1
SalesTicket Body
SalesTicket Footer1
         
那么你可以这样去创建对象:
new Header1(new Footer1(new SalesTicket()));
         
如果你的发票格式为:
SalesTicket Header1          
SalesTicket Header2          
SalesTicket Body          
SalesTicket Footer1 
         
那么你可以这样去创建对象:
new Header1(new Header2(new Footer1(new SalesTicket())));
​
​
​
​
// 原题代码回忆
class Invoice {
    protected Invoice ticket; // 有个是定义了ticket变量
​
    public void printInvoice() {
        System.out.println("This is the content of the invoice!");
    }
}
​
class Decorator extends Invoice {
    public Decorator(Invoice t) {
        ticket = t;
    }
​
    public void printInvoice() {
        if (ticket != null) {
            // 填写区域1:ticket.printInvoice();
            ticket.printInvoice();
        }
    }
}
​
class HeadDecorator extends Decorator {
    public HeadDecorator(Invoice t) {
        super(t);
    }
​
    public void printInvoice() {
        System.out.println("This is the head of the invoice!");
        // 填写区域2:ticket.printInvoice();
        ticket.printInvoice();
    }
}
​
class FootDecorator extends Decorator {
    public FootDecorator(Invoice t) {
        super(t);
    }
​
    public void printInvoice() {
        // 填写区域3:ticket.printInvoice();
        ticket.printInvoice();
        System.out.println("This is the foot of the invoice!");
    }
}
​
public class Test {
    public static void main(String[] args) {
        Invoice t = new Invoice();
        Invoice ticket;
        ticket = new HeadDecorator(new FootDecorator(new Decorator(t)));
        // 填写区域4: new HeadDecorator(new FootDecorator(new Decorator(t)));
        ticket.printInvoice();
        System.out.println("-----------------------");
        ticket = new HeadDecorator(new FootDecorator(new Decorator(null)));
        // 填写区域5:new HeadDecorator(new FootDecorator(new Decorator(null)));
        ticket.printInvoice();
    }
}
​
// 考试时FootDecorator和HeadDecorator继承自Decorator,Decorator继承自Invoice. 注意细节即可

21年

策略模式,模拟赛车轮胎的轨迹花纹,代码填空,一共五个空,都很短,每个空4’ (要求补全代码,使用策略模式。

题目的背景是给了一个Car类,这个Car类有两个子类。然后给了一个策略接口,策略类有两个对应的子类。这些类全部以类图的方式呈现

题目已经给出了部分代码,要求补全策略类接口的代码,还有Car类的代码,总共有5空,一空4分)

19年

1、补全八皇后的全部check()方法、全部print()方法 2、(就三四句,补全关键代码,就是需要用到其他类的代码,【注释很有用】) Image接口有三个实现:GIFImage、JpegImage、TiffImage;(图片) Implementor有两个实现:WindowImp、LinuxImp;(能显示矩阵) 有工具类Matricx:(把图片变矩阵) (桥模式,往空里添代码)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值