Presentation is loading. Please wait.

Presentation is loading. Please wait.

第三讲 Java面向对象要素.

Similar presentations


Presentation on theme: "第三讲 Java面向对象要素."— Presentation transcript:

1 第三讲 Java面向对象要素

2 内容 什么是Java类 初始化和清理 隐藏实现细节 重复运用类 多态 接口 内部类 2017/2/24

3 1. 什么是Java类

4 1.1 类的设计模式 类担当的角色: 是服务和功能的提供者,要进行服务的提供,必须具有良好的伸缩性、简单性和适应性,任何类型的程序都能从基于类的设计中受益。构建自己的类,就是要把服务和功能分布到类中去 服务的资源: (私有资源和封装) 服务的管道: (多样性, Cohesion, Coupling) 服务的格式: (Methods) 创建服务 : (Construction Function) 测试 : 使用 : (New ) 2017/2/24

5 1.2 什么是Java类 ? 当用户编写自己的Java程序时,主要工作就是编写类。当程序运行时,已声明的类用来创建新对象。由类创建对象的过程称为实例化(instantiation),每个对象都是类的一个新实例(instance)。 类是一种抽象数据类型,在Java中类也被当作一个数据类型来定义。类的语法结构包括关键字class、跟在后面的类名称。如果其继承自某个基类,还需要使用extends关键字加基类名称。 类通常不需要从头生成。相反,他们可以从其他的类派生而来,继承祖先类的可用类成员,包括:字段、方法等。即使是从头创建的新类也必须是继承自Object类,只不过我们可以合法省略extends Object而已。 class 类名 extends 基类 { //类的函数成员 构造函数1; 构造函数2; ... 方法1; 方法2; //类的数据成员 字段1; 字段2; } 2017/2/24

6 1.3 信息的封装和隐藏 Java中通过将数据封装、声明为私有的(private),再提供一个或多个公开的(public)方法实现对该属性的操作,以实现下述目的: 隐藏一个类的实现细节; 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作; 便于修改,增强代码的可维护性 2017/2/24

7 1.4 类成员 2017/2/24 类成员包括数据成员和函数成员。
数据成员是面向对象的术语,用于表示类中的数据变量,即Java中的字段(field)。 函数成员也是面向对象的术语,用于表示类中的操作。Java的函数成员包括方法和构造函数: 方法是一些封装在类中的过程和函数,用于执行类的操作,完成类的任务。 构造函数是一种特殊的方法,用于对象的创建和初始化。 2017/2/24

8 1.5 创建Java自定义类的方法 定义类:定义类就是定义类的属性和服务。类的属性即静态属性,指类内的各项数据;类的服务即成员函数或方法。
语法结构: [修饰符] class 类名 [extends 父类] [implements 接口名] {      类成员变量声明    类方法声明 } 2017/2/24

9 1.6 数据成员(成员变量) 成员变量的声明方式如下:   [public | protected | private ] [static]   [final] [transient] [volatile] type variableName;     其中:   static: 静态变量(类变量;相对于实例变量)   final: 常量   transient: 暂时性变量,串行化该对象时将排除该变量  volatile: 共享变量,用于并发线程的共享 2017/2/24

10 1.7 Method (方法) 方法是在类中定义,用来实现对象操作的过程或者函数。方法是类的成员,并有自己的可访问性。Java程序中,方法的声明和方法的实现是分开的。 方法与普通函数和过程不同,方法只应用于特定类及其祖先类的对象。另外,每一个方法都有一个隐含的参数,称为this,它引用作为方法调用主体的对象。调用一个方法的语法格式是:对象名.方法名(实际参数); 实例字段和实例方法是属于某一具体实例对象的字段和方法,必须先创建这个实例对象,然后才能使用这些字段和方法。对于同一个类创建的不同的实例对象,其字段可以有不同的取值,以反映该对象的不同状态。 除此之外,还有一种通过类就可以直接访问的静态字段和静态方法,这种静态的字段和方法用static关键字标识,不需要创建实例就可以通过类直接访问。 2017/2/24

11 定义方法:Java中,方法只能在类中定义,由方法头和方法体两部分组成。
格式: [修饰符] 返回值类型  方法名([参数列表]) throws 例外名1,例外名2,... {      局部变量/对象声明部分;        语句部分; } 2017/2/24

12 方法声明中的修饰符的含义 [public | protected | private ] [static]   [final | abstract] [native] [synchronized]                static: 静态方法(类方法),可通过类名直接调用   abstract: 抽象方法,没有方法体   final: 方法不能被重写   native: 集成其它语言的代码   synchronized: 控制多个并发线程的访问 例: public static void main(String args[]) //主类方法 2017/2/24

13 2. 初始化和清理

14 2.1 使用对象:对象的创建和销毁 对象是通过类创建的,对象是类的动态实例。每个对象都有生命期。一个对象按其生命期来分析,一般有三个阶段,出生、活动、死亡。而我们在编程中要做的对应为:创建(初始化)、运行、销毁。 通常,当调用构造函数时,该函数返回一个新分配内存并初始化了的类的实例。 Java构造函数是一种比较特殊的函数,它不能由用户来指定返回类型,只能返回所属类的类型;它也不能由用户来指定其它名称,只能使用与类名相同的名称。即便如此,我们仍然可以通过方法的重载(overload)来为一个类提供多个不同的构造函数。 在Java中没有用于销毁和清理对象的析构函数,因为Java提供了垃圾回收(Garbage Collection,简称gc)机制负责释放对象所占用的内存空间及相关的其它资源。 2017/2/24

15 构造函数:用来 初始化对象(为其所有静态属性赋初值)的一个特殊方法:
构造函数名与类名相同 构造函数可以有0个或多个参数 特点:构造函数始终和new关键字一起调用 构造函数没有返回值 构造函数一般不能由编程人员显示地直接调用 在创建一个类的新对象时,系统自动调用构造 函数对其进行初始化。 2017/2/24

16 (Construction Methods)构造方法
语法格式: < modifiers> <class_name>([< argu_list>]) { [< statements>] } 举例: public class Person { private int age; public Person() { age = 18; } public Person(int i) { age = i; } public void setAge(int i) { age = i; } 2017/2/24

17 Java语言中,每个类都至少有一个构造方法; 如果类的定义者没有显式的定义任何构造方法,系统将自动提供一个默认的构造方法:
默认构造方法没有参数 默认构造方法没有方法体 Java类中,一旦类的定义者显式定义了一个或多个构造方法,系统将不再提供默认的构造方法; 2017/2/24

18 2.2 访问对象 2017/2/24 使用new +构造方法创建一个新的对象;
使用“对象名.对象成员”的方式访问对象成员(包括属性和方法); Example: public class Test { public static void main(String[] args) { Employee d = new Employee(“zhang”, 100.0); d.update_salary(10); System.out.println(d.print_name()+”:" + d.print_salary()); } 2017/2/24

19 从语义上讲,对象是类的实例,类是创建对象的模板;从语言上讲,对象也是类这种数据类型的变量,对象在内存中占有空间。但是在具体使用中,对象与传统的变量,也就是Java中基本类型的变量有区别。
对象是一种引用类型。对象由类创建,存储在堆中;而对象变量则是对象的引用,存储在栈中。在程序中我们通过对象变量,也就是对象的引用,来操纵对象。 引用类型的演示: MyClass a,b;//声明MyClass类型的变量a和b,MyClass是一个Java类 a = new MyClass();//变量a引用了MyClass类创建的对象实例 a.myvar = 24;//给对象实例的字段myvar赋值 b = a; //变量b得到的是变量a的一个引用的副本, //结果使他们都引用了同一个对象实例。 a.myvar = a.myvar+1;//改变对象字段的值 System.out.println(a.myvar); //此时显示a.myvar值为25 System.out.println(b.myvar); //此时显示b.myvar值也为25 2017/2/24

20 2.3 Java的静态修饰符 static是静态修饰符 (1)静态属性
(2)用static修饰符修饰的方法 ,是属于整个类的方法。用static修饰的方法的含义: 调用这个方法时应该使用类名做前缀,不用某个具体的对象名; 该方法属于整个类,它在内存中的代码段随着类的定义而分配和装载; 该方法不能操纵属于某个对象的成员变量,即static方法只能处理static数据。 2017/2/24

21 静态初始化器 2017/2/24 与构造函数类似,完成初始化工作,但两者的区别是:
静态初始化器对每个类初始化,构造函数对每个新创建的对象初始化。 静态初始化器在所属的类加载入内存时由系统调用运行,构造函数则在产生新对象时由系统自动执行。 静态初始化器不是方法,没有方法名、返回值和参数列表。 静态初始化器对整个类完成初始化操作,包括给static类成员赋初值。 //: initialization/Spoon.java public class Spoon { static int i; static { i = 47; } } ///:~ 2017/2/24

22 2017/2/24 class Cups { static Cup cup1; static Cup cup2; static {
cup1 = new Cup(1); cup2 = new Cup(2); } Cups() { print("Cups()"); public class ExplicitStatic { public static void main(String[] args) { print("Inside main()"); Cups.cup1.f(99); // (1) // static Cups cups1 = new Cups(); // (2) // static Cups cups2 = new Cups(); // (2) } /* Output: Inside main() Cup(1) Cup(2) f(99) *///:~ package initialization; //: initialization/ExplicitStatic.java // Explicit static initialization with the "static" clause. import static net.mindview.util.Print.*; class Cup { Cup(int marker) { print("Cup(" + marker + ")"); } void f(int marker) { print("f(" + marker + ")"); 2017/2/24

23 2.4 关于finalize()方法 Java提供了被称终结(finalization)的机制,使用该机制可以定义一些特殊的操作,这些操作在一个对象将要被垃圾回收程序释放时执行。要给一个类增加终结(finalizer),只需要定义finalize ()方法即可。Java回收该类的一个对象时,就会调用这个方法。在finalize ()方法中,你需要指定在一个对象被撤消前必须执行的操作。 垃圾回收周期性地运行,检查对象是否不再被运行状态引用,或间接地通过其他对象引用。就在对象被释放之前,Java运行系统调用该对象的finalize()方法。 使用finalize()方法的一般格式如下: protected void finalize() { // finalization code here } 其中,关键字protected是防止在该类之外定义的代码访问finalize( )方法。 2017/2/24

24 3. 隐藏实现细节

25 3.1 原则 让变动的事物与不变的事物彼此隔离,是面向对象设计的首要考虑 2017/2/24

26 3.2 虚拟机工作过程 虚拟机通过 classloader来装载这些字节码, 也就是通常意义上的类.
classloader 从哪里知道 java 本身的类库及用户自己的类在什么地方呢? 或者有着缺省值(当前路径) 或者要有一个用户指定的变量来表明 这个变量就是类路径(classpath) 或者在运行的时候传参数给虚拟机 2017/2/24

27 java 虚拟机是由 java luncher 初始化的, 也就是 java (或 java. exe)这个程序来做的
1, 引导类: 组成 java 平台的类, 包含 rt.jar 和 i18n.jar 中的类. 2, 扩展类: 使用 java 扩展机制的类, 都是位于扩展目录($JAVA_HOME/jre/lib/ext)中的 .jar 档案包. 3, 用户类: 开发者定义的类或者没有使用 java 扩展机制的第三方产品. 你必须在命令行中使用 -classpath 选项或者使用 CLASSPATH 环境变量来确定这些类的位置. 我们在上面所说的用户自己的类就是特指这些类. 这样, 一般来说, 用户只需指定用户类的位置, 引导类和扩展类是"自动"寻找的. 2017/2/24

28 2017/2/24 可能的来源是: ".", 即当前目录, 这个是缺省值. CLASSPATH 环境变量, 一旦设置, 将缺省值覆盖.
命令行参数 -cp 或者 -classpath, 一旦指定, 将上两者覆盖. 由 -jar 参数指定的 .jar 档案包, 就把所有其他的值覆盖, 所有的类都来自这个指定的档案包中. 由于生成可执行的 .jar 文件, 还需要其他一些知识, 比如 package, 还有特定的配置文件 2017/2/24

29 3.3 Package 一个包就是一些提供访问保护和命名空间管理的相关类与接口的集合. 使用包的目的就是使类容易查找使用, 防止命名冲突, 以及控制访问 作用: 组织相关的源代码文件 不同包中的类名可以相同,用来避免名字冲突 提供包一级的封装和存取权限 public class FullQualification { public static void main(String[] args) { java.util.ArrayList list = new java.util.ArrayList(); } import java.util.ArrayList; public class SingleImport { public static void main(String[] args) { ArrayList list = new ArrayList(); } 2017/2/24

30 包定义语句不是必须的,如果没有定义包,则当前编译单元属于无名包,生成的class文件放在一般与.java文件同目录
定义包的语句必须放在所有程序的最前面 包定义语句不是必须的,如果没有定义包,则当前编译单元属于无名包,生成的class文件放在一般与.java文件同目录 package 名字一般用小写 2017/2/24

31 import java.class051.three. Converter;
引入包: import package[.package….].(classname|*) 例如: import java.class051.three. Converter; import java.class051.three.*; Java编译器会自动引入包 java.lang,对于其他的包,如果程序中使用到包中的类,则必须使用import引入。 2017/2/24

32 3.4 类成员的可访问性 类成员包括数据成员和函数成员,类的每个成员都有一个称为可访问性的属性,用来保护类成员。Java有四种类成员的保护方式,分别为 缺省的 public(公有的) protected(保护的) private(私有的) 2017/2/24

33 (1)类成员具有的控制修饰符 1、public :如果类也是public 修饰的话,这些属性和方法可被所有的类访问和使用
2、private:只能被该类自身访问和调用 3、protected:保护访问控制符,可被同一个包中的类或不同包中该类的子类以及该类自身访问和引用 使用protected的目的就是让所有的子类能访问父类 4、默认(friendly):具有包内访问性,只能被同一个包中的其他类访问和引用 2017/2/24

34 2017/2/24 限访问修饰符关键字 同一个类中 同一个包中 派生类中 其他包中 public √ protected 无访问修饰符关键字
private 2017/2/24

35 为了使程序具有良好易读的风格,建议最好在撰写类代码时用不同的可访问性来组织类成员,并将public成员作为对外公布的接口放在最前面,以便引起关注。
public class X { public void pub1() { /* */ } public void pub2() { /* */ } public void pub3() { /* */ } private void priv1() { /* */ } private void priv2() { /* */ } private void priv3() { /* */ } private int i; // } 2017/2/24

36 class的访问权限 如果希望某个class可以被客户端程序员所用,需要将关键字public置于class前
如果class不想被客户端使用,拿掉class的public修饰,成为friendly 2017/2/24

37 4.重复运用类

38 4.1 复用的精神 复用:软件工程永恒的主题 代码重用 组件重用 模式重用 2017/2/24

39 4.2 组合 将对象引用放入新的类中 2017/2/24 什么时候初始化对象引用? 在对象定义处 在构造函数中 在需要用到该对象的地方
class WaterSource { private String s; WaterSource() { System.out.println("WaterSource()"); s = new String("Constructed"); } public String toString() { return s; } } 什么时候初始化对象引用? 在对象定义处 在构造函数中 在需要用到该对象的地方 注意:没有初始化的对象为空 2017/2/24

40 4.3 继承 继承是面向对象语言不可缺少的部分 2017/2/24

41 2017/2/24 public class Detergent extends Cleanser {
// Change a method: public void scrub() { append(" Detergent.scrub()"); super.scrub(); // Call base-class version } // Add methods to the interface: public void foam() { append(" foam()"); } // Test the new class: public static void main(String[] args) { Detergent x = new Detergent(); x.dilute(); x.apply(); x.scrub(); x.foam(); print(x); print("Testing base class:"); Cleanser.main(args); } /* Output: Cleanser dilute() apply() Detergent.scrub() scrub() foam() Testing base class: Cleanser dilute() apply() scrub() import static net.mindview.util.Print.*; class Cleanser { private String s = "Cleanser"; public void append(String a) { s += a; } public void dilute() { append(" dilute()"); } public void apply() { append(" apply()"); } public void scrub() { append(" scrub()"); } public String toString() { return s; } public static void main(String[] args) { Cleanser x = new Cleanser(); x.dilute(); x.apply(); x.scrub(); print(x); } 2017/2/24

42 说明: 你可以为每个class写main()
仅仅由命令行所调用的那个class的main()会被唤起,这个main()必须是public,其类不一定是public 2017/2/24

43 base class的初始化 在派生类构造函数中插入对基类构造函数的调用 2017/2/24
//: reusing/Cartoon.java // Constructor calls during inheritance. import static net.mindview.util.Print.*; class Art { Art() { print("Art constructor"); } } class Drawing extends Art { Drawing() { print("Drawing constructor"); } public class Cartoon extends Drawing { public Cartoon() { print("Cartoon constructor"); } public static void main(String[] args) { Cartoon x = new Cartoon(); } /* Output: Art constructor Drawing constructor Cartoon constructor *///:~ base class的初始化 在派生类构造函数中插入对基类构造函数的调用 2017/2/24

44 当各个类带有缺省的构造函数,你无需担心,系统会自动处理
//: reusing/Chess.java // Inheritance, constructors and arguments. import static net.mindview.util.Print.*; class Game { Game(int i) { print("Game constructor"); }} class BoardGame extends Game { BoardGame(int i) { super(i); print("BoardGame constructor"); public class Chess extends BoardGame { Chess() { super(11); print("Chess constructor"); } public static void main(String[] args) { Chess x = new Chess(); }} /* Output: Game constructor BoardGame constructor Chess constructor *///:~ 当各个类带有缺省的构造函数,你无需担心,系统会自动处理 当类不具备default构造函数,或者向调用带有引数的base class构造函数,需要运用关键字super,明确写出调用动作 2017/2/24

45 4.4 清理 Java通过垃圾回收器帮你做了“析构” 但是,也意味着你失去了控制 所以有时候你得自己写 2017/2/24

46 2017/2/24 //: reusing/CADSystem.java // Ensuring proper cleanup.
public class CADSystem extends Shape { private Circle c; private Triangle t; private Line[] lines = new Line[3]; public CADSystem(int i) { super(i + 1); for(int j = 0; j < lines.length; j++) lines[j] = new Line(j, j*j); c = new Circle(1); t = new Triangle(1); print("Combined constructor"); } public void dispose() { print("CADSystem.dispose()"); // The order of cleanup is the reverse // of the order of initialization: t.dispose(); c.dispose(); for(int i = lines.length - 1; i >= 0; i--) lines[i].dispose(); super.dispose(); public static void main(String[] args) { CADSystem x = new CADSystem(47); try { // Code and exception handling... } finally { x.dispose(); //: reusing/CADSystem.java // Ensuring proper cleanup. import static net.mindview.util.Print.*; class Shape { Shape(int i) { print("Shape constructor"); } void dispose() { print("Shape dispose"); }} class Circle extends Shape { Circle(int i) { super(i); print("Drawing Circle"); } void dispose() { print("Erasing Circle"); super.dispose(); }} class Triangle extends Shape { Triangle(int i) { super(i); print("Drawing Triangle"); } void dispose() { print("Erasing Triangle"); class Line extends Shape { private int start, end; Line(int start, int end) { super(start); this.start = start; this.end = end; print("Drawing Line: " + start + ", " + end); } void dispose() { print("Erasing Line: " + start + ", " + end); super.dispose(); }} /* Output: Shape constructor Drawing Line: 0, 0 Drawing Line: 1, 1 Drawing Line: 2, 4 Drawing Circle Drawing Triangle Combined constructor CADSystem.dispose() Erasing Triangle Shape dispose Erasing Circle Erasing Line: 2, 4 Erasing Line: 1, 1 Erasing Line: 0, 0 *///:~ 2017/2/24

47 4.5 组合or继承 当你想要在新类中使用既有类的功能,而非接口,可以采用组合技术(has a)
当我们创建一个既有类型的特例时,可以使用继承(IS a) 2017/2/24

48 4.6 Protected说明 2017/2/24 //: reusing/Orc.java
// The protected keyword. import static net.mindview.util.Print.*; class Villain { private String name; protected void set(String nm) { name = nm; } public Villain(String name) { this.name = name; } public String toString() { return "I'm a Villain and my name is " + name; } public class Orc extends Villain { private int orcNumber; public Orc(String name, int orcNumber) { super(name); this.orcNumber = orcNumber; public void change(String name, int orcNumber) { set(name); // Available because it's protected this.orcNumber = orcNumber; } public String toString() { return "Orc " + orcNumber + ": " + super.toString(); public static void main(String[] args) { Orc orc = new Orc("Limburger", 12); print(orc); orc.change("Bob", 19); } /* Output: Orc 12: I'm a Villain and my name is Limburger Orc 19: I'm a Villain and my name is Bob *///:~ 2017/2/24

49 4.7 向上转型 从专用型别移至通用型别 2017/2/24

50 4.8 关键字final 意思:这是不能改变的 Final data 某块数据固定不变
可以定义成编译期就确定的常量,也可以定义成编译期无法知道值,但一旦赋值,就不变的变量 也可以是Blank finals,即允许我们将数据成员声明为final,却不给予初值,但是必须在使用之前进行初始化 Final arguments:在函数中你无法将它改指其他值 2017/2/24

51 Final methods Final classes 锁住这个函数,使派生类无法改变其意义 提高效率 不希望有继承动作发生
2017/2/24

52 5. 多态:接口与实现相分离

53 5.1 多态 相关概念 函数调用的绑定方式:函数调用与函数本体的关联 封装 实现隐藏 编译期绑定:早期绑定 后期绑定:执行期绑定或动态绑定
2017/2/24

54 Shape s = new Circle(); s.draw(); 2017/2/24

55 5.2 覆写和重载 方法的重写Overriding和重载Overloading是Java多态性的不同表现。
如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 2017/2/24

56 Example of overloading : i. public void setValue(int pValue) ii
Example of overloading : i. public void setValue(int pValue) ii. public void setValue(float pValue) If you call the method with value 0, it will call the method i. If you call the method with value 0.0, it will call the method i. Example of override : Class A is the super class of Class B, and both classes have the method setValue(int pValue). public class A { public void setValue(int pValue) { ... }; // i. } public class B extends A { public void setValue(int pValue) { ... }; // ii. } so if you have an object reference of Class B, method ii will be called. if the reference is Class A, method i will be called. 2017/2/24

57 2017/2/24 //: polymorphism/FieldAccess.java
// Direct field access is determined at compile time. class Super { public int field = 0; public int getField() { return field; }} class Sub extends Super { public int field = 1; public int getField() { return field; } public int getSuperField() { return super.field; }} public class FieldAccess { public static void main(String[] args) { Super sup = new Sub(); // Upcast System.out.println("sup.field = " + sup.field + ", sup.getField() = " + sup.getField()); Sub sub = new Sub(); System.out.println("sub.field = " + sub.field + ", sub.getField() = " + sub.getField() + ", sub.getSuperField() = " + sub.getSuperField()); } } /* Output: sup.field = 0, sup.getField() = 1 sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0 *///:~ 2017/2/24

58 2017/2/24 //: polymorphism/StaticPolymorphism.java
// Static methods are not polymorphic. class StaticSuper { public static String staticGet() { return "Base staticGet()"; } public String dynamicGet() { return "Base dynamicGet()"; class StaticSub extends StaticSuper { return "Derived staticGet()"; return "Derived dynamicGet()"; public class StaticPolymorphism { public static void main(String[] args) { StaticSuper sup = new StaticSub(); // Upcast System.out.println(sup.staticGet()); System.out.println(sup.dynamicGet()); } /* Output: Base staticGet() Derived dynamicGet() *///:~ 2017/2/24

59 5.3 抽象类和抽象方法 在Java语言中,将abstract关键字修饰的类称为抽象类.与此对应,用abstract关键字修饰的方法称为抽象方法。 abstract类必须被继承,abstract方法必须被重写。 抽象类的定义格式: abstract class ClassName {     ………… } 抽象类中可以包含抽象方法,对抽象方法只须声明,不需要实现,格式如下: abstract returnType MethodName([paramlist]); 抽象方法的一个主要目的就是为所有子类定义一个统一的接口,抽象方法必须被重写。抽象类中不一定要包含抽象方法,但是,如果一个类中包含了抽象方法,也就是abstract关键字修饰的方法,那么该类就必须声明为abstract类。 2017/2/24

60 5.4 构造函数和多态 构造函数调用顺序 继承与finalize() 初始化对象的存储空间为零或null值; 调用父类构造函数;
按顺序分别调用类成员变量和实例成员变量的初始化表达式; 调用本身构造函数。 继承与finalize() 如果有某些特别的清理动作,需要在派生类中重写finalize() 如果这么做,一定需要调用base class的finalize(),否则基类的终止动作不会发生 2017/2/24

61 多态方法在构造函数中的行为 如果在构造函数中调用“正在建构中的某个对象”的某个动态绑定的函数,情况会如何? 2017/2/24

62 运用 继承 当不知道选择继承还是组合时,最好先选择组合 纯粹继承和扩充 2017/2/24

63 6. 接口

64 6.1 接口的内涵 An interface is a way to describe what classes should do, without specifying how they should do it. It’s not a class but a set of requirements for classes that want to conform to the interface (类所应提供的服务,而不是如何具体的内容) public interface Comparable { int compareTo(Object otherObject); } 2017/2/24

65 Interface(接口): 又称界面,是由抽象方法和常量组成的特殊类,代表了某一组功能的实现,是用来实现类间多重继承功能的结构。
Java中一个类只能有一个父类,但可以同时实现若干个接口,故可以实现类似于类的多重继承的功能 接口仅仅定义对外接口和规范,并没有实现这个功能;功能是在继承了这个接口的类中完成的。 接口的访问控制符只有public一个,表明可以被所有的类和接口使用,如果没有此修饰符,则只能被同一个包中的其他类和接口利用 接口也具有继承性,用extends来声明,一个接口可以有一个以上的父接口,之间用逗号分隔。 2017/2/24

66 6.2 接口的声明 2017/2/24 接口用关键字interface来声明。 语法:
[public] interface 接口名 [extends 父接口名列表] {         //常量域说明;      [public] [static] [final] 域类型 域名=常量值;      //抽象方法声明      [public] [abstract] [native] 返回值 方法名(参数列表) (throw异常列表); } 2017/2/24

67 6.3 接口的实现 2017/2/24 实现某个或某几个接口时,需注意: 在类的声明部分用implements声明该类将要实现哪些接口;
如果实现某接口的类不是abstract的抽象类,则在类的定义部分必须实现指定接口的所有抽象方法,即为所有抽象方法定义方法体,而且方法头部分应该与接口中的定义完全一致,即有完全相同的返回值和参数列表。 如果实现某接口的类是abstract的抽象类,则它可以不实现该接口所有的方法。但是对于这个抽象类任何一个非抽象的子类而言,它们父类所实现的接口中的所有抽象方法都必须有实在的方法体。这些方法体可以来自抽象的父类,也可以来自子类自身,但是不允许存在未被实现的接口方法。这主要体现了非抽象类中不能存在抽象方法的原则。 一个类在实现某接口的抽象方法时,必须使用完全相同的方法头。如果所实现的方法与抽象方法有相同的方法名和不同的参数列表,则只是在重载一个新的方法,而不是实现已有的抽象方法。 接口的抽象方法的访问限制符都已指定为public,所以类在实现方法时,必须显式地使用public修饰符,否则将被系统警告为缩小了接口中定义的方法的访问控制范围。 2017/2/24

68 使用interface的例子 designing part of a company's accounts system:
Taxable Assets suppose the company wants to compute its annual tax liability suppose buildings vehicles, PCs and photocopies are valuable assets, and the company must pay tax each year, depending on their value the current value for each of them is computed in different ways 2017/2/24

69 公司的类图 2017/2/24 Object Employee Vehicle Car Van Building Shop
Warehouse Equipment TypeWriter PC Copier SalesStaff WarehouseStaff AdminStaff 2017/2/24

70 2017/2/24 public class Building { protected double floorSpace;
protected double landRate; protected String address; public double getTaxableValue() { return floorSpace * landRate; } public class Vehicle { protected double purchasePrice; protected double depreciation; protected String registrationNum; return purchasePrice – depreciation; 2017/2/24

71 2017/2/24 public class PC { protected double purchasePrice;
protected int age; public double getTaxableValue() { return purchasePrice / age; } public class Copier { private static final double SCRAP_VALUE = 10.0; pubic double getTaxableValue() { return SCRAP_VALUE; 2017/2/24

72 Defining an interface 2017/2/24
Go back to original hierarchy, and define an interface: public interface ITaxable { public double getTaxableValue(); } Remember: Java interfaces act as specifications. This one specifies what it means to be a taxable object. Now add "implement ITaxable" to Building, PC, etc – i.e. tell java that they meet the specification, so they are taxable, and we can invoke the method 2017/2/24

73 using the interface now we can use the original hierarchy ...
Set assets<ITaxable> = new HashSet<ITaxable>(); assets.add(new Shop(...)); assets.add(new Car(...)); //... double totalAssetValue = 0.0; for (ITaxable asset : assets) { totalAssetValue += asset.getTaxableValue(); } 2017/2/24

74 接口应用举例(1) 2017/2/24 public interface Runner { public void start();
public void run(); public void stop(); } public class Person implements Runner { public void start() { // 准备工作:弯腰、蹬腿、咬牙、瞪眼 public void run() { // 摆动手臂 // 维持直线方向 public void stop() { // 减速直至停止、喝水。 2017/2/24

75 <<interface>>
接口应用举例(2) <<interface>> Runner +start() +run() +stop() Person +start() +run() +stop() +dance() Car +start() +run() +stop() +fillFuel() +crack() Bird +start() +run() +stop() +fly() 2017/2/24

76 接口应用举例(3) 2017/2/24 interface Runner { public void run();}
interface Swimmer {public double swim();} class Animal {public int eat();} //非法,为什么? class Person extends Animal implements Runner,Swimmer { public void run() {……} public double swim() {……} public int eat() {……} } public class Test{ public static void main(String args[]){ Test t = new Test(); Person p = new Person(); t.m1(p); t.m2(p); t.m3(p); public String m1(Runner f) { f.run(); } public void m2(Swimmer s) {s.swim();} public void m3(Animal a) {a.eat();} 2017/2/24

77 接口的其它用途 产生常量群(所有接口中的数据成员自动成为static 和final) 2017/2/24

78 将interface内的数据成员初始化 在类首次被装载时,必须保证被初始化 2017/2/24

79 接口用法总结 通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。 通过接口可以指明多个类需要实现的方法。
通过接口可以了解对象的交互界面,而不需了解对象所对应的类。 2017/2/24

80 7.内部类

81 public interface Contents {   int value();  } public interface Destination {   String readLabel();  } public class Goods {  private class Content implements Contents {   private int i = 11;   public int value() {    return i;   }  } protected class GDestination implements Destination {   private String label;   private GDestination(String whereTo) {    label = whereTo;   }   public String readLabel() {    return label;  } } public Destination dest(String s) {   return new GDestination(s);   }  public Contents cont() {   return new Content();   }  } class TestGoods {   public static void main(String[] args) {   Goods p = new Goods();   Contents c = p.cont();   Destination d = p.dest("Beijing");   }  } 内部类的第一个好处:隐藏你不想让别人知道的操作,也即封装性。 在外部类作用范围之外得到内部类对象的第一个方法,那就是利用其外部类的方法创建并返回。 别的方法,其语法格式如下: outerObject=new outerClass(Constructor Parameters); outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters); Content代表着Goods的内容,而GDestination代表着Goods的目的地。它们分别实现了两个接口Content和Destination。在后面的main方法里,直接用 Contents c和Destination d进行操作,你甚至连这两个内部类的名字都没有看见! 2017/2/24

82 内部类 在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类 内部类和外层封装它的类之间存在逻辑上的所属关系
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。Inner class的名字不能与包含它的类名相同; Inner class可以使用包含它的类的静态和实例成员变量,也可以使用它所在方法的局部变量; 2017/2/24

83 Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象
public class Goods {  private valueRate=2;   private class Content implements Contents {   private int i = 11*valueRate;   public int value() {    return i;   }   }   protected class GDestination implements Destination {   private String label;   private GDestination(String whereTo) {    label = whereTo;   }   public String readLabel() {    return label;   }   }   public Destination dest(String s) {   return new GDestination(s);   }   public Contents cont() {   return new Content();   }   } Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象 在这里我们给Goods类增加了一个private成员变量valueRate,意义是货物的价值系数,在内部类Content的方法value()计算价值时把它乘上。我们发现,value()可以访问valueRate,这也是内部类的第二个好处——一个内部类对象可以访问创建它的外部类对象的内容,甚至包括私有变量!这是一个非常有用的特性,为我们在设计时提供了更多的思路和捷径。要想实现这个功能,内部类对象就必须有指向外部类对象的引用。Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。 2017/2/24

84 内部类举例 堆内存 栈内存 处内存状态 2017/2/24 public class A { private int s;
public class B { public void mb() { System.out.println(s); } public class Test { public static void main(String[] args) { A a = new A(); // 创建一个依赖于a而存在的b A.B b = a.new B(); b.mb(); 堆内存 栈内存 B类对象 A.this mb this b s main a A类对象 处内存状态 2017/2/24

85 内部类举例 2017/2/24 public class A{ private int s = 111; public class B {
public void mb(int s) { System.out.println(s); // 局部变量s System.out.println(this.s); // 内部类对象的属性s System.out.println(A.this.s); // 外层类对象属性s } public static void main(String args[]){ A a = new A(); A.B b = a.new B(); b.mb(333); 2017/2/24

86 2017/2/24 //: innerclasses/Parcel5.java
// Nesting a class within a scope. public class Parcel6 { private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); // Can't use it here! Out of scope: //! TrackingSlip ts = new TrackingSlip("x"); public void track() { internalTracking(true); } public static void main(String[] args) { Parcel6 p = new Parcel6(); p.track(); } ///:~ //: innerclasses/Parcel5.java // Nesting a class within a method. public class Parcel5 { public Destination destination(String s) { class PDestination implements Destination { private String label; private PDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } return new PDestination(s); public static void main(String[] args) { Parcel5 p = new Parcel5(); Destination d = p.destination("Tasmania"); } ///:~ 2017/2/24

87 2017/2/24 //: innerclasses/Parcel7.java
// Returning an instance of an anonymous inner class. public class Parcel7 { public Contents contents() { return new Contents() { // Insert a class definition private int i = 11; public int value() { return i; } }; // Semicolon required in this case } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); } ///:~ 2017/2/24

88 内部类特性 Inner class可以声明为抽象类,因此可以被其它的内部类继承,也可以声明为final;
和外层类不同,Inner class可以声明为private或protected; Inner class 可以声明为static的,但此时就不能再使用外层封装类的非static的成员变量; 非static的内部类中的成员不能声明为static的,只有在顶层类或static的内部类中才可声明static成员 2017/2/24

89 小结 什么是Java类 初始化和清理 隐藏实现细节 重复运用类 多态 接口 内部类 2017/2/24


Download ppt "第三讲 Java面向对象要素."

Similar presentations


Ads by Google