面向对象的程序设计-Java张白一第三版第4章.ppt
第4章 类与对象,4.1 类与对象的概念 4.2 封装机制 4.3 数据成员 4.4 成员方法,4.1 类与对象的概念程序设计所面对的问题域客观世界,是由许多事物构成的,这些事物既可以是有形的(比如一辆汽车),也可以是无形的(比如一次会议)。在面向对象的程序设计中,客观世界中的事物映射为对象。对象是面向对象程序设计中用来描述客观事物的基本单位。客观世界中的许多对象,无论其属性还是其行为常常有许多共同性,抽象出这些对象的共同性便可以构成类。所以,类是对象的抽象和归纳,对象是类的实例。,4.1.1 抽象原则所谓抽象(abstraction),就是从被研究对象中舍弃个别的、非本质的,或与研究主旨无关的次要特征,而抽取与研究工作有关的实质性内容加以考察,形成对所研究问题正确的、简明扼要的认识。例如,“马”就是一个抽象的概念,实际上没有任何两匹马是完全相同的,但是我们舍弃了每匹马个体之间的差异,抽取其共同的、本质性的特征,就形成了“马”这个概念。,抽象是科学研究中经常使用的一种方法,是形成概念的必要手段。在计算机软件开发领域,所有编程语言都提供抽象机制,人们所能够解决的问题的复杂性直接取决于抽象的层次和质量。编程语言的抽象是指求解问题时是否根据运行解决方案的计算机结构来描述问题,它是以“机器语言汇编语言面向过程的语言面向对象的语言”这样的路径发展的。随着不同抽象层次的进展,目前主要强调的是过程抽象和数据抽象。,1.过程抽象过程抽象(procedural abstraction)是指任何一个完成确定功能的操作序列,其使用者都可把它看做一个单一的实体,尽管这个操作可能是由一系列更低级的操作完成的。过程抽象隐藏了过程的具体实现。例如,用于求一个正整数平方的过程可以有下面的不同实现方式。方式1:int square(int k)return k*k;,方式2:int square(int k)int result=0;for(int i=0;ik;i+)result+=k;return result;以上两种实现方式代表了相同的抽象操作:当传递一个正整数调用square过程时,它们都返回输入值的平方,不同的实现方式并不影响任何一个调用square过程的程序的正确性。,面向过程的语言(如Fortran、Pascal、C等)的程序设计采用的是过程抽象。过程在C语言中称为函数,在其他语言中称为子程序等。当求解一个问题时,过程抽象的程序设计是将一个复杂的问题分解为多个子问题,如果子问题仍然比较复杂,可再分解为多个子问题,形成层次结构。每一个子问题就是一个子过程,高层的过程可以将它下一层中的过程当做抽象操作来使用,而不用考虑它下层过程的实现方法。最后,从最底层的过程逐个求解,合并形成原问题的解。,过程抽象有两个主要优点:通过将过程看做抽象操作,编程人员可以在无需知道过程是如何实现的情况下使用它们。只要抽象操作的功能是确定的,即使过程的实现被修改,也不会影响使用这个过程的程序。然而,过程抽象只关注操作,没有把操作和被操作的数据作为一个整体来看待,存在一定的弊端。二十世纪七十年代,学者们提出了抽象数据类型的概念,后来进一步发展成数据抽象的概念。,2.数据抽象数据抽象(Data Abstraction)把系统中需要处理的数据和施加于这些数据之上的操作结合为一个不可分的系统单位(即对象),根据功能、性质、作用等因素把它们抽象成不同的抽象数据类型。每个抽象数据类型既包含了数据,也包含了针对这些数据的授权操作,并限定数据的值只能由这些操作来观察和修改。因此,数据抽象是相对于过程抽象的更为严格、更为合理的抽象方法。,在数据抽象中,一个抽象数据类型(值或对象)表示一组数据和一组公共操作,这些操作构成这些数据的接口。数据值的实现包括它的内部表示和基于这些表示的操作的实现。数据抽象仅提供给编程人员数据值的接口而屏蔽了它的实现,编程人员通过接口访问数据。使用数据抽象有很多优点。首先,用户不需要了解详细的实现细节就可使用它。其次,由于对用户屏蔽了数据类型的实现,因此,只要保持接口不变,数据实现的改变并不影响用户的使用。另外,由于接口规定了用户与数据之间所有可能的交互,因此,也就避免了用户对数据的非授权操作。,面向对象的程序设计就是采用数据抽象这一方法来构建程序中的类和对象的。它强调把数据和操作结合为一个不可分的系统单位类/对象,对象的外部只需要知道这个对象能做什么,而不必知道它是如何做的。,3.面向过程程序设计和面向对象程序设计的不同下面通过编写求长方形面积的程序实例来说明面向过程的程序设计与面向对象的程序设计的不同。(1)在面向过程的程序设计中,把计算长方形的面积看成一个长方形过程,在过程中给出长和宽变量及求长方形面积的语句,将长和宽的值作为长方形过程的参数,通过调用该过程就可以得到该长方形的面积。,int area(int l,int w)int length=l;int width=w;return(length*width);t=area(30,20);/将长和宽的值作为长方形过程的参数,调用长方形的过程,得到长方形的面积,(2)面向对象的程序设计。首先,把长方形看成一个长方形对象,将长方形对象的共性抽象出来设计成长方形类,定义类的属性(静态特征)和方法(动态特征)。然后,创建长方形类的对象,将长和宽的值的信息传递给对象的方法,引用对象的方法求对象的面积。,4.1.2 对象只要仔细研究程序设计所面对的问题域客观世界,就可以看到:客观世界是由一些具体的事物构成的,每个事物都具有自己的一组静态特征(属性)和一组动态特征(行为)。例如,一辆汽车有颜色、型号、马力、生产厂家等静态特征,又具有行驶、转弯、停车等动态特征。把客观世界的这一事实映射到面向对象的程序设计中,则是把问题域中的事物抽象成了对象(object),把事物的静态特征(属性)抽象成了一组数据,把事物的动态特征(行为)抽象成了一组方法。因此,对象具有下述特征。,(1)对象标识:即对象的名字,是用户和系统识别它的唯一标志。例如,汽车的牌照可作为每一辆汽车对象的标识。对象标识有“外部标识”和“内部标识”之分。外部标识供对象的定义者或使用者使用,内部标识供系统内部唯一地识别每一个对象。在计算机世界中,可以把对象看成计算机存储器中一块可标识的区域,它能保存固定或可变数目的数据(或数据的集合)。(2)属性:即一组数据,用来描述对象的静态特征,例如汽车的颜色、型号、马力、生产厂家等。在Java程序中,这组数据被称为数据成员。,(3)方法:也称为服务或操作,它是对对象动态特征(行为)的描述。每一个方法确定对象的一种行为或功能。例如,汽车的行驶、转弯、停车等动作可分别用move()、rotate()、stop()等方法来描述。为避免混淆,本书把方法称为成员方法。在Java程序中,类是创建对象的模板,对象是类的实例,任何一个对象都是隶属于某个类的。Java程序设计是从类的设计开始的,所以,在进一步讲述对象的知识之前,必须先掌握类的概念。,4.1.3 类对象是对事物的抽象,而类是对对象的抽象和归纳。人类在认识客观世界时经常采用的思维方法就是把众多的事物归纳成一些类。分类所依据的原则是抽象,即抽象出与当前目标有关的本质特征,而忽略那些与当前目标无关的非本质特征,从而找出事物的共性,把具有共同性质的事物归结为一类,得出一个抽象的概念类。,在面向对象的编程语言中,类是一个独立的程序单位,是具有相同属性和方法的一组对象的集合。类的概念使我们能对属于该类的全部对象进行统一的描述。例如,“树具有树根、树干、树枝和树叶,它能进行光合作用”。这个描述适合于所有的树,从而不必对每棵具体的树都进行一次这样的描述。因此,在定义对象之前应先定义类。描述一个类需要指明下述三个方面的内容。(1)类标识:类的一个有别于其他类的名字。这是必不可少的。(2)属性说明:用来描述相同对象的静态特征。(3)方法说明:用来描述相同对象的动态特征。,例如,下面是对Dog类进行的描述:,类的方法(动态特征),4.1.4 类与对象的关系类给出了属于该类的全部对象的抽象定义,而对象则是符合这种定义的一个实体。类与对象之间的关系就如同一个模具与用这个模具铸造出来的铸件之间的关系一样。也就是说,可以把类与对象之间的关系看成是抽象与具体的关系。在面向对象的程序设计中,对象被称做类的一个实例(instance),而类是对象的模板(template)。类是多个实例的综合抽象,而实例又是类的个体实物。图4.1所示为类与对象的关系。由于对象是类的实例,因此在定义对象之前应先定义类。在定义了类之后,才可以在类的基础上创建对象。,图4.1 类与对象的关系,4.1.5 定义类的一般格式进行Java程序设计,实际上就是定义类的过程。一个Java源程序文件往往是由许多个类组成的。从用户的角度看,Java源程序中的类分为两种:(1)系统定义的类,即Java类库,是系统定义好的类。类库是Java语言的重要组成部分。Java语言由语法规则和类库两部分组成。语法规则确定Java程序的书写规范;类库则提供了Java程序与运行它的系统软件(Java虚拟机)之间的接口。Java类库是一组由它的发明者Sun公司以及其他软件开发商编写好的Java程序模块,每个模块通常对应一种特定的基本功能和任务,且这些模块都是经过严格测试的,,因而也总是正确有效的。当自己编写的Java程序需要完成其中某一功能的时候,就可以直接利用这些现成的类库,而不需要一切从头编写,这样不仅可以提高编程效率,也可以保证软件的质量。关于Java类库的更多内容将在节及以后的章节中讲述。(2)用户自己定义的类。系统定义的类虽然实现了许多常见的功能,但是用户程序仍然需要针对特定问题的特定逻辑来定义自己的类。用户按照Java的语法规则,把所研究的问题描述成Java程序中的类,以解决特定问题。进行Java程序设计,首先应学会怎样定义类。,在Java程序中,用户自己定义类的一般格式如下:class类名 数据成员 成员方法可以看出,类的结构是由类说明和类体两部分组成的。类的说明部分由关键字class与类名组成,类名的命名遵循Java标识符的定义规则;类体是类声明中花括号所包括的全部内容,它又由数据成员(属性)和成员方法(方法)两部分组成。数据成员描述对象的属性;成员方法刻画对象的行为或动作,每一个成员方法确定一个功能或操作。图4.2为类的图形表示。,图4.2 类的图形表示,【示例程序C4_1.java】定义一个有数据成员及成员方法的类。import;/引用系统类库中的awt包的类import;/引用系统类库中的applet包的类public class C4_1 extends Applet/由Applet类派生的C4_1类 int a=5;/数据成员a double b=23.4;/数据成员b public void paint(Graphics g)/成员方法paint/以下使用g对象的drawString方法,该方法继承自Graphics类 g.drawString(a=+a,25,25);g.drawString(b=+b,25,35);该程序的运行结果如图4.3所示。,图4.3 程序C4_1的运行结果,4.1.6 Java类库要想掌握好Java的面向对象编程技术,编写出高质量的程序,必须对Java的类库有足够的了解。Java的类库是系统提供的已实现的标准类的集合,是Java编程的API(Application Program Interface),它可以帮助开发者方便、快捷地开发Java程序。Java类库的主要部分是由它的发明者Sun公司提供的(这些类库称为基础类库(JFC),也有少量则是由其他软件开发商以商品形式提供的。有了类库中的系统类,编写Java程序时就不必一切从头做起,避免了代码的重复和可能的错误,也提高了编程的效率。一个用户程序中系统标准类使用得越多、越全面、越准确,这个程序的质量,就越高;相反,离开了系统标准类和类库,Java程序几乎寸步难行。所以,学习Java语言程序设计,一是要学习其语法规则,即第23章中的基本数据类型、基本运算和基本语句等,这是编写Java程序的基本功;二是要学习使用类库,这是提高编程效率和质量的必由之路,甚至从一定程度上说,能否熟练自如地掌握尽可能多的Java类库,决定了一个Java程序员编程能力的高低。本书从第7章起主要讲述类库的使用。,在Java系统中,系统定义好的类根据实现功能的不同,可以划分成不同的集合。每个集合称为一个包,所有包合称为类库。根据功能的不同,Java类库的每个包中都有若干个具有特定功能和相互关系的类和接口。例如,java.lang包中包含了运行Java程序必不可少的系统类,它们包括基本数据类型、基本数学方法、字符串处理、线程、异常处理等类;java.awt包中包括了Java语言用来构建图形用户界面(GUI)的类库。,对于类库中系统定义好的类,有三种使用方式:一种是直接使用系统类,例如在字符界面向系统标准输出设备输出字符串时使用的方法(),就是系统类System的动态属性out的方法;另一种方式是继承系统类,在用户程序里创建系统类的子类,例如每个Java Applet的主类都是java.applet包中的Applet类的子类;最后一种方式是创建系统类的对象,例如当图形界面的程序要接受用户的输入时,就可以创建一个系统类TextField类的对象来完成这个任务。,无论采用哪种方式,使用系统类的前提条件是这个系统类应该是用户程序可见的类。为此用户程序需要用import语句引入它所用到的系统类或系统类所在的包。例如使用图形用户界面的程序,应该用语句:import java.awt.*;import.*;引入java.awt包和包。,类库包中的程序都是字节码形式的程序,利用import语句将一个包引入到程序里,就相当于在编译过程中将该包中所有系统类的字节码加入到用户的Java程序中,这样用户的Java程序就可以使用这些系统类及其中的各种功能了。下面列出一些在Java程序设计中经常使用的包。1java.lang包java.lang包是Java语言的核心类库,包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等。每个Java程序运行时,系统都会自动地引入java.lang包,所以这个包的加载是缺省的。,2java.io包java.io包中包含了实现Java程序与操作系统、用户界面以及其他Java程序之间进行数据交换所使用的类,如基本输入/输出流、文件输入/输出流、过滤输入/输出流、管道输入/输出流、随机输入/输出流等。凡是需要完成与操作系统有关的较底层的输入/输出操作的Java程序,都要用到java.io包。,3java.awt包java.awt包是Java语言用来构建图形用户界面(GUI)的类库,它包括了许多界面元素和资源。利用java.awt包,开发人员可以很方便地编写出美观、方便、标准化的应用程序界面。java.awt包主要在三个方面提供界面设计支持:低级绘图操作,如Graphics类等;图形界面组件和布局管理,如Checkbox类、Container类、LayoutManager接口等;界面用户交互控制和事件响应,如Event类。,4包包是对JDK 1.0版本中原有的Event类的一个扩充,它使得程序可以用不同的方式来处理不同类型的事件,并使每个图形界面的元素本身可以拥有处理它上面事件的能力。5包包是用来处理和操纵来自于网上的图片的Java工具类库。6java.applet包java.applet包是用来实现运行于Internet浏览器中的Java Applet的工具类库,它仅包含少量几个接口和一个非常有用的类。,包包是Java语言用来实现网络功能的类库。由于Java语言还在不停地发展和扩充,因此它的功能,尤其是网络功能,也在不断地扩充。目前已经实现的Java网络功能类主要有:底层的网络通信类,如实现套接字通信的Socket类、ServerSocket类;编写用户自己的Telnet、FTP、邮件服务等实现网上通信的类;用于访问Internet上资源和进行CGI网关调用的类,如URL等。利用包中的类,开发者可以编写自己的具有网络功能的程序。,8java.rmi包、包和包这三个包用来实现RMI(Remote Method Invocation,远程方法调用)功能。利用RMI功能,用户程序可以在远程计算机(服务器)上创建对象,并在本地计算机(客户机)上使用这个对象。9java.util包java.util包中包括了Java语言中一些低级的实用工具,如处理时间的Date类,处理变长数组的Vector类,实现栈的Stack类和实现哈希(散列)表的HashTable类等,开发者使用这些类可以更方便快捷地编程。,10java.sql包java.sql包是实现JDBC(Java DataBase Connection)的类库,利用这个包可以使Java程序具有访问不同种类数据库(如Oracle、Sybase、DB2、SQLServer等)的功能。只要安装了合适的驱动程序,同一个Java程序不需修改就可以存取、修改这些不同数据库中的数据。JDBC的这种功能,再加上Java程序本身具有的平台无关性,大大拓宽了Java程序的应用范围,尤其是商业应用的适用领域。,11java.security包、包和包这三个包提供了更完善的Java程序安全性控制和管理,利用它们可以对Java程序加密,也可以把特定的Java Applet标记为“可信赖的”,使它能够具有与Java Application相近的安全权限。12包包是实现Java语言跨平台特性的手段之一。这个包虽然在程序中很少直接用到,但它的作用是将不同的平台包裹、隐藏起来,使这些平台在用户程序面前呈现基本相同的面貌。,13包包用来实现文件压缩功能。14包包提供了处理数据传输的工具类,包括剪贴板、字符串发送器等。15包包提供了用于反射对象的工具,反射允许程序监视一个正在运行的对象并获得它的构造函数、方法和属性。,16java.corba包和包这两个包将CORBA(Common Object Request Broker Architecture,公共对象请求代理体系结构)嵌入到Java环境中,使得Java程序可以存取、调用CORBA对象,并与CORBA对象共同工作。这样,Java程序就可以方便、动态地使用已经存在的由Java或其他面向对象语言开发的部件,简化软件的开发。,4.1.7 创建对象创建对象通常包括声明引用变量、创建对象和初始化对象三步。1声明引用变量引用变量通常也被称为对象。为了弄清这两者的区别与联系,有人曾以遥控器操纵电视机为例说明如下:“在用遥控器操纵电视中,电视机是对象,遥控器是引用变量。”需要注意的是:强调引用变量与对象的不同是强调二者的存储关系,而从逻辑上看,引用变量是引用对象的变量,这个变量名在逻辑上指代着对象,否则,几句话说不清对象名是什么。因此,在此后的讲述中,在不强调二者的存储关系时,我们就把引用变量名简称为对象名或对象。,声明引用变量的格式如下:类名 引用变量名表;其中,“类名”是指对象所属类的名字,它是在声明类时定义的;“引用变量名表”是指一个或多个引用变量名,若为多个引用变量名时,用逗号进行分隔。例如:class_name object1,object2;这条语句声明了两个引用变量object1和object2,它们都属于class_name类。声明引用变量时,系统只为该变量分配引用空间,存放在Java定义的栈内存中,其值为null,如图4.4所示。此时,并未创建具体的对象。,图4.4 声明引用变量的内存分配图,2创建对象一旦声明了一个引用变量,就可以将它与一个创建的对象相关联。通常用new操作符来实现这一目的。创建对象的格式如下:引用变量名=new 构造方法()例如:object1=new class_name();object2=new class_name();也可以在声明引用变量的同时创建对象,格式如下:类名 引用变量名=new 构造方法(),例如:class_name Object1=new class_name();class_name Object2=new class_name();其中,new是Java的关键字,也可将其称为运算符,因为new的作用是创建对象,为对象分配存储空间,并存放在Java定义的堆内存中。引用变量的值是该对象存储的地点,如图4.5所示。执行new class_name()将产生一个class_name()类的实例,即对象。当确定了引用变量和对象时,则可用引用变量来操纵对象。,图4.5 创建对象的内存分配图,3.初始化对象初始化对象是指由一个类生成一个对象时,为这个对象确定初始状态,即为它的数据成员赋初始值的过程。这一过程有三种实现方法:第一种是用默认初始化原则赋初值,如表4.1所示;第二种是用赋值语句赋初值;第三种是由Java提供的专用的方法来完成它,这个方法被称为构造方法。关于构造方法的详细内容请参阅本书节。,表4.1 Java提供的数据成员默认初始化原则,4.1.8 使用对象一个对象可以有许多属性和多个方法。在面向对象的系统中,一个对象的属性和方法被紧密地结合成一个整体,二者是不可分割的,并且限定一个对象的属性值只能由与它关联的引用变量或对象的方法来读取和修改。这便是封装和信息隐藏的一个方面。当一个对象被创建后,这个对象就拥有了自己的数据成员和成员方法,我们可以通过与之关联的引用变量名来引用对象的成员,引用方式如下:引用变量名.数据成员名对象的成员方法的引用方式如下:引用变量名.成员方法名(参数表),【示例程序C4_2.java】定义一个Dogs类,使其包括name、weight和height三个数据成员和一个名为showDog的成员方法。为Dogs类创建与引用变量dane关联的对象和与引用变量setter关联的对象,确定两个对象的属性后引用showDog方法显示这两个对象的属性。import;import;class Dogs/定义Dogs类/以下三行定义Dogs类的数据成员,public String name;public int weight;public int height;/以下六行是Dogs类的成员方法showDog()的定义 public void showDog(Graphics g,int x,int y)g.drawString(“Name:”+name,x,y);g.drawString(“Weight:”+weight,x,y+20);g.drawString(“Height:”+height,x,y+40);/成员方法showDog()定义完成/Dogs类定义毕,public class C4_2 extends Applet public void paint(Graphics g)/以下为创建对象 Dogs dane;/声明dane为属于Dogs类的引用变量 dane=new Dogs();/创建由dane引用的Dogs对象 Dogs setter=new Dogs();/创建引用变量setter引用的Dogs对象/以下六句是通过引用变量将一组值赋给对象的数据成员 dane.name=Gread Dane;dane.weight=100;dane.height=23;,setter.name=“Irish Setter”;setter.weight=20;setter.height=30;/以下两行是通过引用变量引用对象的成员方法 dane.showDog(g,25,25);setter.showDog(g,25,120);该程序的运行结果见图4.6。,图4.6 程序C4_2的运行结果,注意:“Dogs setter=new Dogs();”语句执行后,则完成了下面三项工作。(1)声明setter为属于Dogs类的引用变量;(2)使用new操作符来创建一个Dogs类的对象与setter相关联。该对象有三个数据成员(Name、Weight和Height)及一个成员方法showDog();(3)用默认初始化原则为对象的数据成员赋初值,如图4.7所示。,图4.7 setter关联对象示意图,4.1.9 对象的初始化与构造方法创建对象后,要为对象的数据成员赋值(如上例中的“dane.name=Gread Dane;”等6条语句),为简化这一操作,Java系统提供了专用的方法构造方法来完成这一操作。构造方法是一个类的方法中方法名与类名相同的类方法。每当使用new关键字创建一个对象,为新建对象开辟了内存空间之后,Java系统将自动调用构造方法初始化这个新建对象。构造方法是类的一种特殊方法,它的特殊性主要体现在以下几个方面:(1)构造方法的方法名与类名相同。,(2)构造方法是类的方法,它能够简化对象数据成员的初始化操作。(3)不能对构造方法指定类型,它有隐含的返回值,该值由系统内部使用。(4)构造方法一般不能由编程人员显式地直接调用,在创建一个类的对象的同时,系统会自动调用该类的构造方法将新对象初始化。(5)构造方法可以重载,即可定义多个具有不同参数的构造方法。(6)构造方法可以继承,即子类可以继承父类的构造方法。,(7)如果用户在一个自定义类中未定义该类的构造方法,系统将为这个类定义一个缺省的空构造方法。这个空构造方法没有形式参数,也没有任何具体语句,不能完成任何操作。但在创建一个类的新对象时,系统要调用该类的构造方法将新对象初始化。,【示例程序C4_3.java】将示例程序C4_2改写为定义了构造方法的程序。import;import;class Dogs1/定义Dogs1类 public String name;public int weight;public int height;public Dogs1(String cName,int cWeight,int cHeight)/构造方法,name=cName;weight=cWeight;height=cHeight;/构造方法定义毕 public void ShowDog(Graphics g,int x,int y)g.drawString(Name:+name,x,y);g.drawString(Weight:+weight,x,y+20);g.drawString(Height:+height,x,y+40);/Dogs1类定义毕,public class C4_3 extends Applet public void paint(Graphics g)Dogs1 dane=new Dogs1(“Gread Dane”,100,23);Dogs1 setter=new Dogs1(“Irish Setter”,20,30);dane.showDog(g,25,25);setter.showDog(g,25,120);该程序的运行结果与示例程序C4_2.java的运行结果相同。,注意:如果构造方法中的参数名与数据成员名相同,例如,public Dogs1(String name,int weight,int height)name=name;/左边的标识符与右边的标识符都为参数名。下同 weight=weight;height=height;,则对同名数据成员名封闭,左边的标识符与右边的标识符都为参数名。可以使用代表本类对象的关键字this指出数据成员名之所在。例如,public dogs(String name,int weight,int height)this.name=name;/用this指出数据成员名 this.weight=weight;this.height=height;关于this,将在节讲述。,4.2 封 装 机 制封装是面向对象系统的一个重要特性,是数据抽象思想的具体体现。4.2.1 封装的概念在面向对象的程序设计中,封装就是把对象的属性和行为结合成一个独立的单位,并尽可能隐藏对象的内部细节(称为信息隐藏)。用户无需知道对象内部方法的实现细节,但可以根据对象提供的外部接口访问该对象。,封装反映了事物的相对独立性。封装在编程上的作用是使对象以外的部分不能随意存取对象的内部数据(属性),从而有效地避免了外部错误对它的“交叉感染”。另一方面,当对象的内部做了某些修改时,由于它只通过少量的接口对外提供服务,因此大大减少了内部的修改对外部的影响。封装具有下述特征:(1)在类的定义中设置访问对象属性(数据成员)及方法(成员方法)的权限,限制本类对象及其他类的对象使用的范围。(2)提供一个接口来描述其他对象的使用方法。(3)其他对象不能直接修改本对象所拥有的属性和方法。,面向对象系统中类的概念本身具有封装的意义,因为对象的特性是由它所属的类说明来描述的。Java提供四种访问控制级别对对象的属性和方法进行封装:公共(public)、保护(protected)、包(package)和私有(private)。其中,包是用来封装一组相关类的。4.2.2 类的严谨定义在节中,我们已经给出了定义类的一般格式,那时我们给出的类的结构是:class 类名 数据成员 成员方法,这一结构只给出了定义一个类所必不可少的内容,而忽略了类定义的许多细节。有了封装的概念后,就可以进一步来学习类的严谨定义了。类的严谨定义格式如下:类修饰符 class 类名 extends 父类名 implements 接口列表 数据成员 成员方法可以看出,在类的严谨定义格式中,类的说明部分增加了类修饰符、extends父类名和implements接口列表三个可选项。合理地使用这些可选项,可以充分地展示封装、继承和信息隐藏等面向对象的特性。由于这部分内容比较庞杂,在这里作一简要说明后,将分别在此后的章节中详细讨论。,类修饰符(qualifier):用于规定类的一些特殊性,主要是说明对它的访问限制。类名:遵从节所述标识命名规则,但按惯例通常首字母大写。extends父类名:指明新定义的类是由已存在的父类派生出来的。这样,这个新定义的类就可以继承一个已存在类父类的某些特征。Java只支持单继承,一个类只能有一个父类名。implements 接口列表:一个类可以实现多个接口,接口之间用逗号分隔。通过接口机制可以实现多重继承。类体:包括花括号括起来的所有内容。,4.2.3 类修饰符类的修饰符用于说明对它的访问限制。一个类可以没有修饰符,也可以有public、final、abstract等几种不同的修饰符。它们的作用是不同的,下面分别予以介绍。1无修饰符的情况如果一个类前无修饰符,则这个类只能被同一个包里的类使用。Java规定,同一个程序文件中的所有类都在同一个包中。这就是说,无修饰符的类可以被同一个程序文件中的类使用,但不能被其他程序文件中的类(即其他包中的类)使用。,【示例程序C4_4.java】无修饰符类的使用。class Pp1/无修饰符的类Pp1 int a=45;/Pp1类的数据成员apublic class C4_4/公共类C4_4 public static void main(String args)Pp1 p1=new Pp1();/类C4_4中创建了一个无修饰符类Pp1的对象 System.out.println(p1.a);,在这个程序中定义了两个类:无修饰符的类Pp1和公共类C4_4。它们是同一个程序文件(即同一个包)中的两个类,所以,在类C4_4中可以创建Pp1类的对象,且p1可以引用这个对象的数据成员a。关于数据成员的访问限制,将在4.3节中论述。,2public修饰符如果一个类的修饰符是public,则这个类是公共类。公共类不但可供它所在包中的其他类使用,也可供其他包中的类使用。在程序中可以用import语句引用其他包中的public类。Java规定,在一个程序文件中,只能定义一个public类,其余的类可以是无修饰符的类,也可以是用final修饰符定义的最终类;否则,编译时系统会报错。,【示例程序C4_5.java】public修饰符类的使用。class Pp C4_5 f1=new C4_5();/在Pp类中创建C4_5类的对象 int add()/下面的语句用引用变量f1引用C4_5类对象的数据成员b和c return(f1.b+f1.c);,public class C4_5/定义公共类C4_5 int b=20,c=3;/C4_5类的数据成员b和c public static void main(String args)Pp p1=new Pp();/创建Pp类的对象 System.out.println(p1.add();该程序的运行结果如下:23,在程序C4_5.java中定义了两个类:无修饰符的默认类Pp和公共类C4_5。它们是两个无继承关系的类,但由于类C4_5是公共类,因此,在类Pp中可以创建C4_5类的对象,引用变量f1可以引用该对象的数据成员b和c。在公共类C4_5中创建了一个Pp类的对象,对象的数据成员f1是C4_5类的引用变量,如图4.8所示。p1可以引用Pp类的成员方法add()。关于数据成员的访问限制,将在4.3节中论述。,图4.8 p1关联对象示意图,3final修饰符用final修饰符修饰的类被称为最终类。最终类是不能被任何其他类所继承的。定义最终类的目的有三:(1)用来完成某种标准功能。如Java系统定义好的用来实现网络功能的InetAddress、Socket等类都是final类。将一个类定义为final类,则可以将它的内容、属性和功能固定下来,与它的类名形成稳定的映射关系,从而保证引用这个类时所实现的功能是正确无误的。,(2)提高程序的可读性。从父类派生子类,再从子类派生子类,使软件变得越来越复杂。而在必要的层次上设置final类,可以提高程序的可读性。(3)提高安全性。病毒的闯入途径之一是在一些处理关键信息的类中派生子类,再用子类去代替原来的类。由于用final修饰符定义的类不能再派生子类,截断了病毒闯入的途径,因而提高了程序的安全性。,【示例程序C4_6.java】final修饰符类的使用。import;import;final class P1/用final修饰的类P1 int i=7;int j=1;void f(Graphics g)g.drawString(“OK”,20,50);/public class C4_6 extends P1 错,用final修饰的类P1不能有继承类public class C4_6 extends Applet,public void paint(Graphics g)P1 n=new P1();n.f(g);n.i=40;n.j+;g.drawString(“i=”+n.i,20,70);g.drawString(“j=”+n.j,20,90);该程序的运行结果见图4.9。,图4.9 程序C4_6的运行结果,4abstract修饰符用abstract修饰符修饰的类称为抽象类。抽象类刻画了研究对象的公有行为特征,并通过继承机制将这些特征传送给它的派生类。其作用在于将许多有关的类组织在一起,提供一个公共的基类,为派生具体类奠定基础。此外,当一个类中出现了一个或多个用abstract修饰符定义的方法时,必须在这个类的前面加上abstract修饰符,将其定义为抽象类。有关抽象类及抽象方法的详细内容将在5.5节介绍。,5类修饰符使用注意事项可以同时使用两个修饰符来修饰一个类,当使用两个修饰符修饰一个类时,这些修饰符之间用空格分开,写在关键字class之前,修饰符的顺序对类的性质没有任何影响。需要注意的是:一个类可以被修饰为public abstract,也可以被修饰为public final,但不能被修饰为abstract final,这是因为abstract类自身没有对象,需要派生子类后再创建子类的对象,而fi