目录
[1.1 面向对象 vs. 面向过程](#1.1 面向对象 vs. 面向过程)
[1.2 类的定义与实例化](#1.2 类的定义与实例化)
[1.3 构造方法与对象初始化](#1.3 构造方法与对象初始化)
[1.4 this引用与封装](#1.4 this引用与封装)
[1.5 static成员与代码块](#1.5 static成员与代码块)
[2.1 继承:代码复用的基石](#2.1 继承:代码复用的基石)
[2.2 super关键字与构造方法链](#2.2 super关键字与构造方法链)
[2.3 多态:同一接口,多种实现](#2.3 多态:同一接口,多种实现)
[2.4 final关键字](#2.4 final关键字)
[3.1 抽象类:不完整的蓝图](#3.1 抽象类:不完整的蓝图)
[3.2 接口:纯粹的行为契约](#3.2 接口:纯粹的行为契约)
[3.3 抽象类 vs 接口:核心抉择](#3.3 抽象类 vs 接口:核心抉择)
[3.4 Object类:所有类的根](#3.4 Object类:所有类的根)
前言
面向对象编程是Java的基石,理解其核心概念是成为合格Java开发者的必经之路。
面向对象编程(Object-Oriented Programming,OOP)是一种基于对象 的软件开发范式,它通过将现实世界中的实体抽象为程序中的类和对象,使代码更符合人类思维模式,更易于理解、扩展和维护。Java作为一门纯面向对象语言 ,其核心特性包括:封装、继承、多态。本文将系统性地讲解这些核心概念,并结合代码示例与图表,助你构建完整的Java面向对象知识体系。
一、类与对象:万物皆对象的起点
1.1 面向对象 vs. 面向过程
面向过程 关注的是步骤和流程,如同传统洗衣:浸泡、搓洗、漂洗、拧干,每一步都需要亲手完成。其代码结构常表现为一系列函数的调用。
面向对象 关注的是对象及其交互,如同现代洗衣:人、衣服、洗衣机、洗衣粉四个对象通过交互完成任务。开发者更关注对象能提供什么服务,而非其内部如何实现。
java
// 面向过程的"洗衣服"
void washClothes() {
soak(); // 浸泡
scrub(); // 搓洗
rinse(); // 漂洗
wring(); // 拧干
}
// 面向对象的"洗衣服"
Person person = new Person();
WashingMachine machine = new WashingMachine();
Detergent detergent = new Detergent();
Clothes clothes = new Clothes();
person.load(clothes, machine);
person.add(detergent, machine);
machine.start();
核心差异 :面向过程是动词思维 (如何做),面向对象是名词思维 (谁来做)。后者通过封装隐藏了实现细节,提高了代码的模块化和复用性。
1.2 类的定义与实例化
类 是对象的蓝图或模板,用于描述一类实体的属性 (成员变量)和行为 (成员方法)。它是一种自定义的数据类型。
java
// 定义一个"学生"类
public class Student {
// 成员变量(属性):描述状态
public String name;
public String gender;
public int age;
public double score;
// 成员方法(行为):描述功能
public void attendClass() {
System.out.println(name + "正在上课");
}
public void doHomework() {
System.out.println(name + "正在写作业");
}
}
对象 是类的具体实例,是内存中根据类模板创建的实体。使用 new 关键字进行实例化。
java
public class Main {
public static void main(String[] args) {
// 实例化对象
Student student1 = new Student();
// 访问和设置属性
student1.name = "张三";
student1.age = 20;
// 调用方法
student1.attendClass();
}
}
内存视角:类定义本身不占数据空间(如同建筑设计图),而每个对象都在堆内存中拥有独立的存储区域,存放各自的属性值。
1.3 构造方法与对象初始化
构造方法 是一种特殊的成员方法,用于在创建对象时初始化其成员变量。其名称必须与类名相同,且无返回值类型。
java
public class Student {
public String name;
public int age;
// 构造方法
public Student(String initName, int initAge) {
name = initName; // 初始化
age = initAge;
System.out.println("学生对象被创建");
}
}
// 使用构造方法创建对象
Student stu = new Student("李四", 22);
Java对象初始化遵循特定顺序:
-
为对象分配内存,成员变量赋予默认值(0, false, null等)。
-
执行显式初始化(在声明时赋值)。
-
执行构造代码块。
-
执行构造方法中的代码。
1.4 this引用与封装
this 关键字代表当前对象的引用,用于在成员方法或构造方法中,区分同名的局部变量和成员变量。
java
public class Student {
private String name;
public void setName(String name) {
this.name = name; // this.name指成员变量,name指形参
}
}
封装 是OOP的第一大特性,它将数据(属性)和操作数据的方法(行为)绑定在一起,并隐藏内部实现细节,仅通过公共接口与外界交互。
java
public class BankAccount {
// 私有化成员变量,隐藏数据
private double balance;
private String password;
// 提供公共方法作为访问接口
public double getBalance(String inputPwd) {
if (verifyPassword(inputPwd)) {
return balance;
}
return -1; // 或抛出异常
}
private boolean verifyPassword(String input) {
// 内部验证逻辑,对外不可见
return this.password.equals(input);
}
}
访问权限修饰符是实现封装的关键:
| 修饰符 | 同类 | 同包 | 子类 | 不同包 |
|---|---|---|---|---|
private |
✔ | ✘ | ✘ | ✘ |
默认 |
✔ | ✔ | ✘ | ✘ |
protected |
✔ | ✔ | ✔ | ✘ |
public |
✔ | ✔ | ✔ | ✔ |
封装的意义:
-
安全性:防止外部代码随意修改敏感数据。
-
易用性:使用者无需了解复杂内部逻辑,只需调用简单接口。
-
可维护性:内部实现修改不影响外部调用。
1.5 static成员与代码块
被 static 修饰的成员(变量或方法)属于类本身,而非某个特定对象。它们在类加载时初始化,被所有对象共享。
java
public class Student {
private String name;
// 静态成员变量:所有学生共享的教室
public static String classroom = "101教室";
// 静态成员方法:可通过类名直接调用
public static void printSchoolRule() {
System.out.println("遵守校规");
// System.out.println(name); // 错误!静态方法中不能直接访问非静态成员
}
}
// 使用
System.out.println(Student.classroom);
Student.printSchoolRule();
代码块用于组织代码,分为:
-
实例代码块:每次创建对象时执行,用于初始化实例成员。
-
静态代码块 :在类首次加载时执行且仅执行一次,用于初始化静态成员。
java
public class Example {
static {
System.out.println("静态代码块执行");
}
{
System.out.println("实例代码块执行");
}
public Example() {
System.out.println("构造方法执行");
}
}
二、继承与多态:构建层次与实现灵活
2.1 继承:代码复用的基石
继承 允许一个类(子类/派生类)基于另一个类(父类/基类)来构建,复用其属性和方法,并可进行扩展。这体现了"is-a"的关系(例如,狗是一种动物)。
java
// 父类:动物
class Animal {
String name;
int age;
public void eat() {
System.out.println(name + "正在吃饭");
}
}
// 子类:狗 继承自动物
class Dog extends Animal {
// 继承了name, age属性和eat()方法
public void bark() {
System.out.println(name + "汪汪叫"); // 可直接使用父类属性
}
}
继承的优势:
-
代码复用:消除重复代码。
-
逻辑清晰:建立清晰的类层次结构。
-
便于扩展:子类可添加新功能或重写父类功能。
2.2 super关键字与构造方法链
super 用于在子类中访问父类的成员(变量、方法或构造方法)。
java
class Animal {
String name = "动物";
}
class Dog extends Animal {
String name = "狗";
public void printName() {
System.out.println(name); // 输出"狗"(子类属性)
System.out.println(super.name); // 输出"动物"(父类属性)
}
}
构造方法的继承链 :创建子类对象时,必须先初始化父类部分。
-
子类构造方法默认首行会隐式调用
super()(父类无参构造)。 -
若父类没有无参构造,子类必须显式调用
super(...)并匹配父类构造方法的参数。
java
class Animal {
public Animal(String name) { /* ... */ }
}
class Dog extends Animal {
public Dog(String name) {
super(name); // 必须显式调用,且在第一行
// 子类自己的初始化
}
}
2.3 多态:同一接口,多种实现
多态 指同一操作作用于不同对象,可以产生不同的行为。在Java中主要通过方法重写 和父类引用指向子类对象来实现。
java
class Animal { public void makeSound() { System.out.println("动物叫"); } }
class Dog extends Animal {
@Override // 注解,表示重写
public void makeSound() { System.out.println("汪汪"); }
}
class Cat extends Animal {
@Override
public void makeSound() { System.out.println("喵喵"); }
}
// 多态的应用
public class Test {
public static void animalSound(Animal a) { // 父类引用作形参
a.makeSound(); // 运行时确定调用哪个子类的方法
}
public static void main(String[] args) {
Animal myDog = new Dog(); // 向上转型
Animal myCat = new Cat();
animalSound(myDog); // 输出"汪汪"
animalSound(myCat); // 输出"喵喵"
}
}
方法重写规则:
-
方法名、参数列表必须完全相同。
-
返回值类型可以相同或是父类返回值的子类(协变返回类型)。
-
访问权限不能比父类更严格。
-
不能重写
private,static,final方法。
重写(Override) vs 重载(Overload):
| 特性 | 重写 (Override) | 重载 (Overload) |
|---|---|---|
| 发生范围 | 父子类之间 | 同一个类内部 |
| 方法签名 | 必须相同 | 必须不同(参数类型、个数、顺序) |
| 返回类型 | 可相同或协变 | 可修改 |
| 访问权限 | 不能更严格 | 可修改 |
| 作用 | 实现多态 | 提供同一功能的不同版本 |
2.4 final关键字
final 用于声明不可改变的实体:
-
final变量:常量,值不可修改。 -
final方法:不可被子类重写。 -
final类:不可被继承(如String类)。
三、抽象类与接口:定义规范与实现分离
3.1 抽象类:不完整的蓝图
抽象类 用 abstract 修饰,用于表示一种概念或框架 ,它可能包含未实现的抽象方法(仅有声明,无方法体)。
java
// 抽象类:图形
abstract class Shape {
protected double area; // 属性
// 抽象方法:计算面积,由子类实现
public abstract void calcArea();
// 普通方法
public double getArea() {
return area;
}
}
// 具体子类:圆
class Circle extends Shape {
private double radius;
public Circle(double r) { this.radius = r; }
@Override // 必须实现抽象方法
public void calcArea() {
area = Math.PI * radius * radius;
}
}
抽象类特性:
-
不能直接实例化。
-
可以包含构造方法(供子类调用)、成员变量、普通方法和抽象方法。
-
子类继承抽象类后,必须实现所有抽象方法,除非子类也是抽象类。
3.2 接口:纯粹的行为契约
接口 用 interface 定义,是一组完全抽象的公共行为规范。它定义类"能做什么",而不关心"怎么做"。
java
// 接口:USB设备
public interface USB {
// 默认 public static final
String VERSION = "3.0";
// 默认 public abstract
void openDevice();
void closeDevice();
}
// 实现类:鼠标
public class Mouse implements USB {
@Override
public void openDevice() { System.out.println("打开鼠标"); }
@Override
public void closeDevice() { System.out.println("关闭鼠标"); }
public void click() { System.out.println("鼠标点击"); } // 自有方法
}
接口特性(Java 8+):
-
方法默认是
public abstract。 -
变量默认是
public static final。 -
不能有构造方法 和实例代码块。
-
Java 8起支持
default方法(有默认实现)和static方法。 -
Java 9起支持
private方法。
3.3 抽象类 vs 接口:核心抉择
| 维度 | 抽象类 (Abstract Class) | 接口 (Interface) |
|---|---|---|
| 设计理念 | 是什么 (is-a),定义共性模板 | 能做什么 (has-a),定义行为契约 |
| 成员变量 | 无限制 | 只能是 public static final 常量 |
| 构造方法 | 有 | 无 |
| 方法实现 | 可有具体方法 | Java 8前全抽象,8后可有 default/static 方法 |
| 继承关系 | 单继承 | 多实现 |
| 使用场景 | 为相关类提供通用基类,包含状态(字段) | 定义跨继承树的不同类的共同能力 |
选择原则:
-
需要定义模板 ,包含共享状态或代码 时,用抽象类。
-
需要定义行为规范 ,且不关心实现者是谁时,用接口。
-
现代Java开发中,倾向于优先使用接口 ,组合使用
default方法,以获得更大的灵活性。
3.4 Object类:所有类的根
Java中,所有类都直接或间接继承自 Object 类。它提供了一些通用方法:
-
toString(): 返回对象的字符串表示。建议重写以提供有意义的输出。 -
equals(Object obj): 判断对象内容是否相等。比较内容时必须重写 (==比较引用地址)。 -
hashCode(): 返回对象的哈希码。重写equals时通常也要重写hashCode,以保证一致性。 -
getClass(): 返回对象的运行时类。 -
clone(): 创建并返回对象的副本(需实现Cloneable接口)。
总结:构建面向对象的思维
面向对象编程的核心在于通过对象模拟现实,通过交互解决问题。掌握以下思维模型至关重要:
-
抽象思维:从具体事物中提取共同属性和行为,形成类。
-
封装思维:隐藏内部细节,暴露必要接口,构建高内聚、低耦合的模块。
-
继承思维:识别类之间的"is-a"关系,构建清晰的层次结构,实现代码复用。
-
多态思维:基于父类引用操作子类对象,编写更通用、更灵活的代码。
-
接口思维:面向契约编程,定义"能做什么",而非"是什么",提高系统的可扩展性和可替换性。