
任务3.1 类和对象

3-1 面向对象概述
如何描述一本书?
知识储备
3.1.1 面向对象概述
早期的编程语言采用面向过程的思想进行程序设计,面向过程就是针对某一需求,分析出解决问题所需要的步骤,然后用函数把这些步骤一一实现,使用的时候一个一个依次调用就可以了。随着用户需求的不断增加,软件规模越来越大,传统的面向过程开发方式的弊端也逐渐暴露出来,没有办法把一个包含了多个相互关联的过程的复杂系统表述清楚,这时面向对象的思想就应运而生。
面向对象的思想更加符合人的思维模式,最初起源于20世纪60年代中期的仿真程序设计语言Simula。面向对象的思想力图从实际问题出发,把现实世界的实物抽象出所用的数据和操作数据的方法,把一类事物的数据和操作数据的方法封装在一起,形成一个类,再把类实例化,形成对象。
把日常生活中的事物用学习语言描述出来,把事物的描述信息抽象成事物的属性,把事物能够做的事情抽象成事物的行为,把属性和行为封装起来,就是某一类事物。比如,大家经常使用的手机这一事物,用来描述手机的信息一般就是型号、价格、打电话、发信息等,描述手机的型号和价格(名词)就是手机的属性,描述手机动作的打电话和发信息(动词)就是手机的行为。所有的手机都具有这些属性和行为,可以将这些属性和行为封装起来描述手机类。由此可见,类实质上就是封装某一类事物的属性和行为的载体。某一个具体的手机,比如你手上用的手机,就是手机类抽象出来的一个实例。
面向对象中的类就是对某一类事物的总称,一个手机、一辆汽车和水里游的一条鱼不能称为一个类,具有相同特征和行为的一类事物就是类,对象就是符合某个类定义所产生出来的实例。虽然在现实生活中,大家习惯用类名称呼一个具体的对象,比如说借你的手机打个电话,这个手机虽然是一个类名,但是大家用手机指代的是手机类的一个具体的对象,而不是类,类是抽象的,但是解决实际问题的时候,比如我要用手机给你打个电话,要用手机类的一个对象,也就是一个具体的手机来解决打电话的问题。
3.1.2 面向对象的特点
面向对象的实质就是对现实世界的对象进行建模操作,能够有效地组织和管理一些比较复杂的应用程序的开发,面向对象程序设计的特点可以概括为封装性、继承性和多态性。
1.封装性
封装性是指将对象的属性和行为结合在一起,形成一个不可分割的独立单位,构成一个独立的类,对外隐藏实现的细节,实现了信息的隐藏。例如,人们使用洗衣机洗衣服,只要把脏衣服放到洗衣机里,按一些按钮就可以实现具体的洗涤功能,无须知道洗衣机内部是如何工作的。
采用封装的思想后,使用类的用户不能操作类内部的数据,避免类外部对类内部数据的影响,保证了类内部数据的正确性。
2.继承性
继承性是指使得一个类可以使用另一个类的属性和行为。如果有些类很相似,就可以把几个类共同的属性和行为抽象出来,形成一个新类,这个新类就是父类(或称为超类、基类),其他的类就是子类,称子类继承父类。例如,经理类的属性有姓名、工号、工资、奖金,行为有工作,职员类的属性有姓名、工号、工资,行为类有工作。可以把经理类和员工类的共同的属性和行为抽象出来,定义一个员工类,员工类的属性有姓名、工号和工资,行为有工作,员工类就是经理类和职员类的父类,职员类和经理类就是员工类的子类,可以说,职员类和经理类继承员工类。
继承可以提高代码的重用性。例如,定义经理类时,只需要写出它特有的属性奖金即可,其他共同的属性和行为无须再写。继承也可以提高代码的可维护性,如果需要记录经理和职员的生日,只需要在员工类中增加一个生日属性,经理类和职员类中就都有生日属性了。
3.多态性
多态性是指程序的多种表现形式,父类中的属性和行为被子类继承之后,可以具有不同的表现行为。例如,员工类中定义的工作行为,被经理类和职员类继承后,经理类和职员类的工作方式具有不同的表现形式。
在同一类中,行为的名称虽一样,但也会具有不同的表现形式。例如,计算器可以实现加、减、乘、除的运算,加运算可以实现整数的相加,也可以实现小数的相加,既可以实现两个数的相加,又可以实现三个数的相加。在一个计算器类中,加操作根据参数的类型和数量的不同,可以有不同的表现形式,这也是多态的一种表现。
3.1.3 类

