面向对象编程(OOP)是 Java 的核心思想,掌握其关键概念是学好 Java 的基础。本文将系统梳理面向对象的核心知识点,包括核心定义、三大特征、重载与重写、抽象类与接口、final 关键字、深浅拷贝等,帮助大家建立完整的知识体系。
一、面向对象基础:什么是对象与类?
1. 核心定义
- 类:是对一类事物的抽象描述,包含该类事物的属性(数据)和行为(方法),是创建对象的 "模板"。
- 对象:是类的运行实例,是堆内存中一块具体的内存空间,包含类定义的属性和方法的具体实现。
简单来说:类是 "图纸",对象是根据图纸造出的 "实物"。
2. 面向对象三大核心特征
(1)封装
封装是指将对象的属性(数据)和行为(方法)结合在一起,对外隐藏对象的内部细节,仅通过对象提供的接口与外界交互。
- 核心价值:提高代码安全性(避免属性被随意修改)、降低耦合度。
- 实现方式:属性私有化(private 修饰),通过 get/set 方法暴露访问接口。
(2)继承
继承的本质是代码复用,子类(派生类)可以使用父类(基类)的非私有属性和方法,无需重复编写。
- 核心价值:减少代码冗余,实现代码复用。
- 关键字:extends(Java 中仅支持单继承)。
- 注意:子类只能继承父类的非私有成员,构造方法不能被继承,但子类构造方法会默认调用父类无参构造。
(3)多态
多态是指允许不同类的对象对同一消息作出响应,核心体现为 "一个接口,多种实现"。
- 实现形式 :
- 方法重载(同一类内)
- 方法重写(子类对父类)
- 接口与实现类
- 向上转型(父类引用指向子类对象)
二、核心概念辨析:重载 vs 重写
表格
| 特性 | 方法重载(Overload) | 方法重写(Override) |
|---|---|---|
| 定义位置 | 同一个类中 | 子类与父类之间 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须不同(类型 / 个数 / 顺序) | 必须完全相同 |
| 返回值 | 无强制要求(JDK5 + 支持协变返回类型) | 必须与父类一致(或子类类型,协变返回) |
| 访问修饰符 | 无特殊限制 | 不能缩小父类方法的访问权限(如父类 public,子类不能 private) |
| 异常 | 无强制要求 | 不能抛出比父类更多的检查异常 |
| 关键字 | 无专属关键字 | 推荐使用 @Override 注解(编译器校验) |
| 绑定时机 | 编译期绑定(静态多态) | 运行期绑定(动态多态) |
示例代码
java
运行
// 方法重载示例
class OverloadDemo {
// 重载1:无参数
public void print() {
System.out.println("无参print方法");
}
// 重载2:int参数
public void print(int num) {
System.out.println("int参数print方法:" + num);
}
// 重载3:String参数
public void print(String str) {
System.out.println("String参数print方法:" + str);
}
}
// 方法重写示例
class Parent {
public void sayHello() {
System.out.println("父类:Hello");
}
}
class Child extends Parent {
// 重写父类方法
@Override
public void sayHello() {
System.out.println("子类:Hello");
}
}
三、抽象类与接口:抽象化的两种实现
1. 抽象类 vs 普通类
| 特性 | 普通类 | 抽象类 |
|---|---|---|
| 实例化 | 可直接 new 创建对象 | 不能直接实例化,仅能被继承 |
| 方法 | 仅包含有实现的普通方法 | 可包含普通方法 + 抽象方法 |
| 设计目的 | 直接创建对象使用 | 作为基类,供子类扩展复用 |
2. 抽象类与接口的区别与共同点
(1)共同点
- 都不能被直接实例化;
- 都用于定义规范(抽象方法);
- 都支持多态(向上转型)。
(2)核心区别
| 特性 | 抽象类 (Abstract Class) | 接口 (Interface) |
|---|---|---|
| 实现 / 继承方式 | extends(单继承) | implements(多实现) |
| 方法 | 可包含普通方法(有实现)+ 抽象方法(无实现) | JDK8 前:仅抽象方法;JDK8 后:可加 default/static 方法 |
| 成员变量 | 支持实例变量、静态变量,修饰符灵活 | 仅 public static final 常量(必须赋初值) |
| 构造方法 | 有构造方法(供子类调用) | 无构造方法 |
| 访问修饰符 | 方法 / 变量支持 public/protected/private | 方法默认 public abstract,变量默认 public static final |
| 设计目的 | 强调 "is-a" 继承关系,侧重代码复用 | 强调 "has-a" 行为规范,侧重解耦和扩展 |
3. 关键补充
- 抽象方法:由 abstract 修饰,无方法体,子类继承抽象类必须重写所有抽象方法;
- 抽象类不能用 final 修饰:抽象类的核心是被继承,final 修饰的类无法被继承,二者矛盾;
- 抽象类有构造器:不能直接实例化,但子类创建对象时会调用父类构造器,实现间接实例化。
四、final 关键字:限制修改的核心
final 意为 "最终的",核心作用是限制修改,有三大应用场景:
1. 修饰类
被 final 修饰的类无法被继承,例如 Java 中的 String 类:
final class FinalClass {
// 此类不能被继承
}
// 编译报错:无法继承final类
// class SubClass extends FinalClass {}
2. 修饰方法
被 final 修饰的方法无法被子类重写:
class Parent {
public final void finalMethod() {
System.out.println("此方法不能被重写");
}
}
class Child extends Parent {
// 编译报错:无法重写final方法
// @Override
// public void finalMethod() {}
}
3. 修饰变量
被 final 修饰的变量无法重新赋值:
-
基本数据类型:值不可变;
-
引用数据类型:引用地址不可变,但对象内容可变。
// 基本类型常量
final int NUM = 10;
// NUM = 20; // 编译报错:不能重新赋值// 引用类型
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World"); // 合法:对象内容可修改
// sb = new StringBuilder("New"); // 编译报错:引用地址不可变
五、static 关键字:属于类的成员
static 修饰的成员(变量 / 方法 / 代码块 / 内部类)属于类本身,而非对象。
1. 静态变量
- 共享性:所有类实例共享同一个静态变量,一个实例修改,所有实例可见;
- 初始化时机:类加载时初始化,仅初始化一次;
- 访问方式:推荐通过类名访问(类名。静态变量),也可通过对象访问(不推荐)。
2. 静态方法
- 无实例依赖:无需创建对象即可调用(类名。静态方法);
- 访问限制:不能直接访问非静态成员(无 this 引用),可直接访问静态成员;
- 多态特性:不支持重写(Override),仅能被隐藏(Hide)。
3. 其他应用
- 静态代码块:类加载时执行,用于初始化静态变量,优先于 main 方法执行;
- 静态内部类:不依赖外部类实例,可独立存在,不能直接访问外部类非静态成员。
六、深浅拷贝:对象复制的核心区别
1. 核心定义
- 浅拷贝:仅拷贝对象的引用地址,原对象和拷贝对象共享同一块堆内存,原对象属性修改会影响拷贝对象;
- 深拷贝:拷贝对象的所有属性(包括引用类型),原对象和拷贝对象拥有独立的堆内存,修改互不影响。
2. 深拷贝的三种实现方式
(1)实现 Cloneable 接口,重写 clone () 方法
class Person implements Cloneable {
private String name;
private Address address; // 引用类型属性
// 深拷贝重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
// 对引用类型属性单独拷贝
clone.address = (Address) address.clone();
return clone;
}
}
class Address implements Cloneable {
private String city;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
(2)序列化与反序列化
通过 Serializable 接口实现对象的完整拷贝:
import java.io.*;
class Person implements Serializable {
private String name;
private Address address;
// 深拷贝方法
public Person deepClone() throws IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Person) ois.readObject();
}
}
class Address implements Serializable {
private String city;
}
(3)手动递归复制
手动创建新对象,逐个拷贝所有属性(包括引用类型属性):
class Person {
private String name;
private Address address;
// 手动深拷贝
public Person deepCopy() {
Person newPerson = new Person();
newPerson.name = this.name;
// 手动创建引用类型的新对象
Address newAddress = new Address();
newAddress.setCity(this.address.getCity());
newPerson.address = newAddress;
return newPerson;
}
}
七、补充:为什么 Java 只支持单继承?
Java 设计为单继承(一个类只能继承一个父类),核心原因:
- 避免菱形继承问题:多继承可能导致父类方法冲突(子类无法确定调用哪个父类的方法);
- 简化类结构:单继承让类的继承关系更清晰,降低代码复杂度;
- 减少资源浪费:创建子类对象时会递归创建父类对象,多继承会增加内存开销和初始化复杂度;
- 接口补充扩展:通过接口的多实现机制,既规避了多继承的问题,又满足了扩展需求。