面向对象三大特性(封装、继承、多态)
一、封装(Encapsulation)
概念:
将数据(属性)和对数据的操作(方法)包装在类中,对外隐藏实现细节,只暴露必要的访问接口。
核心思想:“藏”(隐藏内部细节)和**“控”**(控制外部访问)。
使用场景:
-
保护敏感数据(如用户密码、账户余额)。
-
防止外部直接修改对象内部状态(如年龄不能为负数)。
-
统一数据校验逻辑(如邮箱格式验证)。
示例:
public class BankAccount {
// 私有属性:外部无法直接访问
private String accountNumber;//银行账号
private double balance;//余额
// 公开方法:控制访问和修改
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
}
}
// 仅暴露余额查询接口
public double getBalance() {
return balance;
}
}
注意事项:
使用 private 修饰符保护属性。
通过 public 的 getter/setter 方法提供可控访问。
避免过度封装(如所有属性都写 getter/setter,但无实际校验逻辑)。
二、继承(Inheritance)
1.概念:
子类继承父类的属性和方法,实现代码复用,并允许扩展新功能。
核心思想:“复用”(复用父类代码)和**“扩展”**(子类添加新特性)。
使用场景:
-
多个类有共同属性和方法时(如动物 → 猫、狗)。
-
需要实现多态特性时。
-
构建分层系统(如电商系统中的用户 → 普通用户、管理员)。
示例:
// 父类:动物
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
// 子类:狗(继承Animal)
class Dog extends Animal {
// 扩展新方法
public void bark() {
System.out.println("狗在叫");
}
// 重写父类方法
@Override
public void eat() {
System.out.println("狗在啃骨头");
}
}
注意事项:
- 优先使用组合(has-a)而非继承(is-a),避免过度继承导致代码耦合。
- Java 是单继承(一个类只能继承一个父类)。
- 子类通过 super 调用父类方法或构造器。
- 避免“继承滥用”(如让“汽车”继承“空调”)。
2.super,以及super():
super关键字允许子类访问父类的成员,包括字段、方法和构造函数。它主要用于解决子类和父类之间的命名冲突,或者在子类中调用父类的方法或构造函数。
使用场景
Ⅰ.调用父类的构造函数
在子类的构造函数中,super()可以显式地调用父类的构造函数。这是初始化继承自父类的成员变量的常用方法。如果省略super()调用,则默认调用父类的无参构造函数(如果存在)。
class Parent {
Parent() {
System.out.println("Parent constructor called");
}
}
class Child extends Parent {
Child() {
super(); // 显式调用父类的构造函数
System.out.println("Child constructor called");
}
}
Ⅱ.访问父类的字段
当子类和父类有同名的字段时,super可以用来访问父类的字段。
class Parent {
int value = 10;
}
class Child extends Parent {
int value = 20;
void printValues() {
System.out.println("Parent value: " + super.value); // 访问父类的字段
System.out.println("Child value: " + this.value); // 访问子类的字段
}
}
Ⅲ.调用父类的方法
当子类重写了父类的方法时,super可以用来调用父类的方法。
class Parent {
void display() {
System.out.println("Parent display method");
}
}
class Child extends Parent {
@Override
void display() {
super.display(); // 调用父类的方法
System.out.println("Child display method");
}
}
Ⅳ.调用父类的构造函数和方法时的区别
- 调用父类的构造函数必须使用super(),并且它必须是子类构造函数中的第一条语句。
- 调用父类的方法可以使用super.methodName()语法,但也可以省略super关键字(如果方法没有被子类重写的话)。
注意事项
- super只能在子类中使用,不能用于访问静态成员,因为静态成员属于类本身,而不是类的实例。
- 在子类的构造函数中,super()调用必须是第一条语句(如果父类没有无参构造函数,则必须显式调用父类的某个构造函数)。
- super不能用于访问父类的私有成员,但可以通过父类的公共方法间接访问。
- 在使用super时,要确保父类的成员(包括字段和方法)是可见的,否则编译器会报错。
3.方法的覆盖/重写(Override):【开发应用+面试重点】
概念: 子类中定义了和父类相同的方法。
要求:
- 子类的方法名、参数列表、返回值类型必须和父类相同
- 子类的访问修饰符合父类相同或是比父类更宽。
使用: 子类覆盖父类中方法,则优先使用子类自身的方法。
引用名.方法名(实参);
注意:
- 方法名、参数列表相同,返回值类型不同—》编译报错
- 方法名、返回值类型相同,形参列表不同—》特殊的方法重载(编译和运行均通过)
场景: 当父类中的方法不足以满足子类的需求时,子类覆盖父类中的方法。
三、多态(Polymorphism)
1.概念:
同一操作作用于不同对象时,表现出不同的行为。
核心思想:“灵活”(同一接口,多种实现)。
实现方式:
-
编译时多态(方法重载)。
-
运行时多态(方法重写 + 父类引用指向子类对象)。
使用场景:
-
需要统一处理不同子类对象(如绘制多种图形)。
-
解耦代码,提高扩展性(如支付接口支持支付宝、微信支付)。
示例:
// 父类:图形
abstract class Shape {
abstract void draw(); // 抽象方法
}
// 子类:圆形
class Circle extends Shape {
@Override
void draw() {
System.out.println("绘制圆形");
}
}
// 子类:矩形
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("绘制矩形");
}
}
public class Main {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Shape shape1 = new Circle();
Shape shape2 = new Rectangle();
shape1.draw(); // 输出:绘制圆形
shape2.draw(); // 输出:绘制矩形
}
}
注意事项:
-
多态必须满足:继承关系 + 方法重写 + 向上转型。
-
无法通过父类引用调用子类特有的方法(需向下转型)。
-
使用 instanceof 避免类型转换错误。
-
多态的核心是解耦,提高代码可扩展性。
2.instanceof关键字
instanceof关键字用于检查一个对象是否是特定类或其子类的实例,
对象 instanceof 类型
如果对象是指定类型(或其子类型)的实例,则表达式返回true;否则返回false。
示例:
/**
在这个例子中,myDog是Dog类的一个实例,同时也是Animal类的一个实例
(因为Dog继承自Animal)。因此,前两个if语句都会输出相应的消息。
第三个if语句检查myDog是否是String类的实例,
显然不是,所以也会输出相应的消息。
*/
class Animal {
// ...
}
class Dog extends Animal {
// ...
}
public class Test {
public static void main(String[] args) {
Animal myDog = new Dog();
if (myDog instanceof Dog) {
System.out.println("myDog is an instance of Dog");
}
if (myDog instanceof Animal) {
System.out.println("myDog is an instance of Animal");
}
if (!(myDog instanceof String)) {
System.out.println("myDog is not an instance of String");
}
}
}
注意事项
- instanceof检查的是对象的运行时类型,而不是编译时类型。
- instanceof可以用于检查接口类型的实例,因为类可以实现接口。
- instanceof在进行类型检查时考虑了继承关系,即如果对象是指定类型的子类的实例,那么instanceof也会返回true。
- instanceof不能用于比较基本数据类型(如int、float等),只能用于对象类型。
- 在使用instanceof时,要确保对象不是null,因为null不是任何类型的实例,对null使用instanceof会抛出NullPointerException。
四.总结对比表
特性 核心目标 实现方式 典型应用场景
封装 保护数据,隐藏细节 private属性 + public方法 用户类、支付系统、数据校验
继承 代码复用,扩展功能 extends关键字 + 方法重写 分类系统(动物→猫/狗)
多态 统一接口,灵活调用 方法重写 + 向上转型 图形绘制、支付方式切换
实际开发建议
-
封装优先:所有属性默认用 private,按需开放 getter/setter。
-
慎用继承:优先用组合(如 class Car { Engine engine; })。
-
多态解耦:面向接口编程(如 List list = new ArrayList();)。
-
避免过度设计:不要为了用特性而用特性,保持代码简洁。