3-2 类的定义
1.类的定义
Java中最基本的单元是类,用类来描述事物,类的属性用成员变量表示,行为用成员方法表示,定义类的过程就是定义类的成员变量和成员方法的过程。声明类的语法格式如下。

【参数说明】
● []:表示其中的内容为可选项。
● 类修饰符:可以省略,用于控制类的访问权限,可能的取值有public、private和protected。public表示类是公共的,可以被外部访问;private表示类将隐藏其内的所有数据,以免用户直接访问;protected表示同意包内的类可以访问此类的数据。
● class:关键字,定义类的时候,在类名前加关键字class。
● 类名:必须符合标识符的命名规则。
● extends父类:可以省略,extends是关键字,表示继承关键字后面的父类,父类指被继承的类。
● implements接口列表:可以省略,implements是关键字,表示实现关键字后面的接口,接口列表指要实现的接口名称,多个接口之间用逗号分隔。
● {}:大括号{}括起来的部分是类体,里面是类的成员,包括成员变量的定义、成员方法的定义、构造方法的定义。构造方法是一种特殊的成员方法。
例如:

表示定义了一个手机类,class关键字后面的Phone是类名,类的命名除了遵守标识符的命名规则之外,一般约定如果类名是一个单词,则首字母要大写;如果类名由多个单词组成,则每个单词的首字母都需要大写。Phone里面的内容即便为空,大括号也不能省略。
2.成员变量的定义
类的成员变量体现的是类的属性,定义在类体中,并且在成员方法之外。成员变量包含变量和常量,变量又细分为实例变量和类变量,实例变量被类的对象调用,而类变量由类名直接调用。
定义成员变量的语法格式如下。


3-3 成员变量的定义
【参数说明】
● 修饰符:用于指定变量的访问权限。可能的取值有public、protected和private,是可选项。
● static:用于指定该成员变量为静态变量。可以直接通过类名访问;属于类变量,不属于任何一个类的对象;任何对象对它访问时,取得的都是相同的数值;省略static表示该成员变量是实例变量。
● final:用于指定该成员变量为常量。其值不能改变,只能被初始化一次。
● 变量类型:其值可以为Java中的任何一种数据类型,用于指定成员变量的数据类型。
● 变量名:必须是合法的标识符,用于指定成员变量的名称,为必选项。
例如,对手机类Phone进行修改,其成员变量有品牌、颜色和价格,代码如下所示。

例如,定义一个名为Circle的类,为其定义一个常量PI、成员变量r、静态成员变量count来统计Circle类的对象个数,代码如下。

静态成员变量和实例成员变量都是定义在类体内方法外的,不同对象的实例成员变量分配不同的内存空间,而不同对象的静态成员变量共享一个内存空间。静态成员变量和实例成员变量的区别如下。
● 修饰关键字不同。实例成员变量不能用static修饰,静态成员变量用static修饰。
● 生命周期不同。实例成员变量随着对象的创建而创建,随着对象被收回而释放,静态成员变量随着类的加载而存在,随着类的消失而消失。
● 调用方法不同。实例成员变量只能被对象调用,静态成员变量既可以被类调用,又可以被对象调用。
3.成员方法的定义

3-4 成员方法的定义
成员方法表示类所具有的功能或行为,它是一段用来完成某些操作的程序片段,类似于过程化编程语言中的函数,类的成员方法定义在类体中,包含方法的声明和方法体。定义成员方法的语法格式如下。

