ACCP6.0 S2 使用Java实现面向对象编程 第一章

更新时间:2024-01-12 03:46:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

1.1用面向对象设计电子宠物系统 1.1.1 为什么使用面向对象

现实世界就是“面向对象的”。现实世界中的任何事物都可以看作是“对象”,比如人、建筑、交通工具、学习用品等。而事物都有自己的属性和行为。比如人,它具有各种属性:姓名、性别、身高、体重、籍贯等,还可以做很多事情:吃饭、睡觉、劳动、锻炼等。各个事物之间还会发生各种联系,人用木材可以做成一套家具,人用笔可以写出文章等。

面向对象就是采用“现实模拟”的方法设计和开发程序。计算机软件开发规模越来越大,解决的问题也越来越复杂,导致软件开发时间、软件开发成本、软件维护费用甚至软件开发质量等日益难以控制。而面向对象技术利用“面向对象的思想”去描述“面向对象的世界”,实现了虚拟世界和现实世界的一致性,符合人们的思维习惯,使得客户和软件设计开发人员之间、软件设计开发人员内部交流更加顺畅,同时还带来了代码重用性高、可靠性高等优点,大大提高了软件尤其是大型软件的设计和开发效率。

问答

问题:面向过程和面向对象有什么区别?

解答:我们要举办一次北大青鸟的联欢晚会。如果采用面向过程的话,就是全体人员合唱青鸟之歌→主持人宣布晚会开始→领导讲话→主持人过场→演员一表演→主持人过场→演员二表演??→最后一位演员表演→主持人宣布晚会结束,即从头至尾、自上而下的实现功能。而如果采用面向对象实现的话,首先分析晚会需要哪些类:领导、主持人和演员。然后分析各种类的行为:主持人有宣布晚会开始、过场、宣布晚会结束,当然也有唱青鸟之歌。领导有讲话、唱青鸟之歌。演员主要就是表演节目,也有唱青鸟之歌。然后就利用设计好的类创建对应对象,调用相应的方法(行为)来逐步进行晚会。

面向过程的核心概念是函数,以功能为中心,实现了函数级别的代码重用。面向对象的核心概念是封装了属性和方法(行为)的类,以数据为中心,实现了类级别的代码重用。面向对象因为采用了类,具有继承和多态特性,可以进一步重用代码和简化编程,而面向过程中没有继承和多态特性。

1.1.2 使用面向对象进行设计

下面就开始电子宠物系统的设计和开发之路吧,这一章的任务是用类来描述宠物,然后实现领养宠物功能。首先需要根据需求进行面向对象的设计。

问题:

我们要设计一个电子宠物系统,其中领养宠物功能的详细需求如下。 根据控制台提示,输入领养宠物的昵称。

根据控制台提示,选择领养宠物的类型,有两种选择:狗狗和企鹅。 如果类型选择狗狗,要选择狗狗的品种,有两种选择:“聪明的拉布拉多犬”或者“酷酷的雪纳瑞”。

如果类型选择企鹅,要选择企鹅的性别:“Q仔”或“Q妹”。 所领养宠物的健康值默认是100,表示非常健康。

所领养宠物和主人的亲密度默认是0,表示和主人还不熟悉。

在控制台打印出宠物信息,包括昵称、健康值、亲密度、品种或性别,表示领养成功。

如何依据需求,使用面向对象思想来设计我们的电子宠物系统呢?

分析

面向对象设计的过程就是抽象的过程,我们分三步来完成。 第一步:发现类。

第二步:发现类的属性。 第三步:发现类的方法。

面向对象设计的过程就是抽象的过程,根据业务需求,关注与业务相关的属性和行为,忽略不必要的属性和行为,由现实世界中“对象”抽象出软件开发中的“对象”,如图1.2所示。

图1.2 面向对象设计的过程就是抽象的过程

接下来我们就按照发现类、发现类的属性和发现类的方法的步骤完成设计。 我们可以通过在需求中找出名词的方式确定类的属性,找出动词的方式确定方法。并根据需求实现业务的相关程度进行筛选。

第一步:发现类。

