项目三 Java面向对象程序设计 任务一 了解面向对象程序设计的基本概念 任务二 掌握类的使用方法 任务三 熟悉对象的创建与使用方法 任务一 了解面向对象程序设计的基本概念 任务二 掌握类的使用方法 任务三 熟悉对象的创建与使用方法 任务四 了解包的创建与使用方法
任务一 了解面向对象程序设计的基本概念 一、对象 现实世界中,对象(Object)就是客观存在的某一事物。一辆自行车,一台计算机,它们都可以视为对象。对象普遍具有两个特征:状态(属性)和行为。比如,小鸟有名称、体重、颜色等状态和飞行、觅食等行为;同样,自行车也有品牌、外观、重量等状态和刹车、加速、减速等行为。 在面向对象程序设计中,对象是一组数据和相关方法的集合。程序中可通过变量向其传递或获取数据,而通过调用其中的方法执行某些操作。在Java中,对象必须基于类来创建。
二、类 类是用来描述一组具有共同状态和行为的对象的原型,是对这组对象的概括、归纳与抽象表达。 在面向对象程序设计中,可以让具有共同特征的对象形成类,它定义了同类对象共有的变量和方法。通过类可以生成具有特定状态和行为的实例,这便是对象。 从某种程度上讲,Java编程就是设计类,在编程中可以采用自定义方法或继承方法设计一个类。此外,还可在编程时利用类来创建对象,然后改变对象变量值和调用对象方法来实现程序的某些功能。
三、封 装 封装(Encapsulation )是将代码及其处理的数据绑定在一起的一种编程机制,该机制保证了程序和数据都不受外部干扰且不被误用。理解封装性的一个方法就是把它想成一个黑匣子,它可以阻止在外部定义的代码随意访问内部代码和数据。对黑匣子内代码和数据的访问通过一个适当定义的接口严格控制。 封装的目的在于使对象的设计者和使用者分开,使用者不必知道对象行为实现的细节,只需要使用设计者提供的接口来访问对象。 封装是OOP设计者追求的理想境界,它可以为开发员带来两个好处:模块化和数据隐藏。模块化意味着对象代码的编写和维护可以独立进行,不会影响到其他模块,而且有很好的重用性;数据隐藏则使对象有能力保护自己,它可以自行维护自身的数据和方法。因此,封装机制提高了程序的安全性和可维护性。
四、继承 继承是面向对象程序设计中两个类之间的一种关系,是一个类可以继承另一个类(即它的父类)的状态和行为。被继承的类称为超类或父类,继承父类的类称为子类。 一个父类可以同时拥有多个子类,这时这个父类实际上是所有子类的公共变量和方法的集合,每一个子类从父类中继承了这些变量和方法。 然而,子类也可以不受父类提供的状态和行为的限制。子类除了具有从父类继承而来的变量和方法外,还可以增加自己的变量和方法。子类也可以改变从父类继承来的方法,即可以覆盖继承的方法。 继承使父类的代码得到重用,在继承父类提供的共同特性的基础上增加新的代码,从而使编程不必一切从头开始,进而有效提高了编程效率。
五、多态 多态性可以用“一个对外接口,多个内在实现方法”来表示。也就是说,我们可以在一个类中定义多个同名方法,程序在调用某个方法时,系统会自动根据参数类型和个数的不同调用不同的方法,这种机制被称为方法重载。 此外,当我们利用继承由父类创建子类时,如果父类中的某些方法不适合子类,我们无法删除它们,但可以重新定义它们,这被称为覆盖。如此一来,当我们利用子类创建对象时,如果调用对象的某个方法,系统会首先在子类中查找此方法。如果找到,则调用子类的方法;否则,将向上查找,即在父类中查找此方法。这种情况被称为父类与子类之间方法的多态性。
任务二 掌握类的使用方法 下面我们结合Java程序的格式来详细介绍类的声明方法: package 包名 // 声明程序所在包 任务二 掌握类的使用方法 下面我们结合Java程序的格式来详细介绍类的声明方法: package 包名 // 声明程序所在包 import 包名.* // 导入外部包,可包含多条import语句,以导入多个外部包中的类 import 包名.类名 // 声明和定义类 [类修饰符] class 类名[extends 父类名称][implements 接口名称列表]{ // 声明成员变量或常量 [访问控制修饰符][static][final]<数据类型> 变量名或常量名; …… // 定义其他成员变量或常量 // 声明和定义成员方法 [访问控制修饰符][abstract][static][final][native][synchronized] 返回类型 方法名(参数列表) [throws 异常类型列表] { …… // 方法体 } …… // 定义其他方法 …… // 定义其他类
解释说明: (1)在一个Java文档中可以包含多个类,但最多只能有一个为公 共类(即public class,也可以没有)。 (2)如果存在public class的话,该类的类名必须与文档名相同。 (3)main方法是Java应用程序的入口,如果文档中存在public class和main方法,则main方法必须位于public class中。 main方法的格式如下: public class 类名 { // 成员变量列表 public static void main(String[] args) { // 局部变量声明 // 方法体 }
一、类声明 类声明定义了类的名字及其他属性。类声明的一般格式如下: [类修饰符] class 类名[extends 父类名称][implements 接口名称列表]{ …… } 其中,class关键字和类名是必需的,[]表示可选项。类名是要声明的类的名字,它必须是一个合法的Java标识符,习惯上首字母要大写。 1.类修饰符 类修饰符有public、abstract和final。如果没有声明这些类修饰符,Java编译器默认该类为friendly类,对于这些类,只有同一包中的类可以访问。
public(公共的):带有public修饰符的类称为公共类,公共类可以被 任何包中的类访问。不过,要在一个类中使用其他包中的类,必须在 程序中增加import语句 。 abstract(抽象的):带有abstract修饰符的类称为抽象类,相当于类 的抽象。一个抽象类可以包含抽象方法,而抽象方法是没有方法体的 方法,所以抽象类不具备具体功能,只用于衍生出子类。因此,抽象 类不能被实例化。 final(最终的) :带有final修饰符的类称为最终类。不能通过扩展最 终类来创建新类。也就是说,它不能被继承,或者说它不能派生子类。 2.说明一个类的父类 extends关键字用来告诉编译器创建的类是从父类继承来的子类,父类必须是Java系统的预定义类或用户已经定义好的类。一个类只能有一个父类,但一个父类可以有多个子类。
3.说明一个类所实现的接口 implements关键字用来告诉编译器类实现的接口,一个类可以实现多个接口,多个接口之间用逗号分隔,其形式为: implements interface1,interface2,…; 使用接口的主要目的是为了使程序的功能描述和功能的具体实现相分离,从而使程序结构更清晰。 4.类体 类体是类功能实现的主体,是Java语句的集合。类体中一般定义三类要素:成员变量和常量、构造方法和方法。其中,成员变量和常量用来刻画对象的状态,方法用来描述对象的行为,而构造方法一般用来初始化成员变量。
二、成员变量与常量 成员变量或常量声明必须放在类体中,其一般形式为: [访问控制修饰符][static]<数据类型> 变量名; [访问控制修饰符][static][final]<数据类型> 常量名; 1.访问控制修饰符 使用访问控制修饰符可以限制访问成员变量或常量的权限。访问控制修饰符有4个等级:private、protected、public以及默认(即不指定修饰符)。 类型 private 默认 protected public 所属类 可访问 同一个包中的其他类 不可访问 同一个包中的子类 不同包中的子类 不同包中的非子类
2.static变量(类变量或静态变量) Java中包括两种类型的成员变量:实例成员变量和类成员变量,简称实例变量和类变量。如果用static关键字修饰成员变量,则该变量是一个类变量(又称静态变量)。不加static修饰的成员变量称为实例变量。 package Chapter3 class ConcentCircle{ public static int x=100,y=100; //定义圆心坐标变量 public int r; //定义半径变量 public static void main(String args[]){ ConcentCircle t1=new ConcentCircle(); //创建对象 ConcentCircle t2=new ConcentCircle(); t1.x+=100; //设置圆心的横坐标 t1.r=50; //初始化半径变量 t2.x+=200; t2.r=150; System.out.println("Circle1:x="+t1.x+",y="+t1.y+",r="+t1.r); System.out.println("Circle2:x="+t2.x+",y="+t2.y+",r="+t2.r); }
三、方法的声明与实现 方法声明的一般形式为: 1.方法定义的一般形式 在Java中,方法在类体中定义。与类一样,方法的定义也包括两个部分:方法声明和方法体。 方法声明的一般形式为: [访问控制修][abstract][static][final][native][synchronized] 返回类型 方法名(参数列表) [throws 异常类型]{ ……//方法体 }
2.方法的修饰符 方法的修饰符分为访问控制修饰符和非访问控制修饰符。方法的访问控制修饰符与成员变量的访问控制修饰符的用法是一样的 。 (1)abstract方法(抽象方法) 带有abstract修饰的方法称为抽象方法,是指没有方法体的方法。不过,抽象方法只能出现在抽象类中。 (2)static方法(类方法或静态方法) 方法与成员变量类似,也分为实例方法和类方法(又称静态方法)。带有static修饰符的方法称为类方法,不加static修饰的方法称为实例方法。 实例方法是属于某个对象的方法,即每个对象的实例方法都有自己专用的内存空间。类方法是属于整个类的,它被该类的所有对象共享。类方法与实例方法请参考【例5】。
(3)final方法(最终方法) 带有final修饰的方法称为最终方法。在面向对象程序设计中,子类可以覆盖父类的方法。但是,如果父类的某个方法被final修饰,则其子类就不能覆盖这个方法,只能继承这个方法。因此,这样可以防止子类对父类的关键方法进行修改,保证了程序的安全性。 (4)native方法(本地方法) 用其他语言编写的方法在Java程序中称为本地(native)方法。由于native方法的方法体是使用其他语言在程序外部编写的,所以native方法没有方法体。 (5)synchronized方法(同步方法) 同步方法用于多线程编程。多线程在运行时,可能会同时存取一个数据。为了避免数据的不一致性,可以将方法声明为同步方法,进而对数据加锁,以保证线程的安全。 (6)throws异常类型列表 程序在运行时可能会发生异常现象。每一个异常都对应着一个异常类。如果希望方法忽略某种异常,可将其抛出,让它的“上级”(如调用它的对象等)来处理,从而使程序得以继续运行。
如果一个方法具有返回值,则在方法体中使用return语句把一个确定的值返回给调用该方法的语句。 3.方法的返回类型 一个方法必须声明其返回类型,方法的返回类型可以是Java中的任意数据类型。当一个方法不需要返回数据时,返回类型必须是void(空)。 如果一个方法具有返回值,则在方法体中使用return语句把一个确定的值返回给调用该方法的语句。 4.方法的参数传递 当编写一个方法时,一般会在方法名之后给出一个参数列表(称为方法的形参)来声明该方法所需要的参数类型和参数。参数列表由参数类型和参数名称组成,各参数之间用逗号分隔。 在Java中,可传递的参数(称为方法的实参)可以是任何数据类型,包括基本数据类型、数组或对象,它必须与方法的形参完全对应。其中,传递基本类型的参数时,编译器会将参数的值传递到方法中。在方法中修改传递过来的参数的值,并不会影响原参数的值。请参考【例6】与【例7】。
5.重载方法 Java支持重载方法,即多个方法可以共享一个名字。但是,各方法之间必须在参数个数、顺序或类型方面有所区别。 public static String doubleIt(String data){ // 参数类型为String System.out.println(data+data); return data+data; } public static int doubleIt(int data){ // 参数类型为int System.out.println(2*data); return 2*data;
6.方法体中局部变量的特点 在方法体中声明的变量称为局部变量,它只能在方法体内使用。另外,我们也可以利用“{……}”方式来声明代码块,从而限制局部变量的作用域(即变量可被使用的代码块范围)。因此,局部变量的作用域开始于它的声明处,结束于当前代码块结束处。如果没有声明代码块,则其开始于声明处,结束于方法体结束处。 void func(){ int z; //局部变量z作用域开始于它的声明处 { //程序代码块,声明变量作用域仅限于当前代码块 int x=75; } //x的作用域到此结束 int y=23; //局部变量y的作用域开始于此处 z=x+y; //非法,因为当前位置x变量已经消亡 } //局部变量y和z的作用域到此结束。
this用来表示当前类,它主要有以下几种用法: 辅助调用类的构造方法,尤其是当构造方法有多个时。 class Point3D extends Point2D { protected int x, y, z; public Point3D(int x, int y) { // 第一个构造方法 // 调用类的另一个构造方法,调用该构造方法时,传来的实参x //和y被赋予成员变量x和y,而成员变量z被赋予了0 this(x, y, 0); } public Point3D(int x, int y, int z) { // 第二个构造方法 this.x = x; this.y = y; this.z = z;
四、类的构造方法 为了便于在基于类创建对象时向对象传递参数,以及对类的成员变量进行初始化。我们经常会为类编写一个或多个特殊的方法——构造方法。 类的构造方法有如下几个特点: (1)每个类都有一个默认的构造方法,它既无参数又无返回值,其作用是使用new操作符创建新对象后初始化新建对象。 (2)一旦为类编写了构造方法,默认的构造方法将被覆盖 。 (3)构造方法仅在使用new操作符创建新对象时执行一次,而且一般不能用“对象名.方法名”形式来显式调用。 (4)编写构造方法的目的通常是为了向对象传递参数,以及对类的成员变量进行初始化 。 (5)构造方法同样支持方法重载。 (6)构造方法的名称必须与类名完全相同,并且不返回任何值。 (7)构造方法不能被static、final、abstract、synchronized和native等修饰符修饰,并且带参数的构造方法不能被子类继承 。
案例3-1 计算斐波纳契数列 【案例描述】 【技术要点】 案例3-1 计算斐波纳契数列 【案例描述】 意大利著名数学家斐波纳契曾经提出了“著名的兔子问题”:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子不死,问每个月的兔子总数为多少? 答案是一组非常特殊的数字,即1、1、2、3、5、8、13、21…以上这些数字就是著名的“斐波纳契数列”。 【技术要点】 ① 斐波纳契数列的特点是:从第三个数开始,每个数都是前两个数的和。设数列由f1,f2,f3,f4…fn组成,那么由数列特点可知: f1=1 f2=1 f3= f1+ f2 … fn=fn-1+ fn-20(n=0) 从而,总结出该数列的递推公式为:Fib(n)= ② 方法递归是指一个方法用自身的结构来描述自身,它直接或间接地调用自身方法。 0(n=0) 1(n=1) Fib(n-1)+Fib(n-2)(n>1)
package Chapter3; public class Fibonacci { public static long fib_1(int n) { // 定义实现递归算法的方法 long f1, f2; if (n == 0 || n == 1) { // 如果n=0或n=1返回n return n; } else { f1 = fib_1(n - 1); f2 = fib_1(n - 2); return (f1 + f2); // 否则返回fib(n-1)+fib(n-2) } public static long fib_2(int n) { // 定义实现循环算法的方法 long f1 = 0, f2 = 1; long sum = 0; if (n == 0 || n == 1) { for (int i = 1; i < n; i++) { sum = f1 + f2; f1 = f2; f2 = sum; } return sum; public static void main(String[] args) { System.out.println("递归实现:"); for (int i = 1; i < 11; i++) { System.out.print(fib_1(i) + ",");} System.out.println("\n循环实现:"); System.out.print(fib_2(i) + ",");}
任务三 熟悉对象的创建与使用方法 一、对象的创建 1.声明对象引用变量 任务三 熟悉对象的创建与使用方法 类是创建对象的模板,即利用一个已存在的类可以创建多个对象,被创建的对象称为类的实例对象,简称类的实例(或对象)。我们把创建实例对象的过程称为类的实例化。 一、对象的创建 创建一个对象包括声明对象引用变量(即声明对象)和为对象分配内存空间(即创建对象)两个步骤。 1.声明对象引用变量 声明对象引用变量即为对象指定所属类及命名该对象引用变量名称。对象引用变量简称对象变量。 声明对象的一般格式为: 类名 对象名;
2.为声明的对象分配内存 在Java中,使用new运算符和类的构造方法为声明的对象分配内存。new称为操作符或运算符,它的任务是实例化对象,同时负责调用类的构造方法完成新对象的初始化任务。 创建对象的语法格式如下: <对象名>=new <构造方法名>([参数列表]); 其中,参数列表是可选的,它取决于构造方法自身的情况。 提示:对象声明和分配内存两个步骤也可以用一个等价的步骤完成,例如:PaintRectangle b2 = new PaintRectangle(170, 40, 60, 60); 二、对象的使用 创建对象后,一般可通过如下格式来访问对象的变量和调用对象的方法: <对象名>.<变量名> <对象名>.<方法名>
三、Java的垃圾回收机制——对象的清除 Java平台允许创建任意个对象(当然会受到系统资源的限制),Java虚拟机会自动判断创建的对象是否还被引用,如果不再被引用,它会自动释放其占用的内存空间。这种定期检测不再使用的对象并自动释放内存空间的过程称为垃圾回收机制。 1.垃圾回收器 Java有一个垃圾回收器,它周期性扫描对象的内存区,并将没有被引用的对象作为垃圾收集起来,定期地释放不再被使用的内存空间。 2.撤销方法finalize 在一个对象被垃圾回收器收集之前,垃圾回收器将给对象一个机会来调用自己的finalize()方法,将对象从内存中清除。
案例3-2 计算一元二次方程的根 【案例描述】 编写一个一元二次方程的求解程序,实现功能:当判别式大于等于0时,输出两个实数根;当判别式小于0时,输出两个虚数根。 【技术要点】 在类中定义带参数的构造方法用于接收方程的二次系数、一次系数和常数,然后再创建两个方法分别用于计算方程的实根和虚根。利用new运算符为类创建对象时,可在构造方法中通过参数传递为类的成员变量赋值,最后可通过调用对象方法计算出一元二次方程的根。 相关数学公式如下(令:delt=b2-4ac): (1)当delt判别式大于等于0; (2)当delt判别式小于0; x1=(-b+)/(2*a) x1=real+imag i=-b/(2*a)+/(2*a)i x2=(-b-)/(2*a) x2=real-imag i=-b/(2*a)-/(2*a)i
package Chapter3; public class YYECFC { double x1, x2; private double a, b, c; public YYECFC(double a, double b, double c) { //带参数构造方法初始化成员变量 this.a = a; this.b = b; this.c = c; } void real_root(double delt) { //计算方程实根的方法 x1 = (-b + Math.sqrt(delt)) / (2 * a); x2 = (-b - Math.sqrt(delt)) / (2 * a); System.out.println("delt>=0,方程的实根为\nx1=" + x1); //输出方程的实根 System.out.println("x2=" + x2); void imag_root(double delt) { //计算方程虚根的方法 double real, imag; real = (-b) / (2 * a); imag = Math.sqrt(-delt) / (2 * a); System.out.println("delt<0,方程的虚根为\nx1=" + real + "+" + imag + "i"); System.out.println("x2=" + real + "-" + imag + "i"); void showRoot() { //根据判别式delt的值,选择调用的方法 double delt = b * b - 4 * a * c; if (delt >= 0) real_root(delt); else imag_root(delt); public static void main(String[] args) { YYECFC y1, y2; //声明对象 y1 = new YYECFC(1, 5, 10); //使用带参数的构造方法创建对象 y1.showRoot(); //调用对象方法 y2 = new YYECFC(1, 10, 9); y2.showRoot();
任务四 了解包的创建与使用方法 一、包的创建、声明与使用 1.在希望放入某个包的程序中声明其所属包 任务四 了解包的创建与使用方法 一、包的创建、声明与使用 包(package)的组织方式同操作系统中文件夹的组织方式类似,是Java语言中有效管理类的一个机制。 包实际上就是一个存放.class的文件夹,因此,创建包就是创建文件夹。 1.在希望放入某个包的程序中声明其所属包 在要放入包的程序中加入package语句,并且该语句一定要作为程序的第一条语句(程序注释除外),也是唯一的一条语句,其作用是声明该程序位于某个包中,例如: package jinqie.com.chapter1; 如果我们不在程序中利用package语句声明其所属包,则该程序属于无名包。由于无名包没有名字,因此,它将不能被其他程序引用。
2.在希望使用外部包中类的程序中导入类 如果某个程序要使用某个包中的类,应在该程序中加入import语句,以便将外部类导入,从而在程序中使用该类。 import语句有如下两种使用方法: (1)import 包名.公共类名(实际上就是java字节码文件名),例如,import jinqie.com.chapter1.file1,表示导入chapter1包中的file1类,file1为类文件名(file1.class)。这种导入方式被称为单类导入。 (2)import 包名.*,例如,import java.io.*,表示根据程序需要导入当前程序中使用的java.io包中的类(而不是包中的全部类)。因此,这种导入方式又称按需导入。
3.新建或修改CLASSPATH变量 CLASSPATH类似于DOS操作系统中的PATH,它指明了包的前导路径。 例如,如果我们在程序中使用了import jinqie.com.chapter1.myjava;语句,而CLASSPATH的内容为“.;c:\test;”,则系统会首先在当前文件夹内查找\jinqie\com\chapter1文件夹和myjava.class文件。如果无法找到,则会在c:\test文件夹内查找\jinqie\com\chapter1文件夹和myjava.class文件。如果依然无法找到,系统会提示程序错误。 换句话说,真正的包路径实际上是CLASSPATH变量值和import语句中指明的包路径的组合。CLASSPATH的设置方法与环境变量Path类似。
二、Java的常用包 (1)java.lang包:它是Java的核心类库,包含运行Java程序必不可少的系统类。 (2)java.util包:它包括了Java中的一些低级的实用工具,如处理时间的Date类。 (3)java.awt包:它是Java语言用来构建图形用户界面(GUl)的类库,它包括了许多界面元素和资源。 (4)java.awt.datatransfer包:它提供了处理数据传输的工具类 。 (5)java.awt.event包:它是对JDK1.0版本中原有的Event类的一个扩充,它使得程序可以用不同的方式来处理不同类型的事件 。 (6)java.awt.image包:它是用来处理和操纵来自于网上图片的Java工具类库。 (7)java.io包:它包含了实现Java程序与操作系统、用户界面以及其他Java程序进行数据交换所使用的类。 (8)java.sql包:它是实现JDBC(Java database connection)的类库。 (9)java.applet包:它用来实现运行于Internet浏览器中Java Applet的工具类库。 (10)java.net包:它是Java语言用来实现网络功能的类库。 (11)java.rmi包、java.rmi.registry包和java.rmi.server包:它们用来实现(远程方法调用)功能。 (12)java.security包、java.security.acl包和java.security.interfaces包:它们更完善的Java程序安全性控制和管理。
项目小结 本项目主要介绍了Java面向对象程序设计的基本概念和基本方法,其中包括类的定义方法,对象的创建和使用方法,包的创建和使用方法等。大家应着重掌握如下一些内容: 了解公共类和主类的概念; 了解成员变量和成员方法各种访问控制修饰符的意义; 了解成员变量和实例变量的区别; 了解使用方法参数进行数据传递时传值和传地址的区别; 了解this关键字的意义和用法; 了解构造方法的作用与创建和使用方法; 了解对象的创建与使用方法; 了解包的创建、声明和使用方法。