【参数说明】
● 修饰符:用于指定变量的访问权限。可能的取值为public、protected和private,是可选项。
● static:用于指定该成员方法为静态方法。可以直接通过类名访问,是可选项。属于类方法,省略static表示该成员方法是实例方法。
● 方法返回值的类型:用于指定方法的返回值类型。如果方法没有返回值,则在方法声明中可以使用void关键字进行标识。如果方法有返回值,在方法体中必须包含return语句,返回与方法声明中指定的返回类型一样的值,也就是说,方法返回值的类型必须和声明方法时指定的返回值类型相同。
● 方法名:用于指定成员方法的名称。方法名必须是合法的Java标识符。除此之外,一般约定如果方法名是一个单词,则全部小写;如果方法名由多个单词组成,则第一个单词全部小写,从第二个单词开始首字母大写。
● 参数列表:用于指定方法执行需要的参数。参数声明的形式是“参数类型 参数名”,参数类型可以是任何Java数据类型,如果有多个参数,各参数声明之间使用逗号分隔。
● 方法体:方法体是方法的实现部分,是实现类的功能的代码。方法体是可选的,方法体为空时,表示方法什么都不做。
成员变量的定义在方法声明外,但是成员变量的操作在方法体内,在方法体内可以定义其他的变量,这些变量称为局部变量,在方法声明参数列表中指定的变量和在方法体内定义的变量都是局部变量,有效范围只限于方法内。
例如,对添加了成员变量的手机类Phone进行修改,为其添加成员方法打电话phoneCall和发信息sendMessage,代码如下所示。

对Circle类进行修改,为其添加静态成员方法getCircleCount(),返回Circle实例化对象的个数,实例成员方法getArea()计算圆的面积,实例成员方法getCircumference()返回圆的周长,代码如下。

静态方法和实例方法的区别如下。
● 修饰关键字不同。实例方法不能用static修饰;静态方法用static修饰。
● 访问对象不同。实例方法既可以访问静态成员变量和静态方法,又可以访问实例成员变量和实例方法;而静态方法只能访问静态成员变量和静态方法,不能访问实例成员变量和实例方法,因为实例成员变量是属于某个对象的,而执行静态方法的时候,并不一定存在对象。同样的道理,由于实例方法可以访问实例成员变量,如果静态方法可以调用实例方法,将间接地允许它使用实例成员变量,而这是不允许的。
● 调用方法不同。实例方法只能被对象调用,静态方法既可以被类调用,又可以被对象调用。
● 构造方法是特殊的方法,不能声明为静态方法。
成员变量和局部变量的区别如下。
● 定义位置不同。成员变量在类体内,方法外;局部变量定义在方法内和方法参数中。
● 修饰关键字不同。局部变量不能用public、protected、private和static修饰,但可以使用final关键字声明局部变量为常量,成员变量都可以用以上关键字修饰。
● 初始化值不同。成员变量有默认的初始化值;局部变量没有默认的初始化值,定义之后必须赋值才能使用。
● 调用方式不同。局部变量不能用类和对象调用,只能用在方法体内,成员变量中的静态成员变量可以由类和对象调用,实例成员变量可以由对象调用。
● 生命周期不同。局部变量与方法共存亡,成员变量中的静态成员变量与类共存亡,实例成员变量与对象共存亡。
4.构造方法的定义
构造方法是一种特殊的方法,它没有返回值,但是不需要用void标识。构造方法的名称必须与它所在类的名称完全相同,主要用于在对对象进行实例化的时候,给对象的数据进行初始化,但是不能被对象调用,而是在创建类的对象时自动调用构造方法。如果没有为类定义构造方法,Java会提供默认的无参构造方法。
构造方法主要具有以下特点。
● 构造方法名要与构造方法所在类的名称相同。
● 构造方法没有返回值。

