Java面试基础:面向对象(1)
1. 怎么理解面向对象以及其三大特性
面向对象是一种编程范式,它将现实世界中的事物抽象为对象,对象具有属性 (称为字段或属性) 和行为 (称为方法)。
面向对象编程的设计思想是以对象为中心,通过对象之间的交互来完成程序的功能,具有灵活性和可扩展性,通过封装和继承可以更好地应对需求变化。
Java面向对象的三大特性:封装、继承和多态。
- 封装:封装是指将对象的属性 (数据) 和行为 (方法) 结合在一起,对外隐藏对象的内部细节,对象提供的接口与外界交互。封装的目的是增强安全性和简化编程,使得对象更加独立。
- 继承:继承是一种可以使得子类自动共享父类数据结构和方法的机制。它是代码复用的重要手段,通过继承可以建立类与类之间的层次关系,使得结构更加清晰。
- 多态:多态是指允许不同类的对象对同一消息作出响应。即同一个接口,使用不同的实例而执行不同操作。多态性可以分为编译时多态 (重载) 和运行时多态 (重写)。它使得程序具有良好的灵活性和扩展性。
2. 多态体现在哪些方面,多态解决了什么问题
1) 方法重载:指同一类中可以有多个同名方法,它们具有不同的参数列表(参数类型、数量或顺序不同)。虽然方法名相同,但根据传入的参数不同,编译器会在编译时确定调用哪个方法。
例如:对于一个 add 方法, 可以定义一个传入 int,一个传入 double。
int add (int a, int b){}
double add(double a, double b){}
2) 方法重写:指子类能够提供对父类中同名方法的具体实现。在运行时,JVM会根据对象的实际类型确定调用哪个版本的方法。这是实现多态的主要方式。
例如:父类中有一个 eat 方法,而 dog 子类可以重写该方法实现 “吃狗粮”, cat 子类可以重写该方法实现 “吃猫粮”。
3) 接口与实现(implement):多态也体现在接口的使用上,多个类可以实现同一个接口,并且用接口类型的引用来调用这些类的方法。这使得程序在面对不同具体实现时保持一贯的调用方式。
例如:多个类 (如 Dog,cat) 都实现了一个 Animal 接口,当用类型的引用来调用 Animal 中的makesound方法时,会触发对应的实现。
4) 向上转型和向下转型:在Java中,可以使用父类类型的引用指向子类对象,这是向上转型。通过这种方式,可以在运行时期采用不同的子类实现。向下转型是将父类引用转回其子类类型,个
但在执行前需要确认引用实际指向的对象类型以避免 ClassCastException。
3. 重载和重写的区别
重载 (Overloading) 指的是在同一个类中,可以有多个同名方法,它们具有不同的参数列表(参数类型、参数个数或参数顺序不同)编译器根据调用时的参数类型来决定调用哪个方法。
重写 (Overriding) 指的是子类可以重新定义父类中的方法,方法名、参数列表和返回类型必须与父类中的方法一致,通过 @override 注解来明确表示这是对父类方法的重写。
重载是指在同一个类中定义多个同名方法,而重写是指子类重新定义父类中的方法。
4. 面向对象的设计原则有哪些
面向对象编程中的6大原则:
单一职责原则(SRP):一个类应该只有一个引起它变化的原因,即一个类应该只负责一项职责。
例如:考虑一个员工类,它应该只负责管理员工信息,而不应负责其他无关工作。
开放封闭原则(OCP):软件实体应该对扩展开放,对修改封闭。主要通过接口来实现。
例如:定义一个图形类,然后让不同类型的图形继承这个类,而不需要修改图形类本身。
里氏替换原则(LSP):子类对象应该能够替换掉所有父类对象。
例如:一个正方形是一个矩形,但如果修改一个矩形的高度和宽度时,正方形的行为应该如何改变就是一个违反里氏替换原则的例子。
接口隔离原则(ISP):客户端不应该依赖那些它不需要的接口,即接口应该小而专。
例如:通过接口抽象层来实现底层和高层模块之间的解耦,比如使用依赖注入。
依赖倒置原则(DIP):高层模块不应该依赖低层模块,二者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。
例如:如果一个公司类包含部门类,应该考虑使用合成/聚合关系,而不是将公司类继承自部门类。
最少知识原则(Law of Demeter):一个对象应当对其他对象有最少的了解,只与其直接的朋友交互。
5. 抽象类和普通类的区别
- 实例化: 普通类可以直接实例化对象,而抽象类不能被实例化,只能被继承。
- 方法实现: 普通类中的方法可以有具体的实现,而抽象类中的方法可以有实现也可以没有实现。
- 继承: 一个类可以继承一个普通类,而且可以继承多个接口;而一个类只能继承一个抽象类,但可以同时实现多个接口。
- 实现限制: 普通类可以被其他类继承和使用,而抽象类一般用于作为基类,:被其他类继承和扩展使用
6. Java中抽象类和接口的区别
首先我们要知道两者的特点:
- 抽象类用于描述类的共同特性和行为,可以有成员变量、构造方法和具体方法。适用于有明显继承关系的场景。
- 接口用于定义行为规范,可以多实现,有常量和抽象方法(Java8 以后可以有默认方法和静态方法),适用于定义类的能力或功能。
区别:
- 实现方式: 实现接口的关键字为 implements,继承抽象类的关键字为 extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
- 方法方式: 接口只有定义,不能有方法的实现,java1.8中可以定义 default 方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。
- 访问修饰符: 接口成员变量默认为 public static final,必须赋初值,不能被修改;其所有的成员方法都是 public、abstract 的。抽象类中成员变量默认 default,可在子类中被重新定义,也可被重新赋值;抽象方法被 abstract 修饰,不能被 private、static、synchronized 和 native 等修饰,必须以分号结尾,不带花括号。
- 变量: 抽象类可以包含实例变量和静态变量,而接口只能包含常量(即静态常量)。
7. 抽象类可以用final修饰吗
不可以,Java中的抽象类是用来被继承的,而 final 修饰符用于禁止类被继承或方法被重写。
因此,抽象类与 final 修饰符是互斥的,不能同时使用。