当前位置:首页 >> IT/计算机 >>

第四章 类、对象和接口_图文

第四章
类、对象和接口

1

对象和类
对象(object): 对象(object):
– 对象:现实世界中可以明确标识的一个整体。对 对象:现实世界中可以明确标识的一个整体。 象有自己独有的个性、状态和行为。 象有自己独有的个性、状态和行为。 – 对象的状态:对象当前值的数据域 对象的状态: – 对象的方法:对象的行为 对象的方法:

类(class): (class):
– 类:用来定义同一类型对象的结构

2

面向对象的程序设计思想
面向对象程序设计就是使用对象进行程序设 计。 对象有自己独有的特性,状态和行为。 对象有自己独有的特性,状态和行为。
– 对象的状态
由具有当前值的数据域,又称为属性,的集合构成。 由具有当前值的数据域,又称为属性,的集合构成。

– 对象的行为
由方法的集合定义的。 由方法的集合定义的。

3

对象(Objects) 对象(Objects)
数据域 1 ... 数据域 m 方法 1 ... 方法 n (A) 一般对象 (B) 圆对象的例子 行为 状态 (属性 属性) 属性 findArea() 方法, 方法,行 为 radius = 5 数据域, 数据域, 状态属性

对象包括状态和行为。 对象包括状态和行为。 状态定义对象是什么,行为定义对象做什么。 状态定义对象是什么,行为定义对象做什么。 是什么 做什么
4

类(Classes)
类和对象有什么不同呢? 类和对象有什么不同呢?
? 类是对象的蓝本 ? 对象是类的实例

5

类的定义
类的定义: 类的定义:
– 类声明: 类声明:
Class 类名{……} 类名{……}

– 类名:一般首字母大写 类名:

类体: 类体:
– 变量定义:用于刻画属性 变量定义: – 方法定义: 方法定义:

6

类的定义
成员变量: 成员变量:类体中定义的变量 局部变量: 局部变量:方法中定义的变量 例:class People {int boy=5; Float a[ ]; Void f(); {boolean cool; Boy+=10; Workman zhangboy; …..} Cool=boy; }
7

类的定义
方法的定义: 方法的定义:
方法声明 方法体} {方法体} 例:public static void main() Float area(float long,float width)

8

方法
方法体: 方法体:
Class A { void f() {int m=10,sum=0; if(m>9) {int z=10;z=2*m+z;} For(int i=0;i<m;i++) sum+=I; z=i+sum; } }
注:当局部变量与成员变量名字相同时, 当局部变量与成员变量名字相同时, 成员变量被隐藏。使用成员变量时, 成员变量被隐藏。使用成员变量时,必 须使用关键字:this 须使用关键字:

Location{ private int x; private int y; public Location(int x,int y) { this.x=x; this.y=y; }
9

类的定义
重载:多个同名方法写在同一个类中。 重载:多个同名方法写在同一个类中。 例: Class Area { float getArea(float r){return 3.14*r*r;} float getArea(float x,int y){return x*y;} float getArea(int x,float y) {return x*y;} float getArea(float x,float y) {return x*y;}}
注意:所有重载的方法变量必须有所不同! 注意:所有重载的方法变量必须有所不同!
10

类的定义
构造方法:在生成类的对象时调用。 构造方法:在生成类的对象时调用。 构造方法特点: 构造方法特点:
– 与类同名 – 不带返回值,更不能指定返回对象类型; 不带返回值,更不能指定返回对象类型; – 例:构造方法

默认构造方法: 默认构造方法:
– 若在类中无构造方法,系统编译时自动建立的构 若在类中无构造方法, 造方法。 造方法。 例:Sphere(){}
11

构造方法
一个不带参数的构造方法叫做:无参构造方 一个不带参数的构造方法叫做: 法。 构造方法的特点
– 它必须与它所在的类同名。 它必须与它所在的类同名。 – 它没有返回值,甚至连void也没有。 它没有返回值,甚至连void也没有 也没有。 – 它的调用是在创建一个对象时使用new操作符进 它的调用是在创建一个对象时使用new操作符进 行的。构造方法的作用是初始化对象。 行的。构造方法的作用是初始化对象。

12

对象的创建: 对象的创建:

对象

– 格式:类名 对象名 格式:
对象名=new 对象(参数列表) 对象名=new 对象(参数列表)

例:Sphere ball; //声明一个 Sphere类型的变量ball //声明一个 Sphere类型的变量 类型的变量ball
Ball=new Sphere(1.6,4.7,45.6);//为对象分配空间 Sphere(1.6,4.7,45.6);//为对象分配空间

对象的调用: 对象的调用:
– 例:Example4_3 – 例:TextSimpleCircle.java
说明:当一个程序中包含两个类的时候, 说明:当一个程序中包含两个类的时候,必然有一 个类为主类(包含main方法)。在一个 方法)。在一个JAVA文件 个类为主类(包含 方法)。在一个 文件 只仅有主类是public类型,与文件同名。 类型, 中,只仅有主类是 类型 与文件同名。
13

静态变量与实例变量
类变量(static):也称静态(成员)变量。 ):也称静态 类变量(static):也称静态(成员)变量。与类 相关,为该类的所有对象共享, 相关,为该类的所有对象共享,与特定对象无关 实例变量:也称非静态(成员)变量,与对象相关, 实例变量:也称非静态(成员)变量,与对象相关, 每个对象都有不同的值。 每个对象都有不同的值。
PI=3-14

例:public class Sphere{ Static double PI=3-14; Double xCenter; Double yCenter; Double zCenter; ……}

Globe xCenter yCenter zCenter ball xCenter yCenter zCenter
14

类的定义
类定义的注意事项: 类定义的注意事项: – 定义与操作分离: 定义与操作分离: Class A{int x;x=12;..}//错误! 错误! 错误 实例方法既能对类变量操作也能 对实例变量操作, 对实例变量操作,而类方法只能 对类变量进行操作。 对类变量进行操作。
Class A {int a; Static int b ; Void f(int x,int y) {a=x;b=y; //合法 合法} 合法 Static Void t(int z) {b=23;//合法 合法 A=z;//非法 非法} 非法 }

Class A{int x; Void f() {int x=12;..}

15

类的定义
类中的方法可相互调用。 类中的方法可相互调用。实例方法可调用类 方法亦可调用实例方法; 方法亦可调用实例方法;类方法只能调用本 类中的类方法
Class A{ Float a; Void setA(float x,float y) {a=get(x,y);}

Static float get(float x,float y) {float c=max(x,y); Return c;} Static float max(float x,float y) {return x>y?x:y;}
16

可见性修饰符、访问器和修改器 可见性修饰符、
Public:使得类、 Public:使得类、方法和数据域可以在任何类 使得类 中被访问。 中被访问。 Private: Private:使得方法和数据域只能从它自己所 在的类中被访问。 在的类中被访问。
包1: : Public calss C1{ Public int x ; Int y; Private int z; Public void m1(){ } Void m2(){ } Private void m3(){ } } 包2: : Public calss C2{ C1 o=new C1(); Can access o.x; Can access o.y; Cannot access o.z; Can invoke o.m1(); Can invoke o.m2(); Cannot invoke o.m3(); } Public calss C3{ C1 o=new C1(); Can access o.x; Cannot access o.y; Cannot access o.z; Can invoke o.m1(); Cannot invoke o.m2(); Cannot invoke o.m3(); } 17

可见性修饰符、访问器和修改器 可见性修饰符、
若类没有声明成public类型,则仅能在相同的包里被访问 类型, 若类没有声明成 类型 包2: : 仅在类中声明的对象, 仅在类中声明的对象,可以访问所在类中的私有成员 包1: : Public Class C2{ Class Foo{ Public classC1{ Private boolean x; ……} Can access C1 ……} Public Class C3{ Cannot access C1; Can access C2;…… Public class Test{ } Public static void main(String args[]){ Foo foo=new Foo(); System.out.println(foo.x); System.out.println(foo.convert(foo.x);}

Public static void main(String args[]){ Foo foo=new Foo(); System.out.println(foo.x); System.out.println(foo.convert(foo.x);} Private int convert(boolean b){ Return x?1:-1;} Right

Wrong
18

可见性修饰符、访问器和修改器 可见性修饰符、
友好变量和友好方法
– 在类定义中,不用private、public、protected 在类定义中,不用private、public、 修饰符的成员变量和方法 – 友好变量和友好方法可以被同一包的其他对象访 问 – 不同包的子类无法继承父类的友好变量和友好方 法

受保护的成员变量和方法
– Protected,可被同一包的其他对象访问 Protected, – 子类可以继承父类的protected成员变量和方法 子类可以继承父类的protected成员变量和方法
19

可见性修饰符、访问器和修改器 可见性修饰符、
类中的get方法 称为读取器(访问器) 类中的get方法,称为读取器(访问器) 方法,
– 格式:public 返回数据类型 get属性名() 格式: get属性名 属性名()

类中的set方法 称为设置器(修改器) 类中的set方法,称为设置器(修改器) 方法,
– 格式:public void set属性名(数据类型 属性值) 格式: set属性名 属性名( 属性值) – 例:circle.java+TestCircle.java circle.java+
数据封装的优点: 数据封装的优点 1. 防止对封装数据的越权访问。 防止对封装数据的越权访问。 2. 保持数据的完整性 3. 限制私有特性改变时产生的连锁反应
20

Java的访问权限 Java的访问权限
属性和方法的访问权限
– public 可被其他对象和类通过点标记访问。 可被其他对象和类通过点标记访问。 – protected 成员只能在定义它的包中被访问, 成员只能在定义它的包中被访问,如果在其他包中被 访问, 访问,则实现这个方法的类必须是该成员所属类的 子类。 子类。 – default 没有权限修饰词。只能由同一个包中的类所访问。 没有权限修饰词。只能由同一个包中的类所访问。 – private 不能在该对象的外部访问,也不能被子类继承。 不能在该对象的外部访问,也不能被子类继承。
package的概念: 的概念: 的概念 位于同一目录的类可看成在同一个缺省的package中 ①位于同一目录的类可看成在同一个缺省的 中 ②用package语句说明的同一软件包 语句说明的同一软件包

21

static
对象管理它们各自的属性值
Student 类
name:??? ssn:??? name:Fre d ssn:123 name:Mary ssn:456 name:Jac k ssn:789 每个类的实例都创建一个独立的对象, 每个类的实例都创建一个独立的对象 每个对象都复制了类的数据 或属性。 或属性。
22

静态变量
假设有一些常见的信息, 例如, 大学中所 假设有一些常见的信息, 例如, 有在校生的人数总和。 有在校生的人数总和。我们希望所有的 Student类能够共享如下的信息 Student类能够共享如下的信息: 类能够共享如下的信息:
class Student { private int totalStudents; public int getTotalStudents() { return totalStudents; } public void increment() { totalStudents = totalStudents + 1; } }

23

静态变量
Student 类
name:??? ssn:??? name:Fred ssn:123 totalStudents name:Mary ssn:456 totalStudents

1. 必须在任何时候都能在每个 必须在任何时候都能在每个 每个Student对 对 象中使用加法方法; 象中使用加法方法 2. 当生成一个新的 当生成一个新的Student实例的时候, 实例的时候, 实例的时候 必须保证,所有Student对象都实现了总 必须保证,所有 对象都实现了总 人数的计数。 人数的计数。

name:Jack ssn:789 totalStudents

24

静态变量
一个简单的解决办法: 我们可以指定 一个简单的解决办法: 我们可以指定 totalStudents 为一个静态变量: 为一个静态变量:
class Student { private static int totalStudent; public int getTotalStudents() { return totalStudents; } public void increment() { totalStudents = totalStudents + 1; } }
25

静态变量
客户端代码: 客户端代码:
Student s1 = new Student(); s1.setName("Fred"); s1.increment(); Student s2 = new Student(); s2.setName("Mary"); s2.increment(); Student s3 = new Student(); s3.setName("Jack"); s3.increment();
26

静态变量
Student 类
name:??? ssn:??? totalStudents:3 name:Fred ssn:123 (totalStudents)

name:Mary ssn:456 (totalStudents)

静态变量是和整个类相关的 同时这个类的所有对象都可以共 享静态变量。 享静态变量。

name:Jack ssn:789 (totalStudents)

27

静态变量 : 设计的改进
当创建一个新的Student的时候 当创建一个新的Student的时候,希望能实现 的时候, totalStudentCount 加1, 可以将下面的语句加 Student类的构造器 使得操作能够自动完成: 类的构造器, 入Student类的构造器,使得操作能够自动完成:
public class Student { private static int totalStudents; … public Student() { … totalStudents++; } // etc. }
28

静态方法
和totalStudents变量相关的所有方法的声明 totalStudents变量相关的所有方法的声明
public class Student { private static int totalStudents; public Student() { totalStudents++; } public static int getTotalStudents() { return totalStudents; } public static void setTotalStudents(int x) { totalStudents = x; } public static int reportTotalEnrollment() { System.out.println("Total Enrollment: " + getTotalStudents()); } }
29

Static 关键字
类中,静态变量是所有该类对象共享的数据, 类中,静态变量是所有该类对象共享的数据, 该数据被存放于公共内存, 该数据被存放于公共内存,所有对象共用该 内存。一旦静态变量的值发生变化, 内存。一旦静态变量的值发生变化,所有该 类对象的值均将改变。 类对象的值均将改变。 例: CircleWithStaticVariableAndMethod.java
+ TestCirclwWithStaticVariableAndMethod.java
30

复习
找出下面代码的错误
public class Foo { public void method1() { Circle c; System.out.println("radius:" + c.getRadius()); c = new Circle(); } }

31

复习
找出下面代码的错误
class Test { public static void main(String [] args) { C c = new C(5.0); System.out.println(c.value); } } class C { private int value = 2; }
32

This关键字 This关键字
使用this来调用对象的方法 使用this来调用对象的方法 使用this来引用一个对象的属性 使用this来引用一个对象的属性 this可以用在构造方法中 this可以用在构造方法中,调用同一个类中 可以用在构造方法中, 的其他构造方法。 的其他构造方法。

33

this代表调用该方法的对象 this代表调用该方法的对象
class Foo { int i = 5; static double k = 0; void setI(int i) { this.i = i; } static void setK(double k) { Foo.k = k; } Suppose that f1 and f2 are two objects of Foo. Invoking f1.setI(10) is to execute f1.i = 10, where this is replaced by f1 Invoking f2.setI(45) is to execute f2.i = 45, where this is replaced by f2

}

?只要采用“类名.静态变量”形式, 可以访问一个静态变量 (如k) 只要采用“类名 静态变量 形式, 静态变量” 只要采用 ) ?使用关键字 使用关键字this,可以访问一个实例变量(非静态变量)。(如i) )。(如 使用关键字 ,可以访问一个实例变量(非静态变量)。( )

34

调用重载构造方法
public class Circle { private double radius; public Circle(double radius) { this.radius = radius; 在对象的构造方法中,必须明确的使用this来引用 来引用radius数 在对象的构造方法中,必须明确的使用 来引用 数 }
据域。 据域。

public Circle() { this(1.0); } 这里this被用来调用另一个构造器 这里 被用来调用另一个构造器 public double findArea() { return this.radius * this.radius * Math.PI; } }
属于一个对象的每个实例变量都可以用this表示,也可 表示, 属于一个对象的每个实例变量都可以用 表示 以省略。 以省略。
35

注意 ?
一般来说,一个无参或者少参构造器可以通 一般来说, this(参数列表 调用参数较多的构造器。 参数列表) 过this(参数列表)调用参数较多的构造器。 Java需要 Java需要this(参数列表)语句出现在构造器的 需要this(参数列表 参数列表) 所有语句的最前面。 所有语句的最前面。

36


格式: 包名; 格式:package 包名; 包与路径的关系: 包与路径的关系:
– 例: 包文件位置:f:\java\examples\packexam文件夹中 文件夹中; 包文件位置:f:\java\examples\packexam文件夹中; 引入包文件位置: f:\java\examples, 引入包文件位置: f:\java\examples,可直接用语句 import引入 须事先设置好classpath为 import引入(须事先设置好classpath为 引入( f:\java\examples\packexam) f:\java\examples\

当多个java文件放入同一个包中时 当多个java文件放入同一个包中时,须将原文件置 文件放入同一个包中时, 于同一文件夹下, 于同一文件夹下,每个文件的第一条语句必须用 package 包名来指定。 包名来指定。 tran+ 例:tran+Example413.java
37

类的继承
例:有:circle.java,另有一圆柱体,除了底圆 circle.java,另有一圆柱体, 以外,还有高,称为Cylinder。 以外,还有高,称为Cylinder。 Circle circle属性 circle属性 circle方法 circle方法

Cylinder

circle属性 circle属性 Cylinder属性 Cylinder属性

circle方法 circle方法 Cylinder方法 Cylinder方法
38

类的继承
Superclass Circle
-radi us +getRadius +setRadius +findArea

Subclass Cylinder
-length +getLength +setLeng th +findVolume

39

public class Cylinder extends Circle { private double length = 1;
Superclass Subclass Cylinder
-length +getLength +setLength +find Volu me

/** Return length */ public double getLength() { return length; }

Circle
-radius +getRadius +setRadius +findArea

/** Set length */ public void setLength(double length) { this.length = length; } /** Return the volume of this cylinder */ public double findVolume() { return findArea() * length; } }
40

Cylinder cylinder = new Cylinder(); System.out.println("The length is " + cylinder.getLength()); System.out.println("The radius is " + cylinder.getRadius()); System.out.println("The volume of the cylinder is " + cylinder.findVolume()); System.out.println("The area of the circle is " + cylinder.findArea()); 输出: 输出: The The The The length is 1.0 radius is 1.0 volume of the cylinder is 3.14159 area of the circle is 3.14159

41

super关键字 super关键字? 关键字?
super关键字指向使用他的类的父类,super 关键字可以用于两种途径:
调用父类的构造方法 调用父类的方法

例:Example4_23.java
42

注意
? 要调用父类构造方法必须使用关键字super 要调用父类构造方法必须使用关键字 并且这个调用必须在构造方法的第一行。 ,并且这个调用必须在构造方法的第一行。 ?父类的构造方法是不能被子类继承的,在子 父类的构造方法是不能被子类继承的, 父类的构造方法是不能被子类继承的 类中使用父类的构造方法的名字会导致语法错 误。 ?父类的构造方法只能在子类的构造方法中通 父类的构造方法只能在子类的构造方法中通 过关键字super被调用执行。 被调用执行。 过关键字 被调用执行 ? 如果没有显示的使用关键字super,那么父 如果没有显示的使用关键字 , 类的无参数构造方法将被自动执行 例:Faculty.java
43

父类的构造方法总是被执行 ?
构造方法可以调用覆盖的构造方法或父类的构造方法 。如果它们都没有被显式的调用,编译器将把super() 当作构造方法的第一条语句。例如:

public Cylinder() { }

is equivalent to

public Cylinder() { super(); }
public A(double d) { super(); // some statements }
44

public A(double d) { // some statements }

is equivalent to

回顾: this关键字 回顾: this关键字
一般来说,一个无参或者少参构造器可以通过 一般来说, this(参数列表 调用参数较多的构造器。 this(参数列表)调用参数较多的构造器。 参数列表) Java需要 Java需要this(参数列表)语句出现在构造器的所 需要this(参数列表 参数列表) 有语句的最前面。 有语句的最前面。
问题:若希望在构造方法中同时出现 问题:若希望在构造方法中同时出现super和this语句 和 语句 的话,谁放在前面比较好呢? 的话,谁放在前面比较好呢? 回答:不允许同时出现 语句。 回答:不允许同时出现super和this语句。 和 语句

45

找出程序中的错误: 找出程序中的错误 ?
public class Apple extends Fruit { } class Fruit { public Fruit(String name) { System.out.println("Fruit's constructor is invoked"); } }

46

用super调用父类的方法 super调用父类的方法
Super关键字不仅可以引用父类的构造方法, Super关键字不仅可以引用父类的构造方法, 关键字不仅可以引用父类的构造方法 也可以引用父类的方法: 也可以引用父类的方法: Cylinder类中的 类中的findVolume方法改写 如,将Cylinder类中的findVolume方法改写 如下: 如下:
double findVolume() { return super.findArea()*length; }

不过,这里没必要在 前加上super,因为 不过,这里没必要在findArea()前加上 前加上 , findArea()是Circle类的一个方法,可以由 类的一个方法, 类继承。 是 类的一个方法 可以由Cylinder类继承。 类继承
47

方法覆盖
子类从父类中继承方法, 子类从父类中继承方法,有时子类必须修改 父类中定义的方法,这叫做方法覆盖 方法覆盖。 父类中定义的方法,这叫做方法覆盖。 例:student.txt+GraduateStudent.txt
class GraduateStudent extends Student { private String undergraduateDegree; private String undergraduateInstitution; public void print() { super.print(); System.out.println(“Undergrad. Deg:” + undergraduateDegree + ”\n” + “Undergrad. Inst:” + undergraduateInstition); } }

48

说明
– 为了使用覆盖机制,方法头必须保持相同的形式: 为了使用覆盖机制,方法头必须保持相同的形式: public void print(); – 任何没有明确地定义覆盖方法的类将自动继承由 它的直接祖先所使用的方法定义。( 。(如 它的直接祖先所使用的方法定义。(如,C继承 父类A print()方法 方法。) 父类A的print()方法。)
A (Person) print()

B (Student) print() E (Graduate) print()

C (Professor)

D (Undergrad)

F (Continuing)

49

子类不能改变父类方法的目的和含义
例:Student类的print方法用来显示,则子类不能把 Student类的 类的print方法用来显示 方法用来显示, print方法替换成文件操作。 print方法替换成文件操作。 方法替换成文件操作

导出类规则

子类不能替换父类的属性
例:Student类中的birthdate属性是String类型,则子 Student类中的 类中的birthdate属性是 属性是String类型 类型, 类不能再定义birthdate属性为 属性为date类型 类型。 类不能再定义birthdate属性为date类型。

子类不能删除父类中的特征
例:子类不能删除父类中的属性。 子类不能删除父类中的属性。

子类不能改变父类方法的参数类型和返回值
例:Student的方法为void print(); 若子类将方法定义为 Student的方法为 的方法为void void print(noOfCopies);则不能起到覆盖(替换)的作用。 print(noOfCopies);则不能起到覆盖 替换)的作用。 则不能起到覆盖( 这种情况称为重载。
50

注意
? 只有可被访问的方法才能被重写。因为 只有可被访问的方法才能被重写。 私有方法不能在类的外部被访问,因此, 私有方法不能在类的外部被访问,因此, 私有方法不能被覆盖。 私有方法不能被覆盖。 ? 如果子类中定义的方法在父类中是私有 那么这两个方法没有任何关系。 的,那么这两个方法没有任何关系。 与实例方法一样,静态方法能够被继承, 与实例方法一样,静态方法能够被继承, 但是静态方法不能被重写。 但是静态方法不能被重写。 ? 如果父类和子类都定义了相同的静态方 那么父类中的方法将会被隐藏。 法, 那么父类中的方法将会被隐藏。
51

多态( Polymorphism ) 多态(
多态是指不同类的对象对同一消息的不同响 应:
A (Person) print() B (Student) print() E (Graduate) print() C (Professor)

D (Undergrad)

F (Continuing)

一个接口, 一个接口,多种方法

52

Person [ ] personBody = new Person[20]; A Person p = new Person(); (Person) print() Student s = new Student(); B C (Student) (Professor) personBody[0] = p; print() personBody[1] = s; E D F (Graduate) (Continuing) (Undergrad) print() … for (int i = 0; i<20; i++) { personBody[i] .print(); } 当逐个处理对象集时,每个对象会根据自己的类型和 当逐个处理对象集时,
当逐个处理对象集时,每个对象会根据自己的类型和 类的内部知识, 类的内部知识,自动地知道应该执行哪个版本的 print()方法。 方法。 方法 从而采用一种形式 从而采用一种形式personBody[i].print(),实现了多 ,实现了多 一种形式 种方法。 种方法。

53

动态绑定( Dynamic Binding ) 动态绑定(
//main application Person p; Student s; Professor pr; if (some runtime condition) p = new Student(); else p = new Professor(); ? p.print(x,y); class Student extends Person { …… public void print (int a, int b) { …… } …… } class Professor extends Person{ …… public void print(int a, int b) { …… } …… }
54

抽象类
例:假设每个与形状 有关的类都定义了 display方法 display方法,用来 方法, 显示具体对象的形状。 显示具体对象的形状。
Rect Square

Shape

Triangle

Ellipse

Circle

此时,可以利用多态性编写代码, 用来显示各种形状: 此时,可以利用多态性编写代码 用来显示各种形状: Shape [ ] shapeBody = new Shape[20]; shapeBody[0] = new Rect(); shapeBody[1] = new Shape(); … for (int i = 0; i<20; i++) { shapeBody[i] .display(); }
55

然而,由于Shape是一个抽象的概念,如果它也有 是一个抽象的概念, 然而,由于Shape是一个抽象的概念 display方法就不太合情理 可以做以下几种考虑: display方法就不太合情理。可以做以下几种考虑: 方法就不太合情理。
– 去掉Shape类的display方法 去掉Shape类的 类的display方法 损失了多态性的优点。可能导致程序出错,如上例。 损失了多态性的优点。可能导致程序出错,如上例。 – 将Shape类的display方法定义成空语句 Shape类的 类的display方法定义成空语句 public void display() { } – 将Shape类的display方法定义成抽象方法 Shape类的 类的display方法定义成抽象方法 abstract class Shape { … public abstract void display(); }

当一个类中包含了抽象的方法, 当一个类中包含了抽象的方法,此类就被认为是一 个抽象类。此时Shape就不能被实例化了,从而在 就不能被实例化了, 个抽象类。此时 就不能被实例化了 编译时就避免了采用第1种方法可能出现的错误 种方法可能出现的错误。 编译时就避免了采用第 种方法可能出现的错误。
56

抽象类的特点
– 抽象类中的方法不一定都是抽象的,抽象类中可以 抽象类中的方法不一定都是抽象的, 包含抽象的方法,也可以包含具体的方法。 包含抽象的方法,也可以包含具体的方法。 – 不能实例化抽象类 如果Course是抽象类 是抽象类, 例:如果Course是抽象类,则以下语句是错误的 Course c = new Course(); 但是可以声明对Course对象的引用 对象的引用: 但是可以声明对Course对象的引用: Course x; – 抽象类有子类的时候,除非子类采用具体的方法替 抽象类有子类的时候, 代抽象类中的全部抽象方法, 代抽象类中的全部抽象方法,否则子类本身也被自 动被认为是抽象的。 动被认为是抽象的。
抽象类之所以被称之为“抽象” 抽象类之所以被称之为“抽象”的,是因为抽象类省 略了要执行的一种或多种特定行为的细节。 略了要执行的一种或多种特定行为的细节。
例:GeometricObject.java+Rectangle.java Circle9.java+Cylinder9.java+TestGeometricObject.java
57

接口
接口的声明: 接口的声明:
Interface 接口名称 {常量定义; 常量定义; 方法定义;} 方法定义;}
例:

interface Printable{ final int MAX=100; void add(); float sum(float x,float y); }

接口的使用: 接口的使用:
– 类名 implements 接口名1,接口名2。。。 接口名1 接口名2
例:class A implements Printable

58

例如:为了教学,对象可能需要提供以下服务: 例如:为了教学,对象可能需要提供以下服务:
– 同意教授特定的课程 – 指定课程所使用的教科书 – 定义课程的授课提纲 – 批准特定的学生参加课程学习 此时可以建立关于一个Teacher的接口 的接口: 此时可以建立关于一个Teacher的接口:
interface Teacher { public void agreeToTeach(Course c); public void designateTextbook(Textbook b, Course c); public syllabus defineSyllabus(Course c); public boolean approveEnrollmen(Student s, Course c);}
59

接口

有了Teacher接口 有了Teacher接口,就可以把对象的各种类指定为教师 接口, 例如:可以认为Professor能够教学 Student能够教学 能够教学, 能够教学, 例如:可以认为Professor能够教学,Student能够教学,一般 Person也可以教学 也可以教学: 的Person也可以教学:
class Professor implements Teacher { String name; Sting emplyeeId; public void agreeToTeach(Course c) { //编写代码 } public void designateTextbook(Textbook b, Course c) { //编写代码 } public syllabus defineSyllabus(Course c) { //编写代码 } public boolean approveEnrollmen(Student s, Course c) { //编写代码 } } 60

说明: 说明: 1. 通过这样的定义,Professor类替代了Teacher接 通过这样的定义,Professor类替代了 类替代了Teacher接 口的所有方法,因此Professor类是一个具体的类 类是一个具体的类。 口的所有方法,因此Professor类是一个具体的类。 2. 但是如果Professor类没有替换所有的接口,则 但是如果Professor类没有替换所有的接口 类没有替换所有的接口, Professor类只能看成是一个抽象的类 Professor类只能看成是一个抽象的类。编译器会 类只能看成是一个抽象的类。 要求在Professor类的前面加上 类的前面加上abstract关键字 关键字。 要求在Professor类的前面加上abstract关键字。
因此,实现接口实际上就是在创建抽象类的子类时, 因此,实现接口实际上就是在创建抽象类的子类时,给抽象 实际上就是在创建抽象类的子类时 方法“加上具体的内容” 方法“加上具体的内容”。 接口与抽象类的不同在于: 接口与抽象类的不同在于: 接口只抽象行为 而抽象类则要指定属性、具体的方法和抽象的方法。 而抽象类则要指定属性、具体的方法和抽象的方法。
61

一个类可以继承自多个接口
例如:还有一个Administrator接口: 接口: 例如:还有一个Administrator接口 interface Administrator { public boolean approveNewCourse(Course c); public hireProfessor (Professor p); } 则可以指定类实现Teacher和Administrator接口 接口: 则可以指定类实现Teacher和Administrator接口: class Professor implements Teacher,Administrator { … } 在这种情况下, 在这种情况下,类需要替代这两个接口所定义的所有 62 方法。 方法。

用接口实现多重继承
答:因为当一个子类从两个以上的父类继承下来的时候,可 因为当一个子类从两个以上的父类继承下来的时候, 能会出现属性和方法重复或冲突的现象。 能会出现属性和方法重复或冲突的现象。
Person String name ProfessorStudent继承的属性 ProfessorStudent继承的属性 String name String major Student String major int id Professor String title String id int id String name String title ProfessorStudent String id (冲突) 冲突) (重复) 重复)

问:为什么Java不支持多重继承? 为什么Java不支持多重继承 不支持多重继承?

Professor可以讲课,Student类可以听课,为了描述能讲课的 可以讲课, 类可以听课, 可以讲课 类可以听课 学生定义了ProfessorStudent类。 学生定义了 类
63

多重继承时方法也会发生冲突: 多重继承时方法也会发生冲突:
class A{ public void f( ) {…} } class B extends A{ public void g( ) {…} } class C extends A{ public void g( ) {…} }

? 当在 中使用 f ( ) 当在D中使用 时,有B和C二条路 和 二条路 径到达A中的 径到达 中的 f ( ); ? 在D中调用 );时, 中调用g( 时 中调用 无法确定是调用哪个 基础类的g( 方法 基础类的 )方法

class D extends B,extends C{ public void h ( ) { f( ); g( ); } }

64

分析: 分析: 1. 多重继承发生问题原因之一在于属性(数据 多重继承发生问题原因之一在于属性( 结构)冲突,也就是存储空间的冲突。 结构)冲突,也就是存储空间的冲突。由于 接口不与任何存储空间相关联, 接口不与任何存储空间相关联,因此可以解 决存储空间冲突的问题。 决存储空间冲突的问题。 2. 对于继承的方法的冲突,当使用接口之后, 对于继承的方法的冲突,当使用接口之后, 由于接口只定义了方法的抽象, 由于接口只定义了方法的抽象,没有具体的 执行代码,因此也不会发生代码冲突的问题。 执行代码,因此也不会发生代码冲突的问题。

65

Person类 类

Teacher接口 接口

Student类 类

Professor类 类

ProfessorStudent

在此例中把与讲课有关的所有要素提取出来, 在此例中把与讲课有关的所有要素提取出来,放入 Teacher接口。此时,ProfessorStudent类和 接口。此时, 类和Professor 接口 类和 类都具有了讲课的能力。 类都具有了讲课的能力。
语句格式: 语句格式:
class ProfessorStudent extends Student implements Teacher {

… }
66

总结: 总结:
– 一个类可以继承自一个抽象类或具体类,以及 一个类可以继承自一个抽象类或具体类, 多个接口。 多个接口。
抽象类或具体类 接口1 接口 接口n 接口2 … 接口 接口

子类 子类只有将接口中声明的所有方法, 子类只有将接口中声明的所有方法,以及抽象类中所有的 抽象方法都进行定义,这个子类才能成为一个“具体” 抽象方法都进行定义,这个子类才能成为一个“具体”的 才能实例化。 类,才能实例化。 如果基础类可以不带任何属性和方法定义时, 如果基础类可以不带任何属性和方法定义时,可以将它定 义成一个接口。只有在必须带有属性和方法定义的时候, 义成一个接口。只有在必须带有属性和方法定义的时候, 才采用抽象类或具体类。 才采用抽象类或具体类。
67

说明
? 非抽象类(具体类)不能包含抽象方法。 非抽象类(具体类)不能包含抽象方法。 ? 如果抽象父类的子类,没有实现所有的抽象方 如果抽象父类的子类, 它必须声明为抽象的( 法,它必须声明为抽象的(在这个类声明的前面 加上abstract关键词)。 关键词)。 加上 关键词 ?在一个由抽象类扩展出来的非抽象类中,所有 在一个由抽象类扩展出来的非抽象类中, 在一个由抽象类扩展出来的非抽象类中 的抽象方法都必须实现, 的抽象方法都必须实现,即使这个子类不使用它 们。 ?抽象类不能用 抽象类不能用new运算符实例化,但仍应定义它 运算符实例化, 抽象类不能用 运算符实例化 的构造方法, 的构造方法,这种构造方法将在它子类的构造方 法中被调用。 法中被调用。 68

接口与抽象类
Interface1_2 Interface2_2 Interface1_1 Interface1 Interface2_1

Object

Class1

Class2

假设 c 是 Class2类的一个实例,则 c也是 Class2类的一个实例 类的一个实例, Object, Class1, Interface1, Interface1_1, Interface1_2, Interface2_1, 和 Interface2_2的 Interface2_2的 实例. 实例.
69

创建自定义接口
public interface Edible { /** Describe how to eat */ public String howToEat(); } class Animal { } class Chicken extends Animal implements Edible { public String howToEat() { return "Fry it"; } } class Tiger extends Animal { } class Fruit implements Edible { public String howToEat() { return "Eat it fresh"; } } class Apple extends Fruit { public String howToEat() { return "Make apple cider"; } } class Orange extends Fruit { public String howToEat() { return "Make orange juice"; } }

70

接口回调
例:Example4_28.java

71

打包成jar文件 打包成jar文件
1.建立 文件: 建立.mf文件 文件: 建立 2.在当前目录下打包: 在当前目录下打包: 在当前目录下打包

3.在上一级目录打包: 在上一级目录打包: 在上一级目录打包

72