面向对象思想、类与对象 | JavaSE
面向对象思想、类与对象
一、学习目标
完成本章后,你应该能够:
- 理解为什么程序规模增大后,仅使用大量独立变量和数组会越来越难以管理。
- 理解面向对象编程(Object-Oriented Programming,OOP)的基本思想。
- 能够解释什么是类(Class),什么是对象(Object)。
- 能够说明类与对象之间“模板与实例”的关系。
- 能够从现实事物中抽取数据和行为,并设计成一个简单 Java 类。
- 能够使用
new创建对象。 - 能够理解同一个类可以创建多个彼此独立的对象。
- 能够使用“谁的数据谁存储”的思路完成简单业务建模。
- 能够区分“类本身”“对象本身”“保存对象引用的变量”三个不同概念。
二、核心知识
2.1 从变量和数组走向对象
在之前的学习中,我们已经可以使用:
变量
数组
二维数组
保存数据。
例如保存一个学生的数据:
String name = "小雨";
double chinese = 95;
double math = 98;
这当然没有问题。
但是如果要保存两个学生:
String name1 = "小雨";
double chinese1 = 95;
double math1 = 98;
String name2 = "小星";
double chinese2 = 88;
double math2 = 92;
人数继续增加以后:
name1
name2
name3
...
chinese1
chinese2
...
math1
math2
...
数据之间的关系开始变得非常混乱。
2.2 使用数组能不能解决
可以继续使用多个平行数组:
String[] names = {
"小雨",
"小星",
"小林"
};
double[] chineseScores = {
95,
88,
90
};
double[] mathScores = {
98,
92,
96
};
这时约定:
names[0]
chineseScores[0]
mathScores[0]
共同表示:
第一个学生。
这种方式可以使用,但随着业务复杂:
姓名
年龄
性别
学号
班级
语文成绩
数学成绩
英语成绩
联系方式
家庭地址
……
你就会得到越来越多互相关联的数组。
真正的问题是:
一个学生的数据本来应该属于同一个整体,却被拆散到了很多变量和数组中。
2.3 面向对象首先解决“数据属于谁”
面向对象思想会问:
这些数据描述的是谁?
例如:
姓名
年龄
语文成绩
数学成绩
描述的是:
学生
那么就应该设计一个能够表示学生的对象。
于是:
学生对象
├── 姓名
├── 年龄
├── 语文成绩
└── 数学成绩
这样:
属于某个学生的数据,就由这个学生对象自己保存。
源课程将这种思想概括为:
万物皆对象,谁的数据谁存储。
作为入门思维,这句话非常有用。
2.4 什么是对象
在当前阶段,可以把对象理解为:
用来表示某一个具体事物的一组数据,以及围绕这些数据可以执行的行为。
例如现实中有一名学生:
姓名:小雨
年龄:21
语文:95
数学:98
程序中就可以创建一个:
Student 对象
来表示这个具体学生。
另一个学生:
姓名:小星
年龄:22
语文:88
数学:92
则可以对应另一个:
Student 对象
因此:
对象代表的是一个具体实例。
2.5 什么是类
问题来了:
Java 怎么知道 Student 对象应该具有姓名、年龄和成绩?
我们需要先设计:
class Student {
}
这就是:
类(Class)。
类用于描述:
这一类对象应该具有什么数据
这一类对象应该具有什么功能
可以把类理解成:
对象的模板
对象的设计图
例如:
class Student {
String name;
int age;
double chinese;
double math;
}
规定了所有 Student 对象都可以拥有:
name
age
chinese
math
这些数据。
2.6 类与对象的关系
可以使用“设计图与实际产品”来理解。
例如:
汽车设计图
↓
生产
↓
汽车 A
汽车 B
汽车 C
对应:
Student 类
↓
new
↓
Student 对象 1
Student 对象 2
Student 对象 3
因此:
类
→ 描述一类对象共同的结构和行为
对象
→ 根据类创建出来的具体实例
2.7 类不是某一个具体对象
例如:
class Star {
String name;
int age;
String gender;
double height;
double weight;
}
这个 Star 类本身并不是:
王祖贤
杨幂
赵丽颖
中的任何一个人。
它只是描述:
一个明星对象可以具有哪些数据。
然后才能:
Star s1 = new Star();
Star s2 = new Star();
创建具体对象。
2.8 使用 new 创建对象
最基本格式:
类名 对象变量名 = new 类名();
例如:
Student s1 = new Student();
当前阶段可以拆成:
Student
↓
变量的类型
s1
↓
变量名
new Student()
↓
创建一个 Student 对象
因此:
Student s1 = new Student();
表示:
创建一个 Student 对象,并让变量
s1保存对这个对象的引用。
2.9 为什么这里不用“保存对象本身”
观察:
Student s1 = new Student();
更加准确的理解不是:
s1 里面塞进了整个 Student 对象
而是:
s1
│
│ 引用
↓
Student 对象
所以:
s1
通常称为:
引用变量。
引用的内部机制将在下一章重点展开。
当前只需要知道:
变量
s1可以帮助我们找到并操作对应的 Student 对象。
2.10 一个类可以创建很多对象
例如:
Student s1 = new Student();
Student s2 = new Student();
Student s3 = new Student();
这里创建了:
3 个 Student 对象
而不是:
1 个对象的 3 个名字
可以简单画成:
s1 ──→ Student 对象 A
s2 ──→ Student 对象 B
s3 ──→ Student 对象 C
每一次正常执行:
new Student()
都会创建一个新的 Student 实例。
2.11 多个对象的数据彼此独立
例如:
Student s1 = new Student();
s1.name = "小雨";
s1.age = 21;
Student s2 = new Student();
s2.name = "小星";
s2.age = 22;
可以理解:
s1
↓
Student A
├── name = "小雨"
└── age = 21
s2
↓
Student B
├── name = "小星"
└── age = 22
修改:
s1.age = 23;
不会自动把:
s2.age
也修改成 23。
因为:
它们是两个不同对象。
2.12 类中可以描述数据和行为
一个对象不仅可以保存数据,还可以执行与自身有关的操作。
例如:
class Student {
String name;
double chinese;
double math;
void printTotalScore() {
System.out.println(
name + "的总分:"
+ (chinese + math)
);
}
void printAverageScore() {
System.out.println(
name + "的平均分:"
+ (chinese + math) / 2
);
}
}
这里:
name
chinese
math
描述对象的数据。
而:
printTotalScore()
printAverageScore()
描述对象能够执行的行为。
2.13 对象为什么不只是“一张数据表”
源课程为了方便理解,会把对象描述成:
一种特殊的数据结构,可以理解成一张表。
这个比喻适合入门。
但是对象通常不仅包含:
数据
还具有:
行为
例如:
Student
├── 数据
│ ├── name
│ ├── chinese
│ └── math
│
└── 行为
├── printTotalScore()
└── printAverageScore()
因此更加完整的理解应该是:
对象是某种具体事物在程序中的模型,它可以拥有状态,也可以拥有行为。
2.14 面向对象不是“现实世界所有东西都必须建类”
“万物皆对象”是一种帮助初学者建立模型的表达。
真正编程时并不是:
看到一个名词
→ 必须创建一个类
例如:
int count = 10;
没有必要专门设计:
Count 类
面向对象真正重要的是:
当业务中某组数据和行为天然属于一个概念时,把它们组织到对应的对象模型中。
2.15 从现实对象抽取类
假设要开发商城。
现实中:
商品
具有:
商品名称
价格
库存
销量
并具有行为:
展示信息
修改库存
计算销售额
于是可以抽象:
class Product {
String name;
double price;
int stock;
int sales;
void printInfo() {
// ...
}
}
这就是:
从现实业务概念中提取程序模型。
2.16 从对象到抽象
现实里存在:
张三
李四
王五
每一个具体学生的数据不同。
但我们发现他们具有共同结构:
姓名
年龄
成绩
于是把共同特征提取出来:
Student 类
这种从大量具体事物中寻找共同特征的过程,可以理解为:
抽象(Abstraction)。
因此:
具体学生
↓
寻找共同特征
↓
Student 类
而:
Student 类
↓
new
↓
具体 Student 对象
则是根据抽象模型创建实例。
三、使用方法
3.1 设计明星类
例如:
public class Star {
String name;
int age;
String gender;
double height;
double weight;
}
这里描述:
一个明星可以拥有哪些数据
3.2 创建第一个对象
public class StarTest {
public static void main(String[] args) {
Star s1 = new Star();
s1.name = "王祖贤";
s1.age = 50;
s1.gender = "女";
s1.height = 172.5;
s1.weight = 50.5;
System.out.println(s1.name);
System.out.println(s1.age);
System.out.println(s1.gender);
System.out.println(s1.height);
System.out.println(s1.weight);
}
}
执行过程可以抽象为:
设计 Star 类
↓
new Star()
↓
创建 Star 对象
↓
s1 引用对象
↓
向对象中保存数据
3.3 创建第二个对象
Star s2 = new Star();
s2.name = "杨幂";
s2.age = 38;
s2.gender = "女";
s2.height = 171.5;
s2.weight = 52.5;
这时:
s1
↓
对象 A
s2
↓
对象 B
两个对象互相独立。
3.4 Student 案例
定义:
public class Student {
String name;
double chinese;
double math;
public void printTotalScore() {
System.out.println(
name + "同学总分:"
+ (chinese + math)
);
}
public void printAverageScore() {
System.out.println(
name + "同学平均分:"
+ (chinese + math) / 2
);
}
}
创建对象:
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "小雨";
s1.chinese = 100;
s1.math = 100;
s1.printTotalScore();
s1.printAverageScore();
Student s2 = new Student();
s2.name = "小星";
s2.chinese = 59;
s2.math = 99;
s2.printTotalScore();
s2.printAverageScore();
}
}
这里已经形成一个完整的对象模型:
学生的数据
+
处理学生自己数据的方法
3.5 设计 Car 类
在写代码之前先分析:
汽车有什么数据?
品牌
颜色
价格
速度
再分析:
汽车能够做什么?
启动
行驶
停车
展示信息
于是:
class Car {
String brand;
String color;
double price;
double speed;
void start() {
System.out.println("汽车启动");
}
void stop() {
System.out.println("汽车停车");
}
}
这才是更重要的能力:
先设计对象,再写代码。
3.6 对象建模的基本步骤
以后遇到简单面向对象题,可以按照:
第一步:找对象
↓
系统中有哪些重要事物?
第二步:找数据
↓
这个对象应该保存哪些信息?
第三步:找行为
↓
这个对象应该完成哪些操作?
第四步:设计类
↓
使用类描述共同结构
第五步:new 对象
↓
创建具体实例并使用
四、原理与进阶
4.1 Java 中对象的规范含义
从 Java 语言规范角度:
类实例和数组都属于对象。
因此之前学习:
int[] arr = new int[5];
实际上就已经使用过对象。
数组属于对象,只不过:
数组对象
是 Java 提供的一种特殊结构。
而现在学习的是:
我们自己声明类
↓
创建类实例对象
4.2 new Student() 到底产生什么
对于:
new Student()
运行时会创建一个新的 Student 类实例。
例如:
Student s1 = new Student();
Student s2 = new Student();
两次 new Student() 正常情况下对应两个不同的新对象。
因此:
new
可以理解成:
创建对象的关键入口。
关于对象创建过程中构造器到底做什么,下一章会专门学习。
4.3 类、对象和引用变量必须分开
这是整个面向对象阶段最关键的基础之一。
Student s1 = new Student();
这里有三个概念:
Student
↓
类 / 类型
new Student()
↓
新创建的对象
s1
↓
保存对象引用的变量
不要把三者全部混叫成:
Student
以后学习:
参数传递
null
多态
集合
对象数组
GC
时都依赖这个区分。
4.4 类负责描述共同结构,对象负责保存个体状态
例如:
class Student {
String name;
int age;
}
类描述:
Student 都有 name
Student 都有 age
但:
Student s1 = new Student();
Student s2 = new Student();
不同对象可以保存:
s1
name = 小雨
age = 21
s2
name = 小星
age = 22
所以:
类
→ 共同结构
对象
→ 个体状态
4.5 面向对象与面向过程不是非黑即白
即使是面向对象程序:
void printTotalScore() {
double total = chinese + math;
System.out.println(total);
}
方法内部仍然存在:
顺序
分支
循环
算法
所以:
面向对象不是把之前学过的语法全部推翻。
而是增加一个更高层次的组织方式:
基础语法
↓
方法
↓
对象
↓
类与对象协作
↓
更大型的软件结构
五、实践应用
现实 Java 项目中到处都是对象。
例如电商:
User
Product
Order
OrderItem
Address
Coupon
博客系统:
User
Article
Tag
Comment
Tutorial
Chapter
学校系统:
Student
Teacher
Course
Classroom
Score
银行:
Account
Customer
Card
Transaction
这些类背后的核心仍然是:
找到业务对象
↓
描述对象的数据
↓
描述对象的行为
↓
让对象之间协作
因此,面向对象不是为了:
多写几个 class。
而是为了:
用与业务更加接近的模型组织程序。
六、常见问题
6.1 类和对象是不是一回事?
不是。
类
→ 模板、设计图
对象
→ 根据类创建出来的具体实例
6.2 一个类只能创建一个对象吗?
不是。
一个普通类通常可以创建多个对象:
Student s1 = new Student();
Student s2 = new Student();
Student s3 = new Student();
6.3 Student s1 已经创建对象了吗?
没有。
单独:
Student s1;
只是声明一个 Student 类型引用变量。
真正创建 Student 实例通常需要:
new Student()
6.4 new Student() 和 s1 是一回事吗?
不是。
new Student()
→ 创建对象
s1
→ 保存对象引用的变量
6.5 修改 s1 会不会影响 s2?
如果:
Student s1 = new Student();
Student s2 = new Student();
它们指向不同对象,那么修改一个对象的数据不会自动修改另一个对象。
但如果两个变量后来指向同一个对象,情况就不同。
这个问题将在下一章展开。
6.6 面向对象是不是必须模拟现实世界所有细节?
不是。
软件模型只应该保留:
当前业务真正关心的数据和行为。
设计学生选课系统时可能需要:
学号
姓名
专业
课程
未必需要:
鞋码
头发长度
今天早餐
这就是抽象和建模。
6.7 类是不是只能包含变量?
不是。
类通常可以描述:
数据
+
行为
在当前阶段主要对应:
成员变量
成员方法
下一章会正式学习。
6.8 为什么不直接把所有代码都写 main?
小程序当然可以。
但规模增大后,全部塞进 main 会导致:
数据关系混乱
功能职责混乱
重复代码增加
难以维护
类和对象可以帮助程序逐步形成更加清晰的结构。
七、练习与验收
7.1 知识问答
- 什么是面向对象编程?
- 什么是对象?
- 什么是类?
- 类为什么可以理解成对象的模板或设计图?
- 类与对象之间是什么关系?
new在创建对象过程中承担什么作用?Student s1 = new Student();中三个部分分别表示什么?- 一个类能否创建多个对象?
- 多个独立对象的成员数据是否互相独立?
- “万物皆对象,谁的数据谁存储”应该如何理解?
- 对象与普通的一组独立变量相比有什么组织优势?
- 为什么建模时不能把现实事物所有信息都放进类?
- 什么是抽象?
- 类、对象、引用变量三个概念有什么区别?
7.2 代码阅读
禁止运行:
class Student {
String name;
int age;
}
public class OopRead01 {
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
s1.name = "小雨";
s1.age = 21;
s2.name = "小星";
s2.age = 22;
System.out.println(s1.name);
System.out.println(s2.age);
}
}
要求:
- 一共创建了几个 Student 对象?
s1与s2各自是什么?- 画出类、对象、引用变量之间的关系。
- 修改
s1.age是否会直接改变s2.age?
7.3 手写代码
- 设计
Star类,至少包含姓名、年龄、性别、身高、体重五个数据。 - 创建两个不同的 Star 对象并分别保存数据。
- 设计
Book类,至少 3 个数据和 2 个行为。 - 设计
Product类,至少 3 个数据和 2 个行为。 - 设计
Car类,至少 3 个数据和 2 个行为。 - 设计
User类,至少 3 个数据和 2 个行为。 - 为学生设计 Student 类,并创建两个学生对象。
7.4 Debug
下面代码的设计存在问题:
String[] names = {"小雨", "小星"};
double[] chinese = {90, 80};
double[] math = {95, 88};
int[] ages = {21, 22};
需求不断增加,还要继续加入:
英语成绩
学号
手机号
班级
地址
要求:
- 说明平行数组方案会产生什么维护问题。
- 使用面向对象思想重新设计。
- 明确哪些数据应该属于 Student 对象。
7.5 综合训练
将下面的业务建模成对象:
一个博客系统需要管理文章。每篇文章包含标题、摘要、正文、浏览量和发布时间,并具有“展示基本信息”和“增加浏览量”等行为。
要求:
- 先写自然语言模型。
- 找出对象。
- 找出数据。
- 找出行为。
- 设计
Article类。 - 创建两个 Article 对象。
- 不要求使用构造器、private、getter/setter。
7.6 本章验收
关闭资料后确认:
- [ ] 能独立解释面向对象思想。
- [ ] 能区分类与对象。
- [ ] 能从现实业务中找出候选对象。
- [ ] 能从对象中提取数据和行为。
- [ ] 能从零定义一个简单类。
- [ ] 能使用
new创建对象。 - [ ] 能解释
Student s = new Student();。 - [ ] 能创建同一类的多个对象。
- [ ] 能画出类、引用变量与对象之间的基本关系。
- [ ] 不会把类、对象、对象变量三个概念混成一个概念。
如果面对一个新的业务对象时,只会照抄:
class Student {
}
却无法回答:
这个类为什么存在?
它代表谁?
应该保存什么数据?
应该提供什么行为?
那么你还没有真正建立面向对象思维。