第3章面向对象编程基础
在本章我们将一起学习以下内容: 面向对象技术简介。 类的定义、变量和方法的定义。 对象的实例化与清除。 引入类和定义包。 Java文档注释。 用UML绘制类图。相对于硬件技术发展的日新月异,软件技术的发展相当迟缓。软件技术面临着非常多的挑战,在要求软件功能更加新颖、更加强大的同时,人们又要求软件开发人员可以快速、高效地构建质量优、重用性高、维护性强的软件系统。软件开发从汇编语言、过程式语言、面向对象、面向组件发展到面向服务,每一步都体现了不断抽象、更加贴近业务实际的发展趋势。软件开发人员发现采用模块化、面向对象的设计和实现方法可以显著提高软件开发的工作效率,而且面向对象程序更易于理解、重用性高,便于纠错和修正。3.1面向对象技术简介3.1.1面向过程和面向对象目前软件开发领域主要有结构化开发方法和面向对象开发方法两种。结构化开发方法(又称为面向过程)的程序设计简单,可读性强,易于阅读和理解,便于维护,是面向对象程序设计的基础。面向过程把问题求解过程(即算法)放在第一位,主张按功能把软件系统逐步细分,遵循自顶向下,逐步求精的思想。面向过程提供顺序、分支和循环3种逻辑结构,每种逻辑结构要求单入口和单出口。面向对象开发方法是一种以事物为中心的编程思想,用一种更符合人们认识客观世界的思维方式进行程序设计。它克服了面向过程的缺点,达到了软件工程的3个主要目标,即重用性、灵活性和扩展性。面向对象的软件开发方法的生命周期主要包含了以下阶段。1 面向对象的分析OOA(ObjectOriented Analysis): 对用户需求进行分析,确定系统的范围和功能,明确要做什么。2 面向对象的设计OOD(ObjectOriented Design): 对OOA分析的结果做进一步的规范化整理,以便能够被OOP直接接受。目前业界统一采用UML来描述和记录OOA与OOD的结果。3 面向对象的程序设计OOP(ObjectOriented Program): 在面向对象程序设计方法中,程序被看作是相互协作的对象的集合。每个对象都是某个类的实例。所有类构成一个通过继承关系联系的树形层次结构。因此,面向对象程序设计的基本构成是类和对象。类是一组相似对象的描述,描述了该类对象所具有的共同特征。对象封装了描述其状态的数据(属性)以及可以对这些数据进行的操作(方法),对象之间通过发送消息相互协作。面向对象的主要概念包括类和对象、接口、包、属性、方法(一般方法、构造方法、抽象方法)、修饰符、引用类型的转换(上溯下溯造型)等。3.1.2面向对象的特征面向对象具有四大特征,即抽象、封装、继承和多态。1. 抽象抽象其实也是所有计算机语言的特征,指从众多的事物中舍弃个别的、非本质的部分,提炼出计算机系统所关注的、共同的、本质的部分(属性和功能)的过程。抽象包括过程抽象和数据抽象。过程抽象将整个系统的功能划分为若干部分,强调功能完成的过程和步骤,而隐藏其具体的实现。数据抽象是将系统中需要处理的数据和这些数据上的操作结合在一起,抽象成不同的抽象数据类型(ADT)。2.封装封装(Encapsulation)指属性和方法的定义都封装在类定义中,然后通过对其可见性来控制外部对类成员的可访问性。1 类(对象)的属性和方法是不可分割的整体,反映了客观事物的静态特征和动态行为相统一的客观规律,使计算机软件对客观事物的描述更接近人类表述。过去的面向过程的编程语言将功能和数据分离,使软件对客观事物的描述存在偏差,很难自然地用语言表达客观事物的对应关系。在Java中,类封装了属性、方法、构造方法、语句块、内部类等成员,如图31所示。用UML标准符号绘制Car类图如图32所示。
图31类封装示意图
图32UML类图
UML类图可以转换为Java语言:
01class Car{02private int color_number;03private int door_number;04private int speed;05public void brake{}06public void speedUp{}07public void slowDown{}08}
2 封装还涉及类或对象内部细节的隐蔽性。类设计者可以通过使用访问权限控制修饰符private、protected、public 3个关键字、4个访问控制级别来细粒度地控制外部是否可以访问类或对象的成员。一般将属性设置为private,然后提供Getter和Setter方法存取数据的统一接口。访问其他类对象成员的示意图如图33所示。
图33访问其他类对象成员的示意图
【示例程序31】自定义日期类(MyDate.java)功能描述: MyDate日期类型有年、月、日3个私有属性; 提供了无参构造方法,包含全部属性的构造方法; 在自动生成私有属性的Getter和Setter方法的代码的基础上进行了定制。
01public class MyDate {02private int year;03private int month;04private int day;05public MyDate {06super;07}08public MyDateint year, int month, int day {09super;10setYearyear;11setMonthmonth;12setDayday;13}14public int getYear {15return year;16}17public void setYearint year {18ifyear1&&year=1&&month=1&&day [extends ][implements ]{
[初始化语句块]
[成员变量]
[构造方法]
[成员方法]
}
说明:1. 类修饰符(Modifier)说明1 类只有一个权限控制符public。2 用abstract修饰的类叫抽象类。抽象类只能被继承,不能被实例化。3 用final修饰的类叫最终类。最终类只能被实例化,不能被继承。2. extends 其用来指定要继承的父类。3. implements 其用来指定要实现的接口。一个类实现一个接口,即必须实现该接口中所有的抽象方法,否则这个类只能是抽象类。4. Java类封装的成员 静态语句块: 指类中有static修饰的语句块,用于初始化类。 非静态语句块: 指类中没有static修饰的语句块,用于初始化对象。 成员变量(Member Variable),也称为字段(Field)或属性(Atrribute: 主要描述类或对象的静态特征。 成员方法(Method),也称为函数(Function)、过程(Procedure)、子程序(SubRoutine: 表明类或对象所具有的行为。 构造方法(Constructor): 用于实例化对象,并初始化对象成员变量。 内部类(Inner Class): 在类、方法中定义的类。3.2.2成员变量成员变量指定义在类中方法外的变量或常量。成员变量的有效范围是整个类,相当于全局变量。成员变量的语法格式如下:
[成员变量修饰符] 变量名[=初值];
说明:1. 成员变量修饰符1 权限控制修饰符有public、protected、private几种,用来控制变量的可见性,详见4.8节。2 共享修饰符volatile: 用于并发线程的共享。3 易失性修饰符transient: 对象序列化时用于屏蔽敏感信息。4 类修饰符static: 有static修饰的变量是类变量,否则是对象变量。5 常量修饰符final: 有final修饰的变量是常量,只能在定义时赋值一次。2. 成员变量的类型可以是8种基本数据类型,也可以是引用类型(对象)。3. 类成员变量(也叫类属性、类变量、静态变量、静态属性等)类成员变量指有static修饰的成员变量。类成员变量属于类成员,当类载入内存,即分配内存和初始化时,为该类的所有对象所共享。静态方法和非静态方法都可以直接通过类名.变量名来访问类成员变量。4. 对象成员变量(也叫对象属性、对象变量、非静态变量、动态变量等)对象成员变量属于对象,只有在实例化对象时才能分配内存和初始化,为该对象所有。在用静态方法和非静态方法访问对象成员变量时需要先new实例化一个对象,然后通过对象名.变量名的方式访问。当对象成员变量和局部变量重名时用this区分。【示例程序33】类变量和对象变量的测试(StaticTest.java)功能描述: 本程序主要演示类变量属于类,为该类的全部对象共享; 对象变量属于对象,为一个对象私有的语法现象。
01public class StaticTest{02static int score=100;score属于类,为该类的所有对象共享03int flag=10; flag属于对象,为对象独享04public static void mainString args[]{05StaticTest t1=new StaticTest;06StaticTest t2=new StaticTest;07StaticTest t3=new StaticTest;08StaticTest t4=new StaticTest;09System.out.printlnt1.score;10System.out.printlnt1.flag;11t1.score=1000;12t1.flag=888;13System.out.printlnt4.score;14System.out.printlnt4.flag;15}16}
3.2.3局部变量局部变量指定义在方法中的变量,其生命周期范围是整个方法,详见2.2.3节。3.2.4成员方法的定义语句(Statement)多了或部分语句需要反复调用时,应根据功能将其定义成不同的方法; 由于人类瞬间记忆能力的限制,为了方便阅读,一个方法打印出来原则上不应超过一张A4纸。方法(Method)是能完成一定数据处理功能、可以被反复调用的语句的集合。方法要先定义,后使用。方法不能嵌套定义,但可以嵌套调用。与C语言函数的并列独立存在不同,在Java中方法必须在类中定义。在逻辑上方法要么属于类,要么属于对象。用static修饰的方法属于类,没有用static修饰的方法属于对象。方法应该根据功能定义在不同的类中。不同的类(Class)应该根据功能不同定义到不同的包(Package)中。在实际项目开发中不允许将类放到系统默认的无名包中(Default Package)。方法的语法格式如下:
[方法修饰符] 方法名类型1 形式参数1,[throws 异常列表]{
[return 返回值;]
}
说明:1. 方法修饰符说明1 访问权限控制修饰符public、protected、private: 决定方法的可见性。2 静态修饰符static: 有static修饰的方法是类方法,否则是对象方法。3 最终方法修饰符final: 有final修饰的方法是最终方法,该方法不能被覆盖。4 抽象方法修饰符abstract: 有abstract修饰的方法是抽象方法,抽象方法只有方法的定义,没有方法的实现(方法体)。5 Java使用native扩展Java的功能,用CC编程以取得更快的速度和访问操作系统的底层。6 用synchronized修饰的方法在多线程并发时只能互斥调用该方法。2. 返回值类型返回值类型可以是8种基本数据类型和引用类型,在方法体中必须用return语句返回数据,否则会出现编译错误。如果没有返回参数,请用void代替。3. return语句return语句有两种功能: 返回数据和终止方法的执行。4. 形式参数列表方法调用时的实参必须和形参一一对应,类型相容。5. [throws异常列表]声明方法运行时可能产生的异常,这样异常就由该方法的调用者负责处理。3.2.5成员方法的调用用户可以调用自己编写的方法,也可以调用JDK或第三方提供的类中的方法。在JDK文档中详细地给出了每个方法的定义、形式参数、功能等说明。方法的调用原则是先定义,后调用。在调用方法的时候必须明确调用者,通过类.方法名实参数;或对象名.方法名实参;的方式来调用。当然,当前类和当前对象可以省略。这其实很好理解,如猪八戒吃西瓜在面向对象的世界中应该表述为猪八戒.吃(西瓜)。调用方法的语法格式如下:
调用者.方法名[实际参数表];
说明:1 实参的个数、顺序、类型要和形参一一对应。2 在调用其他包中的方法时需要先用import语句引入,再调用。3 调用类方法的方法为包路径.类名.方法名[实参表];,当用import语句引入其他包中的类时包路径可以省略。当前类可以省略。4 调用对象方法的方法: 先实例化一个对象,然后通过对象名.方法名[实参表];调用,当前对象可以省略。3.2.6成员方法的递归调用数学上的递归思想简单、直接、有效,用计算机编程实现时十分方便,易于理解。其缺点是内存消耗大、效率较低。为防止出现方法的无限循环递归调用,要求使用递归调用的前提是通过递归能够不断缩小问题规模,而且缩小到一定规模时一定要有一个结束点。在数学上自然数n(n1)的阶乘的定义有下面两种。1 传统定义: n!=123n。2 递归定义: n!=nn-1!,1!=1。【示例程序34】计算n的阶乘(Factorial.java)功能描述: 本程序演示了阶乘的两种实现方法,即用循环结构求n!的类方法,用递归调用求n!的对象方法,同时演示了类方法和对象如何调用。
01public class Factorial {02类方法,用循环结构实现阶乘03public static int getFactorial1int n {04int f=1;05forint i=1;i 段落:
回车: 加粗: 2. 类或接口前的文档注释,详见示例程序38的02~09行 @author: 作者。 @version: 程序版本。 @since: 从指定JDK版本开始。 @see: 参见可能会关心的类或接口。3. 方法或构造方法前的文档注释,详见示例程序38的11~18行 @param: 形式参数说明信息,建议一个形式参数占一行。 @return: 方法的返回参数类型说明信息。 @throws: 与@exception相同,方法可能抛出的异常说明信息。 @deprecated: 指示该方法已经过时,不推荐使用。 @see: 参见其他方法。 @link: 指向其他Html文档的链接。【示例程序38】Java应用程序常见文档注释(BinarySearch.java)功能描述: 本程序实现二分法查找的功能。在源程序中加上了详细的类文档注释和方法文档注释。
01package chap03;02**03 * BinarySearch类提供了数组的二分法查找功能04 * @author 姓名,学号05 * Date:2016-4-13下午07:25:37 06 * Copyright2016, ZYJ. All Rights Reserved. 07 * @version 1.008 * @since JDK 1.6 09 *10public class BinarySearch {11**12* BinSearch方法的功能: 在数组a中下标为start到end的范围内用13* 二分法查找关键字key14* @param start 数组范围中开始的下标15* @param key 要查找的数字16* @return key的下标,-1代表没有找到17* @throws 没有异常抛出18*19public static int binSearchint[]a,int start,int end,int key{20int mid=start end-start2;21if key == a[mid] {22return mid;23}24if start = end {25return -1;26}27if key 表示构造方法。
图313Triangle UML类图
6. 正向工程与反向工程稍微专业一点的UML设计器都提供了这个非常实用的功能,正向工程指根据UML类图生成 Java源代码,反向工程指根据Java源代码生成UML类图。1 选择UML|Generate Java: 启动正向工程(ForwardEngineering)向导,选择要生成Java代码的源代码目录(一般为src)和生成UML类图的Java类即可生成。2 选择UML|Reverse Engineer UML from Java: 启动反向工程(ReverseEngineering)向导。MyEclipse的Java代码到UML反向工程的过程可以用两种不同的方式实现,即批量处理模式和拖放模式。3.7俄罗斯方块程序的阅读(Tetris.java)通过编译、阅读Tetris.java直观地理解面向对象中常见的语法现象。1 package语句的作用。2 import语句的作用。3 Tetris.java共920多行,在一个Java源文件(.java)中定义了1个接口、9个类、9个内部类(含匿名内部类)。4 *.java、*.class的关系: 在Java中,一个Java源文件可包含多个类和接口的定义,但最多只能定义一个公共类或公共接口,且Java源文件名必须和定义的公共类名或接口名相同。在实际项目开发中,建议一个.java文件中只定义一个类或接口。5 Java源文件编译后每一个类(含内部类)或接口都编译成一个独立的class文件。6 直观地查看类、接口、内部类(匿名内部类)的定义。7 子类继承父类的语法现象。8 类实现接口的语法现象。9 类成员变量、对象成员变量、局部变量、常量的定义。10 构造方法的定义和调用。11 类方法和对象方法的定义和调用。12 如何生成Jar文件。3.8本 章 小 结本章主要介绍了面向对象技术的基础部分,主要包括类的定义、变量和方法的定义、对象的实例化与回收、定义包和引入类、Java文档注释、UML绘制类图等内容。通过本章的学习,读者应该熟练掌握类、构造方法、方法、成员变量、包的定义和使用,掌握对象的实例化和回收,掌握package语句和import语句的使用,在Java程序中加入文档注释,并会在Eclipse IDE中生成标准的Java文档,初步了解UML的相关知识。3.9自测题
一、 填空题1. 面向对象的四大特征: 、、、。2. 面向过程提供、、 3种逻辑结构,每种逻辑结构要求单入口和单出口。3. 对象成员变量建议为,然后为其统一提供和 方法读写。4. java.lang.类是所有Java类的根父类。5. 用修饰的类叫抽象类,抽象类只能被继承,不能被实例化。用修饰的类叫最终类,最终类只能被实例化,不能被继承。6. Java类封装了(表明对象的状态)、(表明对象所具有的行为)、Constructor、Inner Class、静态非静态。7. 创建或实例化对象一般通过 构造方法的方式来完成。8. 构造方法是Java类中的一种特殊方法,用于实例化类的一个对象,为对象分配内存空间和成员变量初始化(数值类型byte、short、int、long、float、double初始化为,boolean类型初始化为,char类型初始化为,引用类型全部初始化为)。9. 对于类或接口前的文档注释,@表示作者,@表示程序版本。10. 对于方法或构造方法前的文档注释,@表示形式参数说明信息; @ 表示方法的返回参数类型说明信息; @与@相同,表示方法可能抛出的异常; @表示该方法已经过时,不推荐使用。11. UML类图的第一个框格表示类的,第二个框格表示类的,第3个框格表示类的。二、 SCJP选择题1. In which two cases does the compiler supply a default constructor for class A? Choose two.
A. class A {}B. class A {
public A {}
}C. class A {
public Aint x {}
}D. class A{
void A{}
}Correct Answers:2. What is the result?
01public class A {02int x;03boolean check {04x;05return true;06}07void zzz {08x = 0;09if check | check ‖ check {10x;11}12System.out.println"x=" x;13}14public static void mainString[] args {15new A.zzz;16}17}
A. x = 0B. x = 1C. x = 2D. x = 3E. x = 4F. Compilation failsCorrect Answers:3. Which two allow the class Thing to be instantiated using new Thing? Choose two.A. public class Thing {}B. class Thing {
public Thing{}
}C. public class Thing{
public Thingvoid{}
}D. public class Thing{
public ThingString s{}
}E. public class Thing{
public void Thing{}
public ThingString s{}
}Correct Answers:4. What allows the programmer to destroy an object x?A. x.deleteB. x.finalizeC. Runtime.getRuntime.gcD. explicitly setting the object''s reference to nullE. ensuring there are no references to the objectF. Only the garbage collection system can destroy an objectCorrect Answers:5. Given:class Bar {}
01class Test {02Bar doBar{03Bar b = new Bar;04return b;05}06public static void main String args[] {07Test t = new Test;08Bar newBar = t.doBar;09System.out.println"newBar";10newBar = new Bar;11System.out.println"finishing";12}13}
At what point is the Bar object, created on line 3, eligible for garbage collection?A. after line 8B. after line 10C. after line 4, when doBar completesD. after line 11, when main completesCorrect Answers:三、 简答题1. 简述如何调用类方法和对象方法。2. 简述构造方法和一般方法有何不同。3. Java对象在什么情况下能够成为可以回收的垃圾?3.10编 程 实 训【编程作业31】编写一个模拟股票的Stock类(Stock.java)具体要求:1 Stock类有4个私有属性,即symbol(标志)、name(名称)、previousClosingPrice(前期收盘价)、currentPrice(当前价);2 生成Stock类的无参构造方法和包含所有属性的构造方法;3 编写所有属性的Getters和Setters方法;4 覆盖Object的toString方法,自定义输出信息;5 写一个StockTest测试类: 创建一个Stock对象,其股票标志为SUNW、名称为Sun,前期收盘价为50,随机设置一个新的当前价,显示价格变化比例。编程提示:1 随机函数的使用请参考5.1.4节。2 在Eclispe中用Source|Generate Constructor using Field生成无参构造方法和所有属性的构造方法;3 在Eclispe中用Source|Generate Getters and Setters生成所有属性的Getters and Setters方法。【编程作业32】学生类的编写和测试(Student.java)具体要求:(1) 学生类有5个私有属性,即sno(学号)、sname(姓名)、sex(性别)、hight(身高)、weight(体重)、brithDate(出生日期);(2) 编写学生类的无参构造方法和包含所有属性的构造方法;(3) 编写所有属性的Getters和Setters方法;(4) 要求覆盖Object的toString方法,自定义输出信息;(5) 要求编写一个根据BMI指数返回该学生体重状况的方法;(6) 生成一个学生类的对象,并输出学生信息Student[20151001,张三,true,170,19800101]。编程提示:1 brithDate可用java.util.Date类,注意String和Date之间的转换,SimpleDateFormat的使用请参考5.2.2节。2 BMI指数(BodyMassIndex,身体质量指数)是目前国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。其计算公式为BMI=weighthighthight,weight的单位为kg,hight的单位为cm。要求当BMI