3-5 构造方法的定义
● 构造方法不用void修饰。
● 不能在构造方法中使用return语句返回值。
● 构造方法只能由new运算符调用。
● 每个类至少有一个构造方法,如果没有为类定义构造方法,系统会自动为该类定义默认的构造方法。
定义构造方法的语法格式如下。

例如,为手机类添加构造方法,代码如下。


Circle类没有定义构造方法,那么系统会自动为其提供一个与Circle同名的构造方法;Phone类提供了两个构造方法,那么系统便不再为其提供构造方法。
使用构造方法的时候,要注意以下几点。
1)构造方法不能使用void修饰。
例如,下面的代码中定义了一个Employee类。

如果通过new运算符来生成Employee的一个对象,代码如下。

本来想为类Employee定义一个有参构造方法为其成员变量赋初始值,但是在构造方法的名字前面加了void,会出现错误“The constructor Employee(String) is undefined”,由于构造方法Employee前面加了void修饰,Employee就变成了普通方法,不再是构造方法,在实例化对象e的时候,就找不到相对应的有参构造方法了。
2)构造方法不能使用private修饰。
对于上面定义的Employee类进行修改,去掉构造方法前的void,同时把public修改为private,结果报错“The constructor Employee(String) is not visible”(构造方法不可见)。
3)在定义类的时候自定义了构造方法,系统就不再提供默认的构造方法。
例如,对于上面定义的类Employee,将其修改正确,代码如下。

通过new运算符来生成Employee的一个对象,代码如下。

用类Employee的有参构造方法为成员变量empname赋值,但是如果用下面的代码实例化一个对象e2,系统会报错“The constructor Employee() is undefined”。

这是由于类Employee自己定义了一个有参构造方法,系统不再为其提供默认的构造方法了。
3.1.4 对象

3-6 对象
在面向对象程序设计中,类的实例就是对象,对象是客观存在的,一个具体的事物就是一个对象,如一个拥有实际的学号、姓名等的学生,就是一个对象,所有学生的泛称就是类。
1.对象的声明
声明一个类,就是定义一个新的引用数据类型,可以用这个数据类型声明这种类型的变量,即对象。把类看作数据类型,对象名看作变量名,一个对象的声明格式和变量的声明格式很类似,语法格式如下。

声明一个对象只是在内存中为其建立一个引用,并置初始值为null,表示不指向任何内存空间。
例如,声明一个Circle类的对象c的代码如下。

声明一个Phone类的对象p的代码如下。

2.创建对象
声明过的对象还不能被引用,必须用new关键字创建这个对象。创建对象也叫实例化对象,创建对象的过程就是为对象分配内存的过程。创建对象的语法格式如下。

类名必须与声明对象时的类名相一致,例如,对象p可以有两种创建方法,代码如下所示。

例如,实例化对象c的代码如下。

由于Circle类没有定义构造函数,系统自动为其添加一个无参构造函数。
在对类的对象进行声明的时候,可以直接为其赋初始值,也可以在声明对象的时候直接实例化。语法格式如下。

例如,在声明一个手机类Phone的对象的时候,直接实例化,声明Phone类的对象p1并用无参构造函数实例化对象p1,声明p2并用有参构造函数创建对象p2,代码如下。

在声明一个Circle类的对象的时候,直接实例化,由于Circle类没有提供构造方法,那么使用new运算符创建其对象时,调用系统提供的默认构造方法,代码如下。

3.成员变量的引用
声明和创建一个对象之后,就能像使用变量那样使用它。使用对象的方式是通过读取它的属性、设置它的属性来实现的。
引用对象的属性,需要使用点分隔符“.”。如果成员变量是静态的,则引用的语法格式如下。

如果成员变量是实例变量,则引用的语法格式如下。

例如,通过手机类Phone的有参构造函数实例化的对象p2,通过构造方法为其成员变量提供值,引用实例成员变量的代码如下。

结果会输出:

通过Circle类的无参构造函数实例化的对象c1,引用成员变量的代码及通过类名Circle引用静态成员变量的代码如下。

4.成员方法的引用
调用对象的成员方法,也需要使用点分隔符。当成员方法没有参数时,成员方法的圆括号也不能省略。如果是调用静态成员方法,语法格式如下。

如果成员变量是实例方法,引用成员方法的语法格式如下。

例如,手机类Phone的对象p2,引用成员方法的代码如下。

结果会输出:

通过Circle类的对象c1,引用实例方法及通过类名Circle引用静态方法的代码如下。

【例3-1】 创建手机类Phone。


定义手机类Phone,为其定义成员变量品牌brand、颜色color、价格price,实例成员方法phoneCall()和sendMessage(),一个空的无参构造方法,一个有参构造方法,其中包含三个参数,分别为三个成员变量赋初始值,然后定义一个主类Example301,测试手机类Phone的用法,程序运行结果如图3-1所示。

图3-1 例3-1程序运行结果
在主类Example301中,定义了一个主方法main,在主方法里实例化了两个手机类p1和p2,分别用无参构造函数和有参构造函数进行实例化对象;p1是无参构造函数实例化的对象,没有为成员变量赋值,需要用“对象名.成员变量名”的方法分别为三个成员变量赋值;p2是有参构造函数实例化的对象,通过构造函数的三个参数分别为三个成员变量赋值;输出两款手机的信息,并通过“对象名.成员方法名”来输出两款手机的行为。
【例3-2】 创建类Circle。

定义Circle类,为其定义常量PI、实例成员变量r、静态成员变量count(count默认初始值为0)、实例成员方法getArea()和getCircumference()、静态成员方法getCircleCount();没有定义构造方法,使用系统提供的默认构造方法。定义一个主类Example302,测试Circle的用法,程序运行结果如图3-2所示。

图3-2 例3-2程序运行结果
在主类Example302中,定义了一个主方法main,在主方法里实例化了两个Circle类的对象c1和c2,每实例化一个对象,用来保存类Circle的对象个数的静态成员变量count的值加1;分别为对象c1和c2的成员变量r赋值;通过类名访问静态成员方法getCircleCount()输出Circle类的对象个数,并分别输出对象c1和c2的面积和周长。
任务实施
定义书类Book,成员变量有书名、页数、单价、出版社、作者,成员方法具有的功能是能够输出“哪个作者出版的书有多少页,价格是多少,由什么出版社出版”这样的信息,并为其定义有参构造方法,用来在创建对象时为其成员变量赋值。

任务演练
【任务描述】
编程创建Student类,为其定义成员变量及成员方法,并定义有参构造方法,每创建一个新的学生对象,就统计一次学生人数。
【任务目的】
1)掌握类和对象的定义。
2)掌握对象的使用。
3)掌握构造方法的定义和使用。
4)了解静态成员变量的用法。
【任务内容】
创建Student类,成员变量由学号id、姓名name、年龄age、性别gender和用于统计学生人数的静态成员变量num;有参构造方法为成员变量赋值,其中静态成员变量的值自增1,成员方法showInfo()用于输出对象的学号、姓名、年龄和性别信息;静态成员方法showNum()用于输出当前创建的学生人数。
步骤如下。
1)启动Eclipse,创建Java项目,项目名称设为“项目实训3_1”。
2)创建类Student,在类体中定义成员变量id(String)、name(String)、age(int)、gender(boolean)和静态成员变量num(int);定义有参构造方法,为实例成员变量赋值,并让num自增1;定义成员方法showInfo(),输出实例成员变量的值,在输出之前对gender成员变量进行预处理,当值为True时,性别为“男”,否则性别为“女”;定义静态成员方法showNum(),输出静态成员变量num的信息。
3)创建主类Project301,在main()方法中创建Student类的多个对象,结束后每个对象都调用showInfo()方法显示学生的信息,并调用静态方法showNum()显示目前学生的人数。
在代码页面上右击,在弹出的快捷菜单中选择“Run As”→“Java Application”命令,运行程序。