440 likes | 617 Views
第 5 章 Java 面向对象程序设计. 教学目的要求. 理解 java 语言的面向对象技术,包括面向对象的基本概念、面向对象的程序设计方法; 理解 java 中的类、包、对象、抽象类、接口和面向对象的特性; 掌握 Java 语言中类、方法和接口的基本定义及使用方法; 了解包的概念及打包和引入方法; 通过技能实训 , 掌握一般 Java 程序的面向对象编程方法 , 达到熟练开发 Java 一般程序的目的。. 本章内容. 5.1 类 5.2 访问控制符与封装 5.3 继承 5.4 非访问控制符 5.5 Java 的名字空间和包 5.6 接口.
E N D
第5章 Java面向对象程序设计 教学目的要求 • 理解java语言的面向对象技术,包括面向对象的基本概念、面向对象的程序设计方法; • 理解java中的类、包、对象、抽象类、接口和面向对象的特性; • 掌握Java语言中类、方法和接口的基本定义及使用方法; • 了解包的概念及打包和引入方法; • 通过技能实训,掌握一般Java程序的面向对象编程方法,达到熟练开发Java一般程序的目的。
本章内容 • 5.1 类 • 5.2 访问控制符与封装 • 5.3 继承 • 5.4 非访问控制符 • 5.5 Java的名字空间和包 • 5.6 接口
5.1 类 • 5.1.1 类的定义 • 5.1.2 类的使用 • 5.1.3 方法重载 • 5.1.4 内部类
面向对象回顾(1)-基本思想 • 面向对象的基本思想面向对象是一种新兴的程序设计方法,或者是一种新的程序设计规范(paradigm),其基本思想是使用对象、类、继承、封装、消息等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。开发一个软件是为了解决某些问题,这些问题所涉及的业务范围称作该软件的问题域。其应用领域不仅仅是软件,还有计算机体系结构和人工智能等。
面向对象回顾(1)-基本思想 • 1. 对象的基本概念 对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。 • 2. 类的基本概念 类是具有相同属性和服务的一组对象的集合,它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和服务两个主要部分。 • 3. 消息 消息就是向对象发出的服务请求,它应该包含下述信息:提供服务的对象标识、服务标识、输入信息和回答信息。服务通常被称为方法或函数。
面向对象回顾(2)-基本特性 • 1.封装性封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,包含两个含义: ◇ 把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位(即对象)。 ◇ 信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系。 封装的原则在软件上的反映是:要求使对象以外的部分不能随意存取对象的内部数据(属性),从而有效的避免了外部错误对它的"交叉感染",使软件错误能够局部化,大大减少查错和排错的难度。
面向对象回顾(2)-基本特性 • 2.继承性特殊类的对象拥有其一般类的全部属性与服务,称作特殊类对一般类的继承。 一个类可以是多个一般类的特殊类,它从多个一般类中继承了属性与服务,这称为多继承。 在java语言中,称一般类为父类(superclass,超类),称特殊类为子类(subclass)。
面向对象回顾(2)-基本特性 • 3.多态性对象的多态性是指在一般类中定义的属性或服务被特殊类继承之后,可以具有不同的数据类型或表现出不同的行为。这使得同一个属性或服务在一般类及其各个特殊类中具有不同的语义。例如:"几何图形"的"绘图"方法,"椭圆"和"多边形"都是"几何图"的子类,其"绘图"方法功能不同。
面向对象回顾(3)-面向对象方法学 • OOA-Object Oriented Analysis 面向对象的分析 • OOD-Object Oriented Design 面向对象的设计 • OOI-Object Oriented Implementation 面向对象的实现
5.1.1 类的定义 • 1.类声明:类声明中包括关键字Class,类名及类的属性。类声明的格式如下: [<修饰符>]class<类名>[extends<父类>][implements<接口名>]
(1)类定义修饰符 • 类的说明性修饰符说明类的性质和访问权限,包括public、缺省修饰符、abstract、final。关于修饰符具体含义后面详细介绍。 • (2)extends • extends说明类的父类,一般形式为extends〈父类名〉Java语言中,如果在类说明中无extends,则隐含地假设其父类为Java.lang.object类。
5.1.1 类的定义 • 2.类主体<类声明> { <成员变量的声明> <成员方法的声明及实现> } • Java语言中没有独立的函数和过程,所有的子程序都是作为类方法定义的。如下所示: classclassName{ memberVariableDeclarations methodDeclarations}
5.1.1 类的定义 • 3.成员变量 成员变量的声明方式如下:[变量修饰符] <变量类型> <变量名> 其中修饰符包括public、protected、private. public:用来说明公有变量; private:用来说明私有变量; protected:用来说明保护变量。
5.1.1 类的定义 • 4.成员方法说明成员方法定义的一般形式为: [方法修饰符] <方法返回值类型> <方法名> ([<参数列表>]){方法体}
5.1.1 类的定义 • ⑴方法修饰符 成员方法修饰符主要有public、private、protected、final、static、abstract和synchronized共7种,前3种的访问权限、说明形式和含义与成员变量一致。修饰符说明详见5.2、5.4节。 • ⑵方法的返回值类型 Java语言要求在成员方法说明中必须指明方法返回值的类型,返回值的类型为Java语言的任何数据类型。如果一个成员方法不需要返回值,则其返回值的类型被说明为void。方法返回值用return语句来实现,如果有返回值,那么return语句要带参数,并且return语句中返回的数据类型必须与方法说明中的方法返回值的类型一致。
⑶方法名 成员方法名是Java语言合法的标识符,为了提高程序的可读性,成员方法名一般具有一定的含义。 • ⑷参数列表 成员方法的参数列表是由逗号分隔的类型及参数名组成,是可选项。类型可以是Java语言的任何数据类型。
⑸方法体 方法体是一个方法定义的主要部分,包含了所有实现方法功能的Java语言程序代码。在方法体中可以定义局部变量,它的作用域仅在方法体内,当方法被执行结束之后,该方法内部的所有局部变量也就失效了,局部变量不能与参数列表中参数名同名。方法体用“{}”括起来。
【例5-2】定义一个方法,方法功能为根据参数r的大小,返回圆的面积。在类中赋参数r,并且输出面积值。【例5-2】定义一个方法,方法功能为根据参数r的大小,返回圆的面积。在类中赋参数r,并且输出面积值。 public class Li5_02{ public static void main(String args[]){ double k; Li5_02 mj=new Li5_02(); k=mj.area(5); System.out.println(k); } public double area(int r){//定义area方法,带有一个参数 final double PI=3.14; //定义常量PI,并赋初值3.14 return PI*r*r; //根据r的值,计算面积后,返回面积值 } } 运行结果为: 78.5
5.1.1 类的定义 • 5.构造方法构造方法主要用于为类的方法中变量赋初始值的功能。当用new创建一个类的新的对象时,构造方法就立即执行。构造方法名字必须与类名相同。除了构造方法,在类中不能出现与类名相同的方法。构造方法的语法为: public 类名([参数列表]){ [语句序列;] } • Java语言中每个类都有构造方法,如果一个类中没有说明该类的构造方法,则系统将提供隐含的不带任何参数的构造方法,这个空的构造方法其实什么也不做。一旦我们定义了自己的构造方法,Java编译器就不再添加这种缺省的构造方法。
5.1.1 类的定义 构造方法的特点: • ⑴构造方法没有返回值类型,甚至没有void。其修饰符只能是访问控制修饰符,即public、private、protected中的任一个。 • ⑵构造方法不能从父类中继承。 • ⑶构造方法可以重载,一个类可以有任意多个构造方法。不同的构造方法根据参数的不同状态来选择合适的构造方法。 • ⑷构造方法不能直接通过方法名引用,必须通过new运算符。 • ⑸在构造方法中可以调用当前类和其父类的另一个构造方法,但必须是方法体的第一条语句。使用当前类的构造方法用this来引用,使用其父类的构造方法用super来引用。
5.1.2 对象的使用 5.1.2.1 对象创建 • 在Java语言中,对象创建包括对象声明,实例化和初始化三方面的内容。一般格式为: type 对象名=new type([参数列表]); 其中type为引用组合类型(包括类和接口)。该格式声明了一个类型为type的对象。对象的声明并不为对象分配内存空间。用运算符new为对象分配内存空间,实例化一个对象。比如: String UserName=new String(“zhangsan”);
5.1.2 对象的使用 5.1.2.2 对象使用 1.引用对象中的变量 引用对象的变量的一般形式为: <对象名>.<变量名> 2.引用对象中的方法 引用对象中的方法的一般形式为: <对象名>.<方法名>([<参数列表>]) 其中,参数列表是可选项。在进行对象方法的引用时,方法中参数的个数、参数的数据类型与原方法中定义的要一致,否则编译时会出错。
5.1.2 对象的使用 5.1.2.3 对象数组 • 数组的元素不仅可以是基本数据类型,也可以是对象。在实际应用中,更普遍的可能是数组元素是对象的对象数组了。下面用CCircle类作为例子介绍对象数组的创建和使用,见例5.4源程序。如何来创建和操纵CCircle类的对象的一个数组呢?分两步进行: • 1.声明类类型的数组变量,并用new分配内存空间给数组; • 2.用new产生新的对象,并分配内存空间给它。
5.1.2 对象的使用 5.1.2.4 对象释放 • Java可回收不使用对象所占的内存空间。其作法是,把指向该对象的值设置为null。该对象便成了无用对象,Java系统通过垃圾回收器GC(Garbage Collector)周期性地释放无用对象所使用的内存,完成对象的清除工作。 • 当不存在着对一个对象的引用时,该对象就成为一个无用对象。GC自动扫描对象的动态内存区,对被引用的对象添加标记,然后把没有引用的对象作为垃圾收集起来并加以释放。 • GC在Java中作为一个线程来运行的,即调用System.gc(),当要求垃圾收集时,该线程与系统同步运行;否则在系统空闲之时异步地运行。 • 此外,在Java系统开始运行时,会自动调用一个名为finalize()方法,使它清除对象所占的内存。比C++中用delete和C中用free来释放内存的措施好得多。
5.1.3 方法重载 在Java类中可以创建名字相同,但是参数不同和返回值不同的几个方法,这就是方法重载(overloading),Java系统通过参数和返回值来分辨具体是哪一个方法。通过方法重载,一个类中可以有多个具有相同名字的方法,传递给它们的不同个数和类型的参数来决定使用那种方法,这就是多态。
5.1.4 内部类 • 内部类 在类内部可定义成员变量和方法,其实,在类内部也可以定义另一个类。如果在类A的内部再定义一个类B,此时类B称为内部类,而类A则称为外部类。 内部类可声明为public或private。当内部类声明成public或private时,其访问限制与成员变量或方法完全相同。关于内部类的用法详见第8章8.3节。
5.2 访问控制符和封装 Java的类成员主要有访问修饰符具体存取权限见下表
5.3 继承 5.3.1 子类和简单继承举例 class 类名A [extends 父类B][implements<接口名>]{ <成员变量的声明> <成员方法的声明及实现> }
5.3 继承 • 5.3.2 变量隐藏和方法覆盖 • 方法覆盖与重载均是Java多态的技巧之一,但两者之间也有不同之处: • 重载:英文名称为overloading,意指在同一个类中,定义多个名称相同,但参数个数或类型不同的方法,Java根据参数的个数或类型,调用相对应的方法。 • 覆盖:英文名称为overriding,是指在子类中,定义名称、参数个数与类型均与父类相同的方法,用以改写父类中的方法的功能。
5.3 继承 5.3.3 super和this Super有三种情况可以使用: ⑴用来访问父类中被覆盖的方法; ⑵用来调用父类中的构造方法; ⑶用来访问父类中被隐藏的成员变量。
调用构造方法遵循以下的几条规则: • ⑴当一个类创建对象时,可以调用该类的父类的构造方法。调用父类的构造方法很简单,只要在类的构造方法的方法体中,第一条为super语句就可以了。super可以调用父类的任何一个带入口参数或不带入口参数的构造方法。 • ⑵如果一个类的构造方法中第一条语句没有用super来调用父类的构造方法,则编译器也会默认在构造方法中用super()语句调用父类的无参构造方法。 • ⑶如果某个类的构造方法的第一条语句是用this来调用本类的另外一个构造方法,那么Java系统就不会默认这个构造方法调用父类的构造方法。 • ⑷一个构造方法是用this语句调用本类的另一个构造方法时,如果被调用的构造方法又是调用父类的构造方法而又没有用super语句,那么编译器会默认它含有super()语句。此时,父类中若不存在无参的构造方法,也将会导致编译出错。
5.3 继承 • 5.3.4 运行时多态与多重继承 • Java中每一种操作,操作数据的类型必须合法。父类有的方法子类都有,凡是能够使父类生成对象的地方,都可以使用子类生成对象。 • 运行时多态有以下两条规则: • ⑴对子类的一个实例,如果子类重写(覆盖)了父类的方法,则运行时系统就调用子类的方法。 • ⑵如果子类继承了父类的方法,则运行时系统就仍调用父类的方法。
5.3.5 对象类型转换 • Java中两个不同类型对象之间可以转换,具体限制:两个转换的对象之间应该具有继承关系,也就是说只是在子类和父类的对象之间进行转换,而不是任意两个类。 • 一个子类对象的类型可以向上转换成它的父类类型,这个转换过程是安全的。因为父类所具有的信息,子类一般全有。当然,转换过程中会丢失属于子类而不属于父类的信息。其中大学生和研究生是学生的子类。
按照前面所说的原则,以下转换过程是安全的:按照前面所说的原则,以下转换过程是安全的: • UndergraduateStudent anUGS=new UndergraduateStudent(); • Student aStudent=(Student)anUGS; • 但是反过来,一个父类对象的类型未必可以向下转换成子类的对象的类型,因为子类具有的信息,父类未必包含。所以只有当父类对象实际上是子类的一个实例的时候,才可以转换,否则不能进行这种转换。
5.3 继承 • 5.3.6 抽象类 • 定义一个抽象类的格式为: abstract class 类名称{ 声明数据成员; 返回值的数据类型 方法名称(参数列表){ 定义普通的方法 } abstract返回值的数据类型 方法名称(参数列表) • } • 注意:在抽象类中,方法的定义可分为两种:一种是普通方法;另一种是抽象方法,此方法以abstract开头,且只声明了返回值的数据类型、方法名称、所需参数,但没有方法体。这样,抽象方法中的处理方式必须在子类中完全实现。
5.4 非访问控制符 • 5.4.1 static • 在类中声明一个变量或方法时,还可以指定它为实例成员或类成员。其格式为: • static 类型 变量名; • static 返回值类型 方法名([参数列表]){ • … • } • 上述用static修饰的变量和方法分别称为类变量和类方法,也称静态变量和静态方法。如果没有static修饰,则分别称为实例变量和实例方法。
5.4 非访问控制符 • 5.4.2 final • 下列情况通常某些类被定义为final类: • ⑴定义为final类通常是一些有固定作用,用来完成某种标准功能的类。例如,Java中的String类,它对编译器与解释器的正常运行有很重要的作用,所以被修饰为final类。 • ⑵如果认为一个类的定义已经很完美,不需要再生成它的子类时,就说明为final类。有些方法不能被重写(覆盖),也把它限定为final方法。格式为: final 返回值类型 方法名([参数列表]){…} • 注意:abstract和final不能同时修饰一个类。可用final来作为常量的修饰符。一个类的成员变量也可以被修饰成final,一旦定义为final,则它的值在整个程序执行过程中都不会改变。
5.4.3 volatile、native、synchronized 用volatile修饰的成员变量称为易失变量,通常这个变量同时被几个线程控制和修改,也就是说,这个成员变量不仅被当前程序所掌握,在运行过程可能存在其他未知的程序操作来影响和改变该变量的取值。 用native修饰的是一种特殊方法,一般用来声明用其他语言编写的方法体并具体实现方法功能。由于native的方法是用其他语言在外部编写,所以,所有的native方法都没有方法体,而使用一个“;”代替。 如果用synchronized修饰一个类方法,那么在调用执行前,将把系统类中对应的当前类的对象加锁。如果用synchronized修饰一个对象方法,那么在调用执行前,将把当前对象加锁。 synchronized主要用于多线程共存的程序中的协调与同步,详见多线程一章部分。 5.4 非访问控制符
5.5 Java的名字空间和包 5.5.1 Java的名字空间 • Java注意解决名字空间的冲突问题,全局变量不再是语言的组成部分,即没有全局的方法,也没有全局的变量。所有的变量和方法都是在类中定义,并且是类的重要组成部分,而每个类又是包的一部分,因此每个Java变量或方法都可以用全限定的名字表示。包括包名、类名、域名三部分,之间用“.”分隔。 • Java编译器会为Java程序的每个类生成一个字节码文件,这些字节码文件名与类同名,扩展名为.class,因此一个含有多个类定义的源文件编译后会生成多个.class文件。 • Java源代码的扩展名一般为.java,其中包括一个或多个类定义,如果在Java文件中有多个类定义,则只能有一个类可以定义为public,并且这个类的名字必须与程序的源代码名(去掉扩展名)一致。
5.5 Java的名字空间和包 5.5.3 自定义包 除了使用Java提供的包以外,每个程序员都可以也应该把自己编写的类分门别类的装在不同的包中。Java的源程序格式在前面已经说过,如果这个源程序开头有: package pack1; 说明该程序所定义的类属于pack1这个包。一般格式为: package pack1[.pack2[.pack3]]; 这些包所放的位置应使编译程序方便找到用户定义的包。有一个简便的方法,即包名和它们的结构应该恰好同目录(文件夹)相对应,每个包对应于当前目录下的一个与包同名的目录,子包的类存在于相应的子目录中。
5.6 接口 • 接口(interface)是Java所提供的另一种重要功能,它的结构和抽象类非常相似。 • 接口是一种特殊的类,但接口与类存在着本质的区别。 • 类有它的成员变量和成员方法,而接口却只有常量和抽象方法,也就是说接口的成员变量必须初始化,同时接口中的所有方法必须全部声明为abstract方法,一个类可以有多个接口。 • Java语言通过接口使得处于不同类甚至互不相关的类具有相同的行为。
5.6.1 接口的声明 • 接口通过关键词interface来定义,接口定义的一般形式为 [接口修饰符] interface〈接口名〉[extends〈父类接口列表〉] {…//接口体} • ⑴接口修饰符:接口修饰符为接口访问权限,有public和缺省两种状态。 ①用public指明任意类均可以使用这个接口。 ②在缺省情况下,只有与该接口定义在同一包中的类才可以访问这个接口,而其他包中的类无权访问该接口。 • ⑵接口名:接口名为合法的Java语言标识符。 • ⑶父类接口列表:一个接口可以继承其他接口,可通过关键词extends来实现,其语法与类的继承相同。被继承的类接口称为父类接口,当有多个父类接口时,用逗号“,”分隔。 • ⑷接口体:接口体中包括接口中所需要说明的常量和抽象方法。
5.6 接口 5.6.2 接口的实现 • 在前面所介绍的类声明中,用implements子句表示一个类用于实现某个接口。一个类可以同时实现多个接口,接口之间用逗号“,”分隔。 • 在类体中可以使用接口中定义的常量,由于接口中的方法为抽象方法,所以必须在类体中加入要实现接口方法的代码,如果一个接口是从别的一个或多个父接口中继承而来,则在类体中必须加入实现该接口及其父接口中所有方法的代码。 • 在实现一个接口时,类中对方法的定义要和接口中的相应方法的定义相匹配,其方法名、方法的返回值类型、方法的访问权限和参数的数目与类型信息要一致。
5.6 接口 • 5.6.3 接口的使用(例5.12) • 5.6.4 扩展接口与接口的多重继承 • 使用extends子句、扩展接口,可以生成子接口。原来的接口称为基本接口(base interface)或父接口(super interface),扩展出的接口称为派生接口或子接口。通过这种机制,派生接口不仅可以保有父接口的成员,同时也可以加入新的成员以满足实际问题的需要。与类不同的是,一个接口可以扩展多个接口,继承它们所有属性,而一个类只能扩展一个类。显然,接口不能扩展类,接口的方法必须全是抽象的。例如: interface A extends B{…} • 这条语句表示定义了接口A并继承了接口B,使A成为B的子接口,并且A具有B的所有属性。