2023 年 30 个 OOP 面试问题和答案
面向对象编程(OOPs)是一种在程序中实现**对象概念的编程范式。它旨在通过在编程中实现继承、抽象、多态等现实世界中的实体,为现实世界中的问题提供更简便的解决方案。OOPs概念被广泛应用于Java、Python、C++等多种流行语言中。
OOPs也是编程面试中最重要的话题之一。本文包含了关于OOPs概念的一些顶级面试问题。
OOP 面试问题
1. 什么是面向对象编程(OOP)?
面向对象编程(也称为OOPs)是一种编程范式,其中完整的软件作为一组相互交流的对象运行。一个对象是一组数据和操作这些数据的方法。
2. 为什么使用OOPs?
OOP的主要优势是更易于管理的代码,具有以下特点:
- 增加了对软件整体理解,使开发人员与用户之间的语言差距缩小。
- 面向对象通过封装简化了维护工作。可以通过保持方法不变轻松地改变底层表示。
- OOP范式主要适用于相对较大规模的软件。
3. 什么是类?
类 是面向对象程序中的构建块。它是一个用户定义的数据类型,包含操作数据成员和成员函数。它类似于具有共同属性和方法的对象蓝图或模板。
4. 什么是对象?
对象 是类的实例。不能直接使用类的数据成员和方法。我们需要创建一个类(或实例)来使用它们。简单来说,它们就是具有状态和行为的真实世界实体。
例如:下面代码展示了如何创建一个类(即一个对象) 的示例
- C++
- Java
- Python3
- C#
c
#include <iostream>
using namespace std;
class Student{
private:
string name;
string surname;
int rollNo;
public:
Student(string studentName, string studentSurname, int studentRollNo){
name = studentName;
surname = studentSurname;
rollNo = studentRollNo;
}
void getStudentDetails(){
cout << "The name of the student is " << name << " " << surname << endl;
cout << "The roll no of the student is " << rollNo << endl;
}
};
int main() {
Student student1("Vivek", "Yadav", 20);
student1.getStudentDetails();
return 0;
}
Output
csharp
The name of the student is Vivek Yadav
The roll no of the student is 20
5. 面向对象编程的主要特点是什么?
面向对象编程(OOPs)的主要特点,也被称为4个支柱或基本原则如下:
- 封装
- 数据抽象
- 多态性
- 继承
OOPs 主要功能
6. 什么是封装?
封装是将数据和操作这些数据的方法绑定到一个单元中,以便将敏感数据隐藏起来,不让用户直接访问。它通过以下过程实现:
- 数据隐藏:
- Java
typescript
//封装的简单演示
//它有一个私有数据成员以及 getter 和 setter 方法。public class Student{
//private data members
private String name;
private int rollNo;
//访问名称的公共获取方法
public String getName(){
return name;
}
//访问 rollNo 的公共 getter 方法
public int getRollNo(){
return rollNo;
}
//设置名称的公共设置方法
public void setName(String name){
this.name=name
}
//设置 rollNo 的公共设置方法
public void setRollNo(int rollNo){
this.rollNo=rollNo;
}
}
7. 什么是抽象?
抽象类似于数据封装,在面向对象编程中非常重要。它意味着只展示必要的信息,并将其他不相关的信息隐藏起来,以便用户使用。抽象通过使用类和接口来实现。
- Java
csharp
//implementation of abstraction through abstract class
abstract class Animal {
abstract void walk();
void eat()
{
System.out.println("The animal is eating.");
}
Animal()
{
System.out.println(
"An Animal is going to be created.");
}
}
class Cow extends Animal {
Cow() { System.out.println("You have created a Cow"); }
void walk() { System.out.println("Cow is walking."); }
}
class Goat extends Animal {
Goat()
{
System.out.println("You have created a Goat");
}
void walk() { System.out.println("Goat is walking."); }
}
public class OOPS {
public static void main(String args[])
{
Cow cow = new Cow();
cow.walk();
cow.eat();
Goat goat = new Goat();
goat.walk();
goat.eat();
}
}
8. 什么是多态性?
"多态性"一词意味着具有许多形式。它是某些代码根据不同的上下文而表现出不同行为的属性。例如,在C++语言中,我们可以定义具有相同名称但根据上下文而有不同工作方式的多个函数。
根据对象或函数调用在何时解析,可以将多态性分为两种类型。它们如下:
A. 编译时多态性 B. 运行时多态性
A) 编译时多态性
编译时多态性,也称为静态多态性或早期绑定,是一种在编译时将调用与其代码绑定的类型的多样化。方法重载或运算符重载都是编译时多态性的示例。
B) 运行时多态性
也称为动态多样化或晚期绑定,运行时 多样化 是一种在运行时间 或执行过程中确定函数实际实现 的 多 样 化 。 方法覆盖 是这种方法 的 一个 示例 。
- Java
csharp
// An example of method overloading
class Student {
String name,surname;
int rollNo;
public void showStudentDetails(String name) {
System.out.println("The name of the student is " + name);
}
public void showStudentDetails(int rollNo) {
System.out.println("the roll no of the student is "+ rollNo);
}
public void showStudentDetails(String name, String surname, int rollNo) {
System.out.println(name);
System.out.println(surname);
System.out.println(age);
}
}
- Java
scala
// 方法规避的一个例子
class Student {
public void read() {
System.out.println("The student is reading");
}
}
class SchoolStudent extends Student {
public void read(String book) {
System.out.println("the student is reding "+ book);
}
}
class CollegeStudent extends Student {
public void read(String researchPaper , String labJournal) {
System.out.println("the student is reading "+researchPaper +" and "+ labJournal);
}
}
9. 什么是继承?它的目的是什么?
继承的概念很简单,一个类派生自另一个类,并使用那个其他类的数据和实现。被派生的类称为子类或派生类
- Java
scala
// 继承的一个例子
class Student {
public void read() {
System.out.println("The student is reading");
}
}
class SchoolStudent extends Student {
public void read(String book) {
System.out.println("the student is reding "+ book);
}
}
10. 访问修饰符是什么?它们在面向对象编程中的重要性是什么?
访问修饰符是一种特殊类型的关键字,用于指定或控制类、方法等实体的可访问性。私有(Private) 、公共(Public) 和受保护(Protected) 都是访问修饰符或访问限定词的示例。 面向对象编程中的关键组成部分------封装和数据隐藏,很大程度上得益于这些访问修饰符。
- Java
typescript
class User {
public String userName;
protected String userEmail;
private String password;
public void setPassword(String password) {
this.password = password;
}
}
public class OOPS {
public static void main(String args[]) {
User user1 = new User();
user1.userName = "Vivek_Kumar_Yadav";
user1.setPassword("abcd@12345");
user1.userEmail = "abc@gmail.com";
}
}
11. 面向对象编程(OOP)的优缺点是什么?
OOP 的优势 | OOP 的缺点 |
---|---|
OOPs 提高了代码的可重用性。 | 程序员应该具备良好的技能,并且在面向对象方面有出色的思维,因为在面向对象编程中一切都被视为对象。 |
代码更容易维护和更新。 | 适当的规划是必要的,因为面向对象编程有点棘手。 |
它通过限制数据访问和避免不必要的暴露,提供更好的数据安全性。 | OOP的概念并不适用于所有类型的问题。 |
快速实施和易于重新设计,从而最大程度地减少整体程序的复杂性。 | 程序的长度与过程化方法相比要大得多。 |
12. 除了面向对象编程(OOP),还有哪些其他的编程范式存在?
编程范式是指编写程序的技术或方法。编程范式可以分为以下几种类型:
1. 命令式编程范式
它是一种通过赋值语句改变程序状态的编程范式。这种范式的主要关注点在于如何实现目标。以下编程范式属于此类别:
0. 过程化编程范式:该编程范式基于过程调用概念。在此范例中,过程(也称为例行程序或函数)是程序的基本构建块。
- 面向对象编程或OOP:在这个范例中,我们将每个实体视为一个对象,并试图根据该对象的状态和行为来组织程序结构。
- 并行编程:并行编程模型通过将指令分成多个较小部分并同时执行它们来进行处理。
2. 声明性编程范式
声明性编码侧重于执行内容而不是如何执行内容。在这种模型中,我们表达计算逻辑而不考虑其控制流。声明性模型可以进一步分类为:
0. 逻辑化编码模型:它基于形式逻辑,在其中程序语句以逻辑形式表达问题的事实和规则。
- 函数化编码模型:在这种模型中,通过应用和组合函数来创建程序。
- 数据库化编码模型:数据库化的代码模型被用于管理以字段、记录和文件形式组织的数据和信息。
13. 结构化编程和面向对象编程之间有什么区别?
结构化程序设计(Structured Programming)是一种被视为 OOP 先驱的技术,通常由结构良好且分离的模块组成。它是程序设计的一个子集。OOP 与结构化编程的区别如下:
面向对象编程 | 结构编程 |
---|---|
面向对象的编程建立在具有状态和行为的对象之上。 | 一个程序的逻辑结构是由结构化编程提供的,它将程序分解为相应的函数。 |
它采用自下而上的方法。 | 它遵循自上而下的方法。 |
限制数据的开放流动,仅向授权部分提供更好的数据安全。 | 没有对数据流的限制。任何人都可以访问数据。 |
由于多态性和继承的概念,代码的可重用性得到了增强。 | 代码的可重用性是通过使用函数和循环来实现 |
在这种情况下,方法是全局编写的,代码行逐行处理,即按顺序运行。 | 在这种情况下,该方法以动态方式工作,根据代码的需要进行调用一段时间。 |
修改和更新代码更容易。 | 修改代码相对于面向对象编程来说是困难的。 |
数据在面向对象编程中更加重要。 | 代码被赋予更多的重要性。 |
14. 一些常用的面向对象编程语言有哪些?
OOPs范式是最流行的编程范式之一。它广泛应用于许多流行的编程语言,例如:C++
, java
python
javascript
c#
Ruby
15. 什么是多态的不同类型?
-
多态性可以根据调用对象或函数的解析时间分为两种类型。它们如下:
- 编译时多态性
- 运行时多态性
多态的类型
A) 编译时多态性
编译时多态性,也称为静态多态性或早期绑定,是一种在编译时将调用与其代码绑定的多态性。方法重载或运算符重载是编译时多态性的示例。
B) 运行时多态性
也称为动态多态性或晚期绑定,运行时多态性是一种在运行时或执行过程中确定函数实际实现的类型。方法覆盖是这种方法的一个例子。
16. 重载和覆盖之间有什么区别?
名为重载的编译时多态特征允许一个实体具有相同名称但不同实现方式的众多版本。方法重载和操作符重载都是两个例子。
覆盖是一种形式的运行时多态,在其中以相同名称但不同实现方式执行一个实体。它借助虚函数来实现。
17. 有关继承是否存在任何限制吗?
当你拥有更多权威时会面临更多挑战。虽然继承是一种非常强大的面向对象编程特性,但它也有显著的缺点。
- 由于必须经过几个类来实现,继承需要更长时间进行处理。
- 基类和子类都参与继承,并且彼此之间关系密切(称为紧耦合)。因此,如果需要进行更改,则可能需要同时在两个类中进行修改。
- 实施继承可能也很困难。因此,如果没有正确实施,可能会导致意想不到的错误或不准确的输出。
18. 有哪些不同类型的继承?
继承可以分为以下5种类型:
- 单继承: 子类直接从基类派生
- 多重继承: 子类派生自多个基类。
- 多级继承: 子类派生自一个同时也派生自另一个基类的类。
- 层次继承: 多个子类派生自一个基类。
- 混合继承: 继承由上述指定的多种继承类型组成。
- Java
scala
// 单继承的一个例子
class Father {
// 父亲的任何特定属性和功能
}
class Son extends Father {
//继承了父类的属性和函数
}
- Java
scala
// 一个层次继承的示例
class Father {
// 任何特定于父亲的属性和函数
}
class Son extends Father {
//继承了父类的属性和函数
}
class Daughter extends Father {
//继承了父类的属性和函数
}
- Java
scala
// 多级继承的一个例子
class Father {
// 父亲的任何特定属性和功能
}
class Son extends Father {
//继承父亲的属性和功能
}
class GrandChild extends Son {
}
19. 什么是 interface?
一种独特的类类型被称为接口,它包含方法但不包含其定义。在接口内部,只允许进行方法声明。你不能使用接口创建对象。相反,你必须将该接口投入使用,并指定执行此操作的步骤。
20. 抽象类与接口有何不同?
抽象类和接口都是特殊类型的类,它们只包含方法的声明而不包含实现。尽管如此,抽象类与接口完全不同。以下是抽象类和接口之间的一些主要区别:
抽象类 | Interface |
---|---|
当一个抽象类被继承时,子类并不需要在实际使用之前提供抽象方法的定义。 | 当一个接口被实现时,子类需要指定所有接口的方法以及它们的实现。 |
一个抽象的类可以同时拥有抽象方法和非抽象方法。 | 一个接口只能拥有抽象方法。 |
一个抽象类可以拥有final、非final、静态和非静态变量。 | 接口只有静态和最终变量。 |
抽象类不支持多重继承。 | 一个接口支持多重继承。 |
21.一个类占用多少内存?
类不使用内存。它们只是作为创建项目的模板而存在。现在,当对象被创建时,它们实际上会初始化类的成员和方法,并在此过程中使用内存。
22. 从类中创建对象是否总是必要的么?
不需要。 如果基类包含非静态方法,则必须构造一个对象。但是,如果类包含静态方法,则不需要生成对象。在这种情况下,您可以使用类名直接调用那些静态方法。
23. 在C++中,结构体和类之间有什么区别?
在C++中,结构体和类之间有什么区别?
- 结构体和类之间的主要区别在于,在结构体中,成员默认为公共访问级别,而在类中,默认为私有访问级别。
- 另一个区别是,在 C++ 中,我们使用 struct 来声明结构,而使用 class 来声明类。
24. 什么是构造函数?
构造函数是一段代码块,用于初始化新创建的对象。构造函数类似于实例方法,但它不是一个方法,因为它没有返回类型。通常情况下,构造函数与类名相同,但在某些语言中可能会有所不同。例如:
在Python中,构造函数被命名为init。
在C++和Java中,构造函数的名称与类名相同。
Example:
- C++
arduino
class Student {
String name;
String surname;
int rollNo;
Student()
{
cout<< "contructor is called";
}
}
- java
arduino
class Student {
String name;
String surname;
int rollNo;
Student()
{
System.out.println("contructor is called");
}
}
- Python
python
class base:
def __init__(self):
print("This is a constructor")
25. C++中有哪些不同类型的构造函数??
C++中有哪些不同类型的构造函数?
- 默认构造函数
- 非参数化构造函数
- 参数化构造函数
- 复制构造函数
1. 默认构造函数
默认构造函数是一个不接受任何参数的构造函数。它是一个非参数化的构造函数,在没有提供显式构造函数定义时,由编译器自动定义。
它将数据成员初始化为它们的默认值。
2. 非参数化构造函数
它是一个没有参数的用户定义构造函数。
Example:
- C++
arduino
class Student {
String name;
String surname;
int rollNo;
Student()
{
cout << "Non-parameterized contructor is called" ;
}
}
- Java
arduino
class Student {
String name;
String surname;
int rollNo;
Student()
{
System.out.println("Non-parameterized contructor is called");
}
}
- Python3
python
class base:
def __init__(self):
print("This is a non-parameterized constructor")
3. 参数化构造函数
The constructors that take some arguments are known as parameterized constructors.
Example:
- C++
arduino
class Student {
String name;
String surname;
int rollNo;
Student(String studentName, String studentSurname, int studentRollNo)
{
cout << "Constructor with argument is called";
}
}
- Java
arduino
class Student {
String name;
String surname;
int rollNo;
Student(String studentName, String studentSurname, int studentRollNo)
{
System.out.println("Constructor with argument is called");
}
}
- Python3
python
class base:
def __init__(self, a):
print("Constructor with argument: {}".format(a))
4. 复制构造函数
一个复制构造函数是一个成员函数,它使用同一类的另一个对象来初始化一个对象。
Example:
- C++
ini
class Student {
String name, surname; int rollNo;
Student(Student& student) // copy constructor
{
name = student.name;
surname=student.surname;
rollNo= student.rollNo;
}
}
- Java
ini
class Student {
String name, surname; int rollNo;
Student(Student student) // copy constructor
{
this.name = student.name;
this.surname=student.surname;
this.rollNo= student.rollNo;
}
}
在Python中,我们没有像Java和C++那样的内置复制构造函数,但是我们可以使用不同的方法来实现一个解决方案。
26. 什么是析构函数?
析构函数是一种在对象超出范围或被销毁时自动调用的方法。
在C++中,析构函数的名称与类名相同,但前缀为(~)波浪线符号。
在Python中,析构函数的名称为del。
Example:
- C++
csharp
class base {
public:
~base() { cout << "This is a destructor"; }
}
- Python3
python
class base:
def __del__(self):
print("This is destructor")
在Java中,垃圾收集器会自动删除无用的对象,因此在Java中没有析构函数的概念。我们可以使用finalize()方法作为Java析构函数的替代方法,但自Java 9起该方法也已被弃用。
27. 我们可以在一个类中重载构造函数吗?
我们可以在类中重载构造函数。事实上,默认构造函数、带参数的构造函数和拷贝构造函数都是构造函数的重载形式。
28. 我们可以在一个类中重载析构函数吗?
不。在一个类中不能重载析构函数。一个类中只能有一个析构函数存在。
29. 什么是虚函数?
虚函数是用于在派生类中覆盖父类方法的函数。它用于提供类的抽象。
在C++中,使用virtual关键字声明虚函数。
在Java中,每个公共、非静态和非最终方法都是虚函数。
Python方法始终是虚拟的。
Example:
- C++
csharp
class base {
virtual void print()
{
cout << "This is a virtual function";
}
}
- Java
csharp
class base {
void func()
{
System.out.printIn("This is a virtual function")
}
}
- Python3
python
class base:
def func(self):
print("This is a virtual function")
30. 什么是纯虚函数?
一个纯虚函数,也被称为抽象函数,是一个不包含任何语句的成员函数。如果需要,这个函数会在派生类中进行定义。
Example:
- C++
csharp
class base {
virtual void pureVirFunc() = 0;
}
- Java
csharp
abstract class base {
abstract void prVirFunc();
}
在Python中,我们使用ABC(抽象基类)模块的@abstractmethod来实现这一点。