需求中名词有控制台、宠物、昵称、狗狗、企鹅、类型、品种、聪明的拉布拉多犬、酷酷的雪纳瑞、性别、Q仔、Q妹、健康值、亲密度和主人等。

根据仔细筛选,发现可以作为类的名词有宠物、狗狗、企鹅和主人。本章要实现领养宠物功能,主要用到两个类:狗狗(Dog)和企鹅(Penguin)。宠物和主人在完善设计和增加功能时再使用。

第二步:发现类的属性。

需求中动词主要有输入、选择、领养、打印等。某些明显与设计无关、不重要的词语可以直接忽略。

通过仔细筛选,发现可作为属性的名词有昵称、健康值、亲密度品种和性别,还有一些名词是作为属性值存在的,例如聪明的拉布拉多犬、酷酷的雪纳瑞是品种的属性值,Q仔和Q妹是性别的属性值。

根据需求,定义狗狗类的属性有昵称(name)、健康值(health)、亲密度(love)和品种(strain)。企鹅类的属性有昵称(name)、健康值(health)、亲密度(love)和性别(sex)。狗狗和企鹅的某些属性,例如年龄、体重、颜色等与领养业务需求无关,不予设置。

第三步:发现类的方法。 通过仔细筛选,发现类的方法主要是打印宠物信息。狗狗和企鹅的方法主要就是打印出自己的信息,取名为print()。至于狗狗睡觉、洗澡等行为,企鹅吃饭、游泳等行为,与领养业务需求无关,现在先不为其设定方法,在后续业务中若有需求再添加。

设计是一个逐步调整、完善的过程,类图是面向对象设计的“图纸”,使用“图纸”进行设计方便沟通和修改。将设计的结果通过类图来表示,如图1.3和图1.4所示。

图1.3 Dog类图 图1.4 Penguin类图 小结

抽象时遵循的原则。

属性、方法的设置是为了解决业务问题的。 关注主要属性、方法。

如没有必要,无增加额外的类、属性与方法。

1.2 通过创建对象实现领养宠物功能 1.2.1 创建类的对象

已经设计出了类及其属性和方法,下面需要把类图表示的内容转变为Java的类代码。 狗狗类的代码如示例1所示。 示例1 /**

*宠物狗狗类。 *@author 北大青鸟 */

public class Dog{

String name = “无名氏”; //昵称,默认值是“无名氏” int health = 100; //健康值,默认值是100 int love = 0; //亲密度

String strain = “聪明的拉布拉多犬”; //品种 /**

*输出狗狗信息 */

public void print(){ System.out.println(“宠物的自白:\\n 我的名字叫” + this.name + ”,健康值” + this.health + “,和主人的亲密度是” + this.love + “,我是一只 ” + this.strain + “。”);

} }

企鹅类的代码如示例2所示。 示例2 /**

*宠物企鹅

* @author 北大青鸟 */

public class Penguin{

String name = “无名氏”; //昵称 int health = 100; //健康值 int love = 0; //亲密度

String sex = “Q仔”; //性别 /**

*输出企鹅信息 */

public void print(){

System.out.println(“宠物的自白:\\n 我的名字叫” + this.name + ”,健康值” + this.health + “,和主人的亲密度是” + this.love + “,性别是 ” + this.sex + “。”);

} }

从示例1和示例2中我们学习了类的基本结构,其主要由属性和行为组成,成为类的成员变量(或者成员属性)和成员方法,同成为类的成员(除此之外,类的成员还包括构造方法、代码块等)。

问题

已经有了狗狗和企鹅类,如何领养宠物呢?

分析

领养宠物的步骤如下

根据控制台提示输入宠物的类型、昵称等内容。 根据输入内容创建相应的宠物对象。 打印出宠物信息表,示领养成功。

通过测试类来创建具体的宠物对象并输出信息,如示例3所示。 示例3

import java.util.Scanner; /**

*领养宠物

* @author 北大青鸟 */

public class Text{

public static void main(String[] args){

Scanner input = new Scanner(System.in); System.out.println(“欢迎您来到宠物店!”); //1.输入宠物名称

System.out.println(“请输入要领养宠物的名字:”); String name = input.next(); //2.选择宠物

System.out.println(“请选择要领养的宠物类型:(1.狗狗 2.企鹅)”); switch(input.nextInt()){ case 1:

// 2.1 如果是狗狗

//2.1.1 选择狗狗的品种 System.out.println(“请选择狗狗的品种:(1.聪明的拉布拉多犬” + “ 2.酷酷的雪纳瑞)”);

String strain = null; if(input.nextInt() == 1){ strain = ”聪明的拉布拉多犬”;

} else {

strain = “酷酷的雪纳瑞”; }

// 2.1.2 创建狗狗对象并赋值

Dog dog = new Dog(); dog.name = name; dog.strain = strain; // 2.1.3 输入狗狗信息 dog.print(); break; case 2: //2.2 如果是企鹅 //2.2.1 选择企鹅性别

System.out.println(“请选择企鹅的性别:(1.Q仔 2.Q妹)”); String sex = null;

if(input.nextInt() == 1) sex = ” Q仔”; else

sex = “Q妹”

//2.2.2 创建企鹅对象并赋值 Penguin pgn = new Penguin(); pgn.name = name; pgn.sex = sex;

//2.2.3 输出企鹅信息 pgn.print(); } }

}

运行结果如图1.5 和图1.6所示。

图1.5 领养狗狗运行结果

图1.6 领养企鹅运行结果

* 无参构造方法 */

public Penguin(){ name = “囡囡”;

love = 20; sex = “Q妹”;

System.out.println(“执行构造方法”); } /**

* 两个参数构造方法 */

public Penguin(String name,String sex){ this.name = name; this.sex = sex; } /**

* 四个参数构造方法 */

public Penguin(String name,int health,int love,String sex){ this.name = name; this.health = health; this.love = love; this.sex = sex; } /**

* 输出企鹅信息 */

public void print(){ System.out.println(\宠物的自白:\\n 我的名字叫\,健康值是\,和主人的亲密度是\,性别是\。\ } /**

* 测试构造方法的使用。 */

public static void main(String[] args){ Penguin pgn = null;

pgn = new Penguin(); pgn.print(); pgn=new Penguin(\亚亚\企鹅\ pgn.print(); pgn=new Penguin(\美美\仔\ pgn.print(); }

}运行结果如图1.13所示。

示例8中共有三个构造方法,方法名相同,参数列表不同,这成为构造方法的重载。可以通过构造方法重载来实现多种初始化行为,我们在创建对象的时候可以根据需要选择合适的构造方法。

下面我们把示例8中无参数的构造方法注释掉,看看会出现什么情况。 运行结果如图1.14所示。

为什么会出现这个错误呢?同C#一样,在没有给类提供任何构造方法时,系统会提供一个无参的方法为空的默认构造方法。一旦提供了自定义构造方法,系统将不会再提供这个默认构造方法。如果要使用它,程序员必须手动添加。强烈建议此时为Java类手动提供默认构造方法。

学习了创建对象,如何销毁对象呢?在Java中,对象的销毁不需要程序员来做,而是通过Java系统中的垃圾回收器再后台自动实现。 如果同一个类中包含了两个或两个以上方法,它们的方法名相同,方法参数个数或参数类型不同,则称该方法被重载了,这个过程称为方法重载。成员方法和构造方法都可以进行重载。

其实之前我们已经无形之中在使用方法重载了。

例如:

System.out.println(45); System.out.println(true);

System.out.println(“狗狗在玩耍!”);

例如:java.lang.Math类中的max方法就实现了重载,如图1.15所示。

注意:

方法重载的判断依据如下。 必须是在同一个类里。 方法名相同。

方法参数个数或参数类型不同。

与方法返回值和方法修饰符没有任何关系。

1.2.3 常见错误

1.在类中可以定义static变量,在方法里是否可以定义static变量

常见错误1 /**

* 宠物狗狗类,测试方法中是否可以定义static变量。 * @author 北大青鸟 */ class Dog{ String name = “无名氏”;

int health = 100; int love = 0;

public void play(int n){ static int staticVar = 5; health = health – n;

System.out.println(name + “ ” + staticVar + “ ” + health); }

public static void main(String[] args){ Dog d =new Dog(); d.play(5); } }

运行结果如图1.16所示。

把static int localv = 5;语句改为int localv=5;,则问题解决。

结论:在方法里不可以定义static变量,也就是说类变量不能是局部变量。 2.给构造方法加上返回值类型会出现什么情况

常见错误2 /**

* 宠物企鹅类,给构造方法加上返回值类型会出现什么情况呢? * @author 北大青鸟 */

class Penguin { String name =”无名氏”; int health=100; String sex=”Q仔”; /** * 给无参构造方法加上返回值类型为void */ public void Penguin(){ name=”欧欧”;

sex=”Q妹”; System.out.println(“执行构造方法”); } /**

* 输出企鹅的信息 */

public void print(){ System.out.println(“企鹅的名字是”+name+”,性别是”+sex+”。”); }

public static void main(String[] args){ Penguin pgn =new Penguin(); pgn.print(); }

}

运行结果如图1.17所示。

从运行结果,我们可以看到,Penguin()方法并没有执行,这是为什么呢?不符合构造方法的定义,自然就不是构造方法了,不会在创建对象时执行。

结论2:构造方法没有返回值类型。如果有,就不是构造方法,而是和构造方法同名的成员方法。

1.3 使用封装优化电子宠物系统的类

问题:

设计的类有没有缺陷呢?比如执行语句 d=new Dog(); d.health=1000;

再比如示例8中的语句

Penguin pgn = new Penguin(“亚亚”,”企鹅”); pgn.print();

这些语句在语法上是完全正确的,但是却不符合实际规定,因为我们规定最大health值是100,企鹅的性别只能是Q仔或Q妹。再比如如果一个类有年龄、成绩属性,实际中是有取值范围的,随意赋值也会出现同样的问题。

分析:

在Java中已经考虑到了这种情况,解决途径就是对类进行封装,通过private 、protectecl、public和默认权限控制符来实现权限控制。在此例中,我们将属性均设为private权限,将只在类内可见。然后再提供public权限的setter方法和getter方法实现对属性的存取,在setter方法中对输入的属性值的范围进行判断。

采用类图来表示封装后的Dog类和Penguin类,运行结果如图1.18和图1.19所示,请大家把他们和图1.3以及图1.4进行比较,看有什么不同。

对Dog类进行封装处理,如示例9 所示。

示例9 /**

* 宠物狗狗类,使用权限修饰符private和public进行封装。 * @author 北大青鸟 */

class Dog{

private String name=”无名氏”; private int health = 100; private int love = 0;

private String strain=”聪明的拉布拉多犬”; /**

* 读取狗狗昵称 * @return 昵称 */

public String getName(){ return name;

} /**

* 指定狗狗昵称

* @param name 昵称 */

public void setName(String name){ this.name=name; } /**

* 读取狗狗健康值 * @return 健康值 */

public int getHealth(){ return health;

} /**

* 指定狗狗健康值,对健康值范围进行判断 * @param health 健康值 */

public void setHealth(int health){ if(health>100|| health<0){ this.health=40; System.out.println(“健康值应该在0和100之间,默认值是40”); }else{ this.health=health;

}

} /**

* 读取狗狗亲密度 * @return 亲密度 */

public int getLove(){ return love;

} /**

* 指定狗狗亲密度 * @param love 亲密度 */

public void setLove(int love){ this.love=1ove; } /**

* 读取狗狗品种 * @return 品种 */ public String getStrain(){ return strain;

} /**

* 指定狗狗品种

* @param strain 品种 */

public void setStrain(String strain){ this.strain=strain; } /**

* 输入狗狗信息 */

public void print(){

System.out.println(“宠物的自白:\\n 我的名字叫”+this.name+”,是”+this.health+”,和主人的亲密度是”+this.love+”,我是一只”+this.strain+”。”);

} }

编写测试类,如示例10所示。

示例10 /**

* 测试类的封装 * @author 北大青鸟 */

健康值class Test{

public static void main(String[] args){ Dog dog = new Dog(); //dog.health=300; dog.setName(“欧欧”); dog.setHealth(300); System.out..println(“昵称是”+dog.getName()); System.out.println(“健康值是”+dog.getHealth()); dog.print();

} }

运行结果如图1.20所示。

去掉示例10中”d.health=300”一行的注释符后并执行,会出现什么结果呢?

运行结果如图1.21所示。

从示例10的两次运行结果图我们可以看到封装之后的两个变化:采用了private修饰符的变量不能在类外部访问,而是通过public修饰的setter方法实现;通过在setter方法中编写相应存取控制语句可以避免出现不符合实际需求的赋值。

封装(Encapsulation)是类的三大特性之一,就是将类的状态信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。

封装的具体步骤:修改属性的可见性来显示对属性的访问;为每个属性创建一对赋值(setter)方法和取值(getter)方法,用于对这些属性的存取;在赋值方法中,加入对属性的存取控制语句。 封装的好处主要有:隐藏累的实现细节:让使用者只能通过程序员规定的方法来访问数据;可以方便地加入存取控制语句,限制不合理操作。

封装时会用到多个权限控制符来修饰成员变量和方法,区别如下。

Private:成员变量和方法只能在类内被访问,被同一个项目中不同包中的子类访问(父类、子类的概念将在第二章讲解)。

Public:可以被同一个项目中所有类访问,具有项目可见性,这是最大的访问权限。 问题:

电子宠物系统有如下要求

领养宠物对象时可以指定昵称、品种,以后不允许改变。

领养宠物对象时健康值和亲密度采用默认值,只有通过玩耍、吃饭、睡觉等行为改变。

分析:

实际开发中封装哪些属性、如何封装取决于业务需求。根据需求,应对示例9做如下修改。

(1)去掉所有的setter方法,保留所有的getter方法

(2)提供有name和strain两个参数的构造方法实现对昵称和品种的赋值。 (3)提供eat()、play()、sleep()等方法实现健康值和亲密度的变化。

采用类图类表示改变封装后的Dog类和Penguin类,结果如图1.22和图1.23所示,请大家把他们和图1.18 以及图1.19进行比较,看有什么不同。

改变封装后的Dog类如示例11所示。

示例11 /**

* 宠物狗狗类,使用权限修饰符private和public进行封装。 * @author 北大青鸟 */

class Dog{

private String name=”无名氏”; private int health=100; private int love=0;

private String strain=”聪明的拉布拉多犬”; /**

* 通过构造方法指定狗狗的昵称、品种 * @param name 昵称 * @param strain 品种 */

public Dog(String name,String strain){ this.name=name; this.strain=strain; } /**

* 通过吃饭增加健康值 */

public void eat(){ if(health>=100){ System.out.println(“狗狗需要多运动呀!”);

}else { health=health+3; System.out.println(“狗狗吃饭了!”); } } /**

* 通过玩游戏增加与主人亲密度,减少健康值

*/

public void play(){ if(health<60){ System.out.println(“狗狗生病了!”);

}else { System.out.println(“狗狗正在和主人玩耍。”); health =health-10; love =love+5; } } /**

* 读取狗狗昵称 * @return 昵称 */

public String getName(){ return name; } /**

* 读取狗狗健康值 * @return 健康值 */

public int getHealth(){ return health; } /**

* 读取狗狗亲密度 * @return 亲密度 */

public int getLove(){ return love; } /**

* 读取狗狗品种 * @return 品种 */

public String getStrain(){ return strain; } /**

* 输出狗狗的信息 */

public void print(){ System.out.println(“宠物的自白:\\n我的名字叫”+this.name+”,健康值是”+this.health+”,和主人的亲密度是”+this.love+”,我是一只 ”+this.strain+”。”);

}

}

编写测试类,如示例12所示。

示例12: /**

* 测试类的封装 * @author 北大青鸟 */

class Test1{

public static void main(String[] args){ Dog dog =new Dog(“欧欧”,”酷酷的雪纳瑞”); dog.play(); System.out.println(“健康值是”+dog.getHealth()); dog.eat(); dog.print(); } }

运行结果如图1.24所示。

图1.24 示例12运行结果 接下来介绍this关键字

在示例9中的一系列setter方法中我们都用到了this这个关键字,this是什么含义呢?他还有什么其他的用法?

this关键字是对一个对象的默认引用。在每个实例方法内部,都有一个this引用变量,指向调用这个方法的对象。

在示例10中,我们创建了一个Dog对象dog,dog对象的昵称是欧欧,健康值是300,但是在shiite9中Dog类代码的编写是早于创建Dog对象的,当时并不知道以后创建的对象的名字呢。this关键字就是用来表示以后调用当前方法的那个对象的引用,当调用dog.setName(“欧欧”)、dog.setHealth(300)时,this就代表dog,而当创建另外Dog对象xxx,然后调用xxx.setName(“yyy”)时,this就表示xxx.this和xxx指向同一个对象。 this使用举例

使用this调用成员变量,解决成员变量和局部变量同名的冲突。 public void setName(String name){

this.name=name;//成员变量和局部变量同名,必须使用this }

public void setName(String xm){

name=xm;//成员变量和局部变量不同名,this可以省略 }

使用this调用成员方法 public void paly(int n){ health=health-n;

this.print(); //this可以省略,直接调用print(); }

使用this调用重载的构造方法,只能在构造方法中使用,必须是构造方法的第一条语句 public Penguin(String name,String sex){ this.name=name; this.sex=sex; }

public Penguin(String name,int health,int love,String sex){ this(name,sex);//调用重载的构造方法 this.health=health; this.love=love; }

注意:

因为this是在对象内部指代自身的引用,所以this只能调用实例变量、实例方法和构造方法。

this不能调用类变量和类方法。 this也不能调用局部变量

1.4 上机练习 上机练习1

练习——用类图设计Dog和Penguin类 训练要点:

面向对象涉及的过程 用类图描述设计 需求说明

根据本章电子宠物系统中领养宠物功能的需求,运用面向对象思想抽象出Dog类和Penguin类,并使用类图表示。 提示

面向对象设计的过程就是抽象的过程,分三步完成: 发现类、发现类的方法和属性类的方法。

上机练习2

指导——领养宠物并打印宠物信息 训练要点 类的结构

对象的创建、类的属性和方法的调用。

需求说明

根据控制台提示信息选择领养宠物为狗狗,输入昵称、品种等信息,然后打印宠物信息表示领养成功。

实现思路及关键代码

(1) 创建Dog类,定义属性和方法,定义print()方法,定义默认构造方法。

(2) 编写Test类,根据控制台提示信息选择领养宠物为狗狗,输入昵称、品种等信息,创建

Dog对象并打印对象信息

上机练习3

练习——给Dog类增加Dog(name)构造方法

训练要点

构造方法的定义和使用

构造方法的重载,是否提供带参构造方法对默认构造方法的影响 需求说明

给Dog增加Dog(name)构造方法,使用该构造方法创建对象;去掉默认构造方法,分析出现问题的原因。

上机练习4

练习——对企鹅对象的性别属性值进行设定和修改

训练要点

static变量和实例变量的区别 使用final修饰变量

需求说明

给Penguin类提供SEX_MALE和SEX_FEMALE两个静态常量,分别取值”Q仔”或”Q妹”,后来要求变化,规定企鹅的性别只能取值”雄”或”雌”,通过修改静态常量值实现该需求。

提示

创建多个企鹅对象,通过静态常量值的修改体会通过这种方式改变企鹅性别取值的高效性。

本章总结

现实世界是“面向对象”的,面向对象就是采用“现实模拟”的方法设计和开发程序。 面向对象技术是目前计算机软件开发中最流行的技术。面向对象设计的过程就是抽象的过程。

类是对某一类事物的描述,是抽象的、概念上的定义。对象是实际存在的该事物的个体,是具体的,现实的。

如果同一个类中包含了两个或两个以上方法,他们的方法名相同,方法参数个数或参数类型不同,则称该方法被重载了,这个过程称为方法重载。

构造方法用于创建类的对象。构造方法的作用主要就是在创建对象时执行一些初始化操作。可以通过构造方法重载来实现多种初始化行为。 封装就是将类的成员属性声明为私有的,同时提供公有的方法实现对该成员属性的存取操

作。

封装的好处主要有:隐藏类的实现细节;让使用者只能通过程序员规定的方法来反问数据;可以方便的加入存取控制语句,显示不合理操作。

本文来源:https://www.bwwdw.com/article/w3no.html

Top