第 4章 对象和类程序是计算机不可缺少的一部分,如何有效地编写程序是人们一直以来最关心的问题,而以怎样的思想来指导程序设计显得更加重要。程序设计方法经历了几个发展阶段,而面向对象程序设计是如今最流行、最高效的程序设计方法。每一种思想和方法都有其自身的概念和相应原理,对象和类是面向对象程序设计中最基本、也是最重要的两个概念。本章主要介绍面向对象程序设计的一些基本概念和原理,重点是如何用
Java语言来表示这些概念。
本章要点
面向对象程序设计的基本概念;
类的创建;
对象实例化;
访问属性控制;
静态成员;
final,this和 null;
包;
综合应用示例。
4.1 面向对象程序设计
要掌握一种新思想、新方法,必须先了解其相关概念。本节主要介绍面向对象的一些相关概念及其相应的描述工具 —— UML图,是一些比较抽象的概念,
读者在学习的过程中,可以先了解其基本内容,待学完后面相关内容后再回来仔细揣摩和体会,必定能收到良好的效果。
所谓面向对象的方法学,就是使我们分析、设计和实现一个系统的方法尽可能地接近我们认识一个系统的方法。包括:
面向对象的分析( OOA,Object-Oriented Analysis)
面向对象的设计( OOD,Object-Oriented Design)
面向对象的程序设计 (OOPL,Object-Oriented Program)
面向对象方法学的核心思想是通过一些基本概念体现出来的。它主要围绕着对象、类、消息、继承性、多态性等基本概念和机制展开。如将,对象,作为一个独立的逻辑单元与现实世界中的客体相对应,用,类,来描述具有相同属性特征和行为方法的一组对象,可利用,继承,来实现具有继承关系的类之间的数据和方法的共享,对象之间以,消息,传递的方式进行,通信,等。下面对面向对象方法学中的部分主要核心概念作简单介绍。
4.1 面向对象程序设计 — 面向对象的概念
1.抽象抽象是人类认识世界的一种方式,它是指从同类型的众多事物中舍弃个别的、非本质的属性和行为,而抽取出共同的、
本质的属性和行为的过程。抽象主要包括事物属性的抽象和行为的抽象两种类型。
属性可用来描述事物在某时刻的状态,常用具体的数据来表示。
行为的抽象也称功能的抽象,即舍弃个别的功能,而抽取共同的功能的过程。
4.1 面向对象程序设计 — 面向对象的概念
2.封装封装是将事物的属性和行为聚集在一起而形成一个完整的逻辑单元的一种机制。利用这种机制可以实现信息的隐藏,外界客体只能通过封装向外界提供的接口才能访问描述事物属性的内部数据。这即有利于客体本身的维护,也有利于保护信息的安全。
在面向对象的程序设计过程中,封装的具体作法就是将描述对象状态的属性和对象固有的行为分别用数据结构和方法来加以描述,并将它们捆绑在一起形成一个可供外界访问的独立的逻辑单元,外界只能通过客体所提供的方法来对其间的数据结构加以访问,而不能直接存取。很明显,封装是实现信息隐藏的有效手段,它尽可能隐蔽对象的内部细节,只保留有限的对外接口,使之与外部发生联系。封装保证了数据的安全性、提高了应用系统的可维护性、也有利于软件的移植与重用。
4.1 面向对象程序设计 — 面向对象的概念
3.对象对象 有两个层次的概念:
现实生活中对象指的是客观世界的实体;
程序中对象就是一组变量和相关方法的集合,其中变量表明对象的状态,方法表明对象所具有的行为。
变量方法
4.1 面向对象程序设计 — 面向对象的概念
4.类
(1).类是描述对象的“基本原型”,它定义一类对象所能拥有的数据和能完成的操作。在面向对象的程序设计中,类是程序的基本单元。
(2).相似的对象可以归并到同一个类中去,就像传统语言中的变量与类型关系一样。
(3).程序中的对象是类的一个实例,是一个软件单元,它由一组结构化的数据和在其上的一组操作构成。
4.1 面向对象程序设计 — 面向对象的概念
……
Car car1;
Car car2;
… …
Car carN;
4.1 面向对象程序设计 — 面向对象的概念
4.类
5.消息一个对象与另一个对象如何协作,共同完成一定功能?对象之间如何相互联系?这一切都依赖于消息的传递来实现。消息是一个对象要求另一个对象实施某项操作的请求,它反映了对象之间的信息通信机制,
是不同的对象之间信息交流的惟一手段。发送消息的对象称为发送者,
接收消息的对象称为接收者。在一条消息中,包含消息的接收者和要求接收者完成某项操作的请求,它只告诉接收者需完成什么,而不指示接收者如何完成,具体的操作过程由接收者自行决定。这样,对象之间就不会相互干扰,保证了系统的模块性。
一个对象可以接收不同形式的消息;同一个消息也可以发送给不同的对象;不同的对象对相同的消息可有不同的解释(这就形成多态性)。
发送者发送消息,接收者通过调用相应的方法响应消息,这个过程不断进行,使得整个应用程序在对象的相互调用过程中完成相应的功能,
得到相应的结果。因此,可以说消息是驱动面向对象程序运转的源泉。
4.1 面向对象程序设计 — 面向对象的概念
6,继承在介绍类的概念时提到,可由已有的类派生出新的类,派生出的新类称为子类,原来的类称为父类,从而构成了类的层次关系,也就是类的继承。继承是类之间的一种常见关 系,它是一种,一般,和
,特殊,的关系。如在 一个学校的人事管理系统中,可定义如下几个类:人员 Person类、学生 Student类和教师 Teacher类,其中 Person
类是 Student类和 Teacher类的父类,而 Student类和 Teacher类是
Personr的子类。它们的关系如图 4.1所示:通过继承,子类继承了母类的属性和行为,在子类中就不用再定义父类中已有的属性和行为了。
如在 Person类中,应该具有姓名、性别、年龄、籍贯、民族等属性和对人信息的修改和打印等行为,它们已被封装在 Person类中,在
Student类和 Teacher类中就不用再定义 Person类中已有的属性和行为了。通过继承,Student类和 Teacher类中已经自动具有了从父类继承下来的属性和行为,而只需在 Student类和 Teacher类中添加其自身所需的属性和行为即可。
4.1 面向对象程序设计 — 面向对象的概念
6,继承
4.1 面向对象程序设计 — 面向对象的概念图 4.1
7,多态在前面介绍消息时提到,不同的对象对相同的消息可有不同的解释,这就形成多态性。其实,简单地讲,多态性表示同一种事物的多种形态。如开拖拉机、开小轿车、开大卡车、开火车、
开摩托艇、开飞机等都是,开,,但作用的对象不同,其具体动作也各不相同。但都表达了一个相同的含义 ----开交通工具,
这也是一种抽象,而且是更高一级的抽象。
多态性是面向对象程序设计的主要精髓之一,在此仅作了简单的提及,后面还将详细介绍。
上述的几个概念中,抽象、封装、继承和多态是面向对象方法学中的四个最基本的概念,人们常常将抽象性、封装性、继承性和多态性称为面向对象的四大特性,只有深入了解了这四大特性,才有可能真正掌握面向对象的方法,才能真正步入面向对象程序设计的殿堂。
4.1 面向对象程序设计 — 面向对象的概念
8,UML静态视图简介
UML是统一建模语言( Unified Modeling Language)的缩写形式。它是一个通用的可视化建模语言,用于对软件进行描述、可视化处理、
构造和建立软件系统制品的文档等。 UML适用于各种软件开发方法、
软件生命周期的各个阶段、各种应用领域以及各种开发工具,是一种总结了以往建模技术的经验并吸收了当今优秀成果的标准建模方法。
UML具有相当广泛的内容,鉴于篇幅的限制,本节打算只介绍其中的稍许部分。
视图是 UML中最核心、最主要的内容。视图分结构视图和动态视图两大类,其中结构视图用于描述系统中的结构成员及其相互关系;动态视图用于描述系统随时间变化的行为。
静态视图是 UML中用得很广泛的一种视图,它是结构视图中的一种。
它之所以被称为静态的是因为它不描述与时间有关的系统行为,只用于描述系统中的结构成员及其相互关系。静态视图主要由类及类之间的相互关系组成,这些相互关系主要包括:关联、聚集和泛化等。
4.1 面向对象程序设计 — 面向对象的概念
8,UML静态视图简介静态视图用类图来实现。在类图中类用矩形框来表示,它的属性和行为分别列在分格中;如果不需要表达详细信息,分格可以省略。一个类可能出现在好几个图中,同一个类的属性和行为可以只在一种图中列出,在其他图中可省略。
关系用类框之间的连线来表示,不同的关系用连线和连线两端的修饰符来区别。
表 4-1是静态视图中经常使用的部分符号及说明,在此列出,以供参考。下面对关联、聚集和泛化分别各举一例,以说明如何绘制这些图。
如在学校里,一个教师( Teacher)可以教授多门课( Course),一门课可由多位教师讲授,因此,存在一个教师授课( TeacherCourse)的关联(如图 4.2所示)。在由界面元素构成的人机界面模型中,框架窗口、菜单、工具条等就形成一种聚集关系(如图 4.3所示)。
4.1 面向对象程序设计 — 面向对象的概念
8,UML静态视图简介
4.1 面向对象程序设计 — 面向对象的概念图 4.2
图 4.3
在前节中,对面向对象的一些基本思想和基本概念作了介绍。从本节开始,我们就在 Java中如何体现这些思想和概念作详细介绍。
可以把类认为是用于创建对象的模板,是数据类型,当在同样的类和创建或实例化对象时,就认为它们具有相同的类型。在 Java中,类的来源分为系统类和用户类两部分。系统类是由 Java系统所带的或由第三方厂家所提供的类,对用户来说只需直接使用即可,这也是在实际编程时所经常使用的方法。然而有时系统类的功能不能满足用户的需求,这就需要用户自已创建所需的类,这就是用户类。本节就如何创建用户类及其相关的知识作详细介绍。
4.2 创建用户类
类定义的完整形式为:
[public] [abstract] [final] class ClassName [extends
SuperClassName] [implements InterFaceName]
{
ClassBody
}
方括号表示可选择的部分,其余部分是必不可少的。所以,
定义类的最简单的形式为:
class ClassName
{
ClassBody
}
4.2 创建用户类 — 类的定义
类定义的完整形式简要说明,
1,class是 Java中用来定义一个类的关键字。
2,ClassName为所定义的类名,其命名符合 Java中变量的命名规则,一般第一个字母大写,每个单词首字母也大写,但并不是必需的。
3.大括号内的内容( ClassBody)为类体,是用来描述类的属性和行为的核心部分,一般它由两部分组成:一部分是属性的描述,常用数据类型来表示;另一部分是行为的描述,
常用方法来表示,有的书上将此处讲的方法也称为函数。对应地,一般将类体前的部分称为类的头部。
4.2 创建用户类 — 类的定义
类定义的完整形式简要说明,
4.定义类时,在类的头部若有关键字 public修饰符,则说明该类可以被任何包中的类所使用(有关包的概念请参阅后续章节的内容,
在此,可简单地把一个包当作一个文件夹)。若没有 public的修饰,
则该类只能被同一个包中的其它类所使用。
当一个源程序文件中有多个类的定义时,最多有一个类可以被声明为 public类,否则编译器会报错。所以,在编程时,将不同的
public类存放在不同的源程序文件中。
5,abstract 抽象类和 final 最终类
abstract和 final是 Java中分别用来定义抽象类和最终类的两个关键字,有关它们的细节内容在后续章节介绍。
6,extends 继承和 implements 接口类的继承与接口的实现在 java中所用的关键字分别为 extends和
implements,有关类的继承与接口的实现请参阅后续章节。
4.2 创建用户类 — 类的定义
成员变量的定义与初始化,
成员变量是用于描述实体的状态属性的。其实在面向对象的程序设计中,对于对象的状态属性的设置与获取也就是对相应成员变量的值的设置与获取。在 Java中,成员变量分为实例变量和类变量两类。实例变量在类实例化对象时,为每个对象都分配相应的变量空间,对同一个实例变量,每个对象都持有一个副本,改变了其中一个对象的实例变量值,其余对象的实例变量值不受影响。类变量也称为静态变量,一个类变量被同一个类所实例化的所有对象共享,
当改变了其中一个对象的类变量值时,其余对象的类变量值也相应被改变,因为它们共享的是同一个量。本节仅说明实例变量的定义形式,而类变量的详细介绍在后续章节。
实例变量定义的最简单的形式为:
DataType variableName;
4.2 创建用户类 — 成员变量的定义与初始化
成员变量的定义与初始化,
实例变量定义的最简单的形式为:
DataType variableName;
其中,DataType是变量的数据类型,它可以是前面介绍过的 Java中的基本数据类型,如 boolean,byte,short,long,double
等,也可以是复杂的数据类型,如数组,字符串等,还可以是某个类。当变量的类型是某个类时,我们一般将其直接称为某类的一个对象或实例。而 varialbleName是变量名,它的命名规则在前面章节已述。实例变量名第一个字母一般小写,
其余每个单词首字母大字;而静态变量名的每个字母一般都大写,但不是必需的。
4.2 创建用户类 — 成员变量的定义与初始化
成员方法的定义:
成员方法相当于其它程序语言中所说的函数或过程,它是一组执行语句序列。一个成员方法一般完成一个相应的功能。
前面提到:类是具有相同或相似属性和行为的一组对象的共同描述,而对象是一组信息及其操作的描述。这里的,信息,
是通过前面所讲的成员变量来描述的,而,操作,正是通过成员方法来具体实现的。
定义一个成员方法的最简单的形式为:
ResultType MethodName(parameterLists)
{
MethodBody
}
4.2 创建用户类 — 成员方法的定义
关于成员方法说明,
1.在方法体内部也可定义变量。一般将方法体内定义的变量称为局部变量。局部变量的作用域为其所在的方法体内,出了方法体就无效了。局部变量是没有默认值的,当出现对局部变量未赋值而直接引用其值时,编译不能通过。
2.成员变量与局部变量的第二个重要区别在于:成员变量可以先定义后引用,也可以先引用后定义;而局部变量必须是先定义后引用,也即定义必须在引用之前。
3.当不需要任何返回值时,方法的返回值类型用 void。 void是
java中的一个关键字,它表示没有任何返回值。注意,没有任何返回值不等于是 null。同时,返回值类型说明符对于成员方法而言不能省略。
4.2 创建用户类 — 成员方法的定义
关于成员方法说明,
4.成员方法不能嵌套定义,但可嵌套调用,同时,Java中的成员方法在定义时还支持递归调用。
5.通常方法的名称在它的类中是唯一的。但是在三种情况下,
方法可能与类或子类中的其他方法同名:覆盖方法、隐藏方法和名称重载。如果方法与超类中的方法有相同的标记或返回类型,那么它就覆盖或隐藏了超类方法。如果同一个类中的多个名称相同但特征标(参数个数、参数的数据类型或对象)不同的方法称为重载。
4.2 创建用户类 — 成员方法的定义
成员方法的重载:
简单地说,成员方法的重载就是在同一个类中,可同时定义名称相同的多个成员方法,而方法的参数类型、个数、顺序至少有一个不同,方法的返回类型和修饰符也可以不同 。
4.2 创建用户类 — 成员方法的重载
public class Point
{ int x,y;
int getX(){return x;}
int getY(){return y;}
void setXY(int dx,int dy){x=dx;y=dy;}//用两个整型值来描述 x,y坐标
void setXY(Point p){x=p.getX();y=p.getY();}//用一个点来描述坐标
}
4.2 创建用户类 — 成员方法的重载示例
Point类中有两个同名的成员方法 setXY(),它们参数不同。
因而,其方法体内的具体实现也不同。第二个 setXY()方法以 Point类的对象为参数,这是我们设置点坐标的一种常用方法,将点作为一个对象,直接设置,这是很自然的一种作法。
在类的定义中,有一种非常特殊的方法称为构造方法(构造器),它有如下特征:
1.形式上,方法名与类名完全一致。
2.没有返回值类型说明符,即使 void也没有(注:前面提到的返回值类型说明符不能省略是对成员方法而言的。成员方法和构造方法是两个不同类型的方法)。
3.它在构造类对象时使用,其主要作用是用于初始化实例变量。
定义构造方法的完整形式为:
[public]ClassName(parameterLists){ StructuralMethodBody}
方括号表示其间所包括的内容是可选的部分。 public为公有访问属性控制符,后面章节将会详细介绍。 ClassName为构造方法名,
也是类名; parameterLists为参数列表; StructuralMethodBody
为构造方法方法体。
4.2 创建用户类 — 构造方法的定义与重载
用 new实例化对象时,所调用的就是类所对应的构造方法,前面所说的,构造类对象时使用,也正是这个意思。
构造方法是通过 new构造对象时所调用的,如果企图像由类的某个对象来调用成员方法那样由类的某个对象来调用构造方法是行不通的。若有语句:
Point p=new Point(); p.Point();这是不行的。
若在定义构造方法时加上了返回值类型说明符,编译也能通过,但它已不是构造方法,而成了成员方法,因为用 new实例化对象时它不会被调用,而通过该类的某个对象像调用成员方法可以一样来调用,如在定义 Point类的构造方法时有,public void Point(){x=0;y=0;},
则如果有语句,Point p=new Point();
此时,new Point()不会调用 public void Point(){x=0;y=0;},而 p.Point()则可调用它。
在定义类时,像例 4-1那样没有构造方法的定义也是可行的。此时,系统会为其提供一个无参的默认的构造方法。所以在例 4-2中,
语句 Point position=new Point();是可行的。
4.2 创建用户类 — 关于构造方法的说明
参数类型可以将任何数据类型的参数传递给方法或构造器。这包括原始的数据类型
(比如整数、字符或双精度数)和引用数据类型(比如类和数组 )。
参数名称在声明方法或构造器的参数时,要为此参数提供一个名称。这个名称在方法体中用于引用数据。参数名称在它的作用范围内必须是唯一的。
参数不能与同一个方法或构造器中的另一个参数同名,也不能与此方法或构造器中的局部变量以及此方法或者构造器中的 catch子句中的任何参数同名。
按值传递参数是按值传递的:当方法或者构造器被调用时,它们接受被传递的变量的值。当参数是原始类型时,方法内不能改变它的值;当参数是引用类型时,,按值传递,意味着方法不能改变此对象的引用,但是可以调用对象的方法并修改对象中可访问变量。
4.2 创建用户类 — 将消息传递给方法或构造器
参数类型在 Java中,类的定义是可以嵌套的,即在一个类的类体部分还可以再定义另一个类。被嵌套在内部的类称为内部类(嵌套的类),
嵌套定义了另一个类的类称为外部类。
4.2 创建用户类 — 嵌套的类
在介绍类的概念时已认识到,对象是类的具体化,是类的一个实例,类就像一个能,生产对象,的机器,通过它,可以产生同类对象的实例。因此,通常将由类产生实例的过程称为对象实例化。本节就如何创建对象、如何使用对象及如何清除对象展开说明。
4.3 对象实例化
要创建一个对象,可细分为三个步骤:声明对象、实例化、初始化。
1.创建对象与定义变量极其相似。对象的声明格式为:
ClassName objectName1 [,objectName2,…,objectNamek];
其中,方括号是可选的内容。 ClassName是类名,这相当于变量类型。
objectNamei为对象名,这相当于变量名,当同时声明同类的多个对象时,
用逗号隔开。
2.对象的实例化是通过 new操作符来实现的。 new操作符用于为对象分配内存空间,new将所分配的内存空间的首地址返回给对象名。 new操作符需要一个参数,就是类的构造方法。
3,实例变量在类实例化对象时,为每个对象都分配相应的变量空间,对同一个实例变量,每个对象都持有一个副本。所以,初始化对象主要是初始化实例变量。所以,对象初始化与对象实例化在形式上是紧密联系在一起的,因为初始化通过构造方法来实现,而构造方法正是实例化对象操作符 new所需的参数。
对象初始化与实例化的形式为:
new ClassName(parameterLists);
4.3 对象实例化 — 创建对象对象创建后,就可通过对对象的使用在不同的对象之间进行消息传递。在介绍面向对象的基本概念时已经说明,消息是驱动面向对象程序运转的源泉,通过消息的发送与接收,驱动程序的运作。
如创建了如下两个对象:
Point pp=new Point(1,1);
Rectangle r=new Rectangle();
若有语句,r.move(pp)执行时,由对象 r去调用 Rectangle类中的方法 。
4.3 对象实例化 — 使用对象
对象一旦创建就为其分配了内存空间,当不再需要使用此对象时,应该将其所占的内存空间回收,清除此对象。 Java中,清除对象有两种途径,一是由系统,自动,清除,另一种则是由程序员,手动,清除。
Java中引入了先进的内存管理机制,也即人们常说的自动垃圾收集功能。 Java将不再使用的对象称为垃圾。自动垃圾收集器(置于 Java虚拟机 JVM内部的一个功能模块)会根据变量的作用域确定哪些变量不会再被使用,当它确定哪个变量不会再被使用时,会自动将其清除。
程序员需要变量时只管分配,而不需考虑何时清除及如何清除,这对编程带来了极大的方便。
而所谓的,手动,清除即由程序员自行清除,这种情况只需为其赋一空值
null即可。
如:
Point p=new Point(1,1);
…,.
p=null; //为 p赋值 null,从而清除对象 p
4.3 对象实例化 — 清除对象
面向对象程序设计提供了访问属性来实现数据的隐藏。不同的访问属性标志着不同的可访问性。
Java提供了四种访问控制属性,分别为:默认访问控制属性,public
(公有)访问控制属性,private(私有)访问控制属性和 protected
(保护)访问控制属性。
前面在介绍类的定义时给出的完整形式中,public就是 Java中的一个访问属性控制符。为了叙述的方便,在介绍成员变量和成员方法时只给出了成员变量和成员方法的最简单的定义形式。其实,在定义成员变量和成员方法时常在定义形式中加上访问属性控制符。下面是加上了访问控制属性后定义成员变量和成员方法的格式:
[public|private|protected] memberVariableName;
[public|private|protected] memberMethodName(parameterLists){methodBody}
方括号表示可选部分,其中的,|”表示其所分隔的部分为最多选择其一。
4.4 访问属性控制
没有指定任何访问控制属性时即为默认访问控制属性。
这四种访问控制属性都可以用来修饰成员变量、成员方法和内部类。
修饰类和接口时只能使用 public或默认访问控制属性,不能使用
private和 protected。
构造方法的定义一般用 public或默认访问控制属性来修饰,当用
private修饰时在别的类中将无法用 new调用相应的构造方法。
这四种访问控制属性有且仅有一个出现,当同时出现两个或多个修饰时编译出错。
4.4 访问属性控制 — 说明
在定义类、接口、成员变量、成员方法、构造方法以及内部类时,若没有指定任何访问属性控制符,则它们的访问控制属性即为默认访问控制属性。默认访问控制属性的可访问范围为同一个包,即具有默认访问控制属性的类、成员变量、成员方法等只能被同一个包中的其它类、成员方法等访问,因此也称默认访问控制属性为包属性。包是类和接口的集合,在 Windows
下,声明为同一个包的类被组织在同一个文件夹中。有关包的详细讨论在后续章节。
4.4 访问属性控制 — 默认访问属性
用 public修饰的类、接口、成员变量、成员方法等具有最宽的可访问范围,它们可以被任何包中的任何类所访问,所以,public
具有最好的开放性。
4.4 访问属性控制 — public
成员变量和成员方法用 private修饰时具有最好的封闭性,这是实现信息隐藏的最好方式。被 private修饰的成员变量和成员方法只能在同一个类中可被访问,在不同包的类中不可访问,在同一个包的不同类中也不可访问。
4.4 访问属性控制 — private
protected访问控制属性主要用于具有泛化关系的类之间在实现类的继承时所使用。被声明为 protected的成员变量、成员方法等类的成员可以被同一个类、同一个包中的不同类以及不同包中的具有泛化关系的子类所访问,它的可访问范围介于默认属性和 public属性之间。在类的继承一节中将对 protected
的使用规则作详细讨论。
4.4 访问属性控制 — protected
4.4 访问属性控制 — 简单总结
我们所讨论过的类成员主要包括成员变量、成员方法、构造方法和内部类等。这些类成员又可分为两种形式:一种是静态( static)的,称为类成员,主要包括类变量和类方法;另一种是非静态的,称为实例成员,主要包括实例变量和实例方法。一般所说的成员方法主要是指实例方法。
关于实例变量与实例方法在前面部分已作了详细讨论,本节主要介绍类变量和类方法。
4.5 静态成员
在介绍成员变量的定义形式时已经说明:类变量也称为静态变量,类变量独立于类的对象,无论创建了类的多少个对象,类变量都只有一个实例,一个类变量被同一个类所实例化的所有对象共享,当改变了其中一个对象的类变量值时,其余对象的类变量值也相应被改变,因为它们共享的是同一个量。所以,它是类所属的,与具体的对象无关,
这也是将其称为类变量的原因。
要定义一个类变量,只需在实例变量的定义形式的访问属性控制符后 ﹑ 类型说明符前加上 static关键字即可,形式如下:
[public|private|protected] static DataType varivableName;
如,public static int a,b;
4.5 静态成员 — 静态成员变量
有了静态成员变量的基础,理解静态成员方法就比较容易了。
静态成员方法的定义形式同类变量的定义形式非常相似,只须在定义成员方法时在方法的头部分的访问属性控制符后 ﹑ 方法的返回值类型前加入关键字 static即可。形式为:
[public|private|protected] static int methodName(parameterLists){… }
如:
public static float getMax(){return MAX;}
4.5 静态成员 — 静态成员方法
final
final可以用来修饰类,也可以用来修饰成员变量和成员方法,它还可以用来修饰局部变量。它用来修饰不同成员时有不同的意义。
当一个类用 final修饰时,意味着该类不能作为父类派生出其它的子类,因此 也将这样的类称为最终类。在设计类时,如果希望该类不再被扩展或修改,则可将其声明为最终类。如前面学过的 String类就是一个最终类。在 SUN公司所提供的标准 API包中,
还有很多类是最终类,如 System类,Math类,Integer类,Long类以及 Double等都是最终类。
final用来修饰成员变量和局部变量时,表明该变量被声明为一个常量,只能在定义时给它赋值,在程序的后续运行过程中,如果企图为其赋值,这是不允许的,只能引用其值。如,final double E=2.71828;这个语句定义了一个常量 E,为其赋初值
2.71828,如果在程序执行过程中有语句,E=2.7,则是不允许的。
用 final来修饰成员方法时,则说明了该方法不能被子类中相同签名的方法所覆盖,这对于一些操作要求限制比较严格的方法是有用的。
如果把类同时定义为 final和 abstract,那将毫不意义。当类 abstract时,它的方法的实现过程只能在它的子类中定义。但是 final类不能派生出子类,所以两个不能同时使用。如果同时使用,编译器就会产生一个错误。
4.6 final,this 和 null
this
在实例方法或构造器中,this引用当前对象,也就是被调用的方法或构造器所属的对象。通过 this关键字,可以在实例方法或构造器中引用当前对象的任何成员。
若有这样一个类的定义:
public class Person{
private String name,ID;
private short age;
public Person(String name,String ID,short age)
{…… }
}
在构造方法中,其功能显然是要将形参的三个值分别赋值给其间的三个实例变量,但问题在于形参名与实例变量名相同了,这如何赋值呢?是不是在构造方法体内加入语句,name=name; ID=ID; age=age; 呢?这显然不行。如果这样,系统如何区别谁是形参,谁是实例呢?是不是在类的定义中形参名与成员变量名相同就不行呢?情况并不是这样的。关键字 this在这里就显得不可或缺了。如果在构造方法中写入如下的语句,则同名问题就解决了,this.name=name; this.ID=ID; this.age=age;
this.name表示所引用的 name变量是当前类( Person)中定义的成员变量,赋值符号右端的 name则是在最近位置所定义的 name(即形参中的 name)。 ID和 age的理解与此相同。这样的格式是经常使用的。
4.6 final,this 和 null
null
在对象清除一节中,已介绍过 null,给一个对象赋予
null,就可,手动,清除该对象。其实,null是 Java中的一个直接量(也称常量),表示类类型(也称引用型)
变量为空的状态。其实,null还有一个经常使用的地方,
那就是通常用来作为类类型变量的初始值。如 String
str=null,以这样的形式为成员变量赋初值时,可使程序醒目,增强了程序的可读性。另外,当这样的形式用于局部变量时,在通常情况下都是赋初值所要求的。
4.6 final,this 和 null
4.7 包包是类和接口的集合,是 Java中组织程序文件的一种树形结构;
包的概念与其它程序设计语言中的函数库或类库比较相似。用户可将功能相似的多个类或接口放在同一个包中,同时也可在某个包中再声明一个子包,从而形成了一个关于包的树形结构。 Java
系统提供了很多标准包,其中包含了大量的类和接口,用户在编程时可直接使用它们。
4.7 包 — 包的声明用户要创建自己的包,首先要进行声明,其声明形式为:
package packageName;
其中,package是用来声明包的关键字; packageName为包的名字。 Package 语句的作用范围是整个源程序文件,该声明语句必须作为源程序文件的第一个语句,前面除了有注释或空行外,不能再有别的语句,一般将该语句写在文件的第一行。注意,
在包名后有一个分号。下面通过例子来说明如何声明一个包 。
4.7 包 — 包的使用
要使用包中的类,可采用下述方法之一:
1.要使用的类位于其他包中,或者引入包中,有多个名称相同的类,可通过全名(包名和类名)来引用。
2.对于频繁使用的类或者包名很长(包含很多子包),可以倒入单个类或者整个包中,这样可以减少工作量,类或包导入后,
只需通过名称可引用相应的类。
在一个包的类中,需要使用别的包中访问属性控制所允许的类时,需要告知系统被引用类的位置,这是通过包的载入语句来完成的。包的载入语句不论是载入系统标准包还是载入用户自定义的包,其形式都是一样的。
Java语言来表示这些概念。
本章要点
面向对象程序设计的基本概念;
类的创建;
对象实例化;
访问属性控制;
静态成员;
final,this和 null;
包;
综合应用示例。
4.1 面向对象程序设计
要掌握一种新思想、新方法,必须先了解其相关概念。本节主要介绍面向对象的一些相关概念及其相应的描述工具 —— UML图,是一些比较抽象的概念,
读者在学习的过程中,可以先了解其基本内容,待学完后面相关内容后再回来仔细揣摩和体会,必定能收到良好的效果。
所谓面向对象的方法学,就是使我们分析、设计和实现一个系统的方法尽可能地接近我们认识一个系统的方法。包括:
面向对象的分析( OOA,Object-Oriented Analysis)
面向对象的设计( OOD,Object-Oriented Design)
面向对象的程序设计 (OOPL,Object-Oriented Program)
面向对象方法学的核心思想是通过一些基本概念体现出来的。它主要围绕着对象、类、消息、继承性、多态性等基本概念和机制展开。如将,对象,作为一个独立的逻辑单元与现实世界中的客体相对应,用,类,来描述具有相同属性特征和行为方法的一组对象,可利用,继承,来实现具有继承关系的类之间的数据和方法的共享,对象之间以,消息,传递的方式进行,通信,等。下面对面向对象方法学中的部分主要核心概念作简单介绍。
4.1 面向对象程序设计 — 面向对象的概念
1.抽象抽象是人类认识世界的一种方式,它是指从同类型的众多事物中舍弃个别的、非本质的属性和行为,而抽取出共同的、
本质的属性和行为的过程。抽象主要包括事物属性的抽象和行为的抽象两种类型。
属性可用来描述事物在某时刻的状态,常用具体的数据来表示。
行为的抽象也称功能的抽象,即舍弃个别的功能,而抽取共同的功能的过程。
4.1 面向对象程序设计 — 面向对象的概念
2.封装封装是将事物的属性和行为聚集在一起而形成一个完整的逻辑单元的一种机制。利用这种机制可以实现信息的隐藏,外界客体只能通过封装向外界提供的接口才能访问描述事物属性的内部数据。这即有利于客体本身的维护,也有利于保护信息的安全。
在面向对象的程序设计过程中,封装的具体作法就是将描述对象状态的属性和对象固有的行为分别用数据结构和方法来加以描述,并将它们捆绑在一起形成一个可供外界访问的独立的逻辑单元,外界只能通过客体所提供的方法来对其间的数据结构加以访问,而不能直接存取。很明显,封装是实现信息隐藏的有效手段,它尽可能隐蔽对象的内部细节,只保留有限的对外接口,使之与外部发生联系。封装保证了数据的安全性、提高了应用系统的可维护性、也有利于软件的移植与重用。
4.1 面向对象程序设计 — 面向对象的概念
3.对象对象 有两个层次的概念:
现实生活中对象指的是客观世界的实体;
程序中对象就是一组变量和相关方法的集合,其中变量表明对象的状态,方法表明对象所具有的行为。
变量方法
4.1 面向对象程序设计 — 面向对象的概念
4.类
(1).类是描述对象的“基本原型”,它定义一类对象所能拥有的数据和能完成的操作。在面向对象的程序设计中,类是程序的基本单元。
(2).相似的对象可以归并到同一个类中去,就像传统语言中的变量与类型关系一样。
(3).程序中的对象是类的一个实例,是一个软件单元,它由一组结构化的数据和在其上的一组操作构成。
4.1 面向对象程序设计 — 面向对象的概念
……
Car car1;
Car car2;
… …
Car carN;
4.1 面向对象程序设计 — 面向对象的概念
4.类
5.消息一个对象与另一个对象如何协作,共同完成一定功能?对象之间如何相互联系?这一切都依赖于消息的传递来实现。消息是一个对象要求另一个对象实施某项操作的请求,它反映了对象之间的信息通信机制,
是不同的对象之间信息交流的惟一手段。发送消息的对象称为发送者,
接收消息的对象称为接收者。在一条消息中,包含消息的接收者和要求接收者完成某项操作的请求,它只告诉接收者需完成什么,而不指示接收者如何完成,具体的操作过程由接收者自行决定。这样,对象之间就不会相互干扰,保证了系统的模块性。
一个对象可以接收不同形式的消息;同一个消息也可以发送给不同的对象;不同的对象对相同的消息可有不同的解释(这就形成多态性)。
发送者发送消息,接收者通过调用相应的方法响应消息,这个过程不断进行,使得整个应用程序在对象的相互调用过程中完成相应的功能,
得到相应的结果。因此,可以说消息是驱动面向对象程序运转的源泉。
4.1 面向对象程序设计 — 面向对象的概念
6,继承在介绍类的概念时提到,可由已有的类派生出新的类,派生出的新类称为子类,原来的类称为父类,从而构成了类的层次关系,也就是类的继承。继承是类之间的一种常见关 系,它是一种,一般,和
,特殊,的关系。如在 一个学校的人事管理系统中,可定义如下几个类:人员 Person类、学生 Student类和教师 Teacher类,其中 Person
类是 Student类和 Teacher类的父类,而 Student类和 Teacher类是
Personr的子类。它们的关系如图 4.1所示:通过继承,子类继承了母类的属性和行为,在子类中就不用再定义父类中已有的属性和行为了。
如在 Person类中,应该具有姓名、性别、年龄、籍贯、民族等属性和对人信息的修改和打印等行为,它们已被封装在 Person类中,在
Student类和 Teacher类中就不用再定义 Person类中已有的属性和行为了。通过继承,Student类和 Teacher类中已经自动具有了从父类继承下来的属性和行为,而只需在 Student类和 Teacher类中添加其自身所需的属性和行为即可。
4.1 面向对象程序设计 — 面向对象的概念
6,继承
4.1 面向对象程序设计 — 面向对象的概念图 4.1
7,多态在前面介绍消息时提到,不同的对象对相同的消息可有不同的解释,这就形成多态性。其实,简单地讲,多态性表示同一种事物的多种形态。如开拖拉机、开小轿车、开大卡车、开火车、
开摩托艇、开飞机等都是,开,,但作用的对象不同,其具体动作也各不相同。但都表达了一个相同的含义 ----开交通工具,
这也是一种抽象,而且是更高一级的抽象。
多态性是面向对象程序设计的主要精髓之一,在此仅作了简单的提及,后面还将详细介绍。
上述的几个概念中,抽象、封装、继承和多态是面向对象方法学中的四个最基本的概念,人们常常将抽象性、封装性、继承性和多态性称为面向对象的四大特性,只有深入了解了这四大特性,才有可能真正掌握面向对象的方法,才能真正步入面向对象程序设计的殿堂。
4.1 面向对象程序设计 — 面向对象的概念
8,UML静态视图简介
UML是统一建模语言( Unified Modeling Language)的缩写形式。它是一个通用的可视化建模语言,用于对软件进行描述、可视化处理、
构造和建立软件系统制品的文档等。 UML适用于各种软件开发方法、
软件生命周期的各个阶段、各种应用领域以及各种开发工具,是一种总结了以往建模技术的经验并吸收了当今优秀成果的标准建模方法。
UML具有相当广泛的内容,鉴于篇幅的限制,本节打算只介绍其中的稍许部分。
视图是 UML中最核心、最主要的内容。视图分结构视图和动态视图两大类,其中结构视图用于描述系统中的结构成员及其相互关系;动态视图用于描述系统随时间变化的行为。
静态视图是 UML中用得很广泛的一种视图,它是结构视图中的一种。
它之所以被称为静态的是因为它不描述与时间有关的系统行为,只用于描述系统中的结构成员及其相互关系。静态视图主要由类及类之间的相互关系组成,这些相互关系主要包括:关联、聚集和泛化等。
4.1 面向对象程序设计 — 面向对象的概念
8,UML静态视图简介静态视图用类图来实现。在类图中类用矩形框来表示,它的属性和行为分别列在分格中;如果不需要表达详细信息,分格可以省略。一个类可能出现在好几个图中,同一个类的属性和行为可以只在一种图中列出,在其他图中可省略。
关系用类框之间的连线来表示,不同的关系用连线和连线两端的修饰符来区别。
表 4-1是静态视图中经常使用的部分符号及说明,在此列出,以供参考。下面对关联、聚集和泛化分别各举一例,以说明如何绘制这些图。
如在学校里,一个教师( Teacher)可以教授多门课( Course),一门课可由多位教师讲授,因此,存在一个教师授课( TeacherCourse)的关联(如图 4.2所示)。在由界面元素构成的人机界面模型中,框架窗口、菜单、工具条等就形成一种聚集关系(如图 4.3所示)。
4.1 面向对象程序设计 — 面向对象的概念
8,UML静态视图简介
4.1 面向对象程序设计 — 面向对象的概念图 4.2
图 4.3
在前节中,对面向对象的一些基本思想和基本概念作了介绍。从本节开始,我们就在 Java中如何体现这些思想和概念作详细介绍。
可以把类认为是用于创建对象的模板,是数据类型,当在同样的类和创建或实例化对象时,就认为它们具有相同的类型。在 Java中,类的来源分为系统类和用户类两部分。系统类是由 Java系统所带的或由第三方厂家所提供的类,对用户来说只需直接使用即可,这也是在实际编程时所经常使用的方法。然而有时系统类的功能不能满足用户的需求,这就需要用户自已创建所需的类,这就是用户类。本节就如何创建用户类及其相关的知识作详细介绍。
4.2 创建用户类
类定义的完整形式为:
[public] [abstract] [final] class ClassName [extends
SuperClassName] [implements InterFaceName]
{
ClassBody
}
方括号表示可选择的部分,其余部分是必不可少的。所以,
定义类的最简单的形式为:
class ClassName
{
ClassBody
}
4.2 创建用户类 — 类的定义
类定义的完整形式简要说明,
1,class是 Java中用来定义一个类的关键字。
2,ClassName为所定义的类名,其命名符合 Java中变量的命名规则,一般第一个字母大写,每个单词首字母也大写,但并不是必需的。
3.大括号内的内容( ClassBody)为类体,是用来描述类的属性和行为的核心部分,一般它由两部分组成:一部分是属性的描述,常用数据类型来表示;另一部分是行为的描述,
常用方法来表示,有的书上将此处讲的方法也称为函数。对应地,一般将类体前的部分称为类的头部。
4.2 创建用户类 — 类的定义
类定义的完整形式简要说明,
4.定义类时,在类的头部若有关键字 public修饰符,则说明该类可以被任何包中的类所使用(有关包的概念请参阅后续章节的内容,
在此,可简单地把一个包当作一个文件夹)。若没有 public的修饰,
则该类只能被同一个包中的其它类所使用。
当一个源程序文件中有多个类的定义时,最多有一个类可以被声明为 public类,否则编译器会报错。所以,在编程时,将不同的
public类存放在不同的源程序文件中。
5,abstract 抽象类和 final 最终类
abstract和 final是 Java中分别用来定义抽象类和最终类的两个关键字,有关它们的细节内容在后续章节介绍。
6,extends 继承和 implements 接口类的继承与接口的实现在 java中所用的关键字分别为 extends和
implements,有关类的继承与接口的实现请参阅后续章节。
4.2 创建用户类 — 类的定义
成员变量的定义与初始化,
成员变量是用于描述实体的状态属性的。其实在面向对象的程序设计中,对于对象的状态属性的设置与获取也就是对相应成员变量的值的设置与获取。在 Java中,成员变量分为实例变量和类变量两类。实例变量在类实例化对象时,为每个对象都分配相应的变量空间,对同一个实例变量,每个对象都持有一个副本,改变了其中一个对象的实例变量值,其余对象的实例变量值不受影响。类变量也称为静态变量,一个类变量被同一个类所实例化的所有对象共享,
当改变了其中一个对象的类变量值时,其余对象的类变量值也相应被改变,因为它们共享的是同一个量。本节仅说明实例变量的定义形式,而类变量的详细介绍在后续章节。
实例变量定义的最简单的形式为:
DataType variableName;
4.2 创建用户类 — 成员变量的定义与初始化
成员变量的定义与初始化,
实例变量定义的最简单的形式为:
DataType variableName;
其中,DataType是变量的数据类型,它可以是前面介绍过的 Java中的基本数据类型,如 boolean,byte,short,long,double
等,也可以是复杂的数据类型,如数组,字符串等,还可以是某个类。当变量的类型是某个类时,我们一般将其直接称为某类的一个对象或实例。而 varialbleName是变量名,它的命名规则在前面章节已述。实例变量名第一个字母一般小写,
其余每个单词首字母大字;而静态变量名的每个字母一般都大写,但不是必需的。
4.2 创建用户类 — 成员变量的定义与初始化
成员方法的定义:
成员方法相当于其它程序语言中所说的函数或过程,它是一组执行语句序列。一个成员方法一般完成一个相应的功能。
前面提到:类是具有相同或相似属性和行为的一组对象的共同描述,而对象是一组信息及其操作的描述。这里的,信息,
是通过前面所讲的成员变量来描述的,而,操作,正是通过成员方法来具体实现的。
定义一个成员方法的最简单的形式为:
ResultType MethodName(parameterLists)
{
MethodBody
}
4.2 创建用户类 — 成员方法的定义
关于成员方法说明,
1.在方法体内部也可定义变量。一般将方法体内定义的变量称为局部变量。局部变量的作用域为其所在的方法体内,出了方法体就无效了。局部变量是没有默认值的,当出现对局部变量未赋值而直接引用其值时,编译不能通过。
2.成员变量与局部变量的第二个重要区别在于:成员变量可以先定义后引用,也可以先引用后定义;而局部变量必须是先定义后引用,也即定义必须在引用之前。
3.当不需要任何返回值时,方法的返回值类型用 void。 void是
java中的一个关键字,它表示没有任何返回值。注意,没有任何返回值不等于是 null。同时,返回值类型说明符对于成员方法而言不能省略。
4.2 创建用户类 — 成员方法的定义
关于成员方法说明,
4.成员方法不能嵌套定义,但可嵌套调用,同时,Java中的成员方法在定义时还支持递归调用。
5.通常方法的名称在它的类中是唯一的。但是在三种情况下,
方法可能与类或子类中的其他方法同名:覆盖方法、隐藏方法和名称重载。如果方法与超类中的方法有相同的标记或返回类型,那么它就覆盖或隐藏了超类方法。如果同一个类中的多个名称相同但特征标(参数个数、参数的数据类型或对象)不同的方法称为重载。
4.2 创建用户类 — 成员方法的定义
成员方法的重载:
简单地说,成员方法的重载就是在同一个类中,可同时定义名称相同的多个成员方法,而方法的参数类型、个数、顺序至少有一个不同,方法的返回类型和修饰符也可以不同 。
4.2 创建用户类 — 成员方法的重载
public class Point
{ int x,y;
int getX(){return x;}
int getY(){return y;}
void setXY(int dx,int dy){x=dx;y=dy;}//用两个整型值来描述 x,y坐标
void setXY(Point p){x=p.getX();y=p.getY();}//用一个点来描述坐标
}
4.2 创建用户类 — 成员方法的重载示例
Point类中有两个同名的成员方法 setXY(),它们参数不同。
因而,其方法体内的具体实现也不同。第二个 setXY()方法以 Point类的对象为参数,这是我们设置点坐标的一种常用方法,将点作为一个对象,直接设置,这是很自然的一种作法。
在类的定义中,有一种非常特殊的方法称为构造方法(构造器),它有如下特征:
1.形式上,方法名与类名完全一致。
2.没有返回值类型说明符,即使 void也没有(注:前面提到的返回值类型说明符不能省略是对成员方法而言的。成员方法和构造方法是两个不同类型的方法)。
3.它在构造类对象时使用,其主要作用是用于初始化实例变量。
定义构造方法的完整形式为:
[public]ClassName(parameterLists){ StructuralMethodBody}
方括号表示其间所包括的内容是可选的部分。 public为公有访问属性控制符,后面章节将会详细介绍。 ClassName为构造方法名,
也是类名; parameterLists为参数列表; StructuralMethodBody
为构造方法方法体。
4.2 创建用户类 — 构造方法的定义与重载
用 new实例化对象时,所调用的就是类所对应的构造方法,前面所说的,构造类对象时使用,也正是这个意思。
构造方法是通过 new构造对象时所调用的,如果企图像由类的某个对象来调用成员方法那样由类的某个对象来调用构造方法是行不通的。若有语句:
Point p=new Point(); p.Point();这是不行的。
若在定义构造方法时加上了返回值类型说明符,编译也能通过,但它已不是构造方法,而成了成员方法,因为用 new实例化对象时它不会被调用,而通过该类的某个对象像调用成员方法可以一样来调用,如在定义 Point类的构造方法时有,public void Point(){x=0;y=0;},
则如果有语句,Point p=new Point();
此时,new Point()不会调用 public void Point(){x=0;y=0;},而 p.Point()则可调用它。
在定义类时,像例 4-1那样没有构造方法的定义也是可行的。此时,系统会为其提供一个无参的默认的构造方法。所以在例 4-2中,
语句 Point position=new Point();是可行的。
4.2 创建用户类 — 关于构造方法的说明
参数类型可以将任何数据类型的参数传递给方法或构造器。这包括原始的数据类型
(比如整数、字符或双精度数)和引用数据类型(比如类和数组 )。
参数名称在声明方法或构造器的参数时,要为此参数提供一个名称。这个名称在方法体中用于引用数据。参数名称在它的作用范围内必须是唯一的。
参数不能与同一个方法或构造器中的另一个参数同名,也不能与此方法或构造器中的局部变量以及此方法或者构造器中的 catch子句中的任何参数同名。
按值传递参数是按值传递的:当方法或者构造器被调用时,它们接受被传递的变量的值。当参数是原始类型时,方法内不能改变它的值;当参数是引用类型时,,按值传递,意味着方法不能改变此对象的引用,但是可以调用对象的方法并修改对象中可访问变量。
4.2 创建用户类 — 将消息传递给方法或构造器
参数类型在 Java中,类的定义是可以嵌套的,即在一个类的类体部分还可以再定义另一个类。被嵌套在内部的类称为内部类(嵌套的类),
嵌套定义了另一个类的类称为外部类。
4.2 创建用户类 — 嵌套的类
在介绍类的概念时已认识到,对象是类的具体化,是类的一个实例,类就像一个能,生产对象,的机器,通过它,可以产生同类对象的实例。因此,通常将由类产生实例的过程称为对象实例化。本节就如何创建对象、如何使用对象及如何清除对象展开说明。
4.3 对象实例化
要创建一个对象,可细分为三个步骤:声明对象、实例化、初始化。
1.创建对象与定义变量极其相似。对象的声明格式为:
ClassName objectName1 [,objectName2,…,objectNamek];
其中,方括号是可选的内容。 ClassName是类名,这相当于变量类型。
objectNamei为对象名,这相当于变量名,当同时声明同类的多个对象时,
用逗号隔开。
2.对象的实例化是通过 new操作符来实现的。 new操作符用于为对象分配内存空间,new将所分配的内存空间的首地址返回给对象名。 new操作符需要一个参数,就是类的构造方法。
3,实例变量在类实例化对象时,为每个对象都分配相应的变量空间,对同一个实例变量,每个对象都持有一个副本。所以,初始化对象主要是初始化实例变量。所以,对象初始化与对象实例化在形式上是紧密联系在一起的,因为初始化通过构造方法来实现,而构造方法正是实例化对象操作符 new所需的参数。
对象初始化与实例化的形式为:
new ClassName(parameterLists);
4.3 对象实例化 — 创建对象对象创建后,就可通过对对象的使用在不同的对象之间进行消息传递。在介绍面向对象的基本概念时已经说明,消息是驱动面向对象程序运转的源泉,通过消息的发送与接收,驱动程序的运作。
如创建了如下两个对象:
Point pp=new Point(1,1);
Rectangle r=new Rectangle();
若有语句,r.move(pp)执行时,由对象 r去调用 Rectangle类中的方法 。
4.3 对象实例化 — 使用对象
对象一旦创建就为其分配了内存空间,当不再需要使用此对象时,应该将其所占的内存空间回收,清除此对象。 Java中,清除对象有两种途径,一是由系统,自动,清除,另一种则是由程序员,手动,清除。
Java中引入了先进的内存管理机制,也即人们常说的自动垃圾收集功能。 Java将不再使用的对象称为垃圾。自动垃圾收集器(置于 Java虚拟机 JVM内部的一个功能模块)会根据变量的作用域确定哪些变量不会再被使用,当它确定哪个变量不会再被使用时,会自动将其清除。
程序员需要变量时只管分配,而不需考虑何时清除及如何清除,这对编程带来了极大的方便。
而所谓的,手动,清除即由程序员自行清除,这种情况只需为其赋一空值
null即可。
如:
Point p=new Point(1,1);
…,.
p=null; //为 p赋值 null,从而清除对象 p
4.3 对象实例化 — 清除对象
面向对象程序设计提供了访问属性来实现数据的隐藏。不同的访问属性标志着不同的可访问性。
Java提供了四种访问控制属性,分别为:默认访问控制属性,public
(公有)访问控制属性,private(私有)访问控制属性和 protected
(保护)访问控制属性。
前面在介绍类的定义时给出的完整形式中,public就是 Java中的一个访问属性控制符。为了叙述的方便,在介绍成员变量和成员方法时只给出了成员变量和成员方法的最简单的定义形式。其实,在定义成员变量和成员方法时常在定义形式中加上访问属性控制符。下面是加上了访问控制属性后定义成员变量和成员方法的格式:
[public|private|protected] memberVariableName;
[public|private|protected] memberMethodName(parameterLists){methodBody}
方括号表示可选部分,其中的,|”表示其所分隔的部分为最多选择其一。
4.4 访问属性控制
没有指定任何访问控制属性时即为默认访问控制属性。
这四种访问控制属性都可以用来修饰成员变量、成员方法和内部类。
修饰类和接口时只能使用 public或默认访问控制属性,不能使用
private和 protected。
构造方法的定义一般用 public或默认访问控制属性来修饰,当用
private修饰时在别的类中将无法用 new调用相应的构造方法。
这四种访问控制属性有且仅有一个出现,当同时出现两个或多个修饰时编译出错。
4.4 访问属性控制 — 说明
在定义类、接口、成员变量、成员方法、构造方法以及内部类时,若没有指定任何访问属性控制符,则它们的访问控制属性即为默认访问控制属性。默认访问控制属性的可访问范围为同一个包,即具有默认访问控制属性的类、成员变量、成员方法等只能被同一个包中的其它类、成员方法等访问,因此也称默认访问控制属性为包属性。包是类和接口的集合,在 Windows
下,声明为同一个包的类被组织在同一个文件夹中。有关包的详细讨论在后续章节。
4.4 访问属性控制 — 默认访问属性
用 public修饰的类、接口、成员变量、成员方法等具有最宽的可访问范围,它们可以被任何包中的任何类所访问,所以,public
具有最好的开放性。
4.4 访问属性控制 — public
成员变量和成员方法用 private修饰时具有最好的封闭性,这是实现信息隐藏的最好方式。被 private修饰的成员变量和成员方法只能在同一个类中可被访问,在不同包的类中不可访问,在同一个包的不同类中也不可访问。
4.4 访问属性控制 — private
protected访问控制属性主要用于具有泛化关系的类之间在实现类的继承时所使用。被声明为 protected的成员变量、成员方法等类的成员可以被同一个类、同一个包中的不同类以及不同包中的具有泛化关系的子类所访问,它的可访问范围介于默认属性和 public属性之间。在类的继承一节中将对 protected
的使用规则作详细讨论。
4.4 访问属性控制 — protected
4.4 访问属性控制 — 简单总结
我们所讨论过的类成员主要包括成员变量、成员方法、构造方法和内部类等。这些类成员又可分为两种形式:一种是静态( static)的,称为类成员,主要包括类变量和类方法;另一种是非静态的,称为实例成员,主要包括实例变量和实例方法。一般所说的成员方法主要是指实例方法。
关于实例变量与实例方法在前面部分已作了详细讨论,本节主要介绍类变量和类方法。
4.5 静态成员
在介绍成员变量的定义形式时已经说明:类变量也称为静态变量,类变量独立于类的对象,无论创建了类的多少个对象,类变量都只有一个实例,一个类变量被同一个类所实例化的所有对象共享,当改变了其中一个对象的类变量值时,其余对象的类变量值也相应被改变,因为它们共享的是同一个量。所以,它是类所属的,与具体的对象无关,
这也是将其称为类变量的原因。
要定义一个类变量,只需在实例变量的定义形式的访问属性控制符后 ﹑ 类型说明符前加上 static关键字即可,形式如下:
[public|private|protected] static DataType varivableName;
如,public static int a,b;
4.5 静态成员 — 静态成员变量
有了静态成员变量的基础,理解静态成员方法就比较容易了。
静态成员方法的定义形式同类变量的定义形式非常相似,只须在定义成员方法时在方法的头部分的访问属性控制符后 ﹑ 方法的返回值类型前加入关键字 static即可。形式为:
[public|private|protected] static int methodName(parameterLists){… }
如:
public static float getMax(){return MAX;}
4.5 静态成员 — 静态成员方法
final
final可以用来修饰类,也可以用来修饰成员变量和成员方法,它还可以用来修饰局部变量。它用来修饰不同成员时有不同的意义。
当一个类用 final修饰时,意味着该类不能作为父类派生出其它的子类,因此 也将这样的类称为最终类。在设计类时,如果希望该类不再被扩展或修改,则可将其声明为最终类。如前面学过的 String类就是一个最终类。在 SUN公司所提供的标准 API包中,
还有很多类是最终类,如 System类,Math类,Integer类,Long类以及 Double等都是最终类。
final用来修饰成员变量和局部变量时,表明该变量被声明为一个常量,只能在定义时给它赋值,在程序的后续运行过程中,如果企图为其赋值,这是不允许的,只能引用其值。如,final double E=2.71828;这个语句定义了一个常量 E,为其赋初值
2.71828,如果在程序执行过程中有语句,E=2.7,则是不允许的。
用 final来修饰成员方法时,则说明了该方法不能被子类中相同签名的方法所覆盖,这对于一些操作要求限制比较严格的方法是有用的。
如果把类同时定义为 final和 abstract,那将毫不意义。当类 abstract时,它的方法的实现过程只能在它的子类中定义。但是 final类不能派生出子类,所以两个不能同时使用。如果同时使用,编译器就会产生一个错误。
4.6 final,this 和 null
this
在实例方法或构造器中,this引用当前对象,也就是被调用的方法或构造器所属的对象。通过 this关键字,可以在实例方法或构造器中引用当前对象的任何成员。
若有这样一个类的定义:
public class Person{
private String name,ID;
private short age;
public Person(String name,String ID,short age)
{…… }
}
在构造方法中,其功能显然是要将形参的三个值分别赋值给其间的三个实例变量,但问题在于形参名与实例变量名相同了,这如何赋值呢?是不是在构造方法体内加入语句,name=name; ID=ID; age=age; 呢?这显然不行。如果这样,系统如何区别谁是形参,谁是实例呢?是不是在类的定义中形参名与成员变量名相同就不行呢?情况并不是这样的。关键字 this在这里就显得不可或缺了。如果在构造方法中写入如下的语句,则同名问题就解决了,this.name=name; this.ID=ID; this.age=age;
this.name表示所引用的 name变量是当前类( Person)中定义的成员变量,赋值符号右端的 name则是在最近位置所定义的 name(即形参中的 name)。 ID和 age的理解与此相同。这样的格式是经常使用的。
4.6 final,this 和 null
null
在对象清除一节中,已介绍过 null,给一个对象赋予
null,就可,手动,清除该对象。其实,null是 Java中的一个直接量(也称常量),表示类类型(也称引用型)
变量为空的状态。其实,null还有一个经常使用的地方,
那就是通常用来作为类类型变量的初始值。如 String
str=null,以这样的形式为成员变量赋初值时,可使程序醒目,增强了程序的可读性。另外,当这样的形式用于局部变量时,在通常情况下都是赋初值所要求的。
4.6 final,this 和 null
4.7 包包是类和接口的集合,是 Java中组织程序文件的一种树形结构;
包的概念与其它程序设计语言中的函数库或类库比较相似。用户可将功能相似的多个类或接口放在同一个包中,同时也可在某个包中再声明一个子包,从而形成了一个关于包的树形结构。 Java
系统提供了很多标准包,其中包含了大量的类和接口,用户在编程时可直接使用它们。
4.7 包 — 包的声明用户要创建自己的包,首先要进行声明,其声明形式为:
package packageName;
其中,package是用来声明包的关键字; packageName为包的名字。 Package 语句的作用范围是整个源程序文件,该声明语句必须作为源程序文件的第一个语句,前面除了有注释或空行外,不能再有别的语句,一般将该语句写在文件的第一行。注意,
在包名后有一个分号。下面通过例子来说明如何声明一个包 。
4.7 包 — 包的使用
要使用包中的类,可采用下述方法之一:
1.要使用的类位于其他包中,或者引入包中,有多个名称相同的类,可通过全名(包名和类名)来引用。
2.对于频繁使用的类或者包名很长(包含很多子包),可以倒入单个类或者整个包中,这样可以减少工作量,类或包导入后,
只需通过名称可引用相应的类。
在一个包的类中,需要使用别的包中访问属性控制所允许的类时,需要告知系统被引用类的位置,这是通过包的载入语句来完成的。包的载入语句不论是载入系统标准包还是载入用户自定义的包,其形式都是一样的。