1. 面向对象编程基础概念
面向对象编程(Object Oriented Programming, OOP)是现代软件开发中最主流的编程范式之一。它通过将现实世界中的事物抽象为程序中的对象,使代码更易于理解、维护和扩展。
1.1 面向对象与面向过程的区别
面向过程编程关注的是解决问题的步骤和过程,而面向对象编程关注的是对象和对象之间的交互。让我们通过一个实际例子来理解这个区别:
java复制// 面向过程:关注洗衣的步骤
public class ProcessOrientedLaundry {
public static void main(String[] args) {
openWasher(); // 1. 打开洗衣机
putClothes(); // 2. 放入衣服
addDetergent(); // 3. 添加洗衣粉
startWasher(); // 4. 启动洗衣机
dryClothes(); // 5. 晾晒衣服
}
// 每个步骤对应一个方法
static void openWasher() { /* 实现细节 */ }
static void putClothes() { /* 实现细节 */ }
// ...其他方法实现
}
// 面向对象:关注参与洗衣的对象及其交互
public class ObjectOrientedLaundry {
public static void main(String[] args) {
Person me = new Person();
WashingMachine washer = new WashingMachine();
Clothes myClothes = new Clothes();
me.wash(washer, myClothes); // 人让洗衣机洗衣服
}
}
面向对象编程的优势在于:
- 更贴近现实世界的思维方式
- 代码复用性更高(通过继承和多态)
- 更易于维护和扩展
- 更好的封装性,提高代码安全性
1.2 面向对象的三大特征
1.2.1 封装(Encapsulation)
封装是指将对象的属性和行为包装在一起,并隐藏内部实现细节。在Java中,我们通过private关键字实现封装:
java复制public class BankAccount {
private double balance; // 私有属性,外部无法直接访问
// 公共方法提供对balance的安全访问
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public double getBalance() {
return balance;
}
}
1.2.2 继承(Inheritance)
继承允许我们基于现有类创建新类,实现代码复用。子类继承父类的属性和方法,并可以添加新的特性:
java复制public class Animal { // 父类
public void eat() {
System.out.println("动物在吃东西");
}
}
public class Dog extends Animal { // 子类
public void bark() {
System.out.println("狗在叫");
}
}
1.2.3 多态(Polymorphism)
多态指同一操作作用于不同对象时,可以有不同的解释和执行结果。Java中主要通过方法重写和接口实现多态:
java复制public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
}
2. 类与对象的关系
2.1 类(Class)的定义
类是对象的模板或蓝图,它描述了一类对象的共同特征和行为。类由以下部分组成:
- 成员变量(属性)
- 成员方法(行为)
- 构造方法
- 代码块
- 内部类
java复制public class Student {
// 成员变量(属性)
private String name;
private int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 成员方法(行为)
public void study() {
System.out.println(name + "正在学习");
}
// Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.2 对象(Object)的创建与使用
对象是类的实例,通过new关键字创建:
java复制public class StudentTest {
public static void main(String[] args) {
// 创建Student对象
Student student1 = new Student("张三", 20);
Student student2 = new Student("李四", 21);
// 调用对象方法
student1.study();
student2.study();
// 访问对象属性(通过getter方法)
System.out.println(student1.getName() + "的年龄是:" + student1.getAge());
}
}
2.3 类与对象的内存模型
理解Java内存模型对掌握面向对象编程至关重要。Java内存主要分为:
- 栈内存(Stack):存储局部变量和方法调用
- 堆内存(Heap):存储对象实例
- 方法区(Method Area):存储类信息、常量池等
java复制Student s1 = new Student("张三", 20);
Student s2 = new Student("李四", 21);
内存分配示意图:
code复制栈内存 堆内存
┌───────────┐ ┌───────────────────┐
│ s1:0x100 │ ──────→│ Student对象 │
│ │ │ name="张三" │
│ s2:0x200 │ ───┐ │ age=20 │
└───────────┘ │ └───────────────────┘
│
└──→┌───────────────────┐
│ Student对象 │
│ name="李四" │
│ age=21 │
└───────────────────┘
3. 封装与访问控制
3.1 private关键字的使用
private是Java中最严格的访问修饰符,被private修饰的成员只能在当前类中访问:
java复制public class Person {
private String name;
private int age;
public void setName(String name) {
if (name != null && !name.isEmpty()) {
this.name = name;
}
}
public String getName() {
return name;
}
}
3.2 Getter和Setter方法
Getter和Setter方法是访问私有成员的标准方式,它们提供了对私有属性的受控访问:
java复制public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
System.out.println("存款金额必须大于0");
}
}
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
System.out.println("取款金额不合法或余额不足");
}
}
public double getBalance() {
return balance;
}
}
3.3 封装的好处
- 数据安全性:防止外部代码随意修改对象内部状态
- 数据验证:可以在setter方法中添加业务逻辑验证
- 实现细节隐藏:外部无需知道内部实现细节,只需知道如何使用
- 易于维护:内部实现改变不影响外部代码
4. 构造方法详解
4.1 构造方法的特点
构造方法是一种特殊的方法,用于创建对象时初始化对象。它的特点包括:
- 方法名必须与类名完全相同
- 没有返回类型,连void也不能写
- 创建对象时自动调用,不能手动调用
- 可以重载(定义多个参数不同的构造方法)
java复制public class Book {
private String title;
private String author;
private double price;
// 无参构造方法
public Book() {
this.title = "未知";
this.author = "未知";
this.price = 0.0;
}
// 带参数的构造方法
public Book(String title, String author, double price) {
this.title = title;
this.author = author;
if (price > 0) {
this.price = price;
}
}
}
4.2 构造方法的重载
一个类可以有多个构造方法,只要它们的参数列表不同:
java复制public class Student {
private String name;
private int age;
private String major;
// 无参构造
public Student() {
this("未知", 0, "未定");
}
// 两个参数的构造
public Student(String name, int age) {
this(name, age, "未定");
}
// 全参构造
public Student(String name, int age, String major) {
this.name = name;
this.age = age;
this.major = major;
}
}
4.3 this关键字在构造方法中的使用
this关键字可以在构造方法中调用本类的其他构造方法,但必须放在第一行:
java复制public class Employee {
private String id;
private String name;
private double salary;
public Employee() {
this("E0000", "未知", 0.0);
}
public Employee(String id, String name) {
this(id, name, 5000.0);
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
}
5. this关键字详解
5.1 this的三种用法
- 区分成员变量和局部变量:当成员变量和局部变量同名时,使用this.成员变量名来明确指定
java复制public class Person {
private String name;
public void setName(String name) {
this.name = name; // this.name指成员变量,name指参数
}
}
- 调用本类的成员方法:在一个成员方法中调用另一个成员方法
java复制public class Calculator {
public void add(int a, int b) {
System.out.println(a + b);
}
public void calculate() {
this.add(5, 3); // 调用本类的add方法
}
}
- 调用本类的构造方法:如前面构造方法部分所示
5.2 this的内存原理
this关键字实际上是一个隐式的引用,它指向当前对象。在内存中,this存储的是当前对象的地址:
java复制public class ThisDemo {
private int value;
public void setValue(int value) {
this.value = value; // this指向调用该方法的对象
}
public static void main(String[] args) {
ThisDemo obj1 = new ThisDemo();
ThisDemo obj2 = new ThisDemo();
obj1.setValue(10); // 方法内的this指向obj1
obj2.setValue(20); // 方法内的this指向obj2
}
}
内存示意图:
code复制栈内存 堆内存
┌───────────┐ ┌───────────────────┐
│ obj1:0x100│ ──────→│ ThisDemo对象 │
│ │ │ value=10 │
│ obj2:0x200│ ───┐ └───────────────────┘
└───────────┘ │
└──→┌───────────────────┐
│ ThisDemo对象 │
│ value=20 │
└───────────────────┘
6. 标准JavaBean规范
6.1 JavaBean的定义
JavaBean是一种符合特定规范的Java类,主要用于封装数据。标准JavaBean必须满足以下条件:
- 类是public的
- 有一个public的无参构造方法
- 属性是private的
- 提供public的getter和setter方法
java复制public class User implements Serializable {
// 1. 私有属性
private Long id;
private String username;
private String password;
private String email;
// 2. 无参构造
public User() {}
// 3. 全参构造(可选)
public User(Long id, String username, String password, String email) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
}
// 4. getter和setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
// ...其他getter和setter
}
6.2 JavaBean的应用场景
JavaBean广泛应用于:
- 数据传输对象(DTO)
- 持久化实体(Entity)
- 表单数据封装
- 各种框架的配置对象
6.3 使用IDE快速生成JavaBean
现代IDE都提供了快速生成JavaBean的功能:
在IntelliJ IDEA中:
- 定义好私有属性
- 按Alt+Insert(Windows)或Cmd+N(Mac)
- 选择"Constructor"生成构造方法
- 选择"Getter and Setter"生成getter/setter方法
在Eclipse中:
- 定义好私有属性
- 右键 → Source → Generate Getters and Setters
- 选择要生成getter/setter的属性
7. 对象数组的使用
7.1 对象数组的定义与初始化
对象数组是存储对象引用的数组,与基本类型数组类似:
java复制// 创建能存储5个Student对象的数组
Student[] students = new Student[5];
// 初始化数组元素
students[0] = new Student("张三", 20);
students[1] = new Student("李四", 21);
// ...其他元素初始化
7.2 对象数组的遍历
可以使用普通for循环或增强for循环遍历对象数组:
java复制// 普通for循环
for (int i = 0; i < students.length; i++) {
if (students[i] != null) {
System.out.println(students[i].getName());
}
}
// 增强for循环
for (Student student : students) {
if (student != null) {
System.out.println(student.getName());
}
}
7.3 对象数组的常见操作
7.3.1 查找操作
java复制// 按姓名查找学生
public Student findStudentByName(Student[] students, String name) {
for (Student student : students) {
if (student != null && student.getName().equals(name)) {
return student;
}
}
return null;
}
7.3.2 统计操作
java复制// 计算平均年龄
public double averageAge(Student[] students) {
int sum = 0;
int count = 0;
for (Student student : students) {
if (student != null) {
sum += student.getAge();
count++;
}
}
return count == 0 ? 0 : (double)sum / count;
}
7.3.3 排序操作
java复制// 按年龄排序
public void sortByAge(Student[] students) {
for (int i = 0; i < students.length - 1; i++) {
if (students[i] == null) continue;
for (int j = i + 1; j < students.length; j++) {
if (students[j] == null) continue;
if (students[i].getAge() > students[j].getAge()) {
Student temp = students[i];
students[i] = students[j];
students[j] = temp;
}
}
}
}
7.4 对象数组的注意事项
- 空指针异常:对象数组元素默认是null,使用前必须初始化
- 数组越界:访问不存在的索引会抛出ArrayIndexOutOfBoundsException
- 内存浪费:对象数组大小固定,可能造成内存浪费
- 性能考虑:频繁插入删除操作效率低,考虑使用集合类
8. 综合案例:学生管理系统
8.1 系统设计
我们设计一个简单的学生管理系统,包含以下功能:
- 添加学生
- 显示所有学生信息
- 按学号查找学生
- 按姓名删除学生
- 统计学生数量
- 退出系统
8.2 学生类设计
java复制public class Student {
private String id; // 学号
private String name; // 姓名
private int age; // 年龄
private String gender; // 性别
private double score; // 成绩
// 构造方法
public Student(String id, String name, int age, String gender, double score) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.score = score;
}
// getter和setter方法
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// ...其他getter和setter
// 显示学生信息
public void display() {
System.out.printf("学号:%s 姓名:%s 年龄:%d 性别:%s 成绩:%.1f\n",
id, name, age, gender, score);
}
}
8.3 学生管理类设计
java复制public class StudentManager {
private Student[] students; // 学生数组
private int size; // 当前学生数量
// 构造方法,初始化数组
public StudentManager(int capacity) {
students = new Student[capacity];
size = 0;
}
// 添加学生
public boolean addStudent(Student student) {
if (size >= students.length) {
System.out.println("学生数量已达上限,无法添加");
return false;
}
students[size++] = student;
System.out.println("添加成功");
return true;
}
// 显示所有学生
public void displayAll() {
if (size == 0) {
System.out.println("暂无学生信息");
return;
}
System.out.println("===== 学生列表 =====");
for (int i = 0; i < size; i++) {
students[i].display();
}
}
// 按学号查找学生
public Student findById(String id) {
for (int i = 0; i < size; i++) {
if (students[i].getId().equals(id)) {
return students[i];
}
}
return null;
}
// 按姓名删除学生
public boolean deleteByName(String name) {
int index = -1;
for (int i = 0; i < size; i++) {
if (students[i].getName().equals(name)) {
index = i;
break;
}
}
if (index == -1) {
System.out.println("未找到该学生");
return false;
}
// 将后面的元素前移
for (int i = index; i < size - 1; i++) {
students[i] = students[i + 1];
}
students[--size] = null; // 最后一个元素置为null
System.out.println("删除成功");
return true;
}
// 获取学生数量
public int getSize() {
return size;
}
}
8.4 系统测试类
java复制import java.util.Scanner;
public class StudentSystem {
public static void main(String[] args) {
StudentManager manager = new StudentManager(100);
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("\n===== 学生管理系统 =====");
System.out.println("1. 添加学生");
System.out.println("2. 显示所有学生");
System.out.println("3. 按学号查找学生");
System.out.println("4. 按姓名删除学生");
System.out.println("5. 统计学生数量");
System.out.println("6. 退出系统");
System.out.print("请选择操作:");
int choice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
switch (choice) {
case 1:
System.out.print("请输入学号:");
String id = scanner.nextLine();
System.out.print("请输入姓名:");
String name = scanner.nextLine();
System.out.print("请输入年龄:");
int age = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
System.out.print("请输入性别:");
String gender = scanner.nextLine();
System.out.print("请输入成绩:");
double score = scanner.nextDouble();
scanner.nextLine(); // 消耗换行符
Student student = new Student(id, name, age, gender, score);
manager.addStudent(student);
break;
case 2:
manager.displayAll();
break;
case 3:
System.out.print("请输入要查找的学号:");
String searchId = scanner.nextLine();
Student found = manager.findById(searchId);
if (found != null) {
System.out.println("找到学生:");
found.display();
} else {
System.out.println("未找到该学号的学生");
}
break;
case 4:
System.out.print("请输入要删除的学生姓名:");
String deleteName = scanner.nextLine();
manager.deleteByName(deleteName);
break;
case 5:
System.out.println("当前学生数量:" + manager.getSize());
break;
case 6:
System.out.println("感谢使用学生管理系统,再见!");
scanner.close();
System.exit(0);
default:
System.out.println("无效的选择,请重新输入");
}
}
}
}
9. 常见问题与解决方案
9.1 空指针异常(NullPointerException)
问题描述:尝试访问null对象的成员变量或方法时抛出
示例:
java复制Student[] students = new Student[3];
students[0].setName("张三"); // 抛出NullPointerException
解决方案:
- 在使用对象前检查是否为null
- 确保对象被正确初始化
java复制if (students[0] != null) {
students[0].setName("张三");
}
9.2 数组越界异常(ArrayIndexOutOfBoundsException)
问题描述:访问数组时使用了非法索引(负数或大于等于数组长度)
示例:
java复制Student[] students = new Student[3];
students[3] = new Student(); // 抛出ArrayIndexOutOfBoundsException
解决方案:
- 确保索引在合法范围内
- 使用length属性作为边界条件
java复制for (int i = 0; i < students.length; i++) {
// 安全操作
}
9.3 封装不当导致的数据不一致
问题描述:直接暴露成员变量可能导致数据被非法修改
错误示例:
java复制public class Account {
public double balance; // 不应该公开
}
解决方案:
- 使用private修饰成员变量
- 提供受控的访问方法
java复制public class Account {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public double getBalance() {
return balance;
}
}
9.4 构造方法中的常见错误
问题1:忘记初始化对象状态
错误示例:
java复制public class Person {
private String name;
public Person() {} // 没有初始化name
}
解决方案:
- 在构造方法中初始化所有必要属性
- 提供合理的默认值
java复制public class Person {
private String name;
public Person() {
this.name = "未知";
}
}
问题2:构造方法循环调用
错误示例:
java复制public class Circle {
private double radius;
public Circle() {
this(1.0);
}
public Circle(double radius) {
this(); // 循环调用,导致StackOverflowError
this.radius = radius;
}
}
解决方案:
- 确保构造方法调用链不形成循环
- 将公共初始化代码提取到初始化方法中
java复制public class Circle {
private double radius;
public Circle() {
this(1.0);
}
public Circle(double radius) {
this.radius = radius;
}
}
10. 最佳实践与编码规范
10.1 类设计原则
- 单一职责原则:一个类只负责一个功能领域
- 开闭原则:对扩展开放,对修改关闭
- 高内聚低耦合:类内部高度相关,类之间依赖最小化
10.2 命名规范
- 类名:大驼峰命名法,如StudentManager
- 方法名:小驼峰命名法,如getStudentById
- 变量名:小驼峰命名法,如studentCount
- 常量名:全大写,用下划线分隔,如MAX_STUDENTS
10.3 代码组织
- 成员变量声明在类顶部
- 构造方法紧随成员变量之后
- getter和setter方法放在一起
- 业务方法按功能分组
10.4 注释规范
- 类注释:说明类的用途和主要功能
- 方法注释:说明方法功能、参数和返回值
- 复杂逻辑注释:解释关键算法或业务逻辑
java复制/**
* 学生管理类,负责学生的增删改查操作
*/
public class StudentManager {
/**
* 根据学号查找学生
* @param id 要查找的学号
* @return 找到的学生对象,未找到返回null
*/
public Student findById(String id) {
// 实现代码
}
}
10.5 异常处理
- 对可能出错的操作进行异常处理
- 不要捕获异常后什么都不做
- 使用自定义异常表示特定的业务错误
java复制public void withdraw(double amount) throws InsufficientBalanceException {
if (amount > balance) {
throw new InsufficientBalanceException("余额不足");
}
balance -= amount;
}
11. 进阶主题预告
11.1 继承与多态
继承允许我们创建基于现有类的新类,实现代码复用。多态则让我们可以用统一的方式处理不同类型的对象。
java复制public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
}
11.2 抽象类与接口
抽象类和接口是Java中实现抽象的两种机制,它们定义了行为的规范而不提供具体实现。
java复制// 抽象类
public abstract class Shape {
public abstract double area();
public abstract double perimeter();
}
// 接口
public interface Drawable {
void draw();
}
11.3 集合框架
Java集合框架提供了一套性能优良、使用方便的接口和类,用于存储和操作对象组。
java复制List<Student> studentList = new ArrayList<>();
studentList.add(new Student("张三", 20));
studentList.add(new Student("李四", 21));
Map<String, Student> studentMap = new HashMap<>();
studentMap.put("1001", new Student("张三", 20));
studentMap.put("1002", new Student("李四", 21));
11.4 设计模式
设计模式是解决特定问题的经验总结,常见的有单例模式、工厂模式、观察者模式等。
java复制// 单例模式示例
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
12. 实战练习
12.1 图书管理系统
设计一个图书管理系统,包含以下功能:
- 添加图书
- 显示所有图书
- 按书名查找图书
- 删除图书
- 统计图书数量
图书类设计:
java复制public class Book {
private String isbn;
private String title;
private String author;
private double price;
// 构造方法、getter/setter省略
public void display() {
System.out.printf("ISBN: %s 书名: %s 作者: %s 价格: %.2f\n",
isbn, title, author, price);
}
}
12.2 银行账户系统
设计一个银行账户系统,包含以下功能:
- 开户
- 存款
- 取款
- 转账
- 查询余额
账户类设计:
java复制public class BankAccount {
private String accountNumber;
private String owner;
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) throws InsufficientBalanceException {
if (amount > balance) {
throw new InsufficientBalanceException("余额不足");
}
balance -= amount;
}
public void transfer(BankAccount target, double amount) throws InsufficientBalanceException {
this.withdraw(amount);
target.deposit(amount);
}
}
12.3 员工管理系统
设计一个员工管理系统,包含以下功能:
- 添加员工
- 显示所有员工
- 按部门查找员工
- 计算平均工资
- 给员工加薪
员工类设计:
java复制public class Employee {
private String id;
private String name;
private String department;
private double salary;
public void raiseSalary(double percentage) {
if (percentage > 0) {
salary *= (1 + percentage / 100);
}
}
}
在实际开发中,面向对象编程的思想会贯穿始终。掌握类与对象的基本概念后,可以进一步学习继承、多态、抽象类和接口等高级特性,这些内容将在后续章节中详细介绍。