1.38k likes | 1.6k Views
Java 程序设计基础. 第 5 章 面向对象程序设计. www.avceit.cn. 1. 面向对象程序设计概述. 2. 类 (class). 3. 5. 类的特性. 包. 4. 接口 (interface). Contents. 第 5 章 面 向 对 象 程 序 设 计. 前面主要学习了 Java 语言的数据类型、控制结构、数组等基本知识。作为 Java 学习者,必须将自己的思维转入面向对象的轨道。 本章主要任务: 理解面向对象编程的基本概念 掌握类和对象的创建方法 了解成员变量和成员方法的特性 学习使用面向对象编程方式进行程序设计
E N D
Java程序设计基础 第5章 面向对象程序设计 www.avceit.cn
1 面向对象程序设计概述 2 类(class) 3 5 类的特性 包 4 接口(interface) Contents
第5章 面 向 对 象 程 序 设 计 • 前面主要学习了Java语言的数据类型、控制结构、数组等基本知识。作为Java学习者,必须将自己的思维转入面向对象的轨道。 • 本章主要任务: • 理解面向对象编程的基本概念 • 掌握类和对象的创建方法 • 了解成员变量和成员方法的特性 • 学习使用面向对象编程方式进行程序设计 • 理解接口和包的概念
5.1 面向对象程序设计概述 • Java是一种面向对象的程序设计语言。 • 现实世界里的任何事物,都可以被看作对象。 • 对象是面向对象技术的一个基本概念。 • 类是组成java程序的基本元素。 下面逐一学习面向对象程序设计的有关概念。
5.2 类(class) • 5.2.1 类的声明 • 类是一组具有相同或相似属性和行为对象的抽象描述。 • 类封装了对象的属性和方法,它是用来定义对象的类型,而对象就是类的一个实例。 • 创建类的一般格式: • [修饰符] class 类名 [extends父类名] [implements接口名列表]{ • 成员变量声明 • 成员方法声明 • }
其中,class、extends和implements都是Java的关键字(保留字)。其中,class、extends和implements都是Java的关键字(保留字)。 • 类的修饰符主要有public、abstract 、final及缺省访问修饰符等四种。 • 注意:一个类可以同时有多个修饰符,但是不能有相同的修饰符。当一个类有多个修饰符时,这些修饰符无先后顺序,可任意排列。 • 在java中允许定义没有任何成员的空类。
【例5.1】 定义一个名为Box表示立方体的类,仅包含double类型的长length、宽width和高height三个成员变量。 • class Box{ • double length; • double width; • double height; • } • 通过例5.1,我们可以看到,在类中进行成员变量的声明与一般变量的声明形式完全相同,成员变量的类型可以是任意的,成员变量的名字在一个类中应该保证唯一性。例5.1中的类没有任何方法,下面再看一个有成员方法的类。
【例5.2】定义一个名为Box表示立方体的类,包含double类型的长length、宽width和高height三个成员变量以及设置立方体长、宽、高值的方法initBox()、计算立方体体积的方法getVol()。【例5.2】定义一个名为Box表示立方体的类,包含double类型的长length、宽width和高height三个成员变量以及设置立方体长、宽、高值的方法initBox()、计算立方体体积的方法getVol()。 class Box{ // 定义Box类 double length; double width; double height; double getVol(){ // 计算立方体体积的方法getVol() return length * width * height; } void initBox(double l,double w,double h){ // 初始化立方体长宽高值 length = l; width = w; height = h; } }
一旦定义了所需的类,就创建了一种新的数据类型,使用类类型定义的变量叫对象一旦定义了所需的类,就创建了一种新的数据类型,使用类类型定义的变量叫对象 • 如:Box box1; • 定义的box1是一个对类型为类Box的对象的引用,box1不是对象本身,可理解为指针,可以指向类Box的某一个具体的对象(实例)。
5.2.2 类对象 • 创建类的变量称为类的实例化。 • 类的对象是在程序运行中创建生成的,其所占的空间在程序运行中动态分配。当一个类的对象完成了它的使命,为节省资源,Java的垃圾回收程序就会自动收回这个对象所占的空间,即类对象有自己的生命周期。
1.创建对象 • 创建类的对象一般需要两步: • 第一步,声明该类型的一个变量; • 第二步,用new运算符为对象动态分配(即在运行时分配) 内存空间,并返回对它的一个引用,这个引用被存储在该变量中。 • 利用new创建类的对象的通用形式如下: • 类对象名 = new 类名(); • 例如,创建一个Box类的对象myBox可用以下语句 • Box myBox; • myBox = new Box();
new运算符允许与类声明结合使用来创建一个类的对象,如上面两行语句可组合为:new运算符允许与类声明结合使用来创建一个类的对象,如上面两行语句可组合为: • Box myBox = new myBox(); • 定义好后,可以把myBox作为类Box的对象来使用,而实际上myBox仅是保存了实例对象的地址,利用它来间接的引用实例对象。
2.引用对象 • 创建了类的对象后,就可以对对象的各个成员进行访问,以进行各种处理。访问对象成员的通用形式为 • 类对象名 . 成员变量名 • 类对象名 . 成员方法名( ) //括号不能省略 • 运算符“. ”称为成员运算符,在类对象名和成员变量名、成员方法之间起到连接的作用,以此指明是哪个对象的成员。 • 从上面的访问形式,可以看出,要使用对象变量首先要构造对象,获得类对象名,即类变量名。
【例5.3】利用例5.1定义的Box类,计算长、宽、高分别为5、4、3的立方体体积。【例5.3】利用例5.1定义的Box类,计算长、宽、高分别为5、4、3的立方体体积。 public class BoxDemoOne{ public static void main(String args[]){ Box myBox = new Box(); double vol; myBox.length = 5; myBox.width = 4; myBox.height = 3; vol = myBox.length * myBox.width * myBox.height; System.out.println("立方体的体积是: " + vol); } }
注意:要编译成功,必须将例5.1定义的Box类置于本程序中,以生成Box.class和BoxDemoOne.class两个字节码文件,程序运行结果如下:注意:要编译成功,必须将例5.1定义的Box类置于本程序中,以生成Box.class和BoxDemoOne.class两个字节码文件,程序运行结果如下: • 长方体的体积是: 60.0 • 当程序需要更多的类对象时,可用new运算符多次创建该类的对象,然后按例5.3中的代码形式进行长方体体积的运算。这样一来,程序中对成员变量的访问赋值语句会很多,显得程序不够紧凑。更好的方法是在类定义中加入可对成员数据进行操作的成员方法(如例5.2所定义的类Box),可使程序更加有效率。
【例5.4】用例5.2中定义的类Box来计算长、宽、高分别为5、4、3和30、20、10的两个立方体的体积。【例5.4】用例5.2中定义的类Box来计算长、宽、高分别为5、4、3和30、20、10的两个立方体的体积。 public class BoxDemoTwo{ public static void main(String args[]){ Box myBox1 = new Box(); Box myBox2 = new Box(); double vol; myBox1.initBox(5,4,3); myBox2.initBox(30,20,10); vol = myBox1.getVol(); System.out.println("第一个立方体的体积是: " + vol); vol = myBox2.getVol(); System.out.println("第二个立方体的体积是: " + vol); } }
5.2.3 特殊的方法——构造方法 • 例5.2定义的Box类,专门定义了一个方法initBox()用于对成员变量length、width、height进行赋值初始化。如果在一个对象最初被创建时就能够把它的相关值设置好,程序将会更简单并且更明晰。Java语言允许对象在它们被创建时初始化自己。这种自动的初始化是通过使用一种特殊的方法——构造方法(constructor)来完成的。 • 构造方法的任务就是初始化一个对象的内部状态,以便使创建的实例变量能够完全初始化。它是一种特殊的成员方法,它的特殊性反映在以下几个方面: • 构造方法名称与类名完全相同 • 构造方法不返回任何值,也没有返回类型 • 每一个类可以有零个或多个构造方法 • 构造方法在创建对象时由Java编译器自动调用执行,一般不能显式直接调用
构造方法的通用声明格式如下 • [构造方法修饰符] 构造方法名([形式参数列表])[throws异常列表]{ • 方法体 • } • 构造方法修饰符有: • public private protected
【例5.5】利用构造方法重写例5.4的实例。 class Box{ // 定义Box类 double length; double width; double height; Box(double l,double w,double h){ // 利用构造方法初始化立方体长宽高值 System.out.println("Constructing Box"); length = l; width = w; height = h; } double getVol(){ // 计算立方体体积的方法getVol() return length * width * height; } }
public class BoxDemoThree{ public static void main(String args[]){ Box myBox1 = new Box(5,4,3); // 在new执行前自动调用Box()方法 Box myBox2 = new Box(30,20,10); double vol; vol = myBox1.getVol(); System.out.println("第一个立方体的体积是: " + vol); vol = myBox2.getVol(); System.out.println("第二个立方体的体积是: " + vol); } }
类的构造方法 • 1、无参构造方法 • 一个类若没有任何用户自定义的构造方法,Java会自动提供一个空的无参数构造方法,在创建对象时,使用这个无参的构造方法为类对象的成员变量赋数据类型的默认值。 • 对于以下语句 • Box myBox = new Box(); • 利用new Box()调用构造方法Box()。 • 如果我们不为类定义一个构造方法,Java将为该类创建一个默认的构造方法(无参构造方法)。默认构造方法自动地将所有的实例变量初始化为零。默认构造方法对简单的类是足够的,但是对更复杂的类它就不能满足要求了。 • 一旦我们在某个类中定义了自己的构造方法,默认的构造方法将不再被使用。
2、有参构造方法 • 带有参数的构造方法能够实现这样的功能:当构造一个新对象时,类构造方法可以按需要将一些指定的参数传递给构造方法。 • 如例5.5所示: Box(double l,double w,double h){ System.out.println("Constructing Box"); length = l; width = w; height = h; }
【例5.6】带参数的构造方法应用实例。 class Point{ int x; int y; Point(int x,int y){ // 用户自定义带参数的构造方法Point() this.x=x; this.y=y; } } public class PointDemo{ public static void main(String args[]){ Point p=new Point(10,20); System.out.print("类Point中的实例变量(x,y) = ") ; System.out.println("( "+p.x+","+p.y+" )"); } } 思考此处为什么要使用this?
类的构造方法中的形式参数名与成员变量名完全相同,这样在使用时会产生混淆。在Java语言中,定义了关键字this来解决这个问题。关键字this表示当前对象,可在引用当前对象的所有方法内使用。类的构造方法中的形式参数名与成员变量名完全相同,这样在使用时会产生混淆。在Java语言中,定义了关键字this来解决这个问题。关键字this表示当前对象,可在引用当前对象的所有方法内使用。 • 当然,这样使用this有时还是会引起混淆。所以一些开发人员比较小心,一般不使用和局部变量、正式的自变量同名的隐藏的实例变量。
但关键字this仍然有其用武之地。如在某个类中有多个构造方法时,一个构造方法可以调用另外一个构造方法,调用的格式是但关键字this仍然有其用武之地。如在某个类中有多个构造方法时,一个构造方法可以调用另外一个构造方法,调用的格式是 this(实际参数表); • 该语句的功能是调用与this(指当前对象)中参数匹配的构造方法。 • 在Java语言中,允许构造方法重载,即一个类可以有多个构造方法。
【例5.7】构造方法的重载。 class OverloadBox{ double length; double width; double height; OverloadBox(double l,double w,double h){ length = l; width = w; height = h; } OverloadBox(double x){ length = x; width = x; height = x; } double getVol(){ return length * width * height; } }
public class OverloadConstructorBoxDemo{ public static void main(String args[]){ OverloadBox myBox1 = new OverloadBox(30,20,10); OverloadBox myBox2 = new OverloadBox(10); double vol; vol = myBox1.getVol(); System.out.println("第一个立方体的体积是: " + vol); vol = myBox2.getVol(); System.out.println("第二个立方体的体积是: " + vol); } } 使用构造方法并非只是为了给对象赋初始值方便,更重要的是,它是确 保一个对象有正确起始状态的必要手段。另外,通过使用非public的构 造方法,可以防止程序被其他人错误地使用与扩展。
5.2.4 类和成员的修饰符 • 前面的内容中,给出了定义类及其成员变量的通用格式,在这些格式中,都使用了修饰符(modifier),其目的是用来对类和成员的使用做某些限定。 • 在Java中,一般将修饰符分为两类: • 访问控制符:public、protected、private、package • 作用是给予对象一定的访问权限,实现类和类中成员的信息隐藏。 • 非访问控制符:abstract、static、final、native、synchronized、 volatile、 transient • 某些修饰符只能应用于类的成员,而某些修饰符既可应用于类,也可应用于类的成员。
public修饰符 • public(公共)修饰符用来修饰类、类的成员变量和成员方法。 • 用public修饰的对象能被所有的类访问;public修饰的成员变量可以被项目文件中的任何方法所访问。 • 由于public成员变量不受限制,这易使类的对象引起不希望的修改,建议成员变量尽量不要使用public修饰符。public修饰的方法可以由其他类访问。
protected修饰符 • protected(受保护)修饰符用来修饰类的成员变量和成员方法。 • protected修饰的成员变量和成员方法,可以被本类和它的子类以及同一个包中的其他类访问。 • private修饰符 • private(私有)主要用来修饰类的成员变量和成员方法。private修饰的成员变量和成员方法只能在本类中被访问到,而不能被任何其它类(包括子类)所引用。这种保护方式通常是最为安全的,也是对类进行封装时使用的主要方法。
package修饰符 • package(包)修饰的变量,称为包变量。 • 通常省略package关键字,即没有修饰符的成员就是包成员。 • 包成员可以被本类及本类所在包中的其他类访问。
abstract修饰符 • abstract(抽象)用来修饰类、类的成员方法。abstract修饰的类不能被实例化,它包含有未实现的方法,需要子类继承。abstract修饰的方法为抽象方法,无方法体。抽象类被派生、抽象方法被子类实现后才有实际意义。 • 注意: • 抽象方法只能出现在抽象类中。 • 不能用abstract修饰构造方法、静态方法和私有方法,也不能覆盖父类中的抽象方法,只有返回值类型、方法名、方法参数,而不定义方法体的方法
final修饰符 • final(最终)修饰符用来修饰类、成员方法和成员变量。final的含义为终极或最终,它修饰的类不能被继承,即final类无子类。 • final修饰的成员方法不能被覆盖,即子类的方法名不能与父类中用final修饰的方法名相同。
final修饰的成员变量称为最终成员变量。一开始创建该变量时将其设定了一个值,在以后程序的运行过程当中,变量的值将一直保持这个值不变。如final修饰的成员变量称为最终成员变量。一开始创建该变量时将其设定了一个值,在以后程序的运行过程当中,变量的值将一直保持这个值不变。如 • static final double PI = 3.14159; • final int COUNTER = 10; • 为final变量的所有字符选择大写是一个普遍的编码约定。声明为final的变量在实例中不占用内存。实质上,一个final修饰的变量就是一个常量。
Java中的常量必须是类的成员。对于最终成员变量,任何赋值都将导致编译错误。因为常量在说明以后就不能改变其值,所以常量必须要使用变量初始化来赋初值。无论是实例变量,还是类变量,都可以被声明成常量。Java中的常量必须是类的成员。对于最终成员变量,任何赋值都将导致编译错误。因为常量在说明以后就不能改变其值,所以常量必须要使用变量初始化来赋初值。无论是实例变量,还是类变量,都可以被声明成常量。 • final修饰符和static修饰符并不冲突。但abstract和final绝对不能同时作为一个类的修饰符,因为abstract修饰的类必须要有子类继承,而final修饰的类为终极类,不能有子类继承。
【例5.8】常用修饰符的使用。 class Person{ private int a = 10; } class Teacher extends Person{ public int b =50; protected double c = 100.0; } public class ModifierDemo{ public static void main(String args[]){ Person p = new Person(); Teacher t = new Teacher(); int i,j; double k; * i = p.a; j = t.b; k = t.c; * System.out.println("In Person, value of a is: " + i); System.out.println("In Teacher, value of b is: " + j); System.out.println("In Teacher, value of c is: " + k); } }
上述程序中,加“*”的两条语句试图访问私有成员a将会报错。如果我们注释掉这两行,本例执行结果如下上述程序中,加“*”的两条语句试图访问私有成员a将会报错。如果我们注释掉这两行,本例执行结果如下 • In Teacher, value of b is: 50 • In Teacher, value of c is: 100.0 • 成员b由于被public修饰,其值一定可以访问;成员c由于被protected修饰,在其子类及本包中可以访问到。
static修饰符 • static(静态)修饰符用来修饰类的成员变量和成员方法,以使它们成为静态成员(又称为类成员)。静态成员存储于类的存储区,属于整个类,而不属于一个具体的对象。因为静态成员属于整个类,所以它被所有该类对象共享。在不同的类对象中访问静态成员,访问的是同一个。 • 用static修饰的成员变量又称为类变量,不加static修饰的成员变量又叫实例变量。实例变量依附于具体的对象实例,它的值因具体对象实例的不同而不同,而类变量为该类的所有对象所共享,它的值不因类的对象不同而不同。
用static修饰的方法为静态方法,又叫类方法;无static修饰的方法为实例方法。类方法是该类的所有对象共享的方法。用static修饰的方法为静态方法,又叫类方法;无static修饰的方法为实例方法。类方法是该类的所有对象共享的方法。 • 对静态成员的使用一般应注意以下两点: • 静态方法不能访问属于某个对象的成员变量,而只能处理属于整个类的成员变量,即静态方法只能处理静态变量。 • 可以使用两种方式调用静态成员,它们的作用相同。 • 变量:类名. 变量名 或 类对象. 变量名 • 方法:类名 . 方法名() 或 类对象 . 方法名()
【例5.9】静态成员的使用。 class StaticDemo{ static int A=100; static int B=200; static void callMe(){ System.out.println("A = "+A); } } class StaticByName{ public static void main(String args[]){ StaticDemo.callMe(); // 不需要创建对象,通过类名直接调用静态方法 System.out.println("B = "+StaticDemo.B); // 通过类名直接调用静态变量 } }
synchronized修饰符 • synchronized(同步)修饰符用于修饰成员方法或块。在多线程程序中,对用于共享的方法和块加以互斥锁,使得任一时刻,synchronized修饰的方法或块只能由一个线程执行或操作。使用synchronized主要用以设置同步机制,以实现线程的同步。 • 详见第10章
native修饰符 • native(本地)修饰符一般用来声明用其它语言如C、C++、FORTRAN、汇编等书写方法体并具体实现方法功能的特殊方法。native修饰的方法为本地方法,即方法实现与本机系统有关。native方法可应用于实时性强、执行效率高、运行速度要求较高的场合。 • 除了以上介绍的修饰符外,Java还为我们提供了transient(过渡变量)、volatile(易失变量)等修饰符,有兴趣的读者可利用Java API文档进一步了解掌握。
5.2.5 类的使用 • 私有成员的访问 • 在Java程序设计过程中,为了降低类间的耦合性,提高代码的安全性,可以为类成员指定private修饰符。用private修饰表示该成员只能在该类内部被访问。若需要在其它类中访问私有成员,只有通过取数和送数等操作方法来访问。对于这样的方法,我们常为其命名为getXxx()和setYxx()等形式。
【例5.10】私有成员的访问。 • class Box{ • private double length; • private double width; • private double height; • Box(double l,double w,double h){ • length = l; • width = w; • height = h; • } • double getLength(){ • return length; • }
double getWidth(){ return width; } • double getHeight(){ return height; } • } • public class BoxDemoFour{ • public static void main(String args[]){ • Box myBox = new Box(6,5,4); • double vol; • vol = myBox.getLength() * myBox.getWidth() * myBox.getHeight(); • System.out.println("立方体的体积是: " + vol); • } • }
2.方法参数是类的对象 • 在Java语言中,方法的参数类型除了是基本类型外,还可以是引用类型——类。在类的对象中实际存储为对象的引用,因此在调用类参数时方法间传送的是引用。尽管Java采用值传送,引用从调用方法单向传送到被调方法,但由于调用方法与被调用方法对应类参数的引用相同,它们引用同一对象。所以,若在被调方法中修改了引用类型形式参数的取值,则调用方法对应的实际参数也将发生相应的变化。即调用方法与被调方法之间是“引用单向传送,数据双向传送”。 • 应用引用类型的方法参数,可在方法间传递数据。
例5.11 引用类型的方法参数是方法间传送数据的桥梁。 • class Box{ • double length; • double width; • double height; • double vol; • Box(Box box){ • this.length = box.length; • this.width = box.width; • this.height = box.height; } • Box(double l,double w,double h){ • length = l; • width = w; • height = h; } • Box(){ // 用-1表示一个未初始化的Box对象 • length = -1; • width = -1; • height = -1; }
Box(double edge){ • length = width = height = edge; } • double getVol(Box bx){ • bx.vol = bx.length * bx.width * bx.height; } • } • public class RefParameterBox{ • public static void main(String args[]){ • Box myBox1 = new Box(30,20,10); • Box myBox2 = new Box(); • Box myCube = new Box(10); • Box myClone = new Box(myBox1);
myBox1.getVol(myBox1); • myBox2.getVol(myBox2); • myCube.getVol(myCube); • myClone.getVol(myClone); • System.out.println("Volume of myBox1 is: "+myBox1.vol); • System.out.println("Volume of myBox2 is: "+myBox2.vol); • System.out.println("Volume of myCube is: "+myCube.vol); • System.out.println("Volume of myClone is: "+myClone.vol); • } • }