2.2 面向对象程序设计
2.2.1 知识准备:面向对象编程术语
□类(Class):类是对某种类型的对象定义变量和方法的原形。它表示对现实生活中一类具有相同特征的事物的抽象,是面向对象编程的基础。
□对象(Object):也称为实例(Instance),是类的具体存在。
□属性(Attribute):也称为成员变量、实例变量(Instance Variable)、字段(Field),表示一个对象所具有的状态。本书中,通常使用变量或者属性来称呼它。
□方法(Method):用于表示对象可以执行的动作,它包括名称、参数、返回类型,以及方法体等内容。
□ 构造器(Constructor):也称为“构造方法”。用于创建和初始化一个实例的特殊方法,包括名称、参数及“方法体”等,需要注意的是,它的名称必须和类名一致。
□ 包(Package):可以使用它来组织编写类,使得类的组织更加有序。
2.2.2 知识准备:对象
我们每天的生活、工作,无时无刻不在和“对象”打交道——衣服、食物、房子、汽车等。当处理这些对象时,不会将这些对象的属性(对象所具有的特点)和操作分开。如进出“房间”时,不会将“房门”这个属性和“开门”这个操作分开,它们是联系在一起的。
面向对象的编程思想力图使得程序和现实世界中的具体实体完全一致。
人们通过观察对象的属性和行为来了解对象。对象的属性描述了对象的状态,对象的行为描述了对象的功能。对象一般有如下特性:
(1)有一个名字以区别于其他对象。
(2)有一些状态用来描述它的某些特征。
(3)有一组操作,每个操作决定了对象的一种功能或行为。
这样,可以让程序员乃至非专业人员更好地理解程序。它涉及一个从“具体”到“抽象”,再从“抽象”到“具体”的过程。所谓“从‘具体’到‘抽象’”,也就是将现实世界中的一个个具体的“物体”(或称为“实体(Entity)”)相应的特征和行为抽象出来,并且将各种具有相同特征的“物体”分为一个个的“类”,如“汽车”类、“人”类、“房子”类等;而所谓“从‘抽象’到‘具体’”,就是将上面抽象出来的对应物体的“类”,使用具体的计算机语言来描述,比如,使用Java语言来描述“汽车”类、“人”类、“房子”类等,和使用C++语言来描述这些类是有区别的。
正如前面所述,“类”相对于现实世界中的“实体种类”(Entity Category),如汽车、人类、房子等,它是现实生活中某类实体的抽象。而对象,或者实体(Instance),指的是这些种类中的一个个具体存在,如 Benz-600、Santana-2000 等具体的汽车,或者张三、李四等具体的个人。类和对象是面向对象编程思想中的核心和基础。类是作为对象的蓝图而存在的,所有的对象都依据相应的类来产生,在面向对象的术语中,这个产生对象的过程称为“实例化”。
用一个具体的例子来进一步说明“类”和“对象”之间的联系与区别。以汽车为例,只要是汽车,都应该有以下一些特性:轮子、引擎、方向盘、刹车等组件,可以通过这些组件来操作汽车,改变汽车的状态,如加速、转向、减速等,这些都是汽车的共性。具体到某辆汽车,它可能有80cm的轮子、40cm的方向盘、A6引擎,它是一个确定的实例。使用“汽车类”这个蓝图,就可以生产出一辆辆的汽车“实例”。这和盖房子一样,根据一张图纸,可以盖出任意数量的房子,而这些房子都有一样的特征。
2.2.3 知识准备:类
如果说一切都可以成为对象,那么是什么决定了某一类对象的外观和行为呢?
类是对某个对象的定义,用来描述一组具有相同特征对象的:
(1)应包括的数据。
(2)行为特征。
类包含有关对象动作方式的信息,包括它的名称、方法、属性和事件。实际上类本身并不是对象,因为它不存在于内存中。当引用类的代码运行时,类的一个新的实例,即对象,就在内存中创建了。一个类能从这个类在内存中创建多个相同类型的对象。因此,类应该包括两个方面的内容。
(1)属性:用来描述对象的数据元素称为对象的属性(也称为数据/状态)。
(2)方法:对对象的属性进行的操作称为对象的方法(也称为行为/操作)。
2.2.4 知识准备:类的声明
Java语言中类的声明,也称类的定义,其语法规则如下:
[< modifiers>] class < class_name> { [<attribute_declarations>] [<constructor_declarations>] [<method_declarations>] }
其中各组成部分的含义如下:
<modifiers>为修饰符,可用的有public、abstract和final等关键字(关键字是Java语言中赋以特定含义,并用做专门用途的单词,类名、方法名和属性名及变量名不能是关键字),用于说明所定义的类有关方面的特性。对于各种关键字和它们的含义及各自的适用范围,请看后续章节的介绍。
类成员有3种不同的访问权限:
(1)公有( p ublic关键字修饰 )类成员可以在类外访问。
(2)私有( pri vate关键字修饰 )类成员只能被该类的成员函数访问。
(3)保护( prote cted关键字修饰 )类成员只能被该类的成员函数或派生类的成员函数访问。
class关键字表明这是一个类的定义,将告诉你一个新类型的对象看起来是什么样的。
<class_name>是类的名字,类名一般使用一个用于表示这个类的名词来表示。
<attribute_declarations>是属性(Attribute)声明部分。
<constructor_declarations>是构造器(Constructor)声明部分。
<method_declarations>是方法(Method)声明部分。
可以将上述的“学生”实体的基本特征当成“学生”类的属性,然后,再定义一些方法来对这些属性进行操作。这里,将这些操作属性的方法定义得很简单:每个属性都有相应的设置(setter)和获取(getter)方法,设置方法将传入的参数赋给对象的属性,而获取方法取得对象的属性。
源文件:Student.java
public class Student { // 定义属性 String name; String sex; int grade; int age; // 定义属性“name”的设置方法 public void setName(String _name) { name = _name; } // 定义属性“name”的获取方法 public String getName() { return name; } // 定义属性“Sex”的设置方法 public void setSex(String _sex) { sex = _sex; } // 定义属性“Sex”的获取方法 public String getSex() { return sex; } // 定义属性“grade”的设置方法 public void setGrade(int _grade) { grade = _grade; } // 定义属性“grade”的获取方法 public int getGrade() { return grade; } // 定义属性“age”的设置方法 public void setAge(int _age) { age = _age; } // 定义属性“age”的获取方法 public int getAge() { return age; } }
2.2.5 知识准备:属性的声明
类的定义中所包含的数据称为属性,也称为数据成员,比如 2.2.4 节示例中的Student类中定义了4个属性——name、sex、grade、age。属性声明的语法规则如下:
[< modifiers>] <data_type> < attr_name>;
其中:
□ < modifiers>为修饰符,可用的有public、private、protected、final、static等,用于说明该属性的一些性质。
□ <data_type>是该属性的数据类型,属性可以是Java基本类型的一种,如下面的示例中MyClass类中声明的属性classID等,也可以是任何类型的对象,可以通过引用它进行通信。例如MyClass类中声明的属性myTeacher。
□ <attr_name>是属性名称,属性名称的首字母一般采用小写方式。
MyClass.java public class MyClass { int classID; String className; int studentNumber; Teacher myTeacher; } Teacher.java public class Teacher { String name; int id; String course; }
2.2.6 知识准备:方法的声明
类的定义中还可以包含方法的声明,Java的方法决定了对象可以接收什么样的信息,方法的基本组成部分包括名称、参数、返回值和方法体。其语法规则如下:
< modifiers> <return_type> <name>([< argu_list>]) { [< method body>] }
其中:
□ < modifiers>为修饰符,可用的有public、private、protected、abstract、static和final,用于说明方法的属性。
□ <return_type>是该方法的返回值类型,可以是任何合法的Java数据类型。
□ <name>是方法名。
□ <argu_list>是方法的参数列表,包括要传给方法的信息的类型和名称,如有多个参数,中间用逗号","分隔。
□ <method body>是方法体,有0到多行Java语句。
下面在2.2.5节示例MyClass类中声明一个名为ChangeTeacher的方法,当大多数同学对老师不满意时,可以使用这个方法来换个给力的老师,并返回老师的名字。
public class MyClass { int classID; String className; int studentNumber; Teacher myTeacher; public String ChangeTeacher(Teacher t){ System.out.print("change a teacher"); this.myTeacher = t; return t.name; } }
注意:
1.参数列表传递的实际上是引用,引用的对象类型必须和参数类型一致,不然编译器会报错。
2.Java语言中类、方法及属性声明的次序并无严格要求。
2.2.7 知识准备:构造器(构造方法)
在Java程序中,每个类都必须至少有一个构造器(Constructor)。构造器是创建一个类的实例(对象)时需要调用的一个特殊的方法。
利用构造器可以产生一个类的实例,并且提供了一个地方用来定义创建类的实例时都需要执行的初始化(initialize)代码。构造器的定义语法如下:
<modifier> <class_name> ( [<argument_list>] ) { [<statements>] }
从上面的语法中可以看出,它和类中的方法定义很类似:可以有访问修饰符modifier、有自己的方法名称、有参数列表、有方法体,因此,可以将构造器当成一个特殊的方法(在许多资料里面,就是将Constructor 称为构造方法),这个方法的作用就是用来产生一个类的实例。但是要认识到构造器与普通方法的区别,主要表现在以下3个方面。
(1)修饰符:和方法一样,构造器可以有任何访问的修饰:public、protected、private或者没有修饰。不同于方法的是,构造器不能有以下非访问性质的修饰:abstract、final、native、static或者 sy nchronized。
(2)返回值:方法能返回任何类型的值或者无返回值(void),构造器没有返回值,也不需要void。
(3)命名:构造器使用和类相同的名字,通常为名词。而方法则不同,通常为描述一个操作的动词。按照习惯,方法通常用小写字母开始,而构造器通常用大写字母开始。
下面定义一个用来表示“美食”的类Food。
源文件:Food.java
public class Food { private String food_name ; public Food(String name){ this.food_name = name; } }
在Food类中,定义了一个属性food_name,还定义了一个构造器,在构造器中传入一个字符串类型的参数,将参数值赋给属性 food_name。此时,就可以通过这个构造器来实例化这个类,如下所示。
Food myDinner = new Food("pizza");
这样,就得到了一个food_name 名为“pizza”的实例,还可以再创建一个food_name名为“cola”的实例来搭配你的晚餐。
如果在程序中没有定义任何的构造器,则编译器将会自动加上一个不带任何参数的构造器。默认的构造器不带任何的参数,也没有“方法体”。
通过上面的示例,在Food类中定义了一个带一个参数的构造器。如果上面的Food类没有定义构造器,则编译器会自动加上一个构造器:
public class Food {
private String food_name ;
public Food(){
}
}
所以,这时可以用下面的语句来实例化这个类:
Food myDinner = new Food();
如果在程序中定义了构造器,则编译器将不再提供默认的构造器,即使定义的构造器同样没有参数。如果再使用默认构造器的话,编译器会报错。
2.2.8 知识准备:对象的创建和使用
正确声明了Java类之后,便可以在其他的类或应用程序中使用该类了。使用类是通过创建该类的对象并访问对象成员来实现的。对象成员是指一个对象所拥有的属性或可以调用的方法,现阶段可以理解为对象所属的类中定义的所有属性和方法。一个对象的声明实际上只是对该类的引用,需要使用new+构造器(又称为构造方法)创建对象,此时存储器会给此对象相应的存储空间。当对象被成功创建后,可以使用“对象名.对象成员”的方式访问对象成员。
2.2.9 任务一:创建并引用一个对象
1.任务描述
创建一个美食类,并引用该类的对象,列出一天的食谱。
2.技能要点
(1)声明类、属性及方法。
(2)对象的创建和引用。
3.任务实现过程
(1)定义一个用于表示“食物”的类“Food”;在类中声明一个food_name属性;给food_name属性添加get()、set()方法。
(2)在Recipe类中,通过默认构造方法创建3个“Food”对象,通过setFood_name(String name)方法给food_name赋值,并通过g etFood_name()方法获得food_name属性值,并输出。
源文件:Recipe.java
public class Recipe { public static void main(String args[]) { Food breakfast = new Food(); breakfast.setFood_name("bread"); Food lunch = new Food(); lunch.setFood_name("nuddle"); Food dinner = new Food(); dinner.setFood_name("pizza"); System.out.print("my breakfast is "+breakfast.getFood_name()+"\n"); System.out.print("my lunch is "+lunch.getFood_name()+"\n"); System.out.print("my dinner is "+dinner.getFood_name()); } } class Food { private String food_name ; public String getFood_name() { return food_name; } public void setFood_name(String foodName) { food_name = foodName; } }
程序运行结果:
my breakfast is bread my lunch is nuddle my dinner is pizza
提示:
Java中创建的对象并不用担心销毁问题。因为Java对象不具备和基本类型一样的生命周期,由new创建的对象,会一直保留下去,直到Java的垃圾回收器辨别出此对象不会再被引用,自动释放该对象的内存空间。
2.2.10 技能拓展任务:带参数构造器的声明与使用
1.任务描述
改写任务一的程序,使用带参数构造器,实现同样的输出结果。
2.技能要点
带参数构造器的声明和引用。
3.任务实现过程
(1)定义一个用于表示“食物”的类“Food”;在类中声明一个food_name属性;给food_name属性添加get()、set()方法。
(2)给Food类添加两个构造方法,一个带参数,一个不带参数。
(3)在Recipe类中,通过带参数构造方法创建两个“Food”对象,通过getFood_name()方法获得food_name属性值,并输出。
(4)在Recipe类中,通过不带参数构造方法创建一个“Food”对象,通过setFood_name(String name)方法给food_name赋值并输出。
源文件:Recipe.java
public class Recipe { public static void main(String args[]) { Food breakfast = new Food("bread"); Food lunch = new Food("noddle"); Food dinner = new Food(); dinner.setFood_name("pizza"); System.out.print("my breakfast is "+breakfast.getFood_name()+"\n"); System.out.print("my lunch is "+lunch.getFood_name()+"\n"); System.out.print("my dinner is "+dinner.getFood_name()); } } class Food { private String food_name ; public String getFood_name() { return food_name; } public void setFood_name(String foodName) { food_name = foodName; } public Food(String name){ this.food_name = name; } public Food(){} }
程序运行结果:
my breakfast is bread my lunch is nuddle my dinner is pizza
注意:
因为类“Food”定义了一个带参数的构造器,所以,编译器不会给它加上一个默认的不带参数的构造器,此时,如果还试图使用默认的构造器来创建对象,将不能通过编译。如需要不带参数构造器,则自行定义使用。