面向对象编程
面向对象编程语言 |
---|
面向对象编程(OOP)是一种设计程序和组织代码的方式。它不同于您可能一直使用的函数式编程范式。OOP 范式允许您定义具有特定行为的对象,以抽象和模拟“现实生活”中的行为。在 OOP 中,我们主要关注“是什么”,即对象的特性,而不是“如何”,即特定功能的算法实现。因此,程序设计将首先关注所需的行为,以确保程序结构正确,然后关注实现细节。[1]
Java 是您将在此选修课中学习的编程语言。在纸质考试(考试二)中,您需要阅读和编写 Java 代码。
D.1.1概述对象的普遍性质
对象表示物理世界中的概念、想法或任何实体。例如,玩家、棋盘、骰子等。这些对象将拥有自己的属性。例如,骰子可以被掷出并给出数字,但棋盘不能。通过对象属性及其关系的交互,我们可以在独立的代码结构中更好地模拟复杂问题。
在 Java 中,对象使用 class
关键字在类中定义。这里我们创建了 User
类。
class User {
}
对象由两部分组成
- 属性:对象的参数。例如,用户的姓名、用户语言、用户订阅等…
- 方法:对象的功能和行为。例如,用户可以向某人打招呼、提问、提出索赔等…
让我们为我们的“用户”对象添加一个 name
属性和一个 greet()
方法。
class User {
// The attribute
private String name;
// The method
public void greet(){
System.out.println("Hello");
}
public
和 private
关键字在封装部分进行了解释
现在,让我们看看如何创建和使用我们的对象。但是,在我们开始之前,我们需要定义一个特殊的方法,称为构造函数。构造函数方法将允许我们创建对象的实例。类的实例意味着一个具有自己的内存位置并且可以使用的实际对象。
当我们初始化一个新的对象实例时,会调用构造函数方法。它们允许我们为属性设置初始值。
构造函数方法的名称始终与我们的类名相同,并且不能具有返回类型。这是一个演示
class User {
// The attribute
private String name;
// The constructor
public User(String name){
this.name = name;
}
// The method
public void greet(){
System.out.println("Hello" + " " + this.name);
}
}
请注意,所有类都默认具有构造函数:如果您没有自己创建类构造函数,Java 会为您创建一个。但是,在这种情况下,您将无法再为对象属性设置初始值。这意味着我们可以在此处创建 User(),但我们无法在实例化时设置其名称。
我们还可以注意到 this
关键字的使用。this
关键字在方法或构造函数中引用当前对象。(this
关键字最常见的用法是消除类属性和同名参数之间的混淆,例如当类属性被方法或构造函数参数隐藏时)。[2]
现在让我们实例化我们的 User 对象
class Main {
public static void main(String[] args) {
// Instantiating a User Object with name "John"
User u = new User("John");
// Using the User's method greet()
u.greet();
}
}
在此代码中,我们不在 User
类中。我们在 main()
方法中编写了代码,它充当 Java 程序的入口点,并且是在运行程序时将要执行的方法,位于 Main 类中。
您还可以看到我们使用了 new
关键字来创建我们的 User 实例 u
。此语法类似于我们在 Java 中实例化引用类型时使用的语法。因此,可以将定义类视为编写您自己的用户定义的引用类型。new
关键字之后的 User("John")
是对构造函数方法的调用,其中名称参数在此处为“John”。
实例化我们的 User u
后,我们使用点表示法使用了其可用的方法 greet()
。
如果我们编译我们的 User.java
和 Main.java
文件,然后运行我们编译的 Main.java 文件,那么我们在终端上看到以下结果
Hello John
D.1.2区分对象(定义、模板或类)和实例化。
要使用对象或类,必须先实例化对象(除非使用静态类)。实例化一个类意味着为对象创建内存中的位置。对象中包含的值将对每个对象的实例都是唯一的。类的每个实例通常都具有相同的功能,但这些功能可以根据属性的个别值而表现出不同的行为。
D.1.10 描述如何将数据项作为参数传递给操作和从操作传递数据项。
在我们之前的示例中,我们使用构造方法传递了一个值 'John'
,将其设置为 User
类中的属性 name
。
但是,如果我们想稍后修改该名称怎么办?(也许我们拼写错误了。)为了轻松修改属性,我们通常定义访问器和修改器方法(这些方法有时也称为 getter 和 setter 方法)。
它们看起来像这样
class User {
// The attribute
private String name;
// The constructor
public User(String name) {
this.name = name;
}
// The accessor method
public String getName() {
return this.name;
}
// The mutator method
public void setName(String newName) {
this.name = newName;
}
// The greet method
public void greet() {
System.out.println("Hello" + " " + name);
}
}
按照惯例,我们通常将访问器方法名称定义为“get”+ 我们想要获取的属性的名称。类似地,对于定义修改器方法名称,我们使用“set”+ 我们想要更改的属性的名称。但是,如果存在使代码更易于阅读和理解的命名方式,我们可以偏离该命名约定。
我们可以像这样在 Main
类上使用访问器和修改器方法
class Main {
public static void main(String[] args) {
// Instantiating a User Object with name "John"
User u = new User("John");
// Checking our current name attribute
System.out.println(u.getName());
// Changing our current name attribute
u.setName("Jonathan");
// Using our greet() method
u.greet();
}
}
这将输出
John
Hello Jonathan
在 OOP 中,构造函数、访问器和修改器等方法是修改对象属性的首选方式。与删除和重新创建实例相比,它们提供了更大的灵活性,并且还允许更好的封装,我们将在后续章节中看到这一点。
D.1.8 为给定问题构建相关的对象。
练习
假设您想编写一个数字时钟应用程序
- Clock 对象将有哪些属性?
- Clock 对象将有哪些方法?
- 打开 IDE 并尝试编写此 Clock 对象的代码。
- 标准 Java 库中是否已存在 Clock 对象?
D.1.5 描述分解成几个相关对象的流程。
计算机科学中的分解,也称为分解,是指将复杂问题或系统分解成更易于构思、理解、编程和维护的部分的过程。面向对象的分解将大型系统分解成越来越小的类或对象,这些类或对象负责问题域的某些部分。
对于您的问题,没有“一种”方法可以实现最佳的 OOP 结构,但有一些可以应用的设计流程来提供帮助。大多数这些流程都可以在主题 1 中看到,例如迭代开发、敏捷开发、测试驱动开发……
使用 UML 来表示您的对象及其关系也是设计过程的重要组成部分。(见下文)
D.1.6 描述给定问题中对象之间的关系。
为了能够开发出解决复杂问题的解决方案,我们将需要在项目期间定义不同的对象,并且这些对象将相互交互。
对象主要有三种关系
- “has-a”关系,表示类的属性将是另一个类的实例。例如,Book 对象将包含 Author 对象,或者 HighSchoolClass 对象将包含 Students 和 Teacher。
- “is-a”关系,也称为继承,表示一个对象是另一个对象的子类别。它是对上层类别的更精确的定义。例如,Student 对象扩展了 Human 对象,Car 对象扩展了 Vehicle 对象,或者 Dog 对象扩展了 Animal 对象。
- “uses-a”关系,表示一个对象将在其方法之一中使用一个类的实例。例如,Student 对象在阅读时可能会使用 Book 对象,Shelf 对象在构建时可能会使用钻头,而 Human 对象在游泳时可能会使用 Swimsuit 对象和 Towel 对象。
让我们看一些这三种关系的代码示例。
让我们以 Book 对象为例,它将 Author 对象作为属性。首先让我们定义我们的 Author 类
class Author {
private String firstName;
private String lastName;
public Author(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getName(){
return firstName + " " + lastName;
}
}
现在让我们定义我们的 Book 类
class Book {
private String title;
private int year;
private Author author; //has-a relationship
public Book(String title, int year, Author author) {
this.title = title;
this.year = year;
this.author = author; // the Auhtor instance will be given in the constructor method
}
public void getBookInfos(){
String infos = String.format("The book \"%s\" was written by %s" + " in %d",
this.title, this.author.getName(), this.year); // we use the Authors's method getName() here
System.out.println(infos);
}
public static void main(String[] args) {
//We directly construct our author instance while constructing the book instance
Book b = new Book("Nineteen Eighty-Four", 1949, new Author("George", "Orwell"));
b.getBookInfos();
}
}
如果我们编译我们的 Book.java
和 Author.java
,然后运行我们的 Book.class
文件(因为它包含 main()
方法),我们将获得以下输出
The book "Nineteen Eighty-Four" was written by George Orwell in 1949
练习:尝试在 HighSchoolClass 对象、Students 对象和 Teachers 对象之间定义“has-a”关系。提示:您可能希望定义一个包含多个 Student 对象的 Students 对象。这称为聚合类(对于 Students)。
让我们以 Student
类扩展 Human
类为例。
为此,让我们定义一个非常简单的 Human
类。此 Human
类具有一个属性 name
和一个方法 greet()
。
class Human {
protected String name;
public Human(String name) {
this.name = name;
}
public void greet(){
System.out.printf("Hello I am %s%n",this.name);
}
}
现在,如果我们将 Student
类定义为 Human
的子类(因为所有学生都是人类),这意味着 Student
类将能够访问 name
属性和 greet()
方法,而无需重新定义它们。Student
类还可以具有其他属性或方法。
为了表示从 Human
到 Student
的“is-a”关系,我们在定义子类时使用 extends
关键字。
因此,我们的 Student
类将如下所示
class Student extends Human {
private String school;
public Student(String name, String school) {
super(name); // we refer to the superclasse's constructor
this.school = school;
}
public void studentGreet(){
System.out.printf("Hello I am student %s from %s.%n",this.name, this.school);
}
public void transfers(String newSchool){
this.school = newSchool;
}
public static void main(String[] args) {
Human h = new Human("Lisa");
h.greet();
Student s = new Student("Zara", "H4");
s.greet(); //uses the Human method greet()
s.transfers("EJM");
s.studentGreet(); // uses the Student method studentGreet();
}
}
当我们编译然后运行我们的 Student.java
代码时,我们得到
Hello I am Lisa
Hello I am Zara
Hello I am student Zara from EJM.
请注意,我们也可以选择将我们的 Student
方法命名为 studentGreet()
,而不是 greet()
。在这种情况下,Student
对象将始终执行 greet()
方法的学生版本,而不是 Human
版本。这称为覆盖。我们将在专门的部分中学习继承的更多方面。
练习:尝试在 Animal
类和 Bird
类之间定义“is-a”关系。
让我们研究一个 Student
对象在其方法 reading()
中使用 Book
对象的示例。首先,让我们定义 Book
类
class Book {
String title;
int currentPage = 1;
Book(String title) {
this.title = title;
}
String getTitle(){
return this.title;
}
int getCurrentPage(){
return this.currentPage;
}
void setCurrentPage(int newPageNumber) {
this.currentPage = newPageNumber;
}
}
Book
对象非常简单,它存储书名,我们可以访问书名和当前所在的页码。现在让我们定义 Student
类。Student
将有一个方法 reading()
,它将使用 Book
对象来工作。
class Student {
String name;
Student(String name) {
this.name = name;
}
void reading(Book b){
System.out.printf("Currently reading \"%s\" at page %d.%n", b.getTitle(), b.getCurrentPage());
b.setCurrentPage(b.getCurrentPage() + 1);
}
public static void main(String[] args) {
Book b = new Book("Clean Code");
Student s = new Student("Lisa");
s.reading(b);
s.reading(b);
s.reading(b);
}
}
因此,现在如果我们编译并运行我们的 Student.java
代码,我们将获得以下输出
Currently reading "Clean Code" at page 1.
Currently reading "Clean Code" at page 2.
Currently reading "Clean Code" at page 3.
但是为什么 Book
对象是 reading()
方法的参数而不是作为属性存储?好吧,Book
不是 Student
的必要组成部分,它没有定义 Student
的属性。它偶尔用于特定任务,因此最好将其作为参数传递。
还需要注意的是,b.setCurrentPage(b.getCurrentPage() + 1);
在它正在做什么方面不是很明确。定义一个 turnPage()
方法在这里可能更容易阅读,并且是更好的“整洁代码”实践。
练习:尝试在 Human
类和 Bike
类之间定义“uses-a”关系,在 commuting()
方法中。
UML 的创建最初是为了标准化不同的符号系统和软件设计方法。它是先前对象建模语言(Booch、OMT、OOSE)的综合,主要源于 Grady Booch、James Rumbaugh 和 Ivar Jacobson 的工作成果。它于 1994 年至 1995 年在 Rational Software 开发,并在 1996 年由他们领导进一步开发。1997 年,UML 被对象管理组 (OMG) 采用为标准,并从那时起一直由该组织管理。2005 年,UML 也由国际标准化组织 (ISO) 发布为经批准的 ISO 标准。从那时起,该标准定期修订以涵盖 UML 的最新修订版
统一建模语言 (UML) 是一种基于象形图和视觉元素的图形建模语言。它旨在成为软件开发和面向对象设计领域中标准化的可视化方法。
UML 旨在在整个软件开发生命周期中支持软件开发过程。因此,它提供了许多不同的图表模板,例如部署图、用例图、时序图等......我们对 OOP 和 IB 文凭感兴趣的是类图。它通过显示系统的类、它们的属性、方法以及对象之间的关系来描述系统的结构。
让我们看看类图中使用的不同图形元素。
在图中,类用包含三个隔间的框表示
- 顶部隔间包含类的名称。它以粗体居中打印,并且第一个字母大写。
- 中间隔间包含类的属性。它们左对齐,第一个字母小写。
- 底部隔间包含类可以执行的操作。它们也左对齐,第一个字母小写。
要指定类成员(即任何属性或方法)的可见性,必须在成员名称前放置以下这些符号。
+
|
公有(Public) |
-
|
私有(Private) |
#
|
受保护(Protected) |
~
|
包级(Package) |
上面的 UML 图是 "Book" 类,有两个私有属性:"title" 类型为 String,"currentPage" 类型为 int,默认为 0。类中包含两个方法 "turnPage" 和 "getTitle",均不接受参数。
UML 定义了以下关系
- 关联(Association): 表示一个类正在使用另一个类的实例。它是一种 "has-a" 关系。当看到一个属性是另一个类的实例时,就可以识别出关联关系。例如,一个 Music 对象将有一个 Guitar 对象的实例作为属性,以便它可以在其方法中使用吉他来产生音乐。它用一个从使用另一个类的类发出的简单黑色箭头表示。
- 聚合(Aggregation): 表示一个类包含了另一个类的多个实例。它是一种 "has-a" 关系。例如,一个 Class 将包含多个 Student 实例。当有一个属性是另一个类的实例的集合时,就可以识别出聚合关系。UML 中用一个指向包含另一个类多个实例的类的空心菱形表示聚合关系。
- 继承(Inheritance): 表示正在发生超类/子类关系。它是一种 "is-a" 关系。请参阅下面的部分以了解更多关于继承的信息。它用一个指向超类并起源于子类的白色箭头表示。
- 依赖(Dependency): 表示一个类依赖于另一个类来执行特定操作。它是一种 "uses-a" 关系。例如,一个 Student 对象需要一个 Swimsuit 实例才能去游泳。在代码中,当另一个类的实例被用作方法的参数时,可以识别出依赖关系。它用一个从使用另一个类的类发出的带虚线的简单黑色箭头表示。
D.1.3 构建统一建模语言 (UML) 图以表示对象设计。
D.1.4 解释 UML 图。
class Bike{
private String model;
private float speed;
public Bike(String model, float speed) {
this.model = model;
this.speed = speed;
}
public String getModel() {
return model;
}
public float getSpeed() {
return speed;
}
}
class Human {
private String name;
public Human(String name){
this.name = name;
}
// this method is our "uses-a" relationship (or association in UML)
public void commuting(Bike myBike){
System.out.printf("Currently riding a %s bike at %f km/h !%n", myBike.getModel(), myBike.getSpeed());
}
public void greet(){
System.out.printf("Hi I'm %s !", this.name);
}
}
public class Main {
public static void main(String[] args) {
// instance 1 of Bike class
Bike myBike = new Bike("electric", 25);
// instance 2 of Bike class
Bike rental_bike = new Bike("velib", 18.2f);
// instance of Human
Human h = new Human("Lisa");
h.greet();
// using a first bike to commute
h.commuting(myBike);
System.out.println("Arrived at work ! Catching my breath ...");
// using another bike to commute
h.commuting(rental_bike);
System.out.println("Arrived home! Catching my breath ...");
}
}
输出
Hi I'm Lisa !Currently riding a electric bike at 25,000000 km/h !
Arrived at work ! Catching my breath ...
Currently riding a velib bike at 18,200001 km/h !
Arrived home! Catching my breath ...
D.1.7 概述在给定问题中减少对象之间依赖性的必要性。
- 它增加了维护开销。
- 维护开销是指如果对组件进行更改,则需要对整个系统进行的更改。
例如,如果更改 B,则会影响 C、D 和 E 的工作方式,这意味着您必须花费时间修复它们以与“新的”B 协同工作。
D.1.9 解释表示数据项需要不同数据类型的原因。
- char:机器可寻址的最小单元,可以包含基本字符集。它是一种整数类型。实际类型可以是有符号或无符号的,具体取决于实现。
- int:基本的带符号整数类型。至少在 [−32767,+32767] 范围内,因此大小至少为 16 位。
- float:单精度浮点类型。实际属性未指定(除了最小限制),但在大多数系统上,这是 IEEE 754 单精度二进制浮点格式。
- string:保存从 0 到 65535 范围内的无符号 16 位(2 字节)代码点的序列。每个代码点或字符代码都表示单个 Unicode 字符。字符串可以包含 0 到大约 20 亿 (2 ^ 31) 个 Unicode 字符。
D.2.1 定义封装术语。
封装被定义为“将代码和数据封装到一个单元中的过程”。但这意味着什么呢?从实际意义上讲,封装是通过设置属性和方法的正确访问级别来实现的。您可以将其视为对象的保护盾:我们保护内部机制,并且只公开公众可以使用的某些方法。这就像启动计算机时一样,“开机”按钮隐藏了用户看不到的后台所有启动过程。最终用户不关心实现的细节,也不应该被这些细节打扰。
为了实现封装,我们有以下访问修饰符供我们使用。这些可以为属性和方法设置,并且应该明确指定,而不是使用默认访问修饰符。
- 私有(Private): 最严格的关键字;只能在声明的类中访问。
- 受保护(Protected): 当声明时,只能被另一个包中的子类或成员类中的任何类访问。
- 公有(Public): 可以从任何其他类访问。它是限制最少的关键字。
- 默认(Default): 如果您没有指定任何访问修饰符,则代码只能被同一包中的类访问。如果您没有声明任何包,则意味着您的代码只能在当前项目中访问。
在大多数情况下,我们希望将属性设置为私有,并将访问器和修改器设置为公有。(实际上,这允许更好地控制对属性进行的修改(无直接修改),并有助于调试、管理不需要的行为以及更清晰易读的代码。)
让我们使用我们的第一个User
示例来检查这些访问修饰符的行为。
公有属性
class User {
public String name;
public User(String name){
this.name = name;
}
public void greet(){
System.out.println("Hello" + " " + this.name);
}
}
class Main {
public static void main(String[] args) {
User u = new User("Lily");
u.name = "Joshua";
u.greet();
}
}
编译并运行我们的Main.java
时,我们得到以下输出
Hello Joshua
拥有一个public
属性意味着我们可以从另一个类直接更改属性u.name = "Joshua"
的值。
私有属性
如果我们尝试编译并运行相同的Main.java
,但在User.java
中使用属性private String name;
,则会收到以下错误
java: name has private access in User
实际上,如果属性是私有的,则意味着我们不能从另一个类直接修改它。如果可用,我们将不得不使用公有的访问器和修改器方法。但是,我们仍然可以在其类内部使用该属性,就像我们在greet()
方法中所做的那样。
私有方法
如果我们在Student.java
中将我们的greet()
方法设为私有,并尝试在我们的Main.java
文件中调用u.greet()
,则会收到以下错误
java: greet() has private access in User
实际上,如果greet()
方法是私有的,则意味着任何外部类都无法访问它。我们使用私有方法的唯一方法是在同一类中的另一个方法中,或者如果main()
方法是从该类运行的。
公有方法 公有方法可从外部类访问,并按“预期方式”执行;
D.2.4 解释封装的优点。
封装提供了几个优点
- 它提供了数据隐藏,这意味着对某些信息的访问受到控制和限制。这样,用户(其他类)只能看到其级别的可用操作,而不必担心实现细节。因此,属性的数据管理被隐藏起来。
- 它提供了对数据管理的更好控制,因为如果用户操作受到限制,则可能出现较少的意外行为。
- 它通过保持数据私有并提供公共定义良好的服务方法来提高可用性,对象的职责对于其他对象变得清晰。
- 它提供了灵活性,因为我们可以根据需要和用例更改访问修饰符。
- 它有助于我们拥有易于通过单元测试进行测试的代码。
- 它使代码更易于重用,因为我们本质上关心的是存在哪些方法,而不是实现。我们可以在不更改代码结构的情况下更改方法的实现。它促进了维护,因为代码更改可以独立进行。
D.2.2 定义继承术语。
继承是一个概念,其中子类(也称为子类或派生类)继承自超类(也称为父类或基类)。子类是一个类,它将描述比超类更精确的对象类型。子类可以访问超类的方法和属性。
让我们使用我们之前的超类Human
和子类Student
。
class Human {
protected String name;
public Human(String name) {
this.name = name;
}
public void greet(){
System.out.printf("Hello I am %s%n",this.name);
}
}
class Student extends Human {
private String school;
public Student(String name, String school) {
super(name); // we refer to the superclasse's constructor
this.school = school;
}
public void studentGreet(){
System.out.printf("Hello I am student %s from %s.%n",this.name, this.school);
}
public void transfers(String newSchool){
this.school = newSchool;
}
public static void main(String[] args) {
Human h = new Human("Lisa");
h.greet();
Student s = new Student("Zara", "H4");
s.greet(); //uses the Human method greet()
s.transfers("EJM");
s.studentGreet(); // uses the Student method studentGreet();
}
}
当我们编译然后运行我们的 Student.java
代码时,我们得到
Hello I am Lisa
Hello I am Zara
Hello I am student Zara from EJM.
这里,Student
类是 Human
的子类(因为所有学生都是人类)。由于访问修饰符的存在,Student
类无需重新定义即可访问 name
属性和 greet()
方法。实际上,子类不会继承其父类的 private
成员。只有 public
或 protected
成员会被继承。但是,如果超类具有用于访问其 private
属性的 public
或 protected
方法,则子类也可以使用这些方法。
Student
类还可以具有其他属性或方法。这里 Student
有一个额外的 schoolName
属性和两个额外的方法:transfers()
和 studentGreet()
要定义子类,我们使用 extends
关键字。该关键字仅在子类中使用,而不是在超类中使用。这里 class Student extends Human
构造函数不是类的成员,因此不会被子类继承,但可以从子类中调用超类的构造函数。为了实现这一点,我们使用了 super
关键字。super
关键字允许我们访问超类的属性、方法和构造函数。这里,在我们的 Student 构造函数中,我们通过调用 super(name)
来引用 Human 的构造函数。
要了解更多关于 super
关键字的信息:https://www.programiz.com/java-programming/super-keyword
子类也可以成为另一个子类的超类。这称为**多级继承**。例如,我们可以有:Student 扩展 Human,Human 扩展 Mammal,Mammal 扩展 Animal。这里 Human 是 Mammal 的子类,但 Human 也是 Student 的超类。**在 Java 中,类仅支持单一继承**,因此我们一次只能扩展一个类(我们将编写 class Student extends Human
而不是 class Student extends Mammal extends Animal
。要查看全局继承模式,UML 图非常有用。
一个超类可以被多个类扩展。这称为**层次继承**。例如,Human 类可以被 Student 类扩展,但 Human 类也可以同时被 Teacher 类扩展。同样,要查看全局继承模式,UML 图非常有用。
在重写方法时,使用 @Override
注解是一种良好的代码规范,因为它使重写变得明显且不隐蔽。
当我们在子类中定义一个属性或方法,且其**名称**与超类中的相同,就会发生重写。如果发生这种情况,则超类的属性或方法将被**隐藏**,并且子类的实例将无法再访问它。
例如,如果我们在 Student
类中将 studentGreet()
定义为 greet()
,那么每次我们在 Student
实例上调用 greet()
方法时,都会收到一条包含学生姓名和学校的信息。从 Student
实例中,我们将无法再获得仅包含学生姓名的问候语消息。但是,从 Human
实例中,当我们调用 greet()
时,我们将获得仅包含姓名的问候语消息。
当子类需要比超类更精确地定义某种行为时,重写非常有用。
要了解更多关于 Java 中注解的信息:https://www.programiz.com/java-programming/annotations
**练习:**将 studentGreet()
方法重命名为 greet()
,然后尝试从 Student 实例和 Human 实例调用此方法。会发生什么情况?
所有 Java 对象都源自 Object
类型。
由于 Student 对象也是 Human 对象,因此当需要 Human 对象时可以使用 Student 对象。另一方面,不能将 Human 对象作为 Student 对象来使用。没有什么可以阻止我们编写:Human n = (Human) s
,其中 s
是 Student 实例。这里甚至不需要显式强制转换。从子类到超类的强制转换称为**向上转型**。
**练习:**尝试实现 Teacher 类作为 Human 的子类。
**D.2.5** 解释继承的优点。
继承的主要优点是它允许**代码重用**。超类的代码不需要在子类中重写,从而节省了大量时间并避免了代码重复和错误。
**重写**也是一个很大的优点,因为它提供了**模块化**来涵盖更多继承案例。实际上,可以隐藏超类的细节,以便只关注子类当前更具体的行为。方法重写也称为运行时多态。
**D.2.3** 定义多态这个术语。
多态是以相同的方式访问不同类型多个对象的方法。为此,您将使用一个多态类型,该类型可以以也适用于所有其他对象类型的方式访问。这通常与继承结合使用,其中多个不同的子类可以作为父类类型访问,并提供对父类中所有功能或子类重载的功能的访问。多态子类不允许访问子类中添加的功能。
**D.2.6** 解释多态的优点。
- 可以使用相同的接口创建具有不同实现的方法。
- 减少了区分和处理各种对象的工作量。
- 支持构建可扩展的系统。
- 可以使用相同的方法签名替换完整的实现。
**D.2.7** 描述对象库的优点。
可以导入对象库以避免“重新发明轮子”。应用程序执行的许多常见任务(例如排序)已经编写了经过良好测试的库来执行它们。在获得许可的情况下,可以将其他开发人员的代码与您的代码一起交付,以节省开发时间。这也减少了测试范围,因为经过良好测试的库不太可能出现错误。
**D.2.8** 描述 OOP 的缺点。
面向对象程序的文件大小往往比其他程序大得多。这在早期存储空间有限的时代尤其重要,或者在存储空间仍然非常有限的嵌入式系统中。面向对象程序往往比线性程序慢,因为计算机需要遍历不同的类并理解它们之间的关系。OO 编程语言往往具有陡峭的学习曲线。最后,面向对象程序的创建往往需要付出更多努力,因为它们需要仔细的计划和概念化。
**D.2.9** 讨论编程团队的使用。
团队的建立是为了作为一个团队完成更大的任务 团队使用的优点
- 更多成员意味着更多想法,包括如果没有团队可能不会产生的想法。
- 一些人的优势可以弥补其他人的不足。
- 随着解决问题“人力”的增加,可以承担更大规模的问题和项目。
团队使用的缺点
- 如果处理不当,一些成员的弱点可能会破坏整个团队。
- 不同的编程和工作风格必须学会协调并一起工作。
- 成员之间的沟通必然成为团队的首要任务。
**D.2.10** 解释模块化在程序开发中的优势。
- 如果开发了一个单独的过程,它可以多次重复使用,而无需重新键入代码。
- 由于将整个代码划分为多个部分,程序可以更容易、更有效地设计,因为一个小团队只处理整个代码的一小部分。
- 模块化编程允许许多程序员协作开发同一个应用程序。
- 代码存储在多个文件中;代码也简短、简单且易于理解。
- 变量的作用域可以轻松控制。
- 当错误局限于子程序或函数时,可以轻松识别它们。
**D.3.1** 定义以下术语:类、标识符、基本类型、实例变量、参数变量、局部变量。
**类**:类是创建单个对象的蓝图。
**标识符**:标识符是变量、方法、类、包和接口的名称。它们是引用特定事物的途径。
**基本类型**:由语言预定义并命名为保留关键字的值。
**D.3.2** 定义以下术语:方法、访问器、修改器、构造函数、签名、返回值。
**方法**:方法是执行 Java 代码的程序的一部分。不要将其与构造方法混淆。
**访问器**:访问器方法是用于获取有关对象的信息并用于访问该对象中的数据的方法。
**修改器**:修改器方法是用于设置无法公开访问的私有字段的值的方法。
**构造函数**:构造方法是创建类的一个实例的方法。
**签名**:指的是方法名称及其参数的数量和类型。返回类型和抛出的异常不被视为签名的一部分。
**返回值**:它是程序运行后返回的值或数据类型。
**D.3.3** 定义以下术语:private、protected、public、extends、static。
私有(Private): 最严格的关键字;只能在声明的类中访问。
受保护(Protected): 当声明时,只能被另一个包中的子类或成员类中的任何类访问。
**Public**:可以从任何其他类访问。
**Extends**:当您希望一个类或对象从另一个类或对象继承时,将使用此 Java 关键字。
**Static**:此关键字表示成员变量或方法可以在不需要实例化其所属的类的情况下访问。
**D.3.4** 描述基本数据类型和引用类字符串的使用。
在考试问题中,基本类型将限于 int、long、double、char 和 Boolean。
基本数据类型最常用于存储简单值。它们也用作更复杂抽象数据类型的构建块。
虽然 String 不是基本数据类型,但它被认为是一种“基本”类型,因为它用于存储简单值。
**D.3.5** 构建代码以实现评估语句 D.3.1 - D.3.4。
**D.3.6** 构建与选择语句相关的代码示例。
IF/ELSE 布尔条件,例如 WHILE list.hasNext()
D.3.7 构建与循环语句相关的代码示例。
FOR循环 WHILE循环
D.3.8 构建与静态数组相关的代码示例。
- 从数组中读取数据
- 将数据移入/移出数组
- 打印出数组中的选定部分……等等。
D.3.9 讨论现代编程语言中支持国际化的特性。
- 专门的文本启用
- 排序行为
- 日期和时间格式
- 键盘布局
- UTF-8 编码
- 在许多平台和语言中使用通用字符集,例如 UNICODE
- 平台无关的高级语言(如 Java)使代码能够在许多平台上运行
D.3.10 讨论程序员的伦理和道德义务。
- 对产品进行充分的测试,以防止可能造成的商业或其他损害
- 承认其他程序员的工作(避免剽窃)
- 开源运动
- 机器人和人工智能
- 对产品进行充分的测试,以防止可能造成的商业或其他损害
- 承认其他程序员的工作(避免剽窃)
- 开源运动
- 机器人和人工智能
D.4.1 定义递归的含义。
D.4.2 描述递归算法的应用。
D.4.3 构建使用递归的算法。
D.4.4 追踪递归算法。
D.4.5 定义对象引用的含义。
D.4.6 构建使用引用机制的算法。
D.4.7 识别抽象数据类型 (ADT) 列表的特征。
D.4.8 描述列表的应用。
D.4.9 使用列表的静态实现构建算法。
D.4.10 使用对象引用构建列表算法。
D.4.11 使用 JETS 中包含的标准库集合构建算法。
D.4.12 追踪使用评估陈述 D.4.9 - D.4.11 中描述的实现的算法。
D.4.13 解释使用库集合的优势。
D.4.14 概述 ADT 栈、队列和二叉树的特征。
D.4.15 解释代码中样式和命名规范的重要性。
- ↑ Java OOP 做得好 使用现代 Java 创建您可以引以为豪的面向对象代码 Alan Mellor https://leanpub.com/javaoopdoneright
- ↑ https://w3schools.org.cn/java/ref_keyword_this.asp
- ↑ https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html