111 - Lecture 6 - Objects and Classes
Motivation
动机
1.基本数据类型的局限性:
• int、double等基本数据类型只能存储单一的值,功能非常有限,只能回答“你存储的是什么值?”这一问题。
2.数组的局限性:
• 数组是一种线性数据结构,存储在连续的(contiguous)内存位置,能够快速访问和操作数据,但只能存储同一种数据类型。
• 现实中的数据(例如学生记录、银行账户、地址簿)通常由不同类型(homogeneous/same/single)的数据组成,无法用简单的数组表示。
3.现实世界中的数据:
• 现实中的数据由不同类型的变量组成。例如:
• 学生记录:学号、姓名、专业、入学年份等。
• 银行账户:账号、账户名、货币类型、余额等。
• 通讯录:姓名、地址、联系电话等。
在数据库应用中,这些结构称为记录(Records)。
Example: Temperature
温度转换的例子
• 问题描述:
• 编写一个程序,输入一个温度值(华氏Fahrenheit、摄氏Celsius或开尔文Kelvin),并显示该温度在其他两个单位的转换结果。
• 使用 double degree = 0.0; 和 char scale = 'F'; 来存储温度和温标(scale)。
• 使用函数 f(degree, scale); 来转换温度,并用 System.out.println(degree + " " + scale); 来显示温度。
改进建议:
• 如何将温度和温标组合成一个对象?这个对象是否能够自动在不同温标之间转换,并能自己打印结果?
Object Oriented Solution
面向对象的解决方案
Java中的类和对象:
(类和对象是面向对象编程(OOP)的基本概念,用来表示现实世界中的概念和实体(entities)。)
• 类是对象的蓝图或模型,定义了对象的属性和行为。
• 通过类可以构建“智能对象”,这些对象不仅可以存储数据,还可以回答各种问题并执行操作,例如:
• 你的温度是多少?
• 你的华氏温度是多少?
• 打印你的开尔文温度。
Objects
什么是对象?
• 对象:是一种数据抽象(data abstraction),包含两部分:
1. 内部表示(internal representation):通过数据成员(又称属性properties/attributes)来存储对象的状态。
2. 接口(interface):通过方法(又称过程或函数)与其他对象交互,定义对象的行为并隐藏实现细节(hide implementations)。
Example
现实生活中的例子
• 电梯:
• 属性:长度、宽度、高度、最大容量、当前楼层。
• 功能:移动到不同楼层、添加人、移除人。
• 学生:
• 属性:学号、姓名、出生日期、学习项目、学习年限、学习的模块。
• 功能:添加新模块、移除模块、更新学习进度(study progress update)。
• 打印队列(queue):
• 属性:打印作业列表。
• 功能:添加新的打印作业、移除已完成的打印作业。
• 煎饼堆(stack of pancakes):
• 属性:每个煎饼可以表示为一个字符串。
• 功能:添加(append)煎饼到堆的顶部,移除堆顶的煎饼。
Object oriented programming
面向对象编程(OOP)的优势
• 数据封装(Bundle data):将数据和操作封装在对象中,通过定义良好的接口与外界交互。
• 模块化开发(Divide-and-conquer):可以单独实现和测试每个类的行为,降低系统复杂性。
• 代码复用:类的定义可以被重复使用,Java模块可以通过定义新类来扩展功能。
• 继承(inheritance):子类可以重写或扩展父类的行为,例如“车辆”类的子类可以是“汽车”、“公交车”、“自行车”。
Declaring and Defining a class
类的声明与定义
• 类的定义:类似于定义一个函数,告诉Java虚拟机(JVM)这个类包含的数据成员和操作。
• 数据成员:定义对象需要存储的数据。
• 成员函数:定义对象的功能(如方法)。
• 实例化对象(instances of objects):类似于调用函数时传递不同的参数,通过类创建对象实例。
Example: a point in a coordinate type system
类定义示例:坐标系统中的点
• 类声明:public class Point {} 用来声明一个名为 Point 的类。
• 数据成员(properties):private double x; private double y; 用来存储点的坐标。
• 成员函数(method/capability):例如计算两点之间的距离,可以使用 public double distance(Point p) 方法。
Each class goes in its own file, where name of file must match name of class
▶ E.g., Point class is contained in file “Point.java”
Classes and instances
实例化(Instantiation)
实例化意味着在程序中创建新的对象实例。
• 例如:Point pt = new Point(); 或 Point pt; pt = new Point();。
• 这表示 pt 是一个通过 Point 类的蓝图创建的特定实例。
内存结构:
• 在开始时,Point 类只是内存中的一个类定义。
• 当创建了 pt 这个实例时,一个“对象”就从内存中创建出来。
例如:
• 创建另一个对象:Point ptB = new Point();
• 这些都是存储在内存中的对象。
如何设置 x 和 y 的值?
• 默认情况下,创建的 Point 对象的 x 和 y 均为 0。接下来需要讨论如何设置这些私有属性的值。
设置/更改私有属性的值
几种修改私有属性的方式
1. 修改属性修饰符为 public:
• 这样做会允许用户直接访问或修改属性值(没有任何控制),因此 不推荐 这种做法。
2. 定义方法(Defines methode) 来获取和设置属性值:
• 通过getter和setter方法提供受控访问,允许用户间接访问或修改私有属性。
3. 定义构造函数(constructor) 来在对象创建时为数据成员赋值:
• 构造函数可以在创建对象时自动设置属性的初始值。
• 重点讨论第二种和第三种方式,即通过方法和构造函数来控制属性的访问和修改。
Constructor
构造函数
• 构造函数:是一种特殊的成员函数,其名称与类名相同,用于初始化对象的数据成员。
当一个对象被声明时,构造函数会自动被调用,用于初始化(initializes)对象的数据成员,执行初始化任务
- 默认构造函数(Default-Value Constrctor/default constructors):不接受任何参数,创建对象时赋予默认值(default values)。
To create new instances of a point using default constructor:
Point pt = new Point();
You can only have ONE default-value constructor
- 显式值构造函数(Explicit-Value Constructor):接受参数,用于设置对象的初始值。
To create new instance of a point with explicit-value constructor:
Point pt = new Point(3,4);
You can have as many explicit-value constructors as are necessary
Remark
构造函数的必要性
• 每个类必须有一个构造函数(constructor):每个类在创建对象时都需要一个构造函数,用于初始化对象的属性。
• 自动调用(automatically called):构造函数在对象创建时会自动调用,因此它是对象“出生”时必须执行的操作。
• 构造函数没有返回类型:构造函数不允许有返回类型,甚至不能写 void。构造函数的唯一功能是初始化对象。
默认构造函数的生成
• 如果程序员没有显式定义任何构造函数,编译器将自动生成(generated)一个“默认空构造函数(default empty constrctor)”。这个构造函数不会执行任何操作,但它可以确保对象的正确创建。
示例:
public Point() {
}
• 这个默认构造函数允许创建 Point 对象,而不设置任何初始值。
Get/Set data member values with methods
数据成员的获取与设置
检查器函数(Inspector functions)
Getter函数
• 概念:在Java中,检查器函数通常称为getter函数。它们允许程序员读取类的数据成员,但不允许修改它们。
• 作用:getter函数的主要功能是读取私有属性的值,而不允许直接修改。
• 代码示例:获取 Point 类中的 x 和 y 值的getter函数如下:
public double getX() {
return x;
}
public double getY() {
return y;
}
• 命名约定:在Java中,getter函数的名称通常以 get 开头,后跟属性名称。例如,getX() 用于获取 x 值,这就是为什么这些函数被称为getter函数。
修改器函数(Mutator functions)
Setter函数
• 概念:在Java中,修改器函数通常称为setter函数。它们允许程序员修改类的数据成员的值。
• 作用:setter函数用于设置或更新私有属性的值。
• 代码示例:设置 Point 类中的 x 和 y 值的setter函数如下:
public void setX(double newX) {
x = newX;
}
public void setY(double newY) {
y = newY;
}
• 命名约定:在Java中,setter函数的名称通常以 set 开头,后跟属性名称。例如,setX() 用于设置 x 值,这就是为什么这些函数被称为setter函数。
• 改进代码可读性:图片中提到,我们每次为setter提供新变量名(如newX)时,可以通过优化代码来提高可读性。
this 对象实例变量
this 关键字的概念
• 定义:this 是“当前实例”的缩写,表示类的当前实例,当前实例可以调用它自身的方法。
• 作用:this 关键字允许一个实例(如 pt)在它自己上调用自己的方法(on itself)。
• 使用场景:我们可以使用 this 来引用或调用类中已经存在的成员变量或方法。通过 this,可以避免变量名冲突。
代码示例
• 以下是使用 this 关键字的setter函数:
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
• 在这些代码中,this.x 表示当前对象的 x 值,x 是传入的参数。这避免了参数名与成员变量名相同导致的混淆。
• this 的应用范围:this 可以在类的所有方法中使用,包括构造函数。
TopHat Question
关于 this.getX(); 在 Point 类中的作用
1.其他对象不能调用 getX() 方法
• 分析:这个说法是错误的。getX() 是一个公共(public)的getter方法,其他对象是可以调用它的。只要该方法的访问修饰符为 public,其他类的对象就可以访问它。
2.当前 Point 类的实例正在调用另一个 Point 实例的 getX() 方法
• 分析:这个说法也是错误的。this.getX(); 只在当前对象的上下文中使用,表示调用当前对象本身的 getX() 方法,而不是调用其他对象的方法。
3.当前 Point 类的实例调用了它自身的 getX() 方法
• 分析:这是正确的。this.getX(); 的作用是让当前的 Point 实例调用它自身的 getX() 方法。这是 this 关键字的典型用途,用于引用当前实例的属性或方法。
4.this.getX(); 的调用不会出现在 Point 类的其他地方
• 分析:这个说法也是错误的。this.getX(); 可能会出现在 Point 类的多个方法中,特别是在需要获取当前对象的 x 值时。没有任何限制规定 this.getX(); 只能出现在一个地方。
Interacting with other objects
对象之间的交互
• 使用方法与其他对象交互:一个对象的方法可以接受其他对象作为输入并进行操作。例如,计算两点之间的距离:
public double distance(Point p) {
return Math.sqrt(Math.pow(x - p.x, 2) + Math.pow(y - p.y, 2));
}
面向对象程序设计的基本概念
定义:
• 面向对象程序设计是一种通过定义和实现对象类(Object Classes)来开发程序的方法。
• 程序由对象组成,这些对象通过彼此之间的交互来完成特定任务。
核心概念:
1. 对象类的定义和实现:
• 对象类可以相互关联并交互。
• 例如,一个 Polygon(多边形)类的对象可以通过调用 Pen(画笔)类的对象的方法,在屏幕上绘制多边形。
- 程序的作用:
• 创建、管理和销毁对象:
程序的核心在于管理对象的生命周期。
Java 中的自动内存管理: 对象的销毁由 JVM 的垃圾回收器(Garbage Collector)自动完成。
• 调用对象方法(方法调用):
通过对象的方法,解决问题或完成任务。
面向对象程序设计
• 高层设计(High-Level Design):
1. 分析并确定解决问题所需的数据和操作。
2. 定义对象类。
3. 设计程序结构和顶层算法。
• 低层设计(Low-Level Design):专注于每个对象类的内部实现细节和算法。
数据封装
• 封装性:
• 将数据和操作数据的方法绑定在一起,同时隐藏实现细节。
• 用户只能通过特定的接口(方法)访问数据。
访问修饰符:public、private 和 protected 用来控制类的成员对外的可见性。
• Getter 方法: 用于安全地读取私有数据。
• Setter 方法: 用于安全地更新私有数据,可以添加校验逻辑。
信息隐藏:
• 只暴露必要的接口,其余部分保持隐藏,避免外部依赖内部实现。
总结
• 类:类是对象的蓝图,定义了对象的属性和行为。
• 实例:实例是类的具体化,可以调用类中的方法。
• 方法声明与定义:方法是类的功能实现,包含访问修饰符、返回类型、方法名和参数列表。
• 面向对象程序设计:通过数据封装和信息隐藏来构建复杂系统,减少系统复杂度。