Java1-10_pku

  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Java1-10_pku as PDF for free.

More details

  • Words: 26,051
  • Pages: 572
1

第一章

Java 的类

北京大 学计算机系 代亚 非

2

• • • • • • • • • •

第一章 Java 的类 1.1 有关面向对象的 概念 1.2 Java 语言的 特点 1.3 Java 的开发 环境 1.4 第一个 Java 程序 1.5 Java 类型系 统 1.6 Java 中字符 串的类 1.7 Java 中的类 、方法和变 量 1.8 Java 名空间 及访问规则 1.9 Java 中的抽 象类、接口 和程序包 1.10 对象的构造 函数

3

1.1 有关面 向对象的概 念 • 什么是类? • 类是描述对象的 “基本原型 ”,它定义 一 种对象所能拥有 的数据和能 完成的操作 , 在面向对象的程 序设计中 , 类是程序的 基 • 本单元。 程序中的对 variables 象 是类 的一个实例, 是一个软件单 元,它由一组 methods 结构化的数据 和在其上的一

4

• • • • • • • •

1.1 有关面 向对象的概 念 变量:即指对象 的所知道的 状态 方法:指对象的 功能单元。 什么是消息? 软件对象通过相 互间传递消 息来相互作 用 和通信 message 一个消息由三部 分组成 : 1. 接受消 息的对象 对象 B 2. 要完成 方法的名字 3. 方法需 要的参数 对象 A

5

1.1 有关面 向对象的概 念 • 一个例子 class Hello {

}

private String s ; public void showString ( ) { System.out.println(s);} public void changeString(String str) { s=str; return s;}

• 在程序中操作对 象是类的一 个实例 : 即对 象 • 创建一个对象 Hello obj=new Hello(); • 调用方法 obj.showString(“Hello World!”)

6

1.1 有关面 向对象的概 念 • 什么是封装 ? • 封装把对象的所 有组成部分 组合在一起 , 封装定义程序如 何引用对象 的数据,封 装 实际 上使用 方法将类 公有数据 的数据 隐藏起来 数据 私有 数 ,控制 用户对类 据 的修改 和访问数 方 据的程 度。 方 法 对象 A



对象 B

7

1.1 有关面 向对象的概 念 • 什么是子类 ? • 子类是作为另一 个类的扩充 或修正所定 义 的一个类 . bicycle (super class)

sub class

Mountain bikes

Racing bikes

Tandem bikes

8

1.1 有关面 向对象的概 念 • 什么是继承 ? • 继承是子类利用 父类中定义 的方法和 变量 就像它们属于子 类本身一样 . class Car { int color_number; int door_number; int speed;

class Trash_Car extends Car { double amount; fill_trash() { … }

push_break() { … } add_oil() { … } }

}

9

1.1 有关面 向对象的概 念 • 什么是方法的覆 盖 ? • 在子类中重新定 义父类中已 有的方法。 class Car { int color_number; int door_number; int speed; push_break() { speed=0; } add_oil() { … } }

class Trash_Car extends Car { double amount; fill_trash() { … } push_break() { speed=speed-10; } }

10

1.1 有关面 向对象的概 念 • 什么是 方法的重载 ( 多态性 ) ? • 在同一 个类中至少 有两个方 法用同一个 名 字,但 有不同的参 数。 123



1.23

void show(int int_num) void show(double double_num)

123,1.23

void show(nit nit_ um, float float_um)

11

1.2 Java 语言的 特点 • Java 是什么? Java= “C++”--“ 复杂 性和奇 异性 ” +“ 安全 性和 可移 植性”

• • • • •

Java 由那些特性 ? (1) 面向对象 封装性、多态性 、继承性、 动态链接 (2) 操作平 台无关性 严格的语言定义 :“ 没有依 据机器的不 同 而不同” 或“由 编译器决定” 等字眼 , 最后的目标码都 是一致的 , 初值都 是确定

12

1.2 Java 语言的 特点 • 两种工作模式的 比较

源 程 序

Java 编译器

Java 解释器

Powerpc

Powerpc

pentium

中 间 码

pentium

SPARC

二进制

编译器

Powerpc 源 程 序

pentium

二进制

Powerpc

pentium

SPARC SPARC

SPARC

13

1.2 Java 语言的 特点 • . 编译 , 没有绝对地 址

Java 源程序 (.java 文件 ) Java 编译器

调入包含 , 继承 所 用道的所 有类 , 内 存分配确 定 , 编程 真正可执 行的 机器 码

Bytecode 载入器 字节码 校验器 Bytecode 解释器

Java Bytecode (.class 文件 ) 系统执行平 台

不存 在伪 造的指 针 不违 反访 问权限 不非 法访 问对象 不导 致操 作栈溢 出

14

1.2 Java 语言的 特点 • Java 虚拟机

不同的 操作系 统有 不同的 虚 Java Just-in-time 拟机 . interpreter compiler 它类似 一个小 巧而 高效的 CPU. byte-code 代码是 与平台 无关 的 是虚拟 机的机 器指 令 . Runtime System Java 字节代 码运行 的两 种方式 : interpreter( 解释方 式 ) Operating System Just-in-time( 即时 编译 ): 有代 码 生成器 将字节 代码 转换成 本机 的机器 代码 , 然后可 以以 较高速 Hardware 度执行 .

15

1.2 Java 语言的 特点 • (3) 安全问 题 • Java 是在网络环 境下使用的 语言 • 一个安全的网络 至少要防止 以下几种破 坏 的可能性 : • 毁灭系统资源 • 消耗系统资源 • 挖掘系统或个人 机密 • 骚扰正常工作的 进行

16 public protecte d private final 语言定 义

取消指 针 自动回 收 垃圾

1.2 Java 语言的 特点

限定对 象的 存取权 限 系统堆 栈溢出 参数类 型一致

Java 源程序 (.java 文件 )

Bytecode 载入器

Java 编译器

Bytecode 解释器

Java Bytecode (.class 文件 )

Bytecode 检查

程序执 行系统 系统执行平台 WWW 浏览器 限制 Java 小应用 程序 的不正 当使用

17

1.2 Java 语言的 特点 • (4) 多线程 • Java 提供 现成 的类 Thread ,只要 继承这 个类 就 可以编 写多线 程的 程序。 • (5) Java 与 C 及 C++ 的区 别 • 不再有 #include 和 #define 等于处 理功能 • 不再有 structure,union 及 typedef • 不再有 函数、 不再 有指针 、不 再有多 重继 承 • 不再有 goto • 不再有 操作符 重载 (Operatior Overloading) • 取消自 动类型 转换 , 要求强 制转换

18

1.3 Java 的开发 环境 • Java 开发环境 JDK • JDK 中有 Java 编译器和 bytecode 解释器 Applwtviewer 是小应用程序的 bytecode 解 释器 • JDK 的安装 • 直接执行 JDK.exe

19

1.3 Java 的开发 环境 产生如 下目 录结构

java\bin 的目录 下包含义下 主要文件 : javac: Java 编译器 , 用来将 java 程序编译成 Bytecode java: Java 编译器 , 执行已经转换成 Bytecode 的 java 应用程序 . jdb: Java 调试器 , 用来调试 java 程序 javap: 反编译 , 将类文件还原回 方法和变量 . javadoc :文档 生成器 , 创建 HTML 文件

20

1.3 Java 的开发 环境 • \java\lib 子目录 • 库函数 , 其中 classese.zip 不要解开 . • 在 autoexec.bat 中加入以下两条 命令 1. PATH c:\…\java\bin 2. SET CLASSPATH=. ; c:\...\lib\classes.zip; c:\myclasses;

21

1.3 Java 的开发 环境 C:\

java bin javac

lib classes.zip appletviewer

java

用户类 文件 所在目 录 myclasses

当前目 录

22

1.3 Java 的开发 环境 • • • • • • •

vj1.1++ 的使用 1. 进入 vj, 创建一个 project 2. 加入 java 文件 3. 编辑 java 文件 4. 编译 java 文件 5. Build 一个 project 文件 6. 运行 java 程序 -- 如果是 小应用程序 , 则直接在 VJ 环境 下运行

23

1.4 你的第一个 Java 程序 • Java 应用和 Java 小应用程序 class HelloWorldApp { public static void main(String args[]) { System.out.println(“Hi, ”+args[0]); } }

• 编辑存盘:主类 名 ( 含有 main 方法 ) 和 文件名 要一致性 HelloWorldApp.java • 编译程序 : javac HelloWorldApp.java • 运行程序 : java HelloWorldApp “myjava” • 运行结果 : Hi, myjava

24

1.4 你的第一个 Java 程序 public class HelloWorldApp { public static void main(String args[]) { System.out.println(“Hi, ”+args[0]); }}

1. 声明一 个类 : public class HelloWorldApp{} , 类名的第一 个字母要 大写 2. 一个类 中可有很多 方法 , main 方法是运 行程序的第一个 方法,方法 名的第一个 字母要小写 . 3. System.out.println 是向屏幕输出 , 相当于

25

1.4 你的第一个 Java 程序 public class HelloWorldApp { public static void main(String args[]) { System.out.println(“Hi, ”+args[0]); }}

4. 命令行参数 的获取 c:>java HelloWorldApp myjava! 6. 字符串的拼 接

26

1.4 你的第一个 Java 程序 Java 小应用程序不 能直接执 行和使用 , 必须 要在浏 览器中执 行 import java.applet.Applet; import java.awt.*; public class HelloApplet extends Applet { public void paint(Graphics g) { g.drawString(“Hello World”,0,50); } }

编辑 存盘: 主类 名 (extends Applet) 和文 件名一 致 编译 代码 : javac HelloApplet.java 编写 HTML 文件 : HelloApplet.html

27

1.4 你的第一个 Java 程序

•运行 applet 程序 : 1. appletviwer HelloApplet.html 2. 在浏览器中运行 • 运行结 果 : Hello World!

28

1.4 你的第 一个 Java 程序 import java.applet.Applet; import java.awt.*; public class HelloApplet extends Applet { public void paint(Graphics g) { g.drawString(“Hello World”,0,50); } }

• 理解程序 1. import 语句相 当于 C 语言中的 include 每 2. 每一个 applet 都是 java.applet.Applet 的子 类 用 extends 继承 3. applet 中没有 main 方法

29

1.4 你的第一个 Java 程序 import java.applet.Applet; import java.awt.*; public class HelloApplet extends Applet { public void paint(Graphics g) { g.drawString(“Hello World”,0,50); } } 4. 当 applet 被浏 览器运 行时 ,paint 语句自 动执 行 5. 在 applet 中与 屏幕输 出有 关的操 作通 过 Graphics 对象 来实现 6. 一个 Java 源文件内 最多 只能有 一个 public 类 , 称为主 类,且 必须 和文件 名同 名

30

1.5 Java 类型系统 Java type system

primitive char

numeric

integral

byte

short

reference

boolean class interface array floating-point

int

long

null

float

double

31

1.5 Java 类型系统 • byte 8 位 (byte) 0 • short 16 位 (short) 0 • int 32 位 0 • long 64 位 0L • float 32 位 准 0.0f

2 的补码 2 的补码 2 的补码 2 的补码 IEEE 754 浮点数标

32

1.5 Java 类型系统 • 程序 : 保留字 , 标识符 ( 类名、域名 、方法 名、符号常量、 局部变量, 标号、… ) , 操作符、界定符 ,字面常量 。 • 声明一个变量 • int data=10; Boolean isAlived; • Java 中的操作符 : 与 C 基本一致 • Java 中不再有指 针了

33









1.5 Java 类型系统 在 Java 中”引用“是指 向一个对象 在内 存中的位置 , 在本质上是 一种带有很 强的 完整性和安全性 的限制的指 针 . 当你声明某个类 , 接口或 数组类型的 一个 变量时 , 那个变量的值总 是某个对象 的引 用或者是 null 引用 . 指针就是简单的 地址而已 , 引用除了 表示 地址而外 , 还象被引用的数 据对象的 缩影 , 还提供其他信息 . 指针可以有 ++,-- 运算 , 引用不可以运算 .

34

1.5 Java 类型系统 例 : String s1;String s2; s1=“a string”; s2=s1; class Mydate { int year; int month; int day; }

s1

10001

s2

10001

a string

0 today 10008

Mydate today=new MyDate()

0 0

35

1.5 Java 类型系统 • 能够动 态取得的内 存有两种 , 一种是 对象 实体 , 一种数 组 . • Java 用 new 申请内存 , 不需要 自己释放不 用的内 存 . • 在 Java 中,定 义数组时 不必给出数 组的 大小, 因为 Java 的数组 是动态分配 的。 • 数组变 量实际上是 一个引用 变量,因此 要 实例化 才能使用。 • 例: int Array[] ; Array=new int[8] ;

36

1.5 Java 类型系统 • 例: String arrayOfString[]=new String[10] ;每个数组 元素包含一 个对字 符串对象的引用 ,此时值分 配了包含字 符 串引用的空间, 并没有对字 符串本身分 配 内存,此时不能 访问。 arrayOfString[0] 对 arrayOfString arrayOfString[1] 字 符 对数 组的 引用 arrayOfString[8] 串 arrayOfString[9] 的 引 for(int i=0;i<arrayString.length;i++) 用

arrayOfString[i]=new String(“Hello”+i)

37

• • • • • • • • • •

1.6 Java 中的字 符串

Java 中的 字符 串类 String 和 StringBuffer String 类的 定义和 实例 的生成 例 : String str; str=“this is a string” 或 : String str1=new String(“hello”); 或 : String str2=“aaaa”; String 类与 其它类 型的 转换 例 1: 将实型数转换成字符串 . System.out.println(String.valueOf(Math.PI));

38

1.6 Java 中的字 符串 • • • • • • • •

有两个字符串 str1,str2 str1=str2; 是指 两个引用指 向同一个地 址 str1.equals(str2) 是指两个字符串 内容相等 获取 String 对象的信息 例如: String s=“this is a string”; int len=s.length(); boolean result=s.equal(“this is a 特 test”);

39

1.6 Java 中的字 符串 • StringBuffer 的字符 串的内容是 可以改变 的 • 创建 StringBuffer 对象 • StringBuffer str=new Stringbuffer(“hello”); • 获取 StringBuffer 对象的信 息 • 例int len=str.length(); :StringBuffer sb=new StringBuffer(“Drink Java!”); sb.insert(6,”Hot ”); System.out.println(sb.toString()); Drink Hot Java!

System.out.println 接受的是 String

40

1.7 Java 中的类、方 法和变量 • 试用一 个简单的 类 class CarDemo { public static void main(String args[]) { Car Democar=new Car(); DemoCar.set_number(3838); class Car DemoCar.show_number(); { int car_number; }} void set_number(int car_num) { car_number=car_num;} My car number is 3838

void show_number() { System.out.println (“My car No. is :”+car_number); } }

41 class TrashCar extends Car { int capacity; 1.7 Java 中的类、方 法和变量 void set_capacity(int trash_car_capacity) • 类的继承 { capacity=trash_car_capacity;}

关系

void show_capacity(); { System.out.println(“My capacity is: ”+ capacity);}}

class Car { int car_number; void set_number(int car_num) { car_number=car_num;}

void show_number() {System.out.println (“My car No. is :”+car_number); } }

42

1.7 Java 中的类、方 法和变量 • TrashCar 中继承了 Car 中的两 个方法 class CarDemo { public static void main(String args[]) { TrashCar DemoTrashCar=new TrashCar(); DemoTrashCar.set_number(4949); DemoTrashCar.set_capacity(20); DemoTrashCar.show_number(); DemoTrashCar.show_capacity(); } }

Car 是父类 , TrashCar 是子类

43

1.7 Java 中的类、方 法和变量 • 何时选择继承性 ? • 一个很好的经验 : “B 是一个 A 吗 ?” • 如果是则让 B 做 A 的子类 . 常犯的 错误 A 有一个 B 吗 ? 例如 让汽车 轮子成为 汽车的子类 是错 误的

44

1.7 Java 中的类、方 法和变量 • 类的严格定义及 修饰字public, abstract, final 或者没 有 [ 类的修饰 字 ] class 类名 称 [extends 父类 名称 ] { …. }

•方法和变量的定 义及修饰字 public 、 protected 、 private

[ 变量 修饰 字 ] 变量数 据类型 变 量名 1, 变量名 2[= 变量初值 ]…;

[ 方法修饰 字 ] 方法的 返回值 类型 { …// 方法的内 容 }

方法名 称 ( 参数 1, 参数 2,…

45

1.8 Java 名空间 及访问规则 • 每个类都创造了 有自己的名 字空间,指 方法 和变量可以知道 彼此的存在 ,可以使用 。 • abstract 类不能直接产生 属于这个类 的对象 • final 类不能被其他 任何类所 继承 ( 安全的 考 虑) • public 类不但 可以被同一 程序包中的其 它类 使用 , 别的程 序包中的类 也可以使 用 . • friendly( 缺省 ) 类只能被 本包中的 其它类使 用

46

1.8 Java 名空间 及访问规则 • 变量和方法的修 饰字 public 、 protected 、 private

• public: 任何其它类 、对象只 要可以看到 这个类的话,那 么它就可以 存取变量的 数 据,或使用方法 。 class ABC { public int pub_i=5 ; public void show)() { System.out.println (“pub_i”+pub_i); }

47

1.8 Java 名空间 及访问规则 class Demo { public static void main(String args[]) { ABC abc=new ABC(); System.out.println(“abc.pub_i”+abc.pub_i); abc.pub_i=10; abc.show(); } abc.pub_i=5 } pub_i=10;

48

1.8 Java 名空间及 访问规则 • protected 变量和方法 • 如果一个类中变 量或方法有 修饰字 protected ,同一类, 同一包可以 使用。不 同包的类要使用 ,必须是该 类的子类可 以 存取变量或调用 public class ABC { protected int pro_i=5; protected void show() { System.out.println (“pro_i=” +pro_i;) } }

49

1.8 Java 名空间及 访问规则 • 不同中的类不能 使用 class DEF { public static void main(String args[]) { ABC abc=new ABC(); System.out.println (“abc.pro_i=“+abc.pro_i); abc.pub_i=10; abc.show(); } }

50

1.8 Java 名空间及 访问规则 • 不同包但是是子 类 import mytest.pack.ABC; class DEF extends ABC { public static void main(String agrs[]) { DEF def=new DEF(); System.out.println(def.i); def.i=10; def.show(); } }

pro_i=5 pro_i=10

51

1.8 Java 名空间 及访问规则 • • • •

private 不允许任何其他 类存取和调 用 friendly( 前边没有修 饰字的情况 ) 在同一程序包中 出现的类才 可以直接使 用 它的数据和方法 .

52

1.8 Java 名空间 及访问规则 • 当子类中的变量 名与父类的 相同 , 原来的 变量被遮盖 . class demo { public static void main(String args[]) { C c=new C(); c.println_out(); }} class A { int data_a=3;} data_a=5 class B extends A { int data_a=5;} A.data_a=3 class C extends B B.data_a=5 { void print_out() { System.out.println(“data_a= ”+data_a); System.out.println(“A.data_a= ”+A.data_a); System.out.println(“B.data_a= ”+B.data_a); }}

53

1.8 Java 名空间 及访问规则 • 方法的覆盖 (overriding) 和重载 (overloading) • 子孙类中定义的 方法和祖先 类中某个方 法 同名 , 同参数 行 , 则祖先 类中的该 方法被 覆盖 . • 方法的重载是指void 一个对象的 多态性 , 即多 show(int int_num) 123 个方法用相同的 名称 , 但参数行不同 . 类

1.23

void show(double double_num)

123,1.23

void show(nit nit_ um, float float_um)

54

• • • • • •

1.8 Java 名空间 及访问规则 到此为止 -----final final 在方法之前 , 防止该方 法被覆盖 . final 在类之前 , 标是该类不 能被继承 final 在变量之前 , 定义一个 常量 . b 属于类的变量和 方法 ----static object char data static 在变量或方法之 前 , b static int object c 表明它 们是属于类 的 object ,char data share_data class ABCD { char data; static int_share_data; } class Demo { ABCD a,b,c,d}

char data

Object b char data

55

1.8 Java 名空间 及访问规则 • 静态变量在各实 例间共享 • 如果是 public 静态变 量 , 则其它 类可以不 通过实例化访问 它们 • 静态方法称为类 的方法 , 因此不用实 例化 即可调用 ( 面向过 程 ) • 一个对象的方法 可以访问对 象的数据成 员 , 尽管 不属于方法 的局部变量 • 一个类的方法只 能访问自己 的局部变量

56

• • • • • • • •

1.8 Java 名空间 及访问规则 例 : 不正确的引 用 class StaticError { String mystring=“hello”; public static void main(String args[]) { System.out.println(mystring);} } 错误信息 :can’t make a static reference to nonstatic variable. 为什么不正确 ? 只有对 象的方法可 以访问 对象的变量 .

57

1.8 Java 名空间 及访问规则 • • • • • • •

解决的办法 : 1. 将变量 改称类变量 class StaticError { static String mystring=“hello”; public static void main(String args[]) { System.out.println(mystring);} }

58

1.8 Java 名空间 及访问规则 • • • • • • • •

2. 先创建 一个类的实 例 class NoStaticError { public static void main(String args[]) { String mystring=“hello”; System.out.println(mystring);} } }

59

1.9 Java 中的抽象类、 接口和程 序包 • 抽象类是指不能 直接被实例 化的类 , 因此 一般作为其它类 的超类 , 与 final 类正好相 反 • 抽象类中的抽象 的方法 - 在该类中定义但 不在该类中提供 实现 , 由继承类提供细 节 • public abstract class SomeAbstractClass • { void method1() • { System.out.println(“Hi, Java”);} • void method2(); • }

60

1.9 Java 中的抽象类 、接口和程 序包

// 已有两个 类 Circ le 和 Recta ngle , 完成相 关参 数的计 算 cla ss Ci rcle { p ublic int r; Circle( int r) {t his.r= r} / /this 指 " 这个 对象的 " public int area( ) {ret urn 3*r*r ; } // 取 近似 } cla ss Re ctan ge { p ublic int width ,heig ht; // 这里不 需 "this " Rectang le ( int w , int h)

61

1.9 Java 中的抽象类、 接口和程 序包 • 假设有 若干个 Circle, 以及 若干 个 Rectangle, 希望 计算它 们的总 面积 , 直截了 当的做 法是 将它们 分 别放到 两个数 组中 , 用两个 循环 , 加上一 个加 法 , 这种做 法是不 漂亮 的 • 如果还 有其它 形状 ,triangle,ellipses 等 , 上述 方法 显得“ 累赘” , 我们 希望有 一种 统一的 表示 , 例 如用一 个数组 shape[], 接受 所有 的形状 , 然后用

• for (i=0; i<sha pe.le ngth; i++) { • ar ea_to tal+= shape [i].a rea() ;

62

abstract 1.9class JavaShape 中的抽象、接口 和程序包 { abstract float area();} class Circle extends Shape { public int r; Circle(int r) {this.r=r;} public float area() { return 3.14*r*r; } } class Square extends Shape {public int width,height; Rectangle (int w, int h){ width=w,height=h;} public float area() {return width*height; }

63

1.9 Java 中的抽象类 、接口和程 序包 • 利用接 口可实 现多 重 继承 ( 可同时 实现多 个接口 ) • 接口的 作用和 抽象 类 类似 , 指定原 型 , 不 直 接定义 方法 的内容 . • 关键字 implement 用 来实现 方法 , 即在使 用 时要用 给出方 法的 实

interface Stack { void push(object x); object pop();} class A extends Applet implements Stack { void push(object x) { …;// 具体内 容 } object pop() { …;// 具体内 容 } }

64

1.9 Java 中的抽象类 、接口和程 序包 • 程序包: 相当于 其它语言中 的库函数 • 打包 package Graphics; class Square {…;} class Circle {…;} class Triangle {…;}

• 使用程序包中的类要用 import 命令 . 表示路 径 ,* 表示使用包中的所有类 java • import java.io.FileInputStream ; io • import java.io.* ;

FileInputStream

65

1.10 对象的构造函数

class Demo1 // 数组的 配置 •{ public 使用 new static来做内存配置 void main(String args[]) { int array[][]; array=new int[5][5]; array[1][4]=5; } class myClass // 对象实 体的 产生 } { int data=5;} class Demo2 { public static void main(String args[]) { myClass obj;// 建立一 个引用 obj=new myClass(); System.out.println(obj.data); }}

66

1.10 对象的构造函数 • Constructor 和 finalizer • constructor( 构造函数 ), 在一个类中和类同名 的方法叫构造函数 . 系统在产生对象时会自 动执行 . class UsePoint

class Point { Point point_A=new Point(); { int x,y; Point point_B=new Point(5,7); Point() } { x =0;y=0;} Point(int new_x,int new_y) { x=new_x,y=new_y;} }

67

1.10 对象的构造 函数 • 构造函数应包含 哪些内容 ? • 构造函数多半定 义一些初值 或内存配置 工 作 • 一个类可以有多 个构造函数 ( 重载 ), 根据 参数的不同决定 执行哪一个 . • 如果程序中没有 定义构造函 数 , 则创造 实 例时使用的是缺 省函数 , 它是一个无 内容 的空函数 .

68

1.10 对象的构造函数 • this • this 指自己这个对象 • this 的作用是要将自己这个对象当作参数 , 传 送给别的对象中的方法 . class ThisClass { public static void main() { Bank bank=new Bank(); bank.someMethod(this); } }

class Circle { int r; Circle(int r) {this.r=r;} public area() { return r*r*3;} }

69

• • • • • • • •

1.10 对象的构造 函数 super 指这个对象的父类 super 用来引用父类中的方法及变量数据 . public class apple extends fruits { public apple(int price) { super(price); } } 以上句子表示使用超类的构造函数生成实例 super 必须是子类构造器的第一条语句

70

小结 • 一个 Java 程序 ( 包括小应用 程序 ) 由若干 个类组成 , 它们分布在由 CLASSPATH 指 定的一个目录中 • 一个 Java 程序中只能由一 个主类 , 并且与 文件同名 . • CLASSPATH 指定多 个目录 , 它们和起来 被看成是这个目 录的根 , 根中的类构 成缺 省包 • 目录树中每个子 目录中的类 的集合对应 Java 一个包 (package), 子目录的层 次与包

71

小结 C:\

package myclass.calculate class A{} class B{} classC{}

java lib bin javac

classes.zip appletviewer

java 让 PATH 包含 … \java\bin SET CLASSPATH =.; C:\...\myclasses

javasource

myjava.java

myclass calculate A

B

C

import java.io.*; import java.awt.Graphics; import mycalss.calculate.*; public class test {}

72

小结 • 名空间及访问规 则 • --package,class/object,member, 局部变量 • --public ,private,protected,default 成员的可 视性描 述 public protected 缺省 private 同一 类中 同一 包中 不同包 的 子类中 非子类 也不同 包

yes yes

yes yes

yes yes

yes no

yes

yes*

no

no

yes

no

no

no

73

小结 • • • •

对象模型 ---Java 的类 和对象 ( 一般 , 抽象 , 接口 ) -- 继承机 制 ( 单继承 , 多实现 ) static, import, final 的含义 和作用

74

小结 • 避免在类中使用 太多的基本 类型 • • • • • • • • • •

private String senderName; private String senderStreet; private String senderCity; private String senderState; private String senderZip; private String receiverName; private String receiverStreet; private String receiverCity; private String receiverState; private String receiverZip;

address sender address receiver

75

第 2 章 Java 小应用 北京大 学计算机系 代亚 非

76

第 2 章 Java 小应用 • • • • • • • •

2.1 所有小应用程序 的根源 2.2 小试身手 2.3 图形操作 2.4 URL 类 2.5 载入现有图像文 件 2.6 动画效果 2.7 播放声 音 2.8 小 结

77

2.1 所有小应 用程序 的根源 • 2.1.1 小应用 的特点 • 回忆一下小应用 程序的书写 格式 import java.applet.*; public class MyApplet extends Applet { ;} • applet 都继承自 java.applet.Applet 类 , 由 Sun 公司事先定义好 了 . • 每个小应用都有 一个主程序 类 , 之前必 须 加上 public.

78

2.1 所有小应用程序的根源 http://someLocation/file.html 1. Browser loads URL <Applet code= ….> Applet class

2. Browser loads HTML document 3. Browser loads applet classes

Location: http://someLocation/file.html

Loading...

4. Browser run applet

79

2.1 所有小应 用程序 的根源 • Applet 的限 制 Browser

SERVER Applet 被下载的

SERVER

co

nn

ec ti

on

n n o c

n o i t ec

本地程 序

applet

file

local

与 applet 无关的 本地 方法

80

2.1 所有小应 用程序 的根源 • 2.1.2 applet 的生命周期 Java.applet.Applet public void init() public void start()

public void destroy() public void stop()

public void paint(Graphics g)

• paint() 虽不 在生命周期 内 , 但它的作用相 当于 applet 的灵魂

81

2.1 所有小应 用程序 的根源 • 一个 applet 的可视周期 init start 离开 web 页面 stop destroy

重新装 入或改 变页 面大小 或返回 Web 页面

82

2.1 所有小应 用程序 的根源 • 有关 paint() 方法 • Applet 本身 是一个容器 , 因此任 何输出都 必须用图形方法 paint() • 当小应用首次被 装载,以及 每次窗口放 大 、缩小、刷新时 都要调用 paint 方法 • paint() 是由 浏览器调用 的 , 而不是 由程序 调用,当程序希 望调用 paint 方法 时,用 repaint 命令 • paint 方法的参数 是 Graphics 类的对象 g ,它在 java.awt.Graphics 内

83

2.1 所有小应 用程序 的根源 AWT thread ( waiting ) repaint()

Exposure update () { clear arae call paint ()

paint ()

84

2.2 小试身手 • 2.2.1 起始页 上的时间和 日期 0Mon Dec 07 14:23:50 GMT+08:00 1998

• 介绍两个类 : 1. 类名 :Date 创建一 个实例 Date timeNow=new Date(); 2. 类名 Font 创建一个实 例 Font msgFont=new Font(“TimesRoman”,Font.ITALIC,30);

85

2.2 小试身手 看下 面的例 子 , 想一想 生命周 期的 四个方 法哪 去了 ? import java.awt.*; import java.util.Date; public class showDate extends java.applet.Applet { Date timeNow=new Date(); Font msgFont=new Font(“TimesRoman”,Font.ITALIC,30); public void paint(Graphics g) { g.setFont(msgFont); g.setColor(Color.blue); g.darwString(timeNow.toString(),5,50); }

86

2.2 小试身手 2.2.2 在起始 页中加入 applet • html 中有关的代码 <APPLET CODE=“showdate.class” width=600 height=80> • CODEBASE 的作用 当 class 文件与起始页文 件不在同一 个目 录下时 , 使用 CODEBASE 说明 <APPLET CODE=“showdate.class” width=600 height=80> CODEBASE=“\myjava\class”

87

2.2 小试身手 <APPLET <APPLET CODE=“showdate.class” CODE=“showdate.class” width=600 height=80> height=80> width=600 CODEBASE=“\myjava\class” public public Index.html Index.html

C:\C:\

myjava myjava javacode class javacode class

showdate

showdate

88

2.2 小试身手 • ALIGN,HSPACE,VSPACE 其它 文字 hspace

Java applet vspace

其它 文字

<APPLET CODE=“showdate.class” width=600 height=80> vspace=100 hspace=100

89

2.2 小试身手 • 向 applet 传递参数的 两个步骤 1. 在起始 页中要有 标签 2. 在 applet 中要有 getParameter 方法 在起始 页中有 : <param name=rem value=“ 时间是 : ”> 在 applet 中有 : string title=getParameter(rem); 在显示 时间的命令 中加入 title:

90

2.2 小试身手 import java.awt.*; import java.util.Date; public class showDate extends java.applet.Applet { Date timeNow=new Date(); String title; Font msgFont=new Font(“TimesRoman”,Font.ITALIC,30); public void paint(Graphics g) { g.setFont(msgFont); g.setColor(Color.blue); g.darwString(title+ timeNow.toString(),5,50); }

public void init() {title=getParameter (“rem”); if (title==null) title=“”; }

91

2.2 小试身手 • 例 : 利用一个可 以显示运行 字符串的类 , 显 示自己的字符串 (htmlpara.html) <param name=MESSAGE value=”this is a test"> <param name=FONT value="BOLD"> <param name=POINT_SIZE value=20>

92

2.2 小试身手 public void init() { String paramete; parameter=getParameter("MESSAGE"); if (parameter!=null) message=parameter; parameter=getParameter("FONT"); if (parameter!=null) font_to_use=parameter; parameter=getParameter("POINT_SIZE"); if (parameter!=null) point_size=Integer.parseInt(parameter); }

93

2.3 图形处理 2.3.1 图形坐标系统 x 任何与 绘图有关的 操作 0 第一个 要用的是 java.awt.Graphics 类 Graphics 类的对象不是 y 由 new 产生的 , 而是由系 统或其 他方式直接 将生好的 Graphics 对象当 作方法 的参数 , 再交给程序设 计者去处 理 . 例如 : paint(Graphics g)

94

2.3 图形处理 • Graphics 的方法 paint(Graphics g) { g.clearRect(); g.copyArea(); g.drawAre() ; g.drawLine(); g.drawOval();g.drawRect(); g.drawPolygon(); g.fillArc(); g.fillOval(); g.fillPolygen(); g.fillRect(); g.getColor(); g.getFont() g.setFont(); g.setColor(); g.getFontMetrics() g.fillRoundRect() }

95

2.3 图形处理 2.3.2 字型和 颜色的设置 2.3.2.1 字型设置的 方法 Font font=new Font(“TimesRoman”,Font.ITALIC,24); g.setFont(font); • 在小应用程序中 显示输出的 方法 g.drawString(String, int x, int y); g.drawChars(char data[], int offset, int length, int x, int y);

96

2.3 图形处理 g.drawBytes(byte data[],int offset, int length, int x, int y); 例 :g.drawString(“This is a test”,5,10); • 获取字体的属性 Font font=g.getFont(); • Font 类中常用的 方法 GetFamily() getName() getSize() getStyle() isItalic() isPlain() isBold() toString()

97

2.3 图形处理 import java.awt.Graphics; import java.awt.Font; public class drawtext extends java.applet.Applet { Font fn=new Font("TimesRoman",Font.ITALIC,20); public void paint(Graphics g) { g.setFont(fn); g.drawString(”Font demo”,5,10); } } Font demo

98

2.3 图形处理 •获取更详细 的数据 请查阅 有关 FontMetrics 类的方 法 fontMetrics=getFontMetrics(font); •FontMetrics 中比较 重要的方法 有 : stringWidth, charWidth, getAscent, getDescent, getLeading, getHeigh

99

2.3 图形处理 2.3.2.2 颜色的调整 • Color 对象的使用 创造自 己的颜色 : Color mycolor=new Color(int red, int blue, int green);

• g.setColor(Color.yellow) • g.setColor(mycolor); • 例 : 随机产生颜 色 , 并画圆

100

2.3 图形处理 import java.awt.Graphics; import java.awt.Color; public class drawcircle extends java.applet.Applet { public void paint(Graphics g) { int red,green,blue,x; for (x=0;x<370;x+=30){ red=(int)Math.floor(Math.random()*256); green=(int)Math.floor(Math.random()*256); blue=(int)Math.floor(Math.random()*256); g.setColor(new Color(red,green,blue)); g.fillOval(x,0,30,30); }}}

101

2.4 URL 类 2.4.2 构造 URL 类 ( 全名 java.lang.URL) • 绝对 URL 的构造方法 : URL(String spec) 例 : URL url=new URL (http://www.hit.edu.cn/cv/index.html”) • 相对 URL 的构造方法 : 某绝对地址 : http://rainy.hit.edu.cn/test.html 在该目录下 有两个文件 mywork.html myfamily.html

102

2.4 URL 类 URL base=new URL(“http://rainy.hit.edu.cn”);

URL url1=new (base, “mywork.html”); URL url2=new (base, “mywork.html”); • 其他 URL 的构造方法 : URL url=new URL (“http”, “www.hit.edu.cn”,“/~dyf/test.html”);

103

2.4 URL 类 2.4.3 获取小 应用程序 HTML 页面 的 URL 和小应用程序本 身的 URL • • • •

URL html=getDocumentBase(); System.out.print(html); URL codebase=getCodeBase(); System.out.print(codebase); web page

html applet

浏览 器

服务器

104

2.4 URL 类 • 2.4.4 URL 异常 (MalformedURLException) 当创建 URL 时发生错误 , 系统会 产生异 常 try{ URL url=new URL(str); }catch(MalformedURLException( e) { DisplayErrorMessage();} • 2.4.5 URL 类的基本方法 String getProtocol(), String getHost(), ing getPort(), String getFile(),

105

2.4 URL 类 • 构造 URL 的实例 import java.net.URL; import java.net.MalformedURLException; public class Test { URL url1,url2,url3; void test() { try { url1= new URL(“file:/D:/image/example.gif”); url2= new URL(“http://www.hit.edu.cn/cv/”); url1= new URL(url2, “hit.gif”); }catch (MalformedURLException e); // 处理 例外 } }}

106

2.5 载入现有图像文件 Image 类 • java 支持 gif 和 jpg 两种格式的 图像 • 图像文件的 URL: URL picurl= new URL (“http://xxx.yyy.edu/Applet/img1.gif”); • 取一幅图像构成 图像对象 Image img1 = getImage(picurl); Image img2 = getImage(getCodeBase(), “img2.gif”);

107

2.5 载入现有图像文件 • 显示一幅图像 : g.drawImage(img1, x, y, this); g.drawImage(img1, x, y,Color.red, this); g.drawImage(image1, x, y,x2,y2,Color.red, this); 规定 尺寸

规定 背景

108

2.5 载入现有图像文件 • 完整的过程 不要忘记 AWT 包 定义 Image 对象了 吗?

在类中

指定图像的 URL 了 吗? 把图像取出来吧 .

在 init0 中

还记得画图像用什么方法和命令 吗?

在 paint0 中

109

2.5 载入现有图像文件 import java.applet.*;import java.awt.*; public class image extends Applet { Image img; public void init() { img=getImage(getCodeBase(),"img0001.gif");} public void paint(Graphics g) { int width=img.getWidth(this); int height=img.getHeight(this); g.drawRect(52,52,width+30,height+30); g.drawImage(img,57,57,width+20,height+20,this);}}

110

2.6 动态效果 --- 线程的应 用 2.4 动态效 果 --- 线程的应用 • 什么是线程 ? 线程是执行 中的程序中 的单个顺序控 开始 制流 . • Java 支持多线程 显示进 度

数学运 算

引出最 后结果

线程 1

线程 2

111

2.6 动态效果 --- 线程的应 用 • 静态的 情况 import java.applet.*; import java.awt.Graphics; public class maguee extends Applet { public void paint(Graphics g) { g.drawString("Hello, Java!",0,0); } }

112

2.6 动态效 果 --- 线程的应用 • 动态的 情况 ( 不是多 线程 ) public void init() { x=size().width; y=size().height/2; width=x; } public void paint(Graphics g) { while(true) { g.drawString("Hello, Java!",x,y); x-=10; if(x<0) x=width; } }

113

2.6 动态效果 --- 线程的应 用 • 实现一个线程 让 Applet 类去实现 Runable 接口 , 创建一个线程 类 改写方法 start, 在其中产生一个新的线程来 工作 改写 stop 方法 , 在其中编写结束线程的程 序代码 引入新的方法 , 将分给线程的工作写到 run 中

114

2.6 动态效果 --- 线程的应 用 第一步:实现 Runable 接口 public class xc extends java.applet.Applet implements Runnable { Thread smallthread=null; … } Thread 是一个类 , 只有是它的实例才能具有线 程的功能 主函数中要定义一个线程变量

115

2.6 动态效果 --- 线程的应 用 第二步 :改写 方法 start public void start () { if ( smallthread == null ) { smallthread= new Thread ( this ); smallthread.start(); // 从现 在开始 程序 由两个 线程 在执行 }} 第三步 :改写 stop 方法 public void stop () { smallthread.stop(); // 停止线 程 smallthread = null; // 释放线 程对象 }

116

2.6 动态效果 --- 线程的应 用 第四步 : 新的方法 run 将让线 程要做的事 放 run 中 public void run() { while (true) { repaint(); try {Thread.sleep(1000);} catch(InterruptedException e){} } }

117 import java.applet.*; import java.awt.Graphics; public class Applet用 2.6MovingCharacter 动态效果 ---extends 线程的应 implements Runnable { int x=200; Thread my_thread=null; //------------------------------------------------public void start() { my_thread=new Thread(this); my_thread.start(); } public void run() { while(true) { repaint(); try { Thread.sleep(100); } catch(InterruptedException e){} }}

118

2.6 动态效果 --- 线程的应 用 • .

public void stop() { my_thread.stop(); }

public void paint(Graphics g) { g.drawString("Hello, Java!",x,y); x-=10; if(x<0) x=200; }

119

2.6 动态效果 --- 线程的应 用 • 跳动的小球

up=false; x=x-10; if(x<0) x=width; if (up) y=y+10;else y=y-10; if (y<0) up=true; if (y>height) up=false; g.setColor(Color.red); g.fillOval(x,y,30,30);

120

2.6 动态效果 --- 线程的应 用 例 : 起始页上的小 时钟 一个必 须用到的类 ----Date 类 , 给出系统时 间 Date NowTime=new Date(); NowTime.getHours(), NowTime.getMinutes() 自己需 要写什么样 的类 ? (Hour*60*60+minute*60+second)/43200*2.0*PI Clock--把数字时间 成图形表示 (minute*60+second)/3600*2.0*PI second/60*2.0*PI

121

2.6 动态效果 --- 线程的应 用 主类 取时间

paint() {}

clock 类 clock(){} 初始化

Show(){} 换算 弧度

drawNiddle(){} 画图

122

class Clock 2.6 动态效果 --- 线程的应 用 {int hours,minutes,second,radius; Clock(int hrs,int min,int sec) { hours=hrs%12; minutes=min; second=sec; } void show(Graphics g, int x, int y,int redius) { int hrs_len=(int)(radius*0.5); int min_len=(int)(radius*0.7); int sec_len=(int)(radius*0.85); double theta; g.drawOval(x ,y, radius*2, radius*2);

123

theta=(double)(hours*60*60+minutes*60+second)/ 2.6 动态效果 --- 线程的应 用 43200.0*2.0*Math.PI; drawNiddle(g,Color.blue, x, y, hrs_len, theta); theta=(double)(minutes*60-second)/3600.0*2.0*Math.PI; drawNiddle(g,Color.blue, x, y, min_len,theta); theta=(double)second/60.0*2.0*Math.PI; drawNiddle(g,Color.red, x, y, sec_len, theta); }

124

2.6 动态效果 --- 线程的应 用 private void drawNiddle(Graphics g, Color c, int x, int y, int len, double theta) { g.setColor(c); g.drawLine(x,y,(int)(x+len*Math.sin(theta)), (int)(y-len*Math.cos(theta))); } }

125

2.6 动态效果java.util.Date; --- 线程的应 用 import java.awt.*;import public class ClockDemo extends java.applet.Applet { public void paint() { Date timeNow = new Date(); Clock myClock = new Clock(timeNow.getHours(), timeNow.getMinutes(), timeNow.getSeconds()); myClock.show(g,100,100,100); } }

126

2.6 动态效果 --- 线程的应 用 主类 生成时 间对象,取 时间 生成 Clock 对象,将时 间 传递给 Clock 对象

paint() {}

clock 类 clock(){}

Show(){}

drawNiddle(){}

初始 化

换算弧度

画图

127

2.6 动态效果 --- 线程的应 用 主类 start() 启动 新线 程

clock(){} 初始 化

stop()

paint()

run()

停止线 生成 clock 类实例 repaint() 程 clock 类 Show(){} 换算 弧度

drawNiddle(){} 画图

128

2.6 动态效果 --- 线程的应 用 例 : 在主 页上显 示 字 符串并 且颜 色从左 至右 不断 变化 让我们 来想 一想 : 需要那些 数据 成员 ? String msg, Font fnt, Color clr, spot_clr; Thread thread; String Msg="Welcome to HIT"; 需要哪 些方 法 ? init, start, stop, run, paint; public void init() { fnt= new Font("TimeRoman",Font.PLAIN,30); clr=new Color(255,0,0); spot_clr=new Color(0,0,255);

129

2.6 动态效果 --- 线程的应 用 run() 中做什 么 ? 反复调 用 repaint public void run() { while(true) { repaint(); try{Thread.sleep(50);} catch(InterruptedException e) {} } }

130

2.6 动态效果 --- 线程的应 用 paint() 中做 什么 ? 输出两 次字 符串 , 第一次 用一 种颜色 , 第二 次用另 一种颜 色 ( 该颜 色只作 用于 指定的 区域 )

You are Welcome to HIT g.clipRect(x,y,width,height) public void paint(Graphics g) { FontMetrics fntM=g.getFontMetrics(); int font_height=fntM.getHeight(); int base_line=size().height/2+font_height/2;

131

2.6 动态效果 --- 线程的应 用 g.setFont(fnt); g.setColor(clr); g.drawString(Msg,0,base_line); g.clipRect(strPt-50,0,str_bk_size,size().height);

g.setColor(spot_clr); g.drawString(Msg,0,base_line); strPt=(strPt+1)%(size().width+100); } }

132

2.6 动态效果 --- 线程的应 用 在 Java 中播放 动画 1. 需要多张图片 2 调用图片的方法 ? getImage, 3. 将多幅图像存入 图像对象数 组 Image frame[]=new Image[10]; for (int i=0;i
4. 显示图像

drawImage(x,y,0,0,this),

133

2.6 动态效果 --- 线程的应 用 import java.awt.*; public class nina extends java.applet.Applet implements Runnable {Image frame[]; Thread threadNina; int frame_i; int delay_time; public void init() { frame=new Image[10]; threadNina=null; frame_i=0; for (int i=0;i
134

2.6 动态效果 --- 线程的应 用 public void run() { while(true) { repaint(); try{ Thread.sleep(100);} catch(InterruptedException e) {} frame_i=(frame_i+1)%frame.length; } } public void paint(Graphics g) { g.drawImage(frame[frame_i],0,0,this);}

135

2.7 播放声音 java 支持 au 格式的 声音 两个方 法 : void play(URL url) void play(URL url, String name) 例 :play(getCodeBase(), “boing.au”); ( 注 : 它是一 次性的 ) 如果想 反复播放怎 么办 ? 借用类 AudioClip(loop(),play(),stop())

136

2.7 播放声音 例 :AudioClip bg_sound= getAudioClip(getCodeBase(), “boing.au”); bg_sound.play(); 或 : bg_sound.loop(); import java.applet.AudioClip; public class audio extends java.applet.Applet {AudioClip sound=getAudioClip(getCodeBase(),"boing.au"); public void start() { my_sound.loop(); } public void stop(){ { if(my_sound!=null) my_sound.stop();}}

137

2.7 播放声音 • 图像加声音岂不 是更有吸引 力 1. 在 init 中既取 图像也取声 音片断 frame[i]=getImage(getCodeBase(), "img000"+i+".gif"); SoundClip=getAudioClip(getCodeBase(),"boing.au");

2. 在 init 中加入 SoundClip.loop(); 3. 在 stop 中加入 if (SoundClip!=null) SoundClip.stop();

138

2.8 可通用的代码 • 同时包含 main() 方法和 init() 方法 • 由于 application 本身不是 图形环境 , 因此 需要在程序中加 入图形环境 , 以便可 以作 为普通的 application 使用 • import java.applet.Applet; import java.awt.*; • import java.awt.event.*; • import java.util.*;

139

2.9 小结

• 小应用程序是在 浏览器中运 行的 , 每个小应 用程序中必须有 一个主类 , 冠以 public, 并且 继承自 java.applet. • 小应用程序包括 生命周期的 四个环节和 paint() • 根据程序要求 , 用户可以 在主类中 定义其它 方法 , 或定义 其它类 . • public class myapplet extends Applet • { init() {…};start() {…}; • stop() {…};destroy() {…}; • paint(Graphics g){…} • }

140 init() applet 启动后 第一个 被执 行 , 在此初始 化

2.9 小结

applet 主类

start() init() 后被执 行 , 程序主 要代码 写在 此 paint() start() 后被执 行 , 写与输 出有 关的代 码 stop() 浏览 器变 换页面 时执 行 , 可以 省略 重写 destroy() 浏览器关 闭时 执行 , 可以省 略重写

Classes

自定义方 法 不能 自动 被执行 , 可以 由前 三 个方 法调 用 . 例如 : start() { mymethod()}

mymethod1 mymethode2

. Classes myclass =new Classes() .myclass.method1();

141

class Myclass { int v1; method(int num) {v1=num;} }

2.9 小结

public class Demo extends Applet { public void init() { Myclass test1=new Myclass(); test1.method(20); Myclass test2=new Myclass(); test2.method(10); } }

test1 v1 20

test2 v1 10

内存

142

2.9 小结 • 线程是实现动态 效果的核心 , 运行线程必 须继承 Thread 类或者实现 Runable 接口 . • run 是线程 的主体 , 它反复调用 repaint() 方 法 , 其中必 须有休眠 sleep(). • sleep() 语句要捕获中断 异常 ( 右面 讲 ) • try{Thread.sleep(100);} • catch(InterruptedException e) {} • 有线程的小应用 ,start(),stop() 方法必须重 写. • 需要获取网络资 源时 ( 包括本地资源 ), 要

143

第三章

事 件处理

北京大学计 算机系

代亚 非

144

第 3 章 事件处理 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚

3.1 什么是事件 3.2 鼠标产生的事件 3.3 键盘产生的事件 3.4 动作事件 3.5 通用事件处理程序 3.6 发送自己的事件 3.7 Java 1.1 事件处理模型 3.9 小结

145

3.1 什么是事件 ❚ CGI 之外 的选择 form cgi www 浏览器 url Web server interact applet

外部程序 C 程序 数据库程 序



什么是 事件 ? 用户 用于交 互而 产生的 键盘 或

• •

鼠标动 作 . 响应 用户的 动作 称为处 理事 件 . 在类 Event 中 , 定义了 所有的 事件 处理方 法 , 小应用 已经继 承了 这些方 法 .

146

3.1 什么是事件 ❚ import java.awt.Event; ❚ Event 在 java.awt 包中 , 无论哪 个键 按下或 者 释放 , 还是鼠 标移动 , 按下 鼠标或 释放 鼠标 , AWT 都会 捕获 , 并产生 一个事 件 . ❚ 处理事 件的核 心是 重写处 理事 件的方 法 ❚ 通用方 法 : ❚   handEvent(Event evt, Object arg) ❚ 专用方 法 : ❚ MouseDown(Event evt, Object arg)

147

3.1 什么是 事件 ❚ Even t 类的 数据域 int clickCount

int id

int modifiers

int x

Event

int y

long when

int key

Object target

Ob je ct ar g

148

3.1 什么是 事件 如果你 不 覆盖你 要 处理的 方 法,则 缺 省的方 法 返回一 个 假值, 通 知系统 没 有处理 事 件

MouseUp() Event

MouseDown() MouseDrag()

HandleEvent()

MouseMove() MouseEnter()

action()

MouseExit() keyDown()



KeyUp()

149

3.2 鼠标产生的 事件 ❚ ❚ ❚ ❚ ❚ ❚ ❚

鼠标事 件的三 个参 数 : 1. 事件的 类型 ( 是移动 ? 托拽 ) 2. 是按下 还是 放开 ? 3. 鼠标的 位置 (x,y) 方法的 重写 : public boolean mouseDown(Event evt,int x,int y) {….}

150

3.2 鼠标产生的 事件 ❚ 例 : 在鼠 标单击 的地 方显示 “ ” .(MouseClick.html) 捕获事 件 mouseDown

❚ ❚ ❚ ❚ ❚

获得参数 (x,y) (Event evt, int x, int y)

在 (x,y) 处画 叉 paint() drawLine

思路 : 记忆鼠标点过的所有点 1.Point marks[]=newPoint[20];( 在 init 方法中 ) 2.marks[i++]=new Point(x,y); (MouseDown 方法中 ) 3. 将所有的点画出来 ( 在 paint 方法中 ) g.fillOval(x,y,10,10);

151

import java.awt.*;import java.applet.*; extends Applet事件 .public class mark 3. 2 鼠标产生的 { int i; Point Marks[]; public void init() {Marks[] =new Point[20]; i=20;} boolean mouseDown(Event evt, int x, int y) { Marks[i++]=new Point(x,y); repaint(); } public void paint(Graphics g) { int k; for (k=0;k
152

3.2 鼠标产生的 事件 import java.awt.*;import java.applet.Applet; public class CountClick extends Applet {int CurrentMarks=0; public boolean mouseDown(Event evt,int x,int y) { CurrentMarks++; repaint(); return true; } public void paint(Graphics g) { g.drawString(" "+CurrentMarks,10,10);} } ❚ [ 练习 ] 对鼠 标的点 击动 作计数

153

3.3 键盘产生的 事件 捕获的 方法 keyDown(Event evt, int key) Event 类的键 常量 常量 键 常量 常量 键 DOWN 下箭 头键 END End 键 F1 键 F2 F2 键 F3 F3 键 F4 F4 键 F5 F5 键 F6 F6 键 F7 F7 键 F8 F8 键 F9 F9 键

键 F1

154

3. 3 键盘产生的 事件 ❚ 例题 : 显示用 户按下 的字 母键内 容 import java.applet.Applet;import java.awt.*; { char Presskey; public boolean keyDown(Event evt, int key) { Presskey=(char)key; repaint(); return true; } public void paint(Graphics g) { g.drawString(Presskey,10,10); } }

155

3. 3 键盘产生的 事件 ❚ 键盘事 件处理 通常 包括 : 显示字 符 , 光标 移动 ❚ 特殊键 public boolean keyDown(Event evt, int key) { switch(key) { case Event.F1: {….}; case Event.PGUP: {…} } } ❚ 修正键 if(evt.shiftDown()) if(evt.controlDown());

156

3.3 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚

键盘产 生的事件

练习题 : 在屏幕上 显示 用户输 入的 字符串 在前一 题的基 础上 , 将字符串 起来 , 但是不 能用 : String s; s=s+presskey; 应该用 StringBuffer 对象 的 append 方法 StringBuffer sb; sb.appned(presskey); sb 的内 容不能 直接 用 g.drawString() 应该用 g.drawString(sb.toString(),x,y);

157

3.4 动作 事件 ❚ 凡是由 构件产 生的 事件叫 动作 事件 ACTI ON_EV ENT, 处理这 类事件 的方 法是 : acti on().

music

确定

sports

取消

art

158

3.4 动作事 件 ❚ acti on(Ev ent evt, O bject arg ) ❚ evt. targe t: 指明 事件类 型 ❚ (but ton,c heck box,li st,.. .) int clickCount

int id int modifiers

int x

Event

int y

long when

int key

Object target

Ob je ct ar g

159

3.4 动作 事件 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚

判断组 件类型 ( 如是 but ton 或 chec kbox ) if(e vt.ta rget insta nceof But ton) if(e vt.ta rget insta nceof Che ckbox) 判断是 哪多个 同类 组件中 的哪 一个 if(e vt.ta rget ==butt on1) if(e vt.ta rget =butto n2) 或者通 过判断 标签 内容 if(a rg==“ 确定 ” ) if(a rg==“ 取消 ” )

160

3.4 动作 事件

例 : 记录按 下按 钮的次 数 , 并显示 出来 . import java.awt.*; import java.applet.Applet; public class CountClick extends Applet { int CurrentMarks=0; public init() { Button b1=new Button(“ 按钮 ” ); 按钮 add.b1; } public boolean action(Event evt,Object arg) 10 { CurrentMarks++; repaint(); return true; } public void paint(Graphics g) { g.drawString(" "+CurrentMarks,10,10);} }

161

3.4 动作事 件 ❚ 例 : 根据 用户选 择画 图形 ❚import 思路 :java.awt.*; 园 方 ❚import 1. 设计两个按钮 ( 后面详细讲 ) java.applet.Applet; class drawing ❚public 2. 事件处理 action extends Applet circlemark=true; ❚{ boolean 根据选择 , 分别标记园或方 public init() ❚ {3 Button 根据标记画出相应图形 b1=new Button(“ 园” ); ❚ Button g.drawCirlce(50,50,50,50); b2=new Button(“ 方” ); add.b1; add.b2; ❚ g.drawRect(25,25,75,75); }

162

3.4 动作事 件 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚

public void paint(Graphics g) { if (circlemark) g.filloval(10,10,30,30); else g.fillRect(20,20,40,40}; } public boolean action(Event evt,Object arg) { if (evt.target==b1) circlrmark=true; else circlrmark=false; repaint(); return true; }

163

3. 5 通用 的事件处理 程序 ---- handl eEven t ❚ hand leEve nt 处理 所有的 小应 用程序 所接 受的 事件 , 由它将 事件送 给相 对应的 方法 . ❚ 让我们 看一下 handl eEvent 的缺省 实现 public boolean handleEvent(Event evt) { switch(evt) { case Event.MOUSE_ENTER: return mouseEnter(evt,evt.x,evt.y); case Event.MOUSE_EXIT: return mouseExit(evt,evt.x,evt.y); case Event.MOUSE_MOVE: return mouseMove(evt,evt.x,evt.y);

164

3. 5 通用 的事件处理 程序 ---- hand leEve nt case Event.MOUSE_DRAG: return mouseDrag(evt,evt.x,evt.y); case Event.MOUSE_UP: return mouseUp(evt,evt.x,evt.y); case Event.MOUSE_DOWN: return mouseDown(evt,evt.x,evt.y); case Event.KEY_PRESS: case Event.KEY_ACTION: return keyDown(evt,evt.key); case Event.KEY_RELEASE:

165

3.5

}

通用的 事件处理程 序 --handl eEven t

case Event.KEY_ACTION_RELEASE: return keyUp(evt,evt.key); case Event.ACTION_EVENT: return action(evt,evt.arg); case Event.GOT_FOCUS: return gotFocus(evt,evt.arg); case Event.LOST_FOCUS: return lostFocus(evt,evt.arg); } return false;

3.5

通用的事件 处理程序 --hand leEve nt

166

❚ 覆盖 ha nd le Eve nt 的情 况 ( 原来的 han dle Eve nt 不被 执行 ): 只处理 我们感 兴趣 的事 :

public

bo olean

han dleE vent( Event

evt) { sw itch( evt.id ) { cas e Ev ent.M OUSE_E NTER : //d oing some thing; cas e Ev ent.M OUSE_E XIT: //d oing some thing; default:return super.handelEvent(evt); return false;

}}

167

.

3.7 Java1.1 事件 模型 ❚ Java 1.0 的事 件管理 机制 ❚ 在 Java1.0 中,多数 事件 发生在 类 component 里,并 通过 handleEvent() 方法将 事件 传递给 相应的 处理方 法 , 如果没 有这样 的方 法 , 则沿 着包含 层次传 给上 一级容 器 , 直到最 顶层容 器 仍没有 处理 , 则合理 遗弃 , 不适于 重用 . 例如 一个发 生在按 钮上 的事件 , 处理 它的 action 方 法通常 属于包 含按 钮的父 类容 器 , 这不 符合 面 向对象 的设计 原则 ❚ Java 1.1 的事 件管理 机制 ❚ 在 Java 1.1 中, 一个事 件常 常被其 它对 象处理 ,这些 对象称 为事 件监听 器, 当特定 事件 发 生时, 相应的 监听 器能够 知道 。 AWT 共有 11

168

3.7 Java1.1 事件 模型 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚

在 Java 1.1 中如 何捕获 事件 ? 三个步 骤 : 1. 必须定 义一个 类来 实现接 口 class ButtonTracker implements ActionListener{…} 2. 定义方 法 3. 向构件 注册该 类的 一个实 例 button1.addActionListener(this); checkbox.addActionListener(this);

169

3.7 Java1.1 事件 模型 ❚ Java1.0 和 Java1.1 事件模 型的区 别 ? ❚ 在 Java1.1 中 , 事件 不再沿 着包 含层次 向上 传 递 , 而是 传给一 个监 听器 , 因此在 Java1.1 中 , 你希望 发生在 构件 事件由 哪个 监听器 处理 , 就 将该监 听器注 册到 构件上 .

170

3.7 Java1.1 事件 模型 窗口

panel

窗口或面 板 的监听器

button

按钮的监 听器

171

3.7 Java1.1 事件模型

❚ 常用的 监听器 及其 方法

❚ 键盘监 听器 :KeyListener, 方法 : ❚ keyPressed, KeyReleased, keyTyped. ❚ 鼠标监 听器 : MouseListener, 方法 : ❚ mouseClicked, mousePressed, mouseReleased, ❚ mouseEntered, mouseExited ❚ 构件监 听器 :ActionListener, 方法 : ❚ actionPerformed(); ❚ 如果一 个监听 器有 若干各 方法 , 则必须 将这 些 方法全 部覆盖

172

3.7 Java1.1 事件 模型 ❚ ❚ ❚ ❚ ❚ ❚ ❚

例: import java.awt.event.*;import java.awt.*; import java.applet.*; public class TestButton extends Applet { Button button1; Color color;

❚ }

173

3.7 Java1.1 事件 模型

public void init() { button1 = new Button("Test Button"); button1.addActionListener (new ButtonHandler(this)); add(button1); Font font = new Font("TimesRoman", Font.PLAIN, 48); g.setFont(font); color = Color.black; resize(400, 200); }

174

3.7 Java1.1 事件模型 public void paint(Graphics g) { g.setColor(color); g.drawString("TEST COLOR", 55, 120); }

175 public class ButtonHandler implements ActionListener { TestButton a; 3.7 Java1.1 事件模型 ButtonHandler(TestButton tb) { a=ts;} public void actionPerformed(ActionEvent event) { String arg = event.getActionCommand(); if (arg == "Test Button") { if (a.color == Color.black) a.color = Color.red; else a.color = Color.black; a.repaint(); }

176

3.7 Java1.1 事件 模型 100101

class TestButton button color=black; paint()

class ButtonHandler a 100101 a.color=black;

new ButtonHandler(this);

177 public class TestButtoninner extends Applet { Button button1; Color color; Font font; 3.7 Java1.1 事件 模型 class ButtonHandler implements ActionListener { public void actionPerformed(ActionEvent event) { String arg = event.getActionCommand(); if (arg == "Test Button") { if (color == Color.black) color = Color.red; else color = Color.black; repaint(); } }

178

class Example3.7 Java1.1 事件 模型 {public void init() 部定 义的类 叫内 部类 ❚ 在某个 类的内 Button(“button”); ❚{ Button 内部类button1=new 的特点 : button1.addActionListener(new ButtonHandler() ❚ 内部类 可以访 问外 部类的 数据 和方法 { public void actionPerformed(ActionEvent event) ❚ 内部类 的匿名 是指 : { String arg =形式 event.getActionCommand(); ❚ 在定义 if (arg == "Test 一个新 类的Button") 同时创 建一 个实例 . { if (color == Color.black) color = Color.red; else color = Color.black; repaint(); } . };

179

3.7 Java1.1 事件 模型 ❚ 适配器 ❚ 简化代 码 ❚ 不用适 配器时 , 必须 对某个 适配 器的多 个方 法 进行重 写 , 加入 适配 器后可 以为 这些方 法提 空 实现 class MouseAdapter implements MousListener { public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {}

180

3.7 Java1.1 事件 模型 ❚ 继承某 个适配 器的 子类 , 则程序 中只需 覆盖 你 需要的 方法 , 其余方 法则从 父类 继承 ❚ class MouseTrack extends MouseAdapter ❚ { public void mouseClicked(MouseEvent e) ❚ { //…handle mouse click event; ❚ } ❚ }

181

3.8 小结 ❚ 事件由 用户的 使用 了鼠标 和键 盘动作 引起 的 ❚ 事件由 事件处 理器 来处理 ❚ hand leEve nt() 方法是 App let 从 comp onen t 继承而 来的 . ❚ 重写 hand leEv ent() 方法时 注意返 回 fals e 指 明有些 情况被 忽略 , 将其传给 上层 对象 . ❚ 在组件 上产生 的动 作叫动 作事 件 ,acti on 方法 类处理

182

3. 10 作业 ❚ 1. 在鼠 标按 下的两 点之 间画一 条线

第 4 章

异常

北京大 学计 算机系 代亚非

第 4章 ■ ■ ■ ■ ■ ■ ■

4.1 4.2 4.3 4.4 4.5 4.6 4.7

异常

异常的 概念 异常的 分类 捕获异 常 声明异 常 抛出异 常 创造自 己的异常 总结

184

185 4.1 异常 的概念 ■ 什么是异常 ? 异常实 际上是程序 中错误导致中 断 了正常的指令流 的一种事件 . ■ 没有处理错误的 程序 : read -file { op enThe File; de termi ne it s siz e; al locat e tha t muc h mem ory; cl oseTh eFile ; }



4.1 异常的概念 以常规方法处理 错误

ope nFil es; if (the Files Open) { deter mine t he le nth if (g otTheF ileLe ngth all ocate that much if (gotE nough Memor read the fi le i if (r eadFai led) else errorC ode= }el se e rrorC ode=}else error Code= -4 ; }el se er rorC ode=-5 ;

of the file ; ){ memo ry; y) { nto m emory; erro rCode= -1; -2; 3;

186

4.1 异常的 概念 ■



■ ■

观察前面的程序 在出错处理上了 只把能够想到的 情况无法处理 程序可读性差 出错返回信息量

187

你会发现大 部分精力花 . 错误考虑到 , 对以外 的

太少

4.1 异常的概念 ■

188

用异常的形式处 理错误

rea d-Fil e; { t ry { ope nTheF ile; det ermin e it s size ; all ocate tha t much memo ry; clo seThe File ; }catch( fileo penF ailed) { do some thing; } catch (sizeD eter mineF ailed) {dos ometh ing; } catch (memor yAll ocate Failed ){ doso methi ng;}

4.1 异常的 概念

189

和传统的方法比 较异常的优 点 : 1. 把错误 代码从常规 代码中分离 出来 2. 把错误传播给调 method4 产生异常 method3 传 用堆栈 method2 3. 按错误类型和 递 method1 处理异常 错误差别分 组 4. 系统提供了对于 一些无法预 测的错误 的捕获和处理 5. 克服了传统方法 的错误信息 有限的问 题 ■

4.1 异常的 概念

190

class ■ . ExcepTest { public void main(String args[]) { int b=0; int a; try { a=4/b;} catch(ArithmeticException e) { System.out.println(“divided by 0”);} }} try{ URL url=new URL(http://www.hit.edu.cn/”,”hit.gif”);} catch(MalformedURLEception e) { badURL=true; repaint();}

4.2 异常的分类 ■









191

异常是 一个对 象 , 它继承 自 Throwable 类 , 所 有的 Throwable 类的 子孙类 所产 生的对 象都 是例外 . Error: 由 Java 虚拟 机生成 并抛 出 ,Java 程序 不做处 理 . Runtime Exception( 被 0 除等 系统 错误 , 数组 下标超 范围 ): 由系 统检测 , 用户 的 Java 程 序可不 做处理 , 系统 将它 们交给 缺省 的异常 处理程 序 . Exception( 程序 中的问 题 , 可预知 的 ): Java 编译器 要求 Java 程序必 须捕 获或声 明所 有的 非运行 时异常 throw: 用户 自己产 生异 常

4.2 异常的分类 ■

.

192

Throwable

用户 自己 产生的 异常 Exc ep tion 要处 理

Error RuntimeException

不做 处理 由用户 捕获或 声明并处 理 缺省的异 常 处理程序

4.3 捕获异 常 ■ 捕获并处理异常 try { // 接受监视的 程序块 , 在此区 域内 发生 // 的异常 , 由 ca tch 中指定的程序 处理 ; }ca tch ( 要处理的异常种 类和标识符 ) { // 处理异常 ; }ca tch ( 要处理的异常种 类和标识符 ) {

193

4.3 捕获异常 ■

常见的异常



Arit Arra Arra IOEx File Null Malf Numb OutO

■ ■ ■ ■ ■ ■ ■ ■

hmeti yInde yStor cepti NotFo Point ormed erFor fMemo

cExcep xOutOf eExcep on undExc erExce URLExc matExc ryExce

tion Band sExce ption tion 如果在 使用 能 够产生 异常 的 epti on 方法而 没有 捕 ptio n 获和处 理, 将 epti on 不能通 过编 译 epti on ptio n

194

4.3 捕获异常 ■ ■ ■ ■

例 : 编写 Java 程序 , 包含三种异常 算术异常 , 字符串越界 , 数组越界 观察输出信息 : 每个异常对象可 以直接给出 信息

195

class first_exception 4.3 捕获异常 { public static void main(String args[]) { char c; int a,b=0;int[] array=new int[7]; String s="Hello"; try {a=1/b;} catch(ArithmeticException ae) { System.out.println(“Catch “+ae));} try {array[8]=0;} catch(ArrayIndexOutOfBoundsException ai) { System.out.println((“Catch “+ai);} try{ c=s.charAt(8));} catch(StringIndexOutOfBoundsException se) { System.out.println((“Catch “+se);}}}

196

4.3 捕获异常  一定会执行的程 序块 ---fin ally 异常处 理的统一 出口 try { // 常规的代码 ; } catc h() { // 处理异常 } fina lly { // 不论发生什 么异常 ( 或者不发生任 何异常 ), 都要执行的部 分 ; }

197

4.3 捕获异常 ■ ■ ■ ■ ■ ■ ■ ■

finally 在文件处理 时非常有用 try { 对文件 进行处理的 程序 ; }catch(IOException e) { // 对文件 异常进行处 理 ; }finally { 不论是否发生异 常 , 都关闭 文件 ; }

198

4.4 声明异常

199

一个方 法不处 理它 产生的 异常 , 而是沿 着调用 层次向 上传递 , 由调用 它的 方法来 处理 这些异 常 , 叫声明 异常 .  声明异 常的方 法  在产生 异常的 方法 名后面 加上 要抛出 (thr ows) 的异常 的列表 ■ void com pute( int x) thro ws Arit hmet icExc eption {…} ■ retu rnTy pe meth odNa me([p aramet erli st]) throw s exce ptio nList ■

4.4 声明异常 例 :若 因某 种 原因 不 想在 创 建 URL 的方 法 中处 理 异常

200

public int compute(int x) throws ArithmeticException e) { return z=100/x;} public method1() { int x; try { x=System.in.read(); compute(x);} catch(IOException ioe) { System.out.println(“read error”); } catch(ArithmeticException e) { System.out.println(“devided by 0”); } }

4.4 声明异常

201

method1 处理

computer

抛出

异常

4.4 声明异常

202

例 : 说出 程序执 行结 果 public class exception1 { void Proc(int sel) throws ArithmeticException, ArrayIndexOutOfBoundsException { System.out.println(“In Situation" + sel ); if (sel==0) { System.out.println("no Exception caught"); return; }else if(sel==1) {int iArray[]=new int[4]; iArray[10]=3; }

4.4 声明异 常

203

public static void main(String args[]) { try { Proc(0); Proc(1); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Catch"+e); } } c:>j In S no E In S Catc java on:1

view itua xcep itua h .lan 0

thro tion tion tion

wsExce ptio n 0 caught 1

g.Arr ayInde xOut OfBou ndsExc epti

4.5 抛出异常  抛弃异

204

常 : 不是出 错产生 , 而是 人为地

抛出 ■ throw ThrowableObject; ■ throw new ArithmeticException(); ■ 例 : 编写程序人 为抛出 (JavaThrow.prj) ArithmeticException, ArrayIndexOutOfBoundsException Another method A method Exception StringIndexOutOfBoundsException caught throw

class JavaThrow 4.5 抛出异 常 205 { public static void main(String args[]) { try{ throw new ArithmeticException();} catch(ArithmeticException ae) { System.out.println(ae); } try{ throw new ArrayIndexOutOfBoundsException();} catch(ArrayIndexOutOfBoundsException ai) { System.out.println(ai); } try { throw new StringIndexOutOfBoundsException();} catch(StringIndexOutOfBoundsException si){ { System.out.println(si); }







4.6 创造自己的 异常 206 不是由 Java 系统监测到 的异常 ( 下标越 界 , 被 0- 除等 ), 而是由 用户自己定 义的 异常 . 用户定义的异常 同样要用 try--catch 捕 获 , 但必须由 用户自己 抛出 throw new MyException. 异常是一个类 , 用户定义的 异常必须继 承自 Throwable 或 Exception 类 , 建议 用 Exception 类 .

4.6 创造自己的 异常

207



形如 :



class MyException extends Exception {….}; 例 1 : 计算 两个数 之和 , 当任 意一 个数超 出范 围时 , 抛出自 己的异 常

■ ■

public class NumberRangeException extends Exception { public NumberRangeException(String msg) { super(msg); } }

4.6 创造自己的 异常 ■public .

boolean action(Event evt, Object arg)

{ try { int answer = CalcAnswer(); answerStr = String.valueOf(answer); }catch (NumberRangeException e) { answerStr = e.getMessage(); } repaint(); return true; }

208

public int CalcAnswer() throws NumberRangeException 创造自己的 异常 209 { int int1, int2; 4.6 int answer = -1; String str1 = textField1.getText(); ■ . str2 = textField2.getText(); String try { int1 = Integer.parseInt(str1); int2 = Integer.parseInt(str2); if ((int1 < 10) || (int1 > 20) || (int2 < 10) || (int2 > 20)) { NumberRangeException e = new NumberRangeException (”Numbers not within the specified range."); throw e; } answer = int1 + int2; }catch (NumberFormatException e) { answerStr = e.toString(); } return answer;

4.6 创造自己的 异常 ■









210

例 2 : 在定义 银行类时 , 若取钱数大 于 余额则作为异常 处理 (InsufficientFundsException). 思路 : 产生异常的 条件是余额 少于取额 , 因此是 否抛出异常 要判断条件 取钱是 withdrawal 方法中 定义的动作 , 因此在该方法中 产生异常 . 处理异常安排在 调用 withdrawal 的时 候 , 因此 withdrawal 方法 要声明异常 , 由上级方法调用 要定义好自己的 异常类

class Bank 4.6 创造自己的 异常 { double balance; public ■ . void deposite(double dAmount) { if(dAmount>0.0) {balance+=dAmount;}}

211

public void withdrawal(double dAmount) throws InsufficientFundsException { if (balance
4.6 创造自己的 异常 public class ExceptionDemo { public static void main(String args[]) { try { Bank ba=new Bank(50); ba.withdrawal(100); System.out.println(“Withdrawal successful!”); }catch(Exception e) { System.out.println(e.toString()); } }

212

4.6 创造自己的 异常

213

■ .class InsufficientFundsException extends Exception public { private Bank excepbank; private double excepAmount;

InsufficientFundsException(Bank ba, double dAmount) { excepbank=ba; excepAmount=dAmount; } public String excepMesagge() { String str=“The balance”+ excepbank.showBalance()+ “The withdrawal was”+excepAmount; return str; }

4.7 小结 1. 一般 格式 : 正常程序和 出错处理分 离 开来 try { Java statement; }catche(ExceptionType1 ExceptionObject) { Exception1 handling; } catche(ExceptionType2 ExceptionObject) { Exception2 handling; }…. }finally { final handling; // ( 统一 的出口 , 最终 必定要 执行 )

214

4.4 小结 ■

215

2. 把异 常传播给堆 栈 , 沿着被 调用的顺序 往前寻找 , 只要找到符合该 异常种类彻 底 异常处理程序 , 就交给这部 分程序去处 理 call

Method1

throws

Method2

call Method3 call Read-file

throws

throws 产生 异常

Try catch

4.4 小结 3. 异常可 以人为地抛 出 , 用 throw new 语句 4. 异常可 以是系统已 经定义好的 , 也可 以是用 户自己定义 的 5. 用户自 己定义的异 常一定继承自 Throwable 或 Exception 类

216

作业 ■

217

当用户按“ T” 或“ t” 时 , 人为抛出一个 算术异常 , 处理 方式为打印 出错信息

import java.awt.*; import java.applet.*; 218 public class keyException extends Applet { final int LOWERCASE_T = 116; final int UPPERCASE_T = 84; String str; int count=0; public void paint(Graphics g) { g.drawString(" "+str+count, 5,20); } public boolean keyDown(Event evt, int key) {if ((key == LOWERCASE_T) || (key == UPPERCASE_T)) { try{ throw new ArithmeticException();} catch(ArithmeticException e) { count++; str=e.toString(); repaint();} return true; } return false; }

第 5章 构成用户界 面的窗口环 境 北京大 学计算 机系 代亚非

219

构成用 户界面的窗 口环境

220

✔ 5.1 使用 AWT(Abstract Window Toolkit) 构件 ✔ 5.2 包含 构件的 构件 ---- 构件容 器

( container ) ✔ 5.3 事件 的传递 ✔ 5.4 各种 构件的 应用 实例 ( 一 ) ✔ 5.5 外观 的管理 与控 制 ✔ 5.6 各种 构件的 应用 实例 ( 二 ) ✔ 5.7 总结

5.1 使用 AWT 构件

221

✔ 在 AWT 的概念 中,窗 口系 统所显 示的 各种

对象统 称为构 件: Button , Menu , List 等等都 是构件 。 ✔ Component 是代表 构件最 基本 的类。 ✔ Component 类中定 义了各 种窗 口对象 中最 基

本、最 重要的 方法 和性质 。随 时随处 都有 可能利 用 Component 定义 的方 法。

5.1 使用 AWT 构件

222

✔ 已经 学过 的:

✔ 没有 学过 的:

✔ getFont ,

✔ action,

✔ handleEvent,keyUp ✔ keyDown,mouseUp ,



✔ mouseDown ,



✔ mouseMove ,



✔ mouseEnter ,



✔ mouseExit ,



✔ mouseDrag, repaint



✔ setFont , resize ✔ ✔

disable , enable , getBackground , getForeground , hide , inside , isEnable , isShowing , isVisible , locate , location , move , setBackground , setForeground , show , size

5.2 包含 构件的 构件 ---- 构件容器 (container )

列表

按钮

窗口,对话框 另一个窗口

223

菜单

container container

5.2 包含 构件的 构件 ---- 构件容器 (container ) import java.awt.*; import java.applet.Applet; public class CountClick extends Applet { int CurrentMarks=0; public init() { Button b1=new Button(“ 按钮” ); add.b1; } } 按钮

224

5.2 包含 构件的 构件 ---- 构件容器 (container )

225

✔ AWT 使用 Container 类来定 义最基 本的 构件

容器 , 它有两 个子类 :Window 类和 Panel 类 . ✔ 在 Window 类还有两 个子类 ✔ 1. 定义 对话 框 , 用 Dialog 子类 ; ✔ Java 还提了一 个 Dialog 的子类 ---FileDialog,

用它生 成文件 对话 框 ✔ 2. 定义 一般 意义的 窗口 , 用 Frame 类 .

5.2 包含 构件的 构件 ---- 构件容器 (container )

226

✔ Panel 干什 么用的 呢 ? ✔ 使你更 方便的 组织 你的构件 , 得到赏 心悦目

的布局 ✔ Applet 是 Panel 的子类 , 因此 在小应 用程 序

里可以 直接加 入构 件 , 而一 般的 应用程 序必 须先定 义构件 容器 . ✔ 小应用 程序在 浏览 器中所 显示 的区域 就是

Panel, 所占 的尺寸 就是 缺省得 Panel 尺寸 .

5.2 包含 构件的 构件 ---- 构件容器 (container )

227

Component Container Button

Panel Applet

Menu Textfield Checkbox

Window Frame Dialog FileDialog

5.3 事件的 传递 (1.02)

228

当动作发 生在 按钮上 窗口

panel

时 , 首先看 按钮 这个 类有没有 action 方法 , 如果没有 则看 包含按 钮的容器 类 ( 即 panel) 有没有 action 方法 , 如果没有 事件 就传递 窗口 , 如果 没有 就传 递给 component 的通

按钮

用处理方 法 , 如果程 序中没有 定义 任何 action 方法 , 实际上事

class MyClass extends Frame { MyPanel p=new Mypanel(); add(p); Button b=new Button(exit); add(b); boolean action() {…….;} }

窗口

229

Panel

ok exit

class MyPanel extends Panel class MyBtn extends { Button MyBtn b=new MyBtn(“ok”); { add(b); boolean action() boolean action() { …….; {…….;} return true;} } }

5.3 事件的传递 (1.02)

230

✔ 事件由 包含层 次由 内向外 传递 . ✔ 每个处 理事件 的方 法要有 一个 返回值 , 通知

是否继 续向上 传递 boolean action(Event evt, Object arg) { ……; return true; }

5.3 事件的 传递 (1.1) 窗口

231

panel

窗口获面 板 的监听器

button

按钮 的监 听器

5.4 各种构 件的应用 实例 -- 按钮

232

✔ Button 类 ✔ 功能 : 创建按 钮事件 ✔ 创建一 个 Button ✔ Button myButton = new Button(str); ✔ 将 button 放到 窗口系 统中 :

add(new Button(“ 确定 ” ); 确定 ✔ add(new Button(“ 取消 ” ); ✔ Button 类的常 用方法 ✔ getLabel setLabel ✔

取消

5.4 各种构 件的应用 实例 -- 按钮

233

✔ 处理 button 产生的 事件 ✔ 例 : 创建 一个按 钮 , 每当按 下它时 , 在屏 幕

显示文 字 (singlebutton.html) ✔ 想一想 : ✔ 应该有 哪些类 ? Button ,Font; ✔ 应有哪 些方法 ? init--- 建立 button action--- 接受动 作事 件 , 调用 repaint paint--- 显示文 字

5.4 各种构 件的应用 实例 -- 按钮 import java.awt.*;import java.applet.Applet; public class button extends Applet { Font font; Button b1; public void init() { font= newFont("TimesRoman",Font.BOLD,20); b1=new Button("push"); add(b1); setFont(font); }

234

5.4 各种构 件的应用 实例 -- 按钮 ✔ boolean action(Event evt,Object arg) ✔ { y+=5; repaint(); ✔

return true;

✔} ✔ paint(Graphics g) ✔ { g.drawString("Button”, 10,y);}

235

5.4 各种构 件的应用 实例 -- 按钮 import java.awt.*;\\ 例 : 不在 applet 中的按 钮 class ButtoninFrame { public static void main(String args[]) { Frame myframe=new Frame(); myframe.setTitle("Button in Frame"); myframe.resize(200,200); myframe.show(); Button b1=new Button("Button1"); Button b2=new Button("Button2"); myframe.add(b1);myframe.add(b2); } . }

236

5.4 各种构 件的应用 实例

237

✔ 一般步 骤 :

创建 new

加入 add

响应 action

处理

5.4 Computer 各种构 件的应用 实例 Computer

238

MusicTextField(20); Music New 常用 的方 法 new TextArea( “this is a test”,20,40); Sports getText();setText(); Sports setEchoCharacter(char c) Scrollbar(VERTICAL,50,0,1,100); Art Art CheckboxGroup gr=new CheckboxGroup(); 1 Checkbox(label, New Checkbox(label,gr,null, false); New false); New Checkbox(label,gr,true);

min

start

max

5.4 各种构 件的应用 实例 --CheckBox

239

✔ 应用举 例 ✔ 建立三 个复选 框 , 被选中 者的标 签内 容变成

“ changes” – 应有的 类 :Checkbox – 应有的 方法 : – init: 建立 复选框 – action: 接受动 作事件 – setLabel(“*****”);

5.4 各种构 件的应用 实例 --CheckBox

240

import java.awt.*; public class checkbox extends java.applet.Applet { Checkbox b1,b2,b3; public void init() { b1=new Checkbox("Label1",null,false); b2=new Checkbox("Label2",null,false); b3=new Checkbox("Label3",null,false); add(b1); add(b2); add(b3); }

5.4 各种构 件的应用 实例 --- CheckBox 241 public boolean action(Event evt, Object arg) { if (evt.target instanceof Checkbox){ Checkbox selectedbox=(Checkbox)evt.target; String str=selectedbox.getLabel(); if (str=="Label1") selectedbox.setLabel("Chnage1"); else if (str=="Label2") selectedbox.setLabel("Chnage2"); else if (str=="Label3") selctedbox.setLabel("Change3"); } repaint();return true; }

5.4 各种构 件的应用 实例 --- TextField

242

✔ 例 : 在文 本行中 输入 , 并用 字符串 接受 , 显

示出来 ✔ 类的数 据构 成 :Textfield,Button, String ✔ 类的方 法构 成 : ✔ init(), ✔ action(): 接受按 钮事 件 , 调用 paint() ✔ paint(): 用 getText() 方法 得到输 入内 容 , 并显 示.

5.4 各种构 件的应用 实例 --- TextField

243

import java.awt.*; public class textfieldkey extends java.applet.Applet { TextField t;String s;Button button; public void init() { t=new TextField("",25); add(t); button=new Button("getText"); add(button); }

5.4 各种构 件的应用 实例 --- TextField public boolean action(Event evt, Object arg) { if(evt.target instanceof Button) { repaint();} return true; } public void paint(Graphics g) { s=t.getText(); g.drawString(s,40,80); }

244

5.5 外观的 管理与制

0 1 5

2

3

4

245

0

1

3

4

4

5

5.5 外观的 管理与制

246

✔ Panel 类 ( 面板 ) ✔ 功能 : 容纳其 他对象 , 安排 合理布 局 ✔ 创建面 板 :

Panel myPanel=new Panel(); ✔ add(myPanel); ✔ 将面板 作为容 器 : ✔ mypanel.add(button) button1 ✔

button3

button2 button4

import java.awt.*; 247 5.5 外观的 管理与制 public class Panel extends java.applet.Applet { ✔Panel panel1,panel2; 例 :(panel.htm) Button button1,button2,button3,button4; public void init() { panel1=new Panel(); panel2=new Panel(); add(panel1); add(panel2); button1=new Button("Button1"); button2=new Button("Button2"); button3=new Button("Button3"); button4=new Button("Button4"); panel1.add(button1); panel1.add(button2); panel2.add(button3); panel2.add(button4); }}

5.5 外观的 管理与制

248 北

✔ BorderLayout 类 ✔ 功能 :Applet 分成 五个区

西



✔ 创建 ✔ setLayout(new BorderLayout()); ✔ 将其他 构件加 入 ✔ add(“East”, new Button(“ 东” ); ✔ add(“South”, new Button(“ 南” ); ✔ add(“West”, new Button(“ 西” ); ✔ add(“North”, new Button(“ 北” ); ✔ add(“Center”, new Button(“ 中” );





5.5 外观的 管理与制 ✔ FlowLayout 类 ✔ 缺省的 输出管 理器 ✔ GridLayout 类 ✔ GridLayout mylayout = new ✔ GridLayout(3,3,0,0)

rows

cols

hspace

✔ setLayout();

vspace

249

5.5 外观的 管理与制

250

✔ GridBagLayout 类和

GridBagConstraints 类 ✔ 功能 : 借助于 GridBagConstraints 类 , 实现 更灵活 的外观 管理 ✔ 每个构 件后都 跟随 一个 GridBagLayout 对象 实体 , 来决定 构件的 外观 . ✔ 创建 ✔ GridBagLayout myLayout=new ✔ GridBagLayout(); ✔

5.5 外观的 管理与制

251

✔ GridBagConstraints 类的 约束条 件 gridwidth,

gridheight, gridx, gridy, ✔ weightx, weighty, ipadx, ipady, insets ✔ fill 及其设 置 ✔ GridBagConstraints.NONE ✔ GridBagConstraints.HORIZONTAL ✔ GridBagConstraints.VERTICAL ✔ GridBagConstraints.BOTH ✔ GridBagConstraints.RELATIVE

5.5 外观的 管理与制

252

✔ 例 :(GridBagApplet.html)

button1

button2

button3

button4 button5 button7

button6 button8 button9

public init()管理与制 253 5.5 void 外观的 { GridBagLayout layout=new GridBagLayout(); setLayout(layout); GridBagConstraints GBC = new GridBagConstraints(); Button button1=new Button("button1"); Button button2=new Button("button2"); Button button3=new Button("button3"); Button button4=new Button("button4"); Button button5=new Button("button5"); Button button6=new Button("button6"); Button button7=new Button("button7"); Button button8=new Button("button8"); Button button9=new Button("button9");

GBC.fill=GridBagConstraints.BOTH; 254 5.5 外观的 管理与制 ( 按钮可 以在水 平和 垂直两 个方 向扩展 ) layout.setConstraints(button1,GBC); add(button1); GBC.gridwidth=GridBagConstraints.RELATIVE; (BOTH 依然起 作用 , 紧挨着最 后一 个按钮 ,) layout.setConstraints(button2,GBC); add(button2); GBC.gridwidth=GridBagConstraints.REMAINDER; ( 填充剩 余部分 ) layout.setConstraints(button3,GBC); add(button3); But1 But2 But3

5.5 外观的 管理与制

255

✔ GBC.gridwidth=

GridBagConstraints.REMAINDER; ✔ ( 表示该按 钮独 占一行 ) ✔ layout.setConstraints(button4,GBC); ✔ add(button4); But1

But2 But4

But3

5.5 外观的 管理与制

256

✔ GBC.gridwidth=2; ✔ ( 表示该按 钮占 两个单 元 ) ✔ layout.setConstraints(button5,GBC); ✔ add(button5);

But1

But2 But4 But5

But3 But6

✔ GBC.gridwidth=

GridBagConstraints.REMAINDER; ✔ layout.setConstraints(button6,GBC); ✔ add(button6); ✔

5.5 外观的 管理与制

257

✔ GBC.gridwidth=1; ✔ GBC.gridheight=2; ✔ ( 高度为两 个单 元 ) ✔ layout.setConstraints(button7,GBC); ✔ add(button7); But1

But2 But4 But5

But7

But3 But6

5.5 外观的 管理与制

258

✔ GBC.gridwidth=

GridBagConstraints.REMAINDER; ✔ GBC.gridheight=1; ✔ layout.setConstraints(button8,GBC); ✔ add(button8); But1 But2 But3 ✔ layout.setConstraints But4 ✔ (button9,GBC); But5 But6 ✔ add(button9); But7

But8 But9

5.6 各种构 件的应用 实例 ---Canvas

259

✔ 5.6.2 Canvas 类 ( 画布 ) ✔ 功能 : 制作 其他构 件 , 通常用 来放置 图形 图

像 , 或绘 图 . ✔ 画图可 以直接 在 applet 区域上 进行 , 定义了 Canvas 对象 后将 paint() 语句 作为该 对象 的 方法 , 这些动 作就自 动发 生在画 布区 . ✔ 通常不 需要处 理画 布上发 生的 事件 ✔ 创建 ✔ Canvas canvas=new Canvas(); ✔ add(canvas);

5.6 各种构 件的应用 实例 ---Canvas

260

✔ 例 : 根据 程序说 出运 行结果 ✔ 注意一 个程序 中生 成一个 canvas 类的实 例 ,

另一个 程序没 有

import java.awt.*; import java.applet.*; 件的应用 实例 ---Canvas public 5.6 class各种构 canvas_test_2 extends Applet { public void init() { setLayout(new BorderLayout()); add("North", new Button("button1")); add("South", new Button("button2")); add("West", new Button("button3")); add("East", new Button("button4")); }

261

public void paint(Graphics g) { g.setColor(Color.red); g.fillRect(0,0,50,100); g.setColor(Color.blue); g.fillRect(30,0,100,40);} }

5.6 各种构 件的应用 实例 ---Canvas import java.awt.*; import java.applet.*; public class canvas_test extends Applet { MyCanvas mycanvas=new MyCanvas(); public void init() { setLayout(new BorderLayout()); add("Center",mycanvas); add("North",new Button("button1")); ….; add("East",new Button("button4")); } }

262

5.6 各种构 件的应用 实例 ---Canvas class MyCanvas extends Canvas { public void paint(Graphics g) { g.setColor(Color.red); g.fillRect(0,0,50,100); g.setColor(Color.blue); g.fillRect(30,0,100,40); } }

263

5.6 各种构 件的应用 实例 ---Canvas

264

5.6 各种构 件的应用 实例 ---Canvas

265

✔ 例 : 按动 鼠标改 变画 布的颜 色

(CanvasApplet) ✔ 有哪些 类 ? Canvas, Button, Color; ✔ 哪些方 法 ? init(), action(),swapColor(),paint() color

利用输 出管理 器按钮 和画布

color

color

按钮接收 鼠标事件

变换颜 色

执行重画

5.6 各种构 件的应用 实例 ---Canvas class CanvasApplet extends Applet { MyCanvas mycanvas=new MyCanvas(); public void init() { setLayout(new BorderLayout()); Button button = new Button("Color"); add("North",button); add(“Center”,mycanvas); resize(200,250); } .

266

5.6 各种构 件的应用 实例 ---Canvas ✔ boolean action(Event evt, Object arg) ✔{ ✔ if(arg=="Color") mycanvas.swapColor(); ✔ return true; ✔}

267

class MyCanvas extends Canvas 5.6 各种构 件的应用 实例 ---Canvas { Color color; MyCanvas() { color=Color.red; } public void paint(Graphics g) { g.setColor(color); g.fillRect(20,20,100,100); g.setColor(color.white); g.drawString("CANVAS",40,40);} public void swapColor() { if(color==Color.black) color=Color.red; else if(color==Color.red) color=Color.green; else color=Color.black; repaint(); } . }

268

5.6 各种构 件的应用 实例 ---Frame

269

✔ 5.6.3 Frame 类 ✔ 功能

: 制作 一般的 独立 窗口 , 它是构 件容

器 ✔ 创建 ✔ Frame fmInstance=new Frame(); ✔或 Frame fmInstance= ✔ new Frame(“The window for test”); ✔ 将其显 示到屏 幕上 ✔ fmInstance.show() ✔ 注意 : 不用 add()

5.6 各种构 件的应用 实例 ---Frame ✔ 常用的 方法

dispose,getCursorType,getIconImage, ✔ getMenuBar,getTitle,isResizable, ✔ setCursor,setIconImage,setMenuBar, ✔ setResizable,setTitle ✔ 窗口的 相关事 件 : ✔ Event.WINDOW_DEICONIFY, ✔ _DESTROY ✔ _EXPOSE, ✔ _ICONIFY, ✔ _MOVED ✔

270

5.6 各种构 件的应用 实例 ---Frame

271

✔ 例 : 创建 一个窗 口 , 并用按 钮控制 它的 显示

或 ✔ 隐藏 FrameApplet Frame Window This is CustomFrame window

hide window Show window

5.6 各种构 件的应用 实例 ---Frame ✔ 在 applet 中 action 处理 Button 的事件 button.label is show

action 捕获 button button.label is hide

Frame.show Frame.hide

272

5.6 各种构 件的应用 实例 ---Frame

273

为了 将字 符显 Frame Window 示在 自定 义窗 口中 , 包含 输 This is CustomFrame window 出语 句的 方法 必须 在自 定义 的窗. 口类 中 关闭 窗口的 事件 在窗口 类本 身处理

✔.

注 : 处理 窗口中 的事 件用 handelEvent() public boolean handleEvent(Event evt) { switch(evt.id) { case Event.WINDOW_DESTROY: dispose();System.exit(0); default: return super.handleEvent(evt); } }

public class FrameApplet extends Applet 274 5.6 各种构 件的应用 ---Frame { CustomFrame frame; Button实例 button; public void init() { frame=new CustomFrame ("Custom Frame Window"); button=new Button("Show Window"); add(button); } } public boolean action(Event evt, Object arg) { boolean visible=frame.isShowing(); if(visible){ frame.hide(); button.setLabel("Show window");} else {frame.show();button.setLabel("Hide Window"); return true; } }

class CustomFrame extends Frame 5.6 各种构 件的应用 { CustomFrame(String title) 实例 ---Frame { super(title);}

275

public boolean handleEvent(Event evt) { switch(evt.id) { case Event.WINDOW_DESTROY: dispose();System.exit(0); default: return super.handleEvent(evt); } } public void paint(Graphics g) { resize(200,100); g.drawString("this is a custom window.",30,30); } }

5.6 各种构 件的应用 实例 ---Frame

276

✔ 多窗口 (FrameApplet\ButtonsFrames.class-f1.bat)

button1

button1 Button1 button2 button2

10 1

Button1 button2 button2

0 7

5.6 各种构 件的应用 实例 ---Frame

277

1. 一个 窗口类 创建 两个实 例 2. 由于 有不同 的事 件发生 ( 按钮 , 关窗 口 ), 因 此事件 先由通 用事 件处理 程序 来接收 , 然后 再根据 情况做 相应 的处理 . switch(evt.id) { case Event.WINDOW_DESTROY: dispose(); return true; case Event.ACTION_EVENT: return action(evt, evt.arg); default: return super.handleEvent(evt);} }

5.6 各种构 件的应用 实例 ---Frame

278

✔ 3. 任何 时候 只有一 个窗 口是活 动的 (active)

的因此 不必考 虑那 个判断 是哪 一个窗 口发 生的事 件 ✔ 4. 一般 的结构 ✔ 在 main() 中 , 只做与 窗口有 关的 事情 : 创建 窗口 , 显示窗 口 ✔ 在构造 方法中 , 安排 窗口中 的构 件

import java.awt.*; 5.6 各种构 件的应用 实例 ---Frame class ButtonsInFrames extends Frame { int a1=0,a2=0; public static void main(String args[]) { ButtonsInFrames myframe1=new ButtonsInFrames(); myframe1.setTitle("Button in Frame1"); myframe1.resize(200,200); myframe1.show(); ButtonsInFrames myframe2=new ButtonsInFrames(); myframe2.setTitle("Button in Frame2"); myframe2.resize(200,200); myframe2.show(); }

279

ButtonsInFrames() 5.6 各种构 件的应用 实例 ---Frame { setLayout(new BorderLayout()); Button b1=new Button("Button1"); Button b2=new Button("Button2"); add("North",b1);add("South",b2); } public boolean handleEvent(Event evt) { switch(evt.id) { case Event.WINDOW_DESTROY: dispose(); return true; case Event.ACTION_EVENT: return action(evt, evt.arg); default: return super.handleEvent(evt);} }

280

5.6 各种构 件的应用 实例 ---Frame

281

✔ public boolean action(Event evt, Object arg) ✔{ ✔ ✔ ✔

if(evt.target instanceof Button) if(arg=="Button1") a1++; else a2++; repaint(); return true;

✔} ✔ public void paint(Graphics g) ✔{ ✔ ✔ }}

g.drawString("button1 "+a1,5,80); g.drawString("button2 "+a2,5,100); .

与 5.6 List 各种构 类有关 的事件 public boolean handleEvent(Event 件的应用 实例 --- 练习 evt)282 Event.LIST_DESELECT, { switch(evt.id){ case Event.WINDOW_DESTROY: Event.LIST_SELECT dispose(); System.exit(0); 例 : List List default: return super.handleEvent(evt); (FontDisplay.class---f3.bat) } FontDisplayer add add Arial } FontDisplay! arae

Panel 类

add

TextArea 类 add

area.setfont( Frame 类 handleEvent LIST_SELECT

Courier New Courier New

Times New Roman

18 20 字型 22 , 字体 24 26

You can input something here.

, 字号 )

handleEvent WINDOW_DESTROY

.

import java.awt.*; 283 5.6FontDisplay 各种构 件的应用 实例 --- 练习 class extends Frame { TextArea FontShower; public static void main(String args[]) { FontDisplay myFrame=new FontDisplay(); myFrame.setTitle("FontDisplayer"); myFrame.resize(450,300); myFrame.show(); } public FontDisplay() { setLayout(new BorderLayout(5,5)); FontShower=new TextArea("Font Display! ”); add("West", new FontPanel(FontShower)); add("Center",FontShower);}

5.6 各种构 件的应用 实例 --- 练习 public boolean handleEvent(Event evt) { switch(evt.id){ case Event.WINDOW_DESTROY: dispose(); System.exit(0); default: return super.handleEvent(evt); } }

对窗口 来说只有一 个事 件

284

5.6 各种构 件的应用 实例 --- 练习

285

在 panel 中创建 两个列 表 往列表 中加 入条目 用 additem(str) 得到列 表选项 用 getItem 事件处 理用用 handleEvent, 得到两 个参数 -- 字 型 , 字号 . 对右 边的文 本区 设置属 性 , 利用引 用传递 . void updateFontShower() { area.setFont(new Font(CurrentFontName, Font.PLAIN, CurrentSize));}} .

5.6 各种构 件的应用 实例 --- 练习

286

✔ public boolean handleEvent(Event evt) ✔ { switch(evt.id){

case Event.LIST_SELECT: ✔ List target=(List)evt.target; ✔ String itemName= ✔



target.getItem(((Integer)evt.arg).intValue()); ✔ if(target==FontSelector) ✔ CurrentFontName=itemName; ✔ else CurrentSize=Integer.parseInt(itemName);

5.6 各种构 件的应用 实例 --- 练习

287

✔ String FontNames[]={"Arial", "Courier

New", "Times New Roman"}; ✔ List FontSelector=new List(); ✔ for (i=0;i
5.6 各种构 件的应用 实例 --- 练习

288

class FontPanel extends Panel { int CurrentSize=20; String CurrentFontName; TextArea area;; List FontSelector,SizeSelector;

updateFontShower() { area.setFont(param1,param2,param3)

FontPanel(TextArea FS) // 把另一个 对象 做参数 289 各种构 件的应用 实例 --- "Courier 练习 New", { int i;5.6String FontNames[]={"Arial", "Times New Roman"}; setLayout(new GridLayout(2,1,5,5)); FontSelector=new List(); for (i=0;i
public boolean handleEvent(Event evt) 290 5.6 各种构 件的应用 实例 --- 练习 { switch(evt.id){ case Event.LIST_SELECT: List target=(List)evt.target; String itemName= target.getItem(((Integer)evt.arg).intValue()); if(target==FontSelector) CurrentFontName=itemName; else CurrentSize=Integer.parseInt(itemName); updateFontShower(); return true; default: return super.handleEvent(evt); }} void updateFontShower() { area.setFont(new Font(CurrentFontName, Font.PLAIN, CurrentSize));}}

5.6 各种构 件的应用 实例 ---menu ✔ 5.6.6 菜单 系统 类 MenuBar

菜单系 统 一般菜 单

菜单容 器 类 Menu 类 MenuItem 非菜单 容 器

可撕下 菜单 帮助菜 单

选项 #1 选项 #2 菜单中的 菜单 选项 #3 选项 #4

选项 #1 选项 #2 第三层菜 单

选项 #3

选项 #1 选项 #2

291

5.6 各种构 件的应用 实例 ---menu ✔ 创建菜 单条 ✔ mb=new MenuBar(); ✔ setMenuBar(mb);( 类 Frame 中的 方法 ) file

edit

✔ 创建菜单 ✔ menu1=new Menu(“file”); ✔ menu2=new Menu(“edit) ✔ mb.add(menu1);mb.add(menu2);

292

5.6 各种构 件的应用 实例 ---menu ✔ 创建菜 单项 ✔ ✔ ✔ ✔ ✔

mi1=new MenuItem(“new”); mi2=new MenuItem(“open”); mi3=new MenuItem(“save”); mi4=new MenuItem(“close”); menu1.add(mi1); menu1.add(mi2); File New open Save Close

edit

293

5.6 各种构 件的应用 实例 ---menu 如何处 理事件 public boolean action(Event e, Object arg) { if (e.target instanceof MenuItem) { MenuItem selected=(MenuItem)e.trget; tring s=selected.getLabel(); switch(s) { case “new”: ….; case “open”: ….; case “save”: ….; case “close”: ….; } }

294

5.6 各种构 件的应用 实例 ---menu

295

✔ 在处理 菜单事

件 时应该注 意的事 情是 : 判断层 次

A

A

✔ MenuContainer uplevel;( 定义一个菜单容器 ) ✔ MenuItem target=(MenuItem)evt.target;( 当前被

选中的对象 ) ✔ uplevel=target.getParent(); ✔ strMessage=uplevel.getLabel()( 得到上一级容器 的标签 )

public boolean action(Event evt, Object arg) 296 5.6strMessage; 各种构 件的应用 实例 ---menu { String if(evt.target instanceof MenuItem){ MenuItem target=(MenuItem)evt.target; MenuContainer uplevel; uplevel=target.getParent(); while(uplevel instanceof Menu) { strMessage=((Menu)uplevel).getLabel()+strMessage; uplevel=((Menu)uplevel).getParent(); } strMessage="you selected"+strMessage; taMessage.appendText(strMessage); return true; } else return false;}

5.6 各种构 件的应用 实例 ---menu

297

✔ Java1.1 处理菜 单的方 法 ✔ 两个主 要的策 略 : ✔ 1. 让每 个菜 单项有 一个 唯一的 监听 器 ✔ 2. 将一 个大 的监听 器用 于用于 所有 菜单项

5.6 各种构 件的应用 实例 ---menu

298

class Example { class MenuFileOpen implements ActionListener { public void actionPerformed(ActionEvent e) { openFile(e.getActionCommand());} } class MenuFileSave implements ActionListener { public void actionPerformed(ActionEvent e) { saveFile(e.getActionCommand());} }

5.6 各种构 件的应用 实例 ---menu

299

public void init() { MenuItem OpenItem=new MenuItem(“Open…”); OpenItem.addActionListener (new MenuFileOpen()); MenuItem SaveItem= new MenuItem(“Save…”); SaveItem.addActionListener (new MenuFileSave()); }

5.6 各种构 件的应用 实例 -- 综合练 习

300

✔ 设计用 户界面 可根 据用户 选择 办理银 行业

务 ✔ Bankapp\Bankapp.class---f2.bat ✔ 控制流 程

5.6 各种构 件的应用 实例 -- 综合练 习 ✔ class Bank ✔ { long balance; ✔ public Bank() ✔ { balance=50; } ✔ public void deposite(long amount) ✔ { if(amount>0.0) {balance+=amount;}} ✔ void withdrawal(long amount) ✔ { if(amount>0.0 &&amount <= balance) ✔ { balance-=amount;} ✔ } ✔ public long show_balance() ✔ { return (long)balance; } ✔}

301

5.6 各种构 件的应用 实例 -- 综合练 习 public class BankDemo { public static void main(String agrs[]) { Bank account1=new Bank(); Bank account2=new Bank(); account1.deposite(100); account2.withdrawal(50); account1.show_balance(); account2.show_balance(); } }

302

5.6 各种构 件的应用 实例 -- 综合练 习

WelCome to Bank 100 Create account Show nbalance Deposit Withdrawal

303

5.6 各种构 件的应用 实例 -- 综合练 习 辅类 Bank

主类 Bankapp (Frame 的子类 )

Bank() 创建 账户

main() 定义并显 示窗口

show_balance diposite() withdrawal()

Bankapp() 布局安排 handleEvent() 关闭 窗口 action() 根据按钮 做处 理

满足 Bank 要求

304

辅类 Warningbox Warningbox(String str) 布局安排 , 显示对话 框 显示警告 信息 action() 关闭对话 框 它是模态 的 不满足 Bank 要求

5.6 各种构 件的应用 实例 -- 综合练 习

public static void main(String args[]) { Bankapp frame=new Bankapp(); frame.setTitle("Bank Application"); frame.resize(200,200); frame.show(); }

305

5.6 各种构 件的应用 实例 -- 综合练 习

306

public Bankapp() {setLayout(new GridLayout(6,1)); Label lb=new Label("welcome to Bank",Label.CENTER); tf=new TextField("0",15); add(lb);add(tf); b1=new Button("create account"); add(b1); b2=new Button("show balance"); add(b2); b3=new Button("diposite"); add(b3); b4=new Button("withdrawal"); add(b4); resize(450,100); }

5.6 各种构 件的应用 实例 -- 综合练 习 public boolean handleEvent(Event evt) { switch(evt.id) { case Event.WINDOW_DESTROY: dispose(); System.exit(0); return true; case Event.ACTION_EVENT: return action(evt, evt.arg); default: return super.handleEvent(evt); } }

307

public boolean action(Event evt,Object arg) 5.6 各种构 件的应用 {if(evt.target instanceof Button)实例 -- 综合练 习 308 { String targetLabel=tf.getText(); long amount=Long.valueOf(targetLabel).longValue(); if (arg=="diposite") { if (!haveAccount) {wb=new WarningBox(this,"create a account first!"); wb.show(); } else { if(amount==0) { wb=new WarningBox(this,”need the amount"); wb.show(); } else { bank.deposite(amount); tf.setText("0"); }} return true; } return false; }

public WarningBox(Frame parent,String str) 5.6 各种构 件的应用 实例 -- 综合练 习 { super(parent,"Warning!",true); Panel panel=new Panel(); panel.setLayout(new GridLayout(2,1)); panel.add(new Label(str,Label.CENTER)); panel.add(new Button("OK")); add(info_panel); } public boolean action(Event evt,Object arg) { if (evt.target instanceof Button) { this.dispose(); return true; } return false; }

309

class Bank 各种构 件的应用 实例 -- 综合练 习 { long5.6 balance; public Bank() { balance=50; } public void deposite(long amount) { if(amount>0.0) {balance+=amount;}} void withdrawal(long amount) { if(amount>0.0 &&amount <= balance) { balance-=amount;} } public long show_balance() { return (long)balance; } }

310

5.7 总结

311

✔ 5.8 总结 ✔ 1 使用 AWT 构件的应 用实 例 ✔ 2 事件 的传 递 ✔ 3 外观 的管 理与控 制 了解类 及其常 用方 法 设计 好容 器层 次

创建 输出 管理器 setLayout

定义事件 的处 理 action

创建 类的 实 例 new

加到上 一级容 器中 add

作业

312

✔ 将文本 行的输 入加 入到文 本域 中

追加

第6章

数据流的运用

北京大学计算机系 代亚非

314

第 6 章 数据流的运用 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

6.1 输入 输出方 法 6.2 输入输 出流的 基类 6.3 File I/O 6.4 数据输 入输出 流 6.5 随机存 取文件 6.6 文件的 其它操 作 6.7 java 中的 unicode 6.8 管道 流 6.9 对象 流 6.10 流的 分类 6.11 小结

315

6.1 输入输 出方法 ■

什么是 数据流 ? 文件

起 点

文件 , 字符串 存储 区

文件

程序

程序

网络端 点

终端

数据流

终 点

网络端 点 ■

数据流是指所有的数据通信通道



在 java 中有关流的操作使用 java.io.*



出于安全的考虑 , 小应用不能实现文件 I/O 流

316

6.1 输入输 出方法 ■ ■ ■ ■ ■ ■ ■ ■

System 类管理 标准输 入输 出流和 错误 流 System.out: 把输出 送到缺 省的 显示 ( 通常是 显示器 ) System.in 从标准 输入获 取输 入 ( 通常 是键盘 ) System.err 把错误 信息送 到缺 省的显 示 每当 main 方法被执 行时 , 就自 动生成 上述 三个对 象

317

6.1 输入输 出方法 public class ReadHello { public static void main(String args[]) { char inchar; System.out.println(“Enter a character:”); try{ inChar=(char)System.in.read(); Syste.out.println(“ “+ inChar); }catch(IOException e) { Sytem.out.println(“Error reading from user”); } } }

318

6.1 输入输 出方法 import java.io.*; class IOTest { public statics void main(String args[]) {try { byte bArray[]=new byte[128]; System.out.println(“Enter something:”); System.in.read(bArray); System.out.print(“You entered:”); System.out.println(bArray); }catch(IOException ioe) { System.out.println(ioe.toString()); } } stream\Iostream.class---f4.bat }

319

6.1 输入输 出方法 ■ ■

■ ■ ■ ■ ■ ■ ■

为什么 输入的 是字 符 , 输出是乱 码 ? 原因 :System.out.println(bArray) 输出 的是数 组的地 址而不 是字 符 改进 : 将字符 数组变 换成 字符串 原来是 :System.out.println(bArray); 现在为 :String s=new String(bArray,0); System.out.println(s); System.in 是属 于 BufferedInputStream 类型 System.out 是属 于 PrintStream 类型 System.err 也是 属于 PrintStream 类型

320

6.2 输入输 出流的基类 ■

■ ■ ■ ■ ■ ■ ■ ■ ■

Java 中每 一种 流的基 本功 能依赖 于基 本类 InputStream 和 OutputStream 它们是 抽象类 , 不能 直接使 用 属于 InputStream 类的 方法有 : read(): 从流 中读入 数据 skip(): 跳过 流中 若干字 节数 available(): 返回流 中可 用字节 数 mark(): 在流 中标记 一个 位置 reset(): 返回 标记 过得位 置 markSupport(): 是否支 持标记 和复 位操作 close(): 关闭 流

321

6.2 输入输 出流的基类 ■

■ ■ ■ ■ ■ ■ ■

方法 read() 提供 了三种 从流 中读数 据的 方法 . int read(): 读一个 整数 int read(byte b[]): 读多 个字节 到数 组中 int read(byte,int off,int len); 属于 OutputStream 类的方 法有 : write(int b): 将一个 整数 输出到 流中 write(byte b[]): 将数 组中的 数据 输出到 流中 write(byte b[], int off,int len): 将数组 b 中从 off 指定的 位置开 始 len 长度的 数据输 出到 流中

322

6.2 输入输 出流的基类 ■ ■ ■ ■

■ ■ ■



flush(): 将缓冲 区中的 数据 强制送 出 close(): 关闭流 . PrintStream 类 println() 不属于 OutputStream 类 , 它是 PrintStream 类的子 类 , 能提供 复杂的 输出 PrintStream 类的方 法有 : write, flush, checkError,print, println,close. 其中 println 可以输出 多种 形式的 数据 . 例 如: println(String s), println(char c) 等

323

6.3 File I/O ■ ■ ■ ■ ■

文件对 象的建 立 File fp=new File(“tempfile.txt”); 对文件 操作要 定义 文件流 FileInputStream 类用 来打开 一个 输入文 件 FileOutputStream 类用来 打开 一个输 出文 件 write

输出文件

输入文件

read

324

6.3 File I/O ■ ■ ■

■ ■ ■

文件流 的建立 FileInputStream in=new FileInputStream(fp); FileOutputStream out=new FileOutputStream(fp); 例 : 文件 拷贝 ( 注意要 捕获文 件异 常 ) 输入流 的参数 是用 于输入 的文 件名 输出流 的参数 是用 于输出 的文 件名 file1.txt

输入流

输出流

file2.txt

import java.io.*; 325 class filestream 6.3 File I/O { public static void main(String args[]) { try{ File inFile=new File("file1.txt"); File outFile=new File("file2.txt"); FileInputStream fis=new FileInputStream(inFile); FileOutputStream fos=new FileOutputStream(outFile); int c; while((c=fis.read())!=-1) fos.write(c); fis.close(); fos.close(); }catch(FileNotFoundException e) { System.out.println("FileStreamsTest: "+e); }catch(IOException e) { System.err.println("FileStreamsTest: "+e); }}}

326

6.3 File I/O ■

增加缓 冲区流 , 减少 访问 硬盘的 次数 , 提高 效率

file1.txt

输入 缓冲 区 输出 缓冲 区

输入流

file2.txt 输出流

文件 文件流

缓冲区 流

6.3 File I/O ■ ■

■ ■ ■ ■ ■ ■ ■ ■

327

缓冲区 流 : BufferedInputStream 和 BufferedOutputStream 将它们 与文件 流相 接 FileInputStream in=new FileInputStream(“file1.txt”); BufferedInputStream bin= new BufferedInputStream(in,256) int len; byte bArray[]=new byte[256]; len=bin.read(bArray); len 中得到 是长度 , bArray 中得 到的是 数据

328

6.3 File I/O ■ ■



只有缓 冲区满 时 , 才会将 数据送 到输 出流 . Java 在输出 数据流 中 , 当对方 尚未将 数据 取 走时 , 程序就 会被阻 塞 . 有时要 人为地 将尚 未填满 的缓 冲区中 的数 据 送出 , 使用 flush() 方法 .

文件

329

6.4 数据输 入输出流 ■ ■

■ ■

■ ■ ■

什么时 候需要 数据 输入输 出流 ? 文件流 和缓冲 区流 的处理 对象 是字节 或字 节数组 ,利用 数据 输入输 出流 可以实 现对 文件的 不同数 据类 型的读 写 . DataInputStream 、 DataOutputStream 一种较 为高级 的数 据输入 输出 方式 , 除了字 节和字 节数组 , 还可 以处 理 int,float,boolean 等类型 . 还可以 用 readLine 方法 读取一 行信 息 可使用 的方法 : write,writeBoolean…,read,readByte… 等

330

6.4 数据输 入输出流 ■ ■ ■ ■ ■





数据流 的建立 FileOutputStream fos= new FileOutputStream(”file2.txt")); DataInputStream dis= new DataInputStream(fos) 数据输 出流可 以是 一个已 经建 立好的 输入 数据流 对象 , 例如网 络的 连结 , 文件等 . 下面的 例子显 示如 何利用 数据 输入输 出流 往文件 中写不 同类 型的数 据

331 class datainput_output 数据输 入输出流 args[]) throws IOException { public6.4 static void main(String { FileOutputStream fos=new FileOutputStream(“a.txt”); DataOutputStream dos=new DataOutputStream (fos); try{ dos.writeBoolean(true); dos.writeByte((byte)123); dos.writeChar('J'); dos.writeDouble(3.141592654); dos.writeFloat(2.7182f); dos.writeInt(1234567890); dos.writeLong(998877665544332211L); dos.writeShort((short)11223); }finally{ dos.close(); }

332 DataInputStream dis=new DataInputStream( 6.4 数据输 入输出流 new FileInputStream("a.txt")); try{ System.out.println("\t "+dis.readBoolean()); System.out.println("\t "+dis.readByte()); System.out.println("\t "+dis.readChar()); System.out.println("\t "+dis.readDouble()); System.out.println("\t "+dis.readFloat()); System.out.println("\t "+dis.readInt()); System.out.println("\t "+dis.readLong()); System.out.println("\t "+dis.readShort()); }finally{dis.close();} }}

Stream\datainputandoutput--->f5.bat

333

6.4 数据输 入输出流 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

DateLine(InputStream in)( 计算 字符和 行数 ) { DataInputStream data=new DataInputStream(in); String currentLine; int lineCount=0; int charCount=0; while((currentLine=dataIn.readLine())!=null) { ++lineCount; charCount+=currentLine.length(); } return (charCount/(float)lineCount); }

334

6.5 随机存 取文件 ■ ■ ■

■ ■ ■ ■

类 RandomAccessFile zip 文件需 要用 随机方 法处 理 文件目 录给出 个文 件的入 口 , 可以随 机读取 . 创建一 个随机 文件 new RandomAccessFile(“file1.txt”, “r”); new RandomAccessFile(“file2.txt”, “rw”); 随机文 件可以 同时 完成读 和写 操作 . zip 文件

文件目录

335

6.5 随机存 取文件 ■ ■ ■ ■ ■ ■ ■



支持随 机文件 操作 的方法 : readXXX() 或 writeXXX() skipBytes(); 将指 针乡下 移动 若干字 节 seek(): 将指针 调到 所需位 置 getFilePointer(): 返回 指针当 前位 置 pos length(): 返回文件 长度 利用 seek(long pos) 方法 查找随 机文 件中的 信息 例 : 把若 干个 32 位的 整数写 到一 个名为 “ temp.dat” 的文 件中 , 然后利 用 seek 方法 , 以 相反的 顺序再 读取 这些数 据

336 public class random_file 6.5 随机存 取文件 { public static void main(String args[]) { int data_arr[]={12,31,56,23,27,1,43,65,4,99}; try { RandomAccessFile randf=new RandomAccessFile(“temp.dat”); for (int i=0;i>data_arr.length;i++) randf.writeInt(data_arr[i]); for(int i=data_arr.length-1;i>=0;i--) { randf.seek(i*4); System.out.println(randf.readInt()); } randf.close(); }catch (IOException e) { System.out.println(“File access error: “+e);} } }

337

6.6 文件的其它 操作 ■

使用文 件类获 取文 件的路 径信 息



设 f 是一 个文件 对象



File f=new File(“data”,temp.dat”);



f.getName(): 返回 文件名



f.getParent(): 返回文件 所在 目录名



f.getPath(): 返回 文件路 径



f.getAbsolutePath(): 返回绝 对路 c:\myprog\data\temp.dat

temp.dat data

data\temp.dat

338

6.6 文件的 其它操作 ■

■ ■ ■ ■ ■ ■ ■ ■ ■ ■

例 : 获取当 前目录 下所 有文件 名和 文件的 尺 寸: import java.io.*; public class file_size { public static void main(String args[]) { File files=new File(“.”); String file_list[]=files.list(); for(int i=0;i
339

6.7 java 中的 unicode ■ ■

在 java 中用 unicode 表示字 符和 字符串 DatainputStream 的 readLine 方法 , 以字 节 形式读 入 , 以 unicode 形式输出

byte + 00000000 8 bit ❚ ❚

Unicode 16bit

DataInputStream 不适 合输入 是 unicode 的形 式 处理字 符用 InputStreamReader 类和 BufferedReader 类 (jdk1.1)

6.7 java 中的 unicode import java.io;( 从键盘 上读一 个字 符串 ) public class CharInput { public static void main(String args[]) {String s; throws IOException InputStreamReader ir; BufferedReader in; ir=new InputStreamReader(System.in); in=new BufferedReader(ir); 可以将 字符 串转换 成整 String s=in.readLine(); 数加以 运算 int i=Integer.parseInt(s); i=i*2; 123 System.out.println(“the result is” +i); } }

340

341

6.7 java 中的 unicode ■ ■

■ ■ ■

问题 : 如果字 符流不 是来 自本地 , 有可 能编 码不一 样 , 直接 读取 会读出 不正 确字符 处理方 法 : ir=new InputStreamReader(is,”8859_1”); 采用的 是 iso8859_1 编码方 式 , 在不同 平台 之间正 确转换 字符 .

342 import java.io.*; 6.7filetounicode java 中的 unicode class { public static void main(String args[]) { try{ FileInputStream fis=new FileInputStream("toyamei.txt"); InputStreamReader dis=new InputStreamReader(fis); BufferedReader reader=new String s; BufferedReader(dis); while((s=reader.readLine())!=null) { System.out.println("read: "+s);} dis.close(); }catch(IOException e) { } } }

6.8 使用管 道流 输出流 ■ ■ ■



■ ■



343

输入流

PipedInputStream 和 PipedOutputStream 创建管 道流 : PipedInputStream pis=new PipedInputStream(); PipedOutputStream pos=new PipedOutputStream(pis); 或: PipedOutputStream pos=new PipedOutputStream(); PipedInputStream pis=new PipedInputStream(pos);

6.8 使用管 道流 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

344

管道流 一定是 输入 输出并 用 例 : 将数 据从输 出管 道进 , 从输入 管道出 import java.io.*; class pipedstream { public static void main(String args[]) throws IOException { byte aByteData1=123, aByteData2=111; PipedInputStream pis= new PipedInputStream(); PipedOutputStream pos= new PipedOutputStream(pis); System.out.println("PipedInputStream");

345

6.8 使用管道流 ■ ■ ■ ■ ■ ■ ■ ■ ■

try{ pos.write(aByteData); pos.write(aByteData2); System.out.println((byte)pis.read()); System.out.println((byte)pis.read()); } finally { pis.close(); pos.close(); }

346

6.9 对象流 ■ ■

■ ■ ■ ■ ■ ■

在 java. io 包中 什么是 对象 的持续 性 ? 能够纪 录自己 的状 态一边 将来 再生的 能力 , 叫对象 的持续 性 什么是 串行化 ? 对象通 过写出 描述 自己状 态的 的数值 来记 录自己 的过程 叫串 行化 . 什么是 对象流 ? 能够输 入输出 对象 的流 . 两者什 么关系 ? 将串行 化的对 象通 过对象 输入 输出流 写入 文件或 传送到 其它 地方 .

6.9 对象 流 ■

■ ■ ■ ■ ■ ■ ■ ■ ■ ■

347

一个相 关的例 子 : 从一个源 读入 一个简 单的 对象 import java.net;import java.io public class GetString { public String getStringFromUrl(URL inURL) { InputStream in; try { in =inURL.openStream(); }catch(IOException ioe) {System.out.printlin(“URL error;”+ioe); return null; } return getString(in); } 通过 url 得到 一个字 符串

348

6.9 对象流 ■

■ ■ ■ ■ ■ ■ ■

public String getStringFromSocket(Socket inSocket) { inputStream in; try{ in=inSocket.getInputStreamSream(); }catch(IOEception ioe) {   System.out.println(“Socket error:”+ioe);   return null; } return getString(in); } 通过 socket 得到 一个 字符串

6.9 对象流

349

public String getString(inputStream inStream) { String readString = new String(); DataInputStream in =new DataInputSream(inStream); char inChar; try{ while(true) { inChar=in.readByte(); readString=readString+inChar; } }catch(EOFException eof) { System.out.println(readString);} }catch(IOException ioe) { { System.out.println(“error:”+ieo);} return readString; }

350

6.9 对象流 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

下面的 对象能 读吗 ? Class testObject { int x; int y; float angle; String name; public testObject(int x,int y,float angle, String name); { this.x=x;this.y;this.angle;this.name=name;} } 这仍然 是一个 简单 的对象

6.9 对象流

351

对象 流是怎 样工 作的 ? 允许 可串行 化的 对象在 流中 传输 1. 只有 实现 serializable 接口 的类才 能被 串行 化 public class Student implements Serializable { int id;String name; int age; String department; public Student(int id, String name,int age, String department) { this.id=id; this.name=name; this.age=age; this.department =departmernt; }

6.9 对象流 ■

■ ■ ■ ■ ■ ■

2. 构造对 象的输 入输 出流 ( 将对象保 存到 文件中 , 或者 通过网 络传 送到其 他地 方 ) 相应的 类 :ObjectInput 对象的 输出 : ObjectOutputStream 相应的 方法 :writeObject() 对象的 输入 :ObjectInputStream 相应的 方法 :readObject() 注 :jdk1.1 以上版 本支 持对象 流操 作

352

6.9 对象流

353

对象流 举例 : 将 Student 类的 一个 实例写 到文 件 中 import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class Objectser {public static void main(String args[]) Student stu=new Student(981036,“Li Ming”,16,“CSD”); {try {FileOutputStream fo=new FileOutputStream(“date.ser”); ObjectOutputStream so=new ObjectOutputStream(fo); os.writeObject(stu);so.close(); }catch(Exception e) {;}

354

6.9 对象流 im po rt ja va. io .F ile In put St rea m; im po rt ja va. io .O bje ct Inp ut Str ea m; pu bl ic cl ass O bj ect Rec ov { pu bl ic sta ti c voi d m ai n( Str ing a rg s[] ) { St ude nt st u; tr y { Fil eI np utS tre am f i=n ew Fi leI np ut Str eam (“ da te. se r”) ; Ob je ct Inp utS tr ea m s i=n ew Obj ec tIn pu tSt re am( fi ); st u=( St ud ent )si .r ea dOb jec t( ); si. clo se () ; }c atc h(E xc ep tio n e ) {S yst em .o ut. pri nt ln (e) ;} Sy ste m.o ut .p rin tln (“ ID: ”+ st u.i d+ “na me :”+

355

6.10 流的分类 ■

InputStream 和 OutputStream 是所有 输入输 出流的 祖先 , 它们是 一个 抽象类 .System.in 和 System.out 是它们 的子 类

InputStream

PushbackInputStream

FileInputStream PipedIntputStream FilterInputStream ByteArrayInputStream SequencedInputStream StringBufferInputStream

DataInputStream BufferedInputStream LineNumberInputStream

356

6.10 流的分类 FileOutputStream DataOutputStream OutputStream

PipeOutputStream

BufferedOutputStream

FilterOutputStream

PrintStream

ByteArrayOutputStream

InputStream 中的 基本方 法包 括 : ■ read, available, mark, skip, reset, markSuppposed, close ■ OutputStream 中的基 本方法 包括 : write, flush, close ■

357

6.11 小结 ■ ■





文件

在 Java 中有 数据传 输的 地方都 用到 I/O 流 ( 通常是 文件 , 网络 , 内存 和标准 输入 输出 等) InputStream 和 OutputStream 是所 有 I/O 流 的祖先 ( 只有 RandomAccessFile 类是 一个例 外 ),read 和 write 是它们 最基本 的方 法 , 读 写单位 是字节 . 在众多 的流对 象中 , 并不 是每一 种都 单独使 用 , 其中 过滤 流的子 类在 数据送 出去 之前做 必要的 处理 . 目

文件输 入流

缓冲输 入流

行号输 入流

数据输 入流



358

6.11 小结 ■







File, File(Input/Output)Stream, RandomAccessFile 是处 理本地 文件 的类 Data(Input/Output)Stream 是一个 过滤流 的 子类 , 借此可 以读写 各种 基本数 据 , 在文 件 和网络 中经常 使用 . 如 : readByte, writeBoolean 等 . Buffered(Input/Output)Stream 的作 用是在 数 据送到 目的之 前先 缓存 , 达到一 定数 量时再 送到目 的 , 已减少阻 塞次 数 . Piped(Input/Output)Stream 适合与 一个处 理 的输出 作为另 一个 处理的 输入 的情况

359

作业 作业 : 将键盘 上输入 的一 串字符 写到 文本文 件中

第 7章

多 线程

北京大 学计算机 系 代亚非

第 7章 ■ ■ ■ ■ ■

7.1 7.2 7.3 7.4 7.5

多线 程

多线 程基本 概念 创建 线程的 方式 线程 的挂起 与唤 醒 多线 程问题 小结

361

362

7.1 多线程 基本概念 文件

各种系 统资源 输入输 出装置

文件

各种系 统资源 输入输 出装置

数据区 段

数据区 段

程序区 段

程序区 段

只有一 个地方在 执行

传统的进 程

同时有 数个地方 在执行

多线程的 任务

7.1 多线程基本 概念 ■ ■

■ ■

363

多线程 的优势 : 减轻编 写交互 频繁 、涉及 面多 的程序 的困 难. 程序的 吞吐量 会得 到改善 . 由多个 处理器 的系 统 , 可以 并发 运行不 同 的线程 .( 否则 , 任何时刻 只有 一个线 程在 运行 )

7.1 多线程基本 概念 ■ ■



线程与 进程的 区别 : 多个进 程的内 部数 据和状 态都 是完全 独 立的 , 而多线 程是共 享一 块内存 空间 和一 组系统 资源 , 有可能 互相影 响 . 线程本 身的数 据通 常只有 寄存 器数据 , 以及一 个程序 执行 时使用 的堆 栈,所 以 线程的 切换比 进程 切换的 负担 要小。

364

7.1 多线程基本 概念 ■

对线程 的综合 支持 是 Java 技术 的一个 重 要特色 . 它提供 了 thread 类、 监视器 和条 件变量 的技术 .



虽然 Macintosh,Windows NT,Windows 9 等 操作系 统支持 多线 程 , 但若 要用 C 或 C++ 编写多 线程程 序是 十分困 难的 ,因为 它们 对数据 同步的 支持 不充分 .

365

7.2 创建线 程的方式 1. public class mythread extends Applet implements Runnable ( 小应用或 已经 是某个 类的 子类时 ) 2. 继承类 Thread public class mythread extends Thread 3. 上述两 种方法 中都 可用类 Thread 产生 线 程的对 象 Thread newthread; 4. 创建并 启动线 程 newthread=new Thread(this); newthread.start();

366

7.2 创建线程的 方式 ■





■ ■



367

5. run 方法 是运行 线程 的主体 , 启动 线程时 , 由 java 直接 调用 public void run() 6. 停止 线程 , 由小应 用程序 的 stop 调用 线程 的 stop newthread.stop() 7 sleep 方法的作 用 , 暂停线 程的执 行 , 让其 它线程 得到机 会 ,sleep 要丢出 异常 , 必须 抓 住. Try{sleep(100)}catch(InterruptedException e){} 例 : 小应 用程序 中不 用 Runnable 接口 仍然 可以使 用线程 ( 不调 用主类 的方 法和调 用主 类的方 法 )

7.2 创建线程的 方式

368

import java.applet.*; public class thread extends Applet { mythread t1=new mythread(); public init() { t1.start();} class mythread extends Thread { public void run() { for (int i=0;i<4;i++) System.out.println( “”+i); { try{sleep(400); } catch(InteruptedException e){ } } }

public class mainclass extends Applet 7.2 创建线程的 方式 { C t1=new C(this); public void init() { t1.start();} public void paint(Graphics g) { g.drawString("Hello,java",10,50);}} class C extends Thread { mainclass a; C(mainclass b) { a=b; } public void run() { while(true){ a.repaint(); try{sleep(400);} catch(InterruptedException e){} }}

369

7.2 创建线程的 方式

370

8. 其它 常用的 方法 isAlive : 判断线 程目 前是否 正在 执行状 态中 if(newthread.isAlive()) newthread.stop(); resume: 要求被 暂停得 线程 继续执 行 suspend: 暂停线程 的执 行 join: 等待线程 执行 完毕 thatThread.join(); 被等待 的那个 线程 不结束 , 当前线 程就一 直等 待 . yield: 将执行 的权力 交给 其它线 程 , 自己到 队列的 最后等 待 .

7.2 创建线程的 方式

371

9. 线程 的优先 权 ■ 某一时 刻只有 一个 线程在 执行 , 调度策 略为 固定优 先级调 度 . newthread.setPriority(Thread.MIN_PRIORITY) ■ 级别有 :MIN-PRIORITY ■ NOM_PRIORITY ■ MAX-PRIORITY 10. 自私 的线程 : 有很 高的优 先权 的线程 , 不 主动睡 眠或让 出处 理器控 制权 .

372

7.2 创建线程的 方式 11. 线程的 状态 yield()

new Thread() New Thread

start()

stop()

suspend() sleep() wait()

. . Runnable . stop() or run()exit Dead

Not Runnable resume() stop()

7.2 创建线程的 方式 ■



373

当一个 线程执 行完 所有语 句后 就自动 终止 ,调用 线程的 stop() 方法, 也可以 强制 终止 线程。 如果希 望线程 正常 终止, 可采 用标记 来使 线程中 的 run () 方法退 出。

7.2 创建线程的 方式 public class Xyz implements Runnable { private boolean timeToQuit=false ; public void run () { while (! timeToQuit ) {…..} //clean up before run () ends ; } public void stopRunning() { timeToQuit=true;} }

374

7.2 创建线程的 方式 ■ ■ ■

public class ControlThread { private Runnable r=new Xyz(); private Thread t=new Thread(r);



public void startThread() { t.start(); }

■ ■ ■

publi void stopThread() { r.stopRunning();}

■ ■ ■

}

375

7.3 线程的挂起 与唤醒 ■ ■ ■ ■



暂停线 程的执 行等 待条件 满足 再执行 . 下面的 例子显 示线 程的挂 起和 唤醒 小应用 程序第 一次 开始时 , 线程 被启动 浏览器 改变页 面时 , 小应用 程序 的 stop() 方法被 调用 , 线程被 挂起 . 浏览器 回到原 来的 页面时 , 线程 被唤醒 .

376

7.3 线程的 挂起与唤醒

377

public void start() { if (mythread==null) {mythread=new Thread(); mythread.start();} else { mythread.resume();} } public void run() { while(true){ try{sleep(100);} catch(InterruptedException e) {}} } public void stop() { mythread.suspend(); }.

7.4 多线程问题 --- 执行的顺序

378

多个线 程运行 时 , 调度策 略为固 定优 先级 调度 . 级别相 同时 , 由操作 系统按 时间 片 来分配 ■ 下面给 出的例 子中 , 共运行 三个 线程 , 它 们做同 样的事 , 每次打 印循环 次数 和自 己的序 列号 , 运行结 果表明 , 它们 并不 是 连续运 行的 . ■ 在上例 中如果 给某 个线程 赋予 较高的 优 先权 , 则发现 这个进 程垄 断控制 权 thread.setPriority(Thread.MAX_PRIORITY) ■ thread\multithread.class--f1.bat ■ thread\Priority.class---f2.bat ■

// 多个进 程运行 时执 行顺序 是交 叉的 379 7.3 多线程 问题 Thread class multithread extends { int threadNum; public static void main(String args[]) { multithread array[]=new multithread[3]; for (int i=0;i<3;i++) array[i]=new multithread(i); for (int i=0;i<3;i++) array[i].start(); } multithread(int SerialNum) { super(); threadNum=SerialNum; } public void run() { for(int j=0;j<5;j++) System.out.println(“<"+j+"> +MySerialNum); System.out.println("thread "+threadNum+ "bye.");}}

7.4 多线程 问题 --- 如何写多线 程 1. 分别 定义不 同的 线程类 , 在各自 的 run 方法中 定义线 程的 工作 class mythread1 extends Thread { public void run{….} } class mythread2 extends Thread { public void run{….} } 2. 在主类中 实例 化各线 程类 , 并启 动线程 . public class demo extends Applet { public void init() { mythread t1=new mythread1(); mythread t2=new mythread2(); t1.start(); t2.start();} }

380

7.4 多线程问题 --- 如何写多线程

381

: 将窗口 分为上 下两 个区 , 分别运 行 两个线 程 , 一个在 上面的 区域 中显示 由右 向左游 动的 字符串 , 另一 个在下 面的 区域 从左向 右游 动的字 符串 . ■方法一 : 一个线 程 , 在 paint 方法 中使用 两个输 出字 符串的 语句 ■练习

public void paint(Graphics g) { if y1<0 y1=200 else y1=y1-10; if y2>200 y2=0 else y2=y2+10; g.drawString(“hello, Java!”,20,y1,); g.drawString(“hello, Java!”,40,y2,); }

7.4 多线程问题 --- 如何写多线程 ■



382

方法二 : 定义两 个类 , 运行各 自的线 程 , 各 自有自 己的 paint() 方法 . 注意 : 两个 小应用 程序 必须是 panel 类或者 是 canvas 类 , 将小 应用的 区域 分成两 块 , 否 则不能 运行 paint 语句 .

7.4 多线程 问题 --- 线程间的通 信

383

1. 线程间的 通信 可以用 管道 流 ,. 线程 1 PipedOutputStream PipedInputStream 输出流 outStream 输入流 inStream

线程 2

创建管 道流 : PipedInputStream pis=new PipedInputStream(); PipedOutputStream pos=new PipedOutputStream(pis); 或: PipedOutputStream pos=new PipedOutputStream(); PipedInputStream pis=new PipedInputStream(pos);

384

7.4 多线程 问题 --- 线程间的通 信 ■

管道流不能 直

printStream

接读写

DataInputStream

PrintStream p = new PrintStream( pos ); p.println(“hello”); DataInputStream d=new DataInputStream(pis); d.readLine(); ■ 2. 通过 一个中 间类 来传递 信息 . s s 线程 1

中间类 m

线程 2 s=m.read()

m.write(s) write()

read()

7.4 多线程 问题 -- 线程间的通信

385

管道流 可以连 接两 个线程 间的 通信 ■ 下面的 例子里 有两 个线程 在运 行 , 一个 往外 输出信 息 , 一个 读入信 息 . ■ 将一个 写线程 的输 出通过 管道 流定义 为读 线 程的输 入 . outStream = new PipedOutputStream(); inStream = new PipedInputStream(outStream); new Writer( outStream ).start(); new Reader( inStream ).start(); ■

386

7.4 多线程 问题 -- 线程间的通信 主类 Pipethread 作为参 数传给 Writer 辅类 Writer Writer( outStream ) 线 程 将数据 写 类 到输出 流

管 道 流 输入流

辅类 Reader 线 程 类 从流中 读数据



(thread\Pipethread.class--f3.bat)

387 public class Pipethread 7.4static 多线程 问题 -- 线程间的通信 { public void main(String args[]) { Pipethread thisPipe = new Pipethread(); ■ . thisPipe.process(); } public void process() { PipedInputStream inStream; PipedOutputStream outStream; PrintStream printOut; try{ outStream = new PipedOutputStream(); inStream = new PipedInputStream(outStream); new Writer( outStream ).start(); new Reader( inStream ).start(); }catch( IOException e ){ } } }

class Reader extends Thread 388 { private inStream;// 从中 7.4 PipedInputStream 多线程 问题 --- 线程间的通 信读数据 public Reader(PipedInputStream i) { inStream = i; } public void run() { String line; DataInputStream d; boolean reading = true; try{ d = new DataInputStream( inStream ); while( reading && d != null){ try{line = d.readLine(); if( line != null ){ System.out.println( ”Read: " + line ); } else reading = false; }catch( IOException e){ } } catch( IOException e ){ System.exit(0); } try{ Thread.sleep( 4000 );} catch( InterruptedException e ){}}}

389 class Writer extends Thread 7.4 PipedOutputStream 多线程 问题 -- 线程间的通信 { private outStream;// 将数 据输出 private ■ . String messages[ ]= { "Monday", "Tuesday ", "Wednsday", "Thursday","Friday :", "Saturday:","Sunday :"}; public Writer(PipedOutputStream o) { outStream = o; } public void run() { PrintStream p = new PrintStream( outStream ); for (int i = 0; i < messages.length; i++) { p.println(messages[ i ]); p.flush(); System.out.println("WrIte:" + messages[i] ); } p.close(); p = null;

7.3 多线程 问题 --- 资源协调

390

1. 数据的 完整性 线程 1 线程 2

取过来

加1 后送 回去

资源

变量

线程 10

withdrwal()

余额

透支

withdrwal()

7.3 多线程 问题 --- 资源协调 ■ ■ ■ ■

391

对共享 对象的 访问 必须同 步 , 叫做条 件变量 . Java 语言 允许 通过监 视器 ( 有的参 考书称 其为管 程 ) 使用 条件变 量实 现线程 同步 . 监视器 阻止两 个线 程同时 访问 同一个 条件 变量 . 它的如 同锁一 样作 用在数 据上 . 线程 1 进入 withdrawal 方法时 , 获得 监视器 ( 加锁 ); 当线程 1 的方法执 行完 毕返回 时 , 释放监 视器 ( 开锁 ), 线程 2 的 withdrawal 线程 2方 监视 能进入 .

线程 1



withdrawal()

392

7.3 多线程 问题 --- 资源协调 ■



用 synchronized 来标识 的区域 或方 法即为 监视器 监视的 部分 。 一个类 或一个 对象 由一个 监视 器 , 如果 一 个程序 内有两 个方 法使用 synchronized 标 志 , 则他 们在一 个监 视器管 理之 下 . 线程 1

read



监 视 器

线程 2

write

一般情 况下, 只在 方法的 层次 上使用 关键 区

7.3 多线程 问题 --- 资源协调

393

此处给 出的例 子演 示两个 线程 在同步 限制 下工作 的情 况. class Account { statics int balance=1000; // 为什 么用 static? statics int expense=0; public synchronized void withdrawl(int amount) { if (amount<=balance) { balance-=amount; expense+=amount;} else { System.out.println(“bounced: “+amount);}

7.3 多线程 问题 --- 资源协调 ■

394

2. 等待同步 数据 . 生产者

write.

共享对 象

消费者

read

可能出 现的 问题 : • 生产者 比消费 者快 时 , 消费者会 漏掉 一些数 据没有 取到 • 消费者 比生产 者快 时 , 消费者取 相同 的数据 . • notify() 和 wait () 方法用 来协调 读取 的关系 . • notify() 和 wait () 都只能 从同步 方法 中的调

7.3 多线程 问题 --- 资源协调

395



notify 的作用 是唤醒 正在 等待同 一个 监视 器的线 程 .



wait 的作 用是让 当前 线程等 待 信息版 例子 read() 方法在 读信息 之前 先等待 , 直到信息 可读 , 读完后 通知要 写的 线程 . write() 方法 在写信 息之 前先等 待 , 直到信 息被取 走 , 写完 后通 知要读 的进 程 . DemoWait.class--->f4.bat

■ ■





7.3 多线程 问题 --- 资源协调

writer aaaa aaaa aaaa aaaa aaaa bbbbb cccccccccccc cccc cccc

396

reader aaaa bbbbb cccc

aaaa bbbbb

7.3 多线程 问题 --- 资源协调 ■ ■ ■ ■ ■ ■ ■ ■ ■

397

class WaitNotifyDemo { public static void main(String[] args) { { MessageBoard m = new MessageBoard(); Reader readfrom_m = new Reader(m); Writer writeto_m=new Writer(m); readfrom_m.start(); writeto_m.start(); } }

class MessageBoard { 398 { private message; 7.3String 多线程 问题 --- 资源协调 private boolean ready = false;( 信号 灯 ) public synchronized String read() { while (ready == false) { try { wait(); } catch (InterruptedException e) { } } ready = false; notify(); // 起始 状态先 写后 读 return message; } public synchronized void write(String s) { while (ready == true) { try { wait(); } catch (InterruptedException e) { } } message = s; ready = true; notify(); }}

class Reader extends Thread 7.3 MessageBoard 多线程 问题 mBoard; --- 资源协调 { private public Reader(MessageBoard m) { mBoard = m; } public void run() { String s = " "; boolean reading = true; while( reading ){ s = mBoard.read(); System.out.println("Reader read: " + s); if( s.equals("logoff") ) reading = false; } System.out.println("Finished: 10 seconds..."); try{ sleep( 10000 ); } catch (InterruptedException e) { } } }

399

class Writer extends Thread { private 7.3MessageBoard 多线程 问题mBoard; --- 资源协调 private String messages[ ]= { "Monday :------------------------", “…..”, "Sunday : ----------------------"};

400

public Writer(MessageBoard m) { mBoard = m; } public void run() { { for (int i = 0; i < messages.length; i++) { mBoard.write(messages[ i ]); System.out.println("Writer wrote:" + messages[i] ); try { sleep((int)(Math.random() * 100)); } catch (InterruptedException e) { } }

401

7.3 多线程 问题 --- 资源协调 多线成 问题 --- 资源 的协调 和锁 定 1. 死锁问 题 ■

线程 1

note

把“ pen” 给我 , 我 才能给 你“ note” 把“ note” 给我 , 我 才能给 你“ pen”

线程 2

pen

如果 你的持 有一 个锁并 试图 获取另 一个 锁时 , 就有死 锁的危 险 . 解决 死锁问 题的 方法 : 给条件变 量施 加排序

7.3 多线程 问题 ---daemon 线程 ■

什么是 daemon( 守护 )?



在客户 / 服务 器模式 下 , 服务器 的作用 是 等待用 户发 来请求 , 并按 请求完 成客 户的 工作 request 服务器端 客户端

daemon ■ ■ ■

守护线 程是 为其它 线程 提供服 务的 线程 守护线 程一 般应该 是一 个独立 的线 程 , 它 的 run() 方法是一 个无 限循环 . 守护线 程与 其它线 程的 区别是 , 如果 守护

402

7.4 小结

403

1. 实现线 程有两 种方 法 : ■ 实现 Ruannable 接口 ■ 继承 Thread 类 2. 在小应 用中通 常在 start 中创建 线程 3. 当新 线程被 启动 时 ,java 调用 该线程 的 run 方 法 , 它是 Thread 的核 心 . 4. 线程由 四个状 态 : 新生 , 运行 , 暂停 , 死亡 5. 线程间 的通信 方式 由三种 : 完全 共享数 据 , 通过监 视器 , 通过 join.

7.4 小结

404

6. 两个或 多个线 程竞 争资源 时 , 需要用 同步 的方法 协调资 源 . 7. 多个线 程执行 时 , 要用到 同步方 法 , 即使用 synchronized 的关 键字设 定同 步区 8. wait 和 notify 起协调 作用 9. 守护 进程的 特点 是当程 序中 制胜它 自己 时 , 会自动 中止 .

作业 ■

405

创建两 个线程 的实 例 , 分别 将一 个数组 从小 大大和 从达到 小排 列 . 输出 结果 .

第 8 章网络功能

北京 大学计 算机 系 代亚 非

第 8 章网络功能 ■ ■ ■ ■ ■ ■ ■ ■ ■

8.1 Java 与 internet 8.2 使用 URL 8.3 访问 cgi 8.4 URL 连接 8.5 Socket 8.6 internet 应用 8.7 数据 报 8.8 JDBC 8.9 小结

407

8.1 Java 与网络 ■ ■ ■







408

Java 语言 取得 成功的 领域 之一就 是网 络 ( 其他语 言 ) 数页 代码 ---->(Java) 一条语 句 TCP/IP( 传输控 制协 议 / 网间协 议 ) 是 internet 的主要 协议 , 定义了 计算机 和外 设进行 通信 所使用 的规则 ( 应用 层 , 传输 层 , 网络 层 , 链 路层 ). 大多数 基于 internet 的应用 程序 被看作 TCP/IP 协议 的上 一层 . 如 : ftp, http, smtp, pop3, telnet, nntp 等 IP 地址 :TCP/IP 网络中 的每台 计算 机都有 唯 一的地 址 --IP 地址 . 在 Java 中 , 有一个 用来存 储 internet 地址的 类

8.1 Java 与网络 例 : 获取 本机的 IP 地址 import java.net.*; public class getLocalHostTest { public static void main() { InetAddress myIP=null; try {myIP=InetAddress.getLocalHost();} catch{UnknowHostException e){} System.out.println(myIP); } } 创建 inetAddress 类不用 构造函 数 ( 不用 new) ■

409

8.1 Java 与网络

410

下面的 例子演 示 java 如何根 据域名 自动 到 DNS 上查找 IP 地址 ( 与 DNS 服务器 的连接 减至 一行 ) import java.net.*; public class getIP { public static void main(String args[]) { InetAddress pku=null; try{ pku= InetAddress.getByName(“www.pku.edu.cn”); }catch(UnknowHostException e) {} System.out.println(pku); } }

8.1 Java 与网络

411



Java 提供的网 络功 能有三 大类 : URL, Socket, Datagram.



URL 是三 大功能 中最 高级的 一种 , 通过 URL Java 程序可 以直接 送出 或读入 网络 上 的数据 .



Socket 是传统 网络程 序最 常用的 方式 , 可以 想象为 两个不 同的 程序通 过网 络的通 信信 道.



Datagram 是更 低级的 网络 传输方 式 , 它把 数据的 目的纪 录在 数据包 中 , 然后直 接放在

8.2 使用 URL ■

■ ■

■ ■ ■ ■ ■

412

8.2.3 通过 URL 读取 WWW 服务 器上 的数 据 将 URL 位置的 数据 转成一 个数 据流 URL url=new (http://www.pku.edu.cn/index.html” DataInputStream data=new DataInputStream(url.openStream()); 从数据 流中读 出数 据 String line=data.readLine() whileDataInputStream ( line ! =null ) URL Java 程序 line=data.readLine()

8.2 使用 URL

413

http://www.pku.edu.cn/ <...>

例 : 从给 定的位 置中 读数据 (ReadURLData.prj) connect TextField a

Frame 主类

str=a.getText()

action

URL url=new URL(str) URL url

TextArea c

Button

b=url.openStream()

line_str=b.readLine() c.appendText(line_str); DataInputStream b

public boolean action(Event evt, Object arg) { try{ 8.2 使用 URL String line; String str=textfield.getText(); url=new URL(str); data=new DataInputStream(url.openStream()); while((line=data.readLine())!=null){ textarea.appendText(line); } data.close(); }catch(MalformedURLException me){ System.out.println("Error URL"); }catch(IOException ie){ System.out.println("Error IO"); } return true; }

414

415

8.3 访问 cgi ■

起始页 上的计 数器 及 cgi 程序 (script)

欢迎来访 , 你是第

1 2 7

个来 访者 !

你是 第 个来 访者 客户端 HTML

cgi 程序名 img

服务器 端 CGI 程序

num+ +

416

8.3 访问 cgi Your name form send

Web 服务器

cgi 程序

数据 库 服务 器

reset



8.3 访问 cgi ■



■ ■

417

cgi( 公共网 关 ) 程序 可以用 任何 一种变 成语 言来写 . cgi 将 web 与外部 程序连 接起 来(数 据库 查 询等) cgi 程序 要放在 指定 的目录 下 cgi-bin 目录 . cgi 通常 以 GET 或 POST 接收 数据 ,

8.3 访问 cgi ■



418

从小应 用中运 行 CGI 程序 (GET) Socket runcgi=new Socket(“www.jamsa.com”,80); OutputStream os=runcgi.getOutputStream(); PrintStream ps=new PringtStream(os); ps.println(“GET/cgi-bin/testcgi? arg1=val1& args2=val2&arg3=val3”); http://< 机器 名 >/?< 查询串 >

8.3 访问 cgi ■





■ ■

419

get 方法把 数据作 为命 令行中 的参 数传递 给 此 cgi ( 适用于 少量参 数的 情况 ) Java cgi 程序 通过 main 方法 的串参 数接 受 命令行 . C cgi 程序 通过访 问环 境变量 中的 查询串 QUERY_STRING, 来获得 查询串 中的 信息 . 例如在 C 程序中 : char *str; str=getevn(QUERY_STRING);

import java.io.*; import java.net.*; 420 public class 8.3 test_cgi 访问 cgi { public static void main(String[] args) throws Exception { URL url = new URL ("http://pact518.hit.edu.cn/~wangqb/CGIBIN/test_2.cgi?012345678"); URLConnection connection = url.openConnection(); } } #include "stdio.h” main() { char *Query_String; Query_String = getenv("QUERY_STRING"); /*treatment*/ }

8.3 访问 cgi ■ ■

■ ■ ■ ■ ■ ■ ■

421

从小程 序中运 行 CGI 程序 (post) post 方法 通过标 准输 出向 cgi 传送 参数 ( 适合参 数 较多 的情况 ) 用 post 方法要 有数据 输出 流 Socket runcgi=new Socket(“www.jamsa.com”,80); 建立 socket 连接 DataOutputStream ds=runcgi.getOutputStream(); 建立输 入流 ds.println(“POST/cgi-bin/guestbook.pl”); 运行 cgi 程序

8.3 访问 cgi ■ ■ ■ ■ ■

■ ■

通知发 送的数 据类 型 ds.println(“Content-type:plain/text”); 通知发 送的数 据长 度 String data=“Hello world” ds.println(“Contentlength”+data.length+”\n”); 发送数 据 ds.println(data);

422

8.3 访问 cgi ■



■ ■ ■ ■ ■

423

cgi 接收 post 方法送 来的数 据 , 就像从 标准 输入读 数据一 样 . getchar();gets();(C 语言的 情况 )( 要建 立数据 流) cgi 返回 数据时 要指 明数据 类型 等信息 : printf(“Content-type:text/plain\n\n”); 或 printf(“Content-type:text/html\n\n”); 或 printf(“Content-type:image/gif\n\n”); cgi 规定 服务器 与外 部程序 交换 信息的 协议 .

424

8.3 访问 cgi 客户端 java 程序

URL 连接 DataInputStream

服务 器端 cgi 程序

返回结果

String count_srt URL url=new URL(“http://202.118.239.38/cgi-bin/count.cgi”); DataInputStream data=new DataInputStream(url.openStrean()); count_str=data.readLine();( 此处读 入的 是 cgi 程序的输 出 ) g.drawstring(“You are the “+count_str+ ”visitor”);

8.4 URL 连接

425

URL 双向 通信 (URLConection) ■ URLConnection 是一 个一 http 为中心的 类 1. 建立连接 URL url=new URL(“http://www.yahoo.com/”); URLConnection con=url.openConnection(); 2. 向服务器 端送 数据 PrintStream outStream=new PrintStream(con.getOutputStream()); outStream.println(string_data); ■

8.4 URL 连接

426

3. 从服务 器读数 据 DataInputStream inStream=new DataInputStream(con.getInputStream()); inStream.readLine(); ■ 从 URL 中得到 的输 出流通 常与 一个 CGI 程 序结合 一起工 作 客户端

DataOutputStream 服务 器 connectiuon java 程序 DataInputStream

STDIN CGI 程序 getchar() ( 排序 putchar() sort.cgi) STDOUT

8.4 URL 连接 ■

■ ■





427

URLConnection 同时 支持 get 和 post 两种 机 制一般 的格式 . 缺省情 况下位 post method 方式 URL 和 URLConnection 的区别 在于前 者代 表一个 资源的 位置 , 后者代 表一 种连接 下面的 例子中 Java 程序 访问 cgi 程序 , 并传 给它 10 个数据 ,cgi 程序 胡接收 后 , 排序 , 并 传送回 来 . 连接的 建立、 数据 流的建 立、 java 如何发 数据、 如何接 收数 据、 cgi 程序 如何发 送和 接收

8.4 URL 连接

428

import java.io.*; import java.net.*; public class test_cgi {public static void main(String[] args) throws Exception { URL url = new URL("http://pact518.hit.edu.cn/~wangqb/cgibin/test.cgi"); URLConnection connection = url.openConnection(); connection.setDoOutput(true); PrintStream out = new PrintStream(connection.getOutputStream()); out.println("0123456789");

8.4 URL 连接

429

DataInputStream in = new DataInputStream(connection.getInputStream()); String inputLine; while ((inputLine = in.readLine()) != null) System.out.println(inputLine); System.in .read (); in.close();

#include "stdio.h" main() 8.4 URL 连接 { int array[10]; int i,j,k; printf("Content-type:text/plain\n\n"); for(i=0;i<=9;i++) array[i]=getchar(); //read data from InputStream for(i=0;i<=8;i++) { for(j=i;j<=9;j++) { if(array[i]<array[j]) { k=array[j]; array[j]=array[i]; array[i]=k; } } } printf("\nOutput number!\n"); for(i=0;i<=9;i++) { printf("%c",array[i]); } //write data to OutputStream }

430

431

8.5 Socket

8.3.1 什么 是 socket? ■ Socket 是网络 上运行 的程 序之间 双向 通信链 路的最 后终结 点 网络服 务 IP,port 客户程序

Socket

20 21 23

ftp telnet Mail finger

IP 与端口的 组合 得出 一个套 接字 , 可以完 全 分辨 internet 上运 行的 程序

端口 数据结 构 I/O 缓冲区

432

8.5 Socket ■



端口号 :TCP/IP 协议 为每种 服务 定义了 一个 端口 , 当一台 计算机 上运 行不同 服务 器程序 时 ,, 根据端 口号不 同提 供相应 的服 务 . 端口号 不是计 算机 上的物 理连 接器 , 它只是 具有软 件意义 的假 想端口 internet 服务 端口 号

internet 服务

端口号

ftp telnet

21 23

www nntp

80 119

DNS

53

POP3

110

8.5 Socket ■







433

在服务 器端通 过指 定一个 用来 等待的 连接 的端口 号创建 一个 ServerSocket 实例 . 在客户 端通过 规定 一个主 机和 端口号 创建 一个 socket 实例 , 连到 服务器 上 . ServerSocket 类的 accept 方法使 服务器 处于 阻塞状 态 , 等待 用户 请求 Socket 类和 ServerSocket 是基于 TCP 协议 的 ,TCP 协议提 供 64 个端 口 ,256 以内位保 留端 口.

8.5 Socket ■

■ ■

434

在本地 机器上 测试 网络程 序用 回送地 址 Socket socket=new Socket(“127.0.0.1”,2525); 如果网 络的一 端已 经关闭 , 另一 端读到 null. 建立 socket 连接 后 , 还应 该建立 输入 输出数 据流 .

435

8.5 Socket Server ServerSocket(port #) Server Socket.accept() Socket()

Client Socket(host, port #) Attempt the connection

OutputStream InputStream

OutputStream InputStream

Close Socket

Close Socket

8.5 Socket ■

436

下面的 例子演 示一 个简单 的服 务器与 客户 的交互 , 即服务 器等 待 , 客户访问 , 相互 通 一次信 息 .

. 8.5 Socket 成员 定义数据

定义数据 成员

8.5 Socket

创建 Socket 实例 创建服务 器 ( 端口号 )1111 1 1 1 1 1 waiting for user . .0 0 . 7 2 1 服务器等 待 建立 socket 流 网络连 接 建立 socket 流 connetcting client...

读 socket 流 ( 接收并显 示 ) : n i g

向用户发 出一 个字符 串 lo 读客 户 端信息 User :java 提示 用户 登录成 功

a v a j

n

Logi

送用户 名给 服务 器

l u f s s 读 socket 流 succe

关闭流

437



例 : 显示 服务器 与客 户机间 的通 信 ( 服务 器端 )

8.5 Socket

438

PrintStream ps=null; DataInputStream dis=null; String username; 创建 服务 器 ( 端口号 ) ServerSocket serverSocket=null; Socket clientSocket=null; 定义 数据 成员

服务器 等待 网络连 接

try { serverSocket=new ServerSocket(1111); }catch (IOException e) { System.out.println( “Error”+e); System.exit(1);}

try { clientSocket=serverSocket.accept(); }catch (IOException e){ System.out.println("Accept failed.");System.exit(1);}

439

8.5 Socket 定义 数据 成员 ■

.

创建服务 器 ( 端口号 ) 服务器 等待 网络连 接

建立 socket 流

向客 户发 出登录 要求

ps=new PrintStream(clientSocket.getOutputStream()); dis=new DataInputStream(clientSocket.getInputStream()); ps.println("login:"); ps.flush();

440

8.5 Socket 定义 数据 成员

创建 服务 器 ( 端口号 )

服务器 等待 网络连 接

读客户 端信息

向用 户发 出登录 要求

建立 socket 流

通知 客户 连接完 毕

if ((username=dis.readLine())==null) { System.out.println("readLine returned null"); System.exit(1); } System.out.println("Username:"+username); ps.println("login sucessful"); ps.flush(); System.out.println(username+" has logged off");}

8.5 Socket

441

例 : 显示 服务器 与客 户机间 的通 信 ( 客户 端 PrintStream output; 定义 数据)成员 DataInputStream input; String string; 创建 Socket 实例 Socket socket=null; ■

建立 socket 流 try{socket=new Socket("127.0.0.1",1111);

}catch(IOException e){ System.out.println("Error ”+e); return;} input=new DataInputStream(socket.getInputStream()); output=new PrintStream(socket.getOutputStream());

. 8.5成员 Socket 定义数据

442

System.out.println(input.readLine());

. 实例 创建 Socket ■

建立 socket 流

System.in.read(bArray); String s=new String(bArray,0); output.println(s);

读 socket 流 ( 看到提示 )

System.out.println(input.readLine()); System.out.print("Logging off...");

从键盘上 读送 用户 socket.close(); 名送给 服务 器端 input.close(); 读服务器 反馈 关闭流

output.close(); System.out.println("Done");

. 8.5定义 Socket 数据 成员

定义 数据 成员

. 8.5 Socket

创建 Socket 实例 创建 服务 器 ( 端口号 )1111 1 1 1 1 1 waiting for user . .0 0 . 7 2 1 服务器等 待 建立 socket 流 网络连 接 建立 socket 流 connetcting client...

读 socket 流 ( 看到提示 )

向用 户发 送字符 串 读客 户 端信息 User :java 提示用户 登录 成功

: n i g

lo

a v a j

n

Logi

送用户 名给 服务 器

l u f s s 读 socket 流 succe

关闭流

443

444

8.5 Socket ■ ■ ■

■ ■

支持多 客户 一种解 决方案 : 一台计 算机上 一次 启动多 个服 务器程 序 , 只 要端口 号不同 .\myjava\clientAndServerTest myserver <-------->myclient----f8.bat myserver2<-------->myclient2----f9.bat Server1(1111)

client(1111)

Server2(2222)

client(2222)

Computer 1

445

8.5 Socket ■ ■

第二种 方案 : ( 支持多客 户 )

client1

serverthreadserverthread

Server

client2

将服务 器写 成多线 程的 , 不同 的线程 为不 同 的客户 服务 . main() 只负责 循环等 待 线程负 责网 络连接 , 接收 客户输 入的 信息

446

8.5 Socket ■

.

客户 1

线程 线程( ()

)

服务器 客户 2

线程 线程 2 (

)

. 8.5定义数据 Socket 成员

定义数据 成员

创建 Socket 实例 创建服务 器 ( 端口号 )1111 1 1 1 1 1 waiting for user . .0 0 . 7 2 1 服务器等 待 建立 socket 流 网络连 接 读 socket 流 ( 看到提示 )

建立 socket 流 connetcting client... : n i 提示用户 输入 客户名 log 读客户 端信息 User :java 提示用户 登录 成功

a v a j

n

Logi

送用户 名给 服务 器

l u f s s 读 socket 流 succe

关闭流

447

public static void main(String args[]) 8.5 Socket {ServerSocket serverSocket=null; try{serverSocket=new ServerSocket(1111); }catch(Exception e){ System.out.println(”Error”+e);System.exit(1);} while(true) {Socket clientSocket=null; System.out.println("waiting for users..."); try{ clientSocket=serverSocket.accept(); }catch(IOException e){ System.out.println("accept failed:"+e);} new serverThread(clientSocket).start(); }}

448

class serverThread extends Thread { DataInputStream input; PrintStream output; 8.5 Socket String user; Socket clientSocket; serverThread(Socket clientSocket) { this.clientSocket=clientSocket; } public void run() { try{ input=new DataInputStream (clientSocket.getInputStream()); output=System.out; user=input.readLine(); System.out.println(user+" Connected!"); }catch(IOException e){ } try {while(true) { String string; if((string=input.readLine())==null) break; output.println(user+string); output.flush(); } }catch(Exception e){ return; } System.out.println(user+ "has disconnected."); try{ clientSocket.close(); input.close(); }catch(Exception e){ return; } }}

449

450

8.5 Socket ■

例 : 通过 服务器 交换 信息 (exchangebyserver)

Hello 1076

Hello 1071 abc

def server

WAIT GO…def

WAIT GO…abc

8.5 Socket . IP client1 客

服务器 (1111)



户 线程

451

accept socket

IP client2

客 户

线程

服务器 一端为 了能 接收多 个客 户的信 息 , 它的 输入流 , 输出 流都是 数组 型的 . ServerSocket.accept() 等待 用户连 接 , 一旦连 接 上 , 则调 用服 务程序 . 服务程 序的主 要内 容是网 络的 读写 , 多客户 的 原因 , 网络读 写的功 能用 多线程 实现 , 因此 将 此部分 功能单 独分 离出来 , 构成 线程 类

452

8.5 Socket •服务器端 ServerSocket client1 client2 read_net_input write_net_output()

reader.run

reader.run

server Socket accept() serviceRequest getInputStream getOutputStream reader.start( )

453

8.5 Socket 客户端

socket getInputStream

client()

getOutputStream writer.start()

getLocalPort()

write_net_output()

write.run read_net_input() paint() close_server()

服 务 器

454

8.7 Datagram ■ ■



TCP/IP 传输层 由两个 并列 的协议 :TCP,UDP. 一般套 接字 (TCP) 提供 一个可 靠的 传输模 型作为 两个网 络端 点的字 节流 , 有纠错 能力 . UDP 没有保 持的连 接和 数据流 , 数据 报是一 个网络 上发送 的独 立信息 , 它的 到达 , 到达 时间 , 以及内 容不能 socket 得到 保证 . client

server datagram server

client

8.7 Datagram ■



■ ■

455

TCP 提供 高可靠 性服 务 , 适用 于一次 要传 输 交换大 量报文 的情 况 , 信道 上传 递的包 不需 要源地 址和目 的地 址 UDP 提供高 效率服 务 , 适用于 依次传 输交 换 少量报 文的情 形 ( 如数据 库查询 ), 每个数 据包要 包含目 的地 址和端 口号 . 数据报 文的使 用以 包为中 心 : 打包 , 拆包 . Java.net 包支 持两种 不同 的在网 络上 送数据 的方法 : 一般套 接字和 数据 报文套 接字 .

8.7 Datagram ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

456

发出报 文的标 准步 骤如下 : 1. 定义 数据 成员 DatagramSocket socket; DatagramPacket packet; InetAddress address;( 用来 存放接 收方 的地址 ) int port; ;( 用来 存放 接收方 的端 口号 ) 2. 创建 数据 报文 Socket 对象 try {socket=new DatagramSocket(1111);} catch(java.net.SocketException e) {} socket 绑定到 一个本 地的 可用端 口 , 等待接 收客户 的请求 .

8.7 Datagram ■

■ ■ ■ ■ ■

■ ■ ■

457

3. 分配并 填写数 据缓 冲区 ( 一个字 节类型 的 数组 ) byte[] Buf=new byte[256]; 存放从 客户端 接收 的请求 信息 . 4. 创建一 个 DatagramPacket packet=new DatagramPacket(buf, 256); 用来 从 socket 接收数 据 , 它只有 两个参 数 5. 服务 器阻塞 socket.receive(packet); 在客 户的 请求报 道来 之前一 直等 待

8.7 Datagram ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

6. 从到 来的 包中得 到地 址和端 口号 InetAddress address=packet.getAddress(); int port=packet.getPort(); 7. 将数 据送 入缓冲 区 或来自 文件 , 或键盘 输入 8. 建立 报文 包 , 用来从 socket 上发 送信息 packet=new DatagramPacket (buf,buf.length, address,port); 9. 发送 数据包 10. 关闭 socket socket.send(packet); socket.close();

458

8.7 Datagram 客户端 接收包 的步 骤如下 : 1. 定义数 据成员 int port; InetAddress address; DatagramSocket socket; DatagramPacket packet; byte[] sendBuf=new byte[256]; 2. 建立 socket socket=new DatagramSocket(); ■

459

8.7 Datagram ■ ■ ■ ■ ■ ■ ■ ■ ■ ■

460

3. 向服务 器发出 请求 报文 address=InetAddress.getByName(args[0]); port=parseInt(args[1]); packet=new DatagramPacket(sendBuf,256,address,port); socket.send(packet); 这个包 本身带 有客 户端的 信息 4. 客户机 等待应 答 packet=new DatagramPacket(sendBuf,256); socket.receive(packet);( 如果没有 到就 一直等 待 , 因此实 用程序 要设 置时间 限度 )

8.7 Datagram

461

5. 处理 接收到 的数 据 String received=new String(packet.getData(),0); ■

System.out.println(received);



数据报 套接字 首先 是强调 发送 方和接 收方 的区别 , 同时也 指出 服务器 和客 户之间 的不 同: 一个客 户机必 须事 先知道 服务 器的地 址和 端口 , 以便进 行出事 连接 一个服 务器从 它接 收到的 数据 报文中 读取 客户端 的地址 和端 口 .





462

8.7 Datagram 建立 数据 ■ . 报 socket(); 建立 一个 报文包 packet 等待 请求 报文

建立数据 报 socket 建立一个 请求包 发出 请求

获得对方 地址

创建接收 包

构成 信息 包

等待接收

发送出去

8.8 小结 ■







■ ■

463

实现网 络功能 要靠 URL 类 , URLConection 类 , Socket 类和 DatagramSocket 类 网络上 的数据 传送 是将网 络连 接转换 成输 入输出 流 DataInputStream 和 DataOutputStream (PrintStream) 是网 间流的 载体 . URL 适用 于 web 应用 , 如访问 http 服务 器 是高层 服务 URLConection 的另 一边通 常是 cgi 程序 cgi 程序 完成客 户端 与外部 程序 的交互

8.6 小结 ■ ■





■ ■

■ ■

464

向 cgi 传送数 据有 get 和 post 两种方 法 cgi 通过 访问环 境变 量或读 标准 输入获 得数 据 回绕地 址可用 于在 本地机 器上 调试网 络程 序 Socket 适用于 面向连 接的 , 可靠 性要求 高的 应用 Datagram 适用 于效率 要求 高的应 用 Socket 是由 IP 和端口 构成的 一种 网上通 信 链路的 一端 Socket 通信要 分别运 行服 务器和 客户 程序 服务器 程序是 多线 程的 , 可处理 多个客 户的

作业 ■ ■ ■

编写一 个会话 程序 要求 : 会话双 方可以 自由 通话 , 看到对 方发 来“ bye” 则退 出

465

北 京 大 学

分布对象技术 北京 大学计 算机 系 代亚 非

466

第九章 ■ ■ ■ ■ ■ ■

9.1 9.2 9.3 9.4 9.5 9.6

分布 对象技术

分布对 象技术 要解 决的基 本问 题 分布对 象技术 概论 分布对 象的核 心概 念 分布对 象主流 技术 介绍 分布对 象处理 技术 --- 发展 趋势 分布对 象主流 技术 开发过 程

467

9.1 分布对 象技术要解 决的基本 问题 ■ ■ ■ ■

以下情 况是不 是分 布式? 1 、浏览 2 、两个 及其之 间的 通信 3 、计算 引擎

468

9.1 分布对 象技术要解 决的基本 问题

分布对象技术要解决的问题 C++ 编译器完成连接 C++Main Program

A 机

C++ Object 同一地址空间 B机

就是支持访问异地对象

469

9.1 分布对 象技术要解 决的基本 问题

470

分布 式系 统的客 户 / 服务器 模型 两层 客户 / 服务 器模层的概念 型 ( 胖客户 ) (TIERS): 在客户 / 服务器意 义上将 , 一个层就代表一个具有定义 三层 客户 / 服务 器模 型 ( 瘦客户 ) 好的特定功能平台 , 一个平台就是就 N 层客户 / 服务 器模 型是一个计算机软件和硬件的组合 .

. 应用程 序对象 应用程 序对象

.

应用程 序对象

9.1 分布对 象技术要解 决的基本 问题

471

一个 分布式 系统 的例子 ( 原始 的方法 ) WEB 服务 器 1

电话订购 客户 1

电话订购 客户 2

WEB 服务器 2

...

... 电话 订购 客户 n

中心订 购 服务器

WEB 服务器 m

Internet

9.1 分布对 象技术要解 决的基本 问题 ■

使用多 个服务 器分 割用户 电话订 购 客户 1 电话订 购 客户 2 电话订 购 客户 1 电话订 购 客户 2 电话订 购 客户 1 电话订 购 客户 2

WEB 中心订购 服务器 1

服务器

1

WEB 服务器

2

WEB 中心订购 服务器 1

服务器

1

WEB 服务器

2

WEB 中心订购 服务器 1

服务器

1

WEB 服务器

2

Internet

472

9.1 分布对 象技术要解 决的基本 问题 考虑 负载平 衡的 问题 订购 服务 器 1

调度 程序

...

订购 服务 器 2

... 订购 服务 器 n

WEB 服务 器 1

Internet

473

9.1 分布对 象技术要解 决的基本 问题 问题案例一:如何综合越来越多的数据 库资源,以适应不断发展的业务逻辑 如何处 理用户界面 风格多样性 ? 如何处 理“肥客户 ”应用问题? 如何加 入综合多 个数据库的业 务逻辑? 如何集 成其它非 数据库操作 的业务逻辑?

客户

Oracle

Sybase

Informix

474

9.1 分布对 象技术要解 决的基本 问题 ■

问题案 例二 -- 如何 管理 不断接 入的 新应用 N1

N2

管理器

...

Nk

475

9.1 分布对 象技术要解 决的基本 问题 单机应用开发环境

分布式应用开发环境

汇编语言

Socket API

Fortran 语言

RPC

OOP 语言 分布对象技术 软构件技术

476

9.1 分布对 象技术要解 决的基本 问题 ■

我们都 听到了 哪些 词汇 ? ActiveX Controls CORBA/IIOP COM/DCOM

RPC

构件 模型 Java/RMI 分布 对象

EJB 还有 还有 … ...

477

9.2 分布对 象技术概论

478

分布计 算是近 二十 年来影 响计 算机急 速发 展的 最活跃 因素之 一 ■ 经历了 两种不 同的 技术路 线 1. 理想 的技术 路线 (80 年代 学术界 普遍 追求的 目 标) 试图在 互连的 计算 机硬件 上部 署全新 的分 布 集中 式 式操 作系统 2. 现实 的技术 路线 (90 年代工 业界普 遍遵 守的路 线 ) 客户 / 服务 器 在网络 计算平 台上 部署分 布计 算环境 提供开 发工具 和公 共服务 ■

9.2 分布对 象技术概论

479

分布式 系统比 想象 的要复 杂 : 1. 异构 环境下 的应 用互操 作的 问题 2. 系统 管理问 题 3. 系统 安全的 问题 4. 传统 的面向 过程 的技术 在开 发大型 软件 系统 的局限 性 ■ 面向对 象技术 用于 分布式 ( 分布 对象技 术 ) 标 志第二 代分布 式计 算技术 进入 成熟和 蓬勃 发 展时期 . ■ 分布式 对象技 术始 于 90 年代初 ,已 经发展 成 为当今 分布异 构环 境下建 立应 用系统 集成 框 架和标 准构件 的核 心技术 ■

480

9.2 分布对 象技术概论 第一 代 (80 年代中 ~90 年代 初 ) 面向的主 要问题

体系结构

关键技术 的特点

成果

第二代 (90 年代 )

信息共享

异构环境下 的信 息互操作

经典 的客户 / 服务器模 型

面向对 象的多 层客户 服务器 模型

沿用 传统的计 算概 念和设施 ( 如过 程调用和 文件 设施 ) 能够提 供丰富的分 布式系 统服务 , 良 好的分 布式系统管 理和典 型的分布系 统应用

第三 代 ( 从现在开 始) 智能化的 协同工作 自主的多 agent 模型

将面向对 象技 术应用到 分布 计算

面向 agent 的 拟人化交 互环 境

已经成为 建立 集成构架 和软 构件标准 的核 心技术

概念 验证系统 令人 鼓舞 , 尚 未达 到广泛应 用与 协同工作 的成 熟程度

9.2 分布对 象技术概论 ■

481

分布对 象技术 的核 心概念 有那 些 ? 1 分布 对象的 位置 对客户 透明 2 可以 被远程 客户 应用以 方法 调用的 形式 访问 3 分布 对象所 使用 的语言 要对 客户透 明 4. 分布 对象运 行的 系统平 台对 客户透 明 5. 分布 对象具 有移 动性

( 允许时 间 , 空间 , 语言 , 操作平台 的差 异性 )

9.2 分布对 象技术概论

482

C++ 对象 B机 VB

一个应用逻辑 一个应 用逻辑 对象 对象

对象 C机

对象 对象

对象 A机

Java

VB

A 机 Java 对象 D机

9.2 分布对 象技术概论

分布式 系统 , 分布 对象与 软构 件是什 么关 系 ? 1. 独立 于特定 程序 设计语 言和 应用系 统 , 可重 用和自 包含的 软件 成分称 为软 构件 . 2. 分布 对象是 一种 典型的 软构 件 3. 分布 式系统 是分 散在两 个或 更多地 方的 计算 机上为 了实现 一个 共同的 目标 而共同 工作 的 软件实 体集合 . ■ 分布式 应用系 统中 的成员 : 1. 创建 和维护 分布 对象的 实体 的应用 称为 服务 器 2. 按照 接口访 问该 对象的 应用 称为客 户 3. 服务 对象同 时可 以是其 它服 务对象 的客 户 ■

483

9.2 分布对 象技术概论

软构件 模型的 开发 的目的 是什 么 ? 重用 , 高层 开发 , 通过工 具进 行自动 化开 发 , 简化开发 过程 等 . ■ 软构件 模型的 开发 的手段 是什 么 ? 软构件 开发者 : 1. 处理 底层的 实现 细节以 及与 此相关 的类 . 2. 定义 构件的 基本 的体系 结构 , 构件界面 的结 构 , 与 其它 构件 及容器 相互 作用的 机制 软构件 应用者 : 在可视 的开发 环境 将以存 在的 构件进 行简 单的组 装。 ■

484

9.2 分布对 象技术概论 ■ ■

软构件 模型的 例子 ? OMG 的 COR BA 、 Mic rosof t 的 COM/ DCOM 和 SUN 的 Jav a/EJ B 。

485

486

9.3 分布对 象技术 ---- 基本工 作原理 ■ ■

什么是 对象请 求代 理 ? 支持客 户访问 异地 分布对 象的 核心机 制称 为 对象请 求代理 ORB(Object Request Broker) 对象 B机

网 一个应用逻 辑 络 C++Main 计 一个应 用逻辑 对象适配器 A 的代理 算 Program 对象 环 对象 Agent 1 对象 境 对象 对象 A机 A机

ORB

Object A 对象 Object Object A A

C机

Agent 2 对象 D机

9.3 分布对 象技术 ---- 基本工 作原理

客户

客户构造 构架

服务器

相同的接口

相同的接口 应用逻辑

服务器构 造构架

分布对象的实现 ➏







对象请求分发器 ORB

分布对象 代理





对象请求适配器 ➑

对象请求感知器 ➒

网络传输访问 ➋



487

488

9.3 分布对象技术 ---- 基本工作原理 如何管理客户应用 O1

客户

O2

预约 客户

通知

查询

还书

通知 预约 客户

虚拟 图书馆

预约服务

O1R O2R O3R O4R O5R O6R

ORB

ORB

客户

Oracle

O3

还 / 借处理

O5

O4

O6

Sybase

Informix

9.3 分布对 象技术 ---- 基本工 作原理 ■

489

对象服 务 : 支持 分布式 系统 正常工 作的 各类基 本的系 统级服 务 1. 名字 管理 2. 事件 通告 3. 对象事务 管理 4. 对象生命 周期

Ó¦ ÓöÔÏ ó

Í ¨ Óù ¦ ÄÜ

¶ÔÏ óÇëÇó´ úÀí (Object Request Broker)

¶ÔÏ ó· þÎ ñ

利用这 些服务 创建 一个新 的对 象而不 必知 道对象 在哪

9.3 分布对 象技术 ---- 基本工 作原理 ■

■ ■ ■ ■ ■

490

通用功 能 : 支持 分布式 系统 高效开 发和 有效 工作的 各类面 向领 域的常 规服 务和工 具 Ó¦ ÓöÔÏ ó Í ¨ Óù ¦ ÄÜ GUI 数据库 服务 电子邮 件服务 ¶ÔÏ óÇëÇó´ úÀí (Object Request Broker) 系统管 理服务 面象应 用领域 的领 域构架 ¶ÔÏ ó· þÎ ñ

9.3 分布对 象技术 ---- 基本工 作原理 ■



491

应用对 象 : 涉及 各种应 用软 件 , 它在对象 服 务和公 共设施 帮助 下完成 相应 的应用 逻辑 ORB 是一 条软总 线 , 把分 布式系 统中 各类 对象和 应用连 接成 相互作 用的 整体 Ó¦ ÓöÔÏ ó

Í ¨ Óù ¦ ÄÜ

¶ÔÏ óÇëÇó´ úÀí (Object Request Broker)

¶ÔÏ ó· þÎ ñ

9.4 分布对 象主流技术 ■



492

分布对 象技术 的实 质性进 步 : 1. 使面 向对象 技术 能够在 异构 的网络 环 境中得 以全面 , 彻底 , 方便的 实施 2. 有效 地控制 系统 的开发 , 管理 , 维护的 复杂性 分布对 象的主 流技 术 1. OMG的CORBA 2. Microsoft的ActiveX/DCOM 3. SUN的Java/RMI

9.4 分布对 象主流技术 ---CORBA

493

CORBA ■









OMG 是一 个非盈 利性 国际组 织 , 致力于 使 CORBA 成为 “无 所不在 的中 间件” 1989 年成立 , 当时 仅有 8 家成员 , 目前 有 900 家成员 OMG 制定 的分布 对象 计算标 准规 范 , 按照 这些规 范设计 的开 发的分 布式 计算软 件环 境可以 在几乎 所有 的主流 硬件 平台和 操作 系统上 运行 CORBA 的通 信协 议是 IIOP(Internet InterORB Protocol). CORBA 结构 集中 围绕着 两个 主要元 素 : ORB 和 IDL,CORBA 的语言 独立性 和平 台独

9.4 分布对 象主流技术 ---CORBA ■ ■

494

ORB 负责定位 服务 器对象 IDL 是建 立接口 的基 础 ¿Í »§ (Client)

¶¯Ì ¬ ¼¤»î ½Ó¿Ú

IDL Stubs

¶ÔÏ óÊµÏ Ö (Server)

ORB ½Ó¿Ú

¾² Ì ¬IDL Skeletons

ORBÄÚº Ë

¶¯Ì ¬ Skeleton

¶ÔÏ ó ÊÊÅäÆ÷

9.4 分布对 象主流技术 ---CORBA ■







495

IDL Stubs 、 IDL Skeleton 是由 IDL 编译器 编译产 生的, 用于 静态调 用, 有较好 的性 能 和效率 。 动态调 用接口 和动 态 Skeleton 用于 动态调 用 ,使得 即使在 编译 时不知 道可 用的服 务器 和 接口消 息,也 能完 成客户 与服 务器的 作用 CORBA 中的说 明语言 ,称 作 OMGIDL ,用以 描述对 象的 接口 。它本 身不 是一个 可编 程语 言,但 它为程 序员 提供了 语言 的独立 性, 他 们不必 知道调 用者 所采用 的语 言。 IDL 的词 法,语 法规 则遵从 C++ 的规定 ,但 加入了 一些关 键词 支持分 布概 念。用 接口 描 述语言 ( Interface Description Language )

9.4 分布对 象主流技术 ---CORBA CORBA 规范 的特 点 : 1. 互操 作性: CORBA 在客 户和服 务器 之间提 供了两 层可操 作性 。一方 面它 将客户 与服 务 器都抽 象为对 象, 所有功 能都 封装在 对象 内 部,对 外提供 简单 的接口 ,可 被其它 对象 以 动态或 静态方 式调 用。另 一方 面,对 象间 的 通信是 通过 ORB 代理实 现,对 象不 必关心 细 节,由 ORB 定址发 送请求 。是 ORB 对象间 的“通 信总线 ”。 ■

496

9.4 分布对 象主流技术 ---CORBA

497

2. 提供了软总件机制:所谓软总线是指 CORBA 规范定义了 一组接口规范,任何应用程序,软件系统或工具只要具有与 该接口规范相符合的接口定义,就能方便地集成对 CORBA 系统中,而这个接口规范是独立于任何实现语言和环境。。

3. 跨平台 性:由 于 CORBA 规范中 定义了 IDL 语 言这一 与平台 无关 的语言 ,并 引入代 理 (ORB) 概念 ,因 此 CORBA 应用 具有 较好的 跨 平台性 。

9.4 分布对 象主流技术 ---CORBA ■

CORBA 结构 一次 请求的 过程 OBJECT REFERENCE CLIENT

SERVER SKELETONS

STUBS ORB

SERVANT

IIOP ORB METHOD REQUEST

CORBA 结构一次请求的过程

498

9.4 分布对 象主流技术 ---COM/DCOM COM,DCOM,OLE 和 ActiveX ■



499

OLE 对象链 接和嵌 入 (Object Linking and Embedding) 从用户 角度看 OLE 1. OLE 文档 : 以文档 为中心 ,OLE 集成 数据 2. OLE 自动化 3. OLE 控件 : 可以被 嵌入应 用程 序中的 自包 含 的, 可重用的 组件 .

9.4 分布对 象主流技术 ---COM/DCOM

500

9.4 分布对 象主流技术 ---COM/DCOM

501

9.4 分布对 象主流技术 ---COM/DCOM

从程 序员的 角度 看 OLE

OLE 控件 属性 为 将 来 而 建 造

OLE 文档

剪贴板 一致数 据传输 器 (UDT)

事件

就地激活 ( 可视编程 ) 拖放

嵌入

结构化 存储

链接

跟踪 器 ( 永久命名 )

组件对 象模型 (COM)

OLE 自动 化

502

9.4 分布对 象主流技术 ---COM/DCOM ■





■ ■ ■

503

构件对 象模型 COM (Component Object Model ) 是一 个用于 在交 互平台 上开 发客户 / 服务器 应用程 序开 放结构 . COM 是微软 的构件 对象 模型 , 是构件 之间 实现互 操作的 二进 制标准 . COM 对象通 过接口 来显 示功能 。接 口是 COM 对象与 外部世 界的 一个绑 定约 定。 COM 可以用 不同语 言实 现 . COM 只能运 行在 WINDOWS 平台 上 COM 实现了 OLE 对象的 底层通 信 , 其作用 类似于 CORBA/ORB

9.4 分布对 象主流技术 ---COM/DCOM ■





■ ■

504

ActiveX 是 OLE 技术和 COM 技术 在 Internet 网上的 一个扩 展 . 但是 它的内 容更 多 , 它还组 成了一 系列用 来产 生丰富 的 Internet 网和 多媒 体服务 . ActiveX 文档 : 将 OLE 文档 扩展到 internet ,能够 由浏 览器 打开的 word , powerpoint 文件 ActiveX 控件 : 将 OLE 控件扩 展到 internet 。如 一个页 面 包含 ActiveX 控件 ,该 控件可 以自 动下载 到用 户端。 ActiveX 脚本 :

9.4 分布对 象主流技术 ---COM/DCOM ■







505

分布式 组件对 象模 型 (DCOM 或 ActiveX/DCOM ): 被称 为网络 OLE, 是二 进 制的 COM 对象在 局域 , 广域 网和 Internet 上 的扩展 . COM 能够使 客户在 本地 处理中 透明 地访问 库 中的对 象, DCOM 允许 在远程 处理 中透明 地 访问对 象。( DCOM 的功能 实现使 得程 序员 不必编 写网络 代码 , 或仅 仅知道 如何 编写网 络 代码) DCOM 基于 (RPC--- remote procedure call ) 工作, 它不是 一种 编程语 言, 而是一 种规 范 ,一种 服务, 是一 种高级 网络 协议。 DCOM 是用 COM 实现 的。

9.4 分布对 象主流技术 ---COM/DCOM ■







506

ActiveX 控件 通过底 层 DCOM 进行 通信, 允 许控件 互相访 问对 方公布 了的 方法, 而不 必 考虑控 件是否 在一 个容器 或同 一台机 器上 。 ActiveX 控件 由事件 连接 在一起 , 开发环境 支 持开发 者创建 方法 ,当任 何一 个构件 出发 了 一个事 件 , 这些方 法都被 调用 . 目前有 十几家 公司 提供大 量的 控件 , 你可 以访 问这些 公司的 WEB 站点 , 下载你 需要的 ActiveX 控件 , 进行编程 : www.activex.com. Windows98 和 WindowsNT5.0 都把 DCOM 作 为操作 系统的 一部 分 .

9.4 分布对 象主流技术 ---Java/RMI









507

Java/RMI RMI 是分 布在 网络中 的各 类 Java 对象之 间的进 行方法 调用的 ORB 机制 . Java 语言支 持通信 的最 基本机 制是 Socket. 但 是 Socket 要求客 户和服 务器 在应用 程序 级上对 交换信 息编码 的协 议达成 一致 . RPC 把通 信接口 抽象 到子程 序级 , 而不是直 接 与 Socket 打交道 , 但由 于不 涉及对 象 , 在分布 式运算 中效果 不好 . Java/RMI(Remote Method Invocation) 是 Java 特有的 分布式 计算 技术 , 它允 许运行 在一 个 Java 虚拟机 上的对 象调 用运行 在另 一个 Java 虚 拟机上 的对象 的方 法 .

508

9.4 分布对 象主流技术 ---Java/RMI RMI 系统 的一 般结构 1. Stub 就是 代表 远程对 象的 客户方 代理 , 定义 远程对 象 版本所 支持的 所有 接口 . 2. Skeletons 是与服务 器方 的 RRL 接口的 服务器 方构 件 3. RRL 负责维 护不与 具体 Stub 或 Skeleton 模型相 关 的独立 引用协 议 . 这个灵 活 性使 RRL 的改变 不会影 响 另外两 层 . 4. 传输 层由 4 个抽象 构成 : ■

Application Client

Server

Stubs

Skeletons

Remote Reference Layer Transport RMI System

509

9.4 分布对 象主流技术 ---Java/RMI RMI 系统 的一 般原理 : 1. 定位 远程对 象 : RMI 的命名 工具注 册 远程对 象 . 2. 和远 程对象 通信 : RMI 传输层 处理通 信 细节 3. 装载 被串对 象的 字节 码: RMI 提供了 传递数 据 和 装载一个 对象 代码 的必要 机制 ■

Web Server URL protocol

URL protocol

Client RMI

RMI

Server RMI

Web Server

registry URL protocol

■ ■ ■ ■

9.4 分布对 象主流技术 ---EJB

510

EJB---(Enterprise JavaBean1.0) 是 Java 服务 器端构 件模型 . 构件模 型通常 有客户 端构 件和服 务器 端构件 构件 客户端 构件模 型 JavaBean 专门用 于处 理程序 的表示 及用户 界面 的问题 . 服务器 端构件 模型 Enterprise 客户 EJB, 把构件 模型的 JavaBean 开发和 中间件 联系 容器连 接 容器 起来 , 面向 事务处 理 的中间 件提供 基础 EJB 服务器 设施 .

9.4 分布对 象主流技术 ---EJB

511

为什么 要有服 务器 端构件 模型 EJB? 1. EJB 将成为 用 Java 语言 开发 分布式 的、 面向 对象的 企业应 用系 统的标 准构 件体系 结构 , EJB 使得通 过组合 构件 得到分 布式 应用成 为 可能 2. EJB 不需要 应用开 发人 员了解 底层 的事务 处 理细节 , 状态 管理 , 多线 程 , 资源 共享 管理 , 以 及其它 底层 API 细节 . 3. EJB 遵循 Java 的“ write once, run anywhere” 的原 则 . 一个 EJB 可以部 署在任 何 EJB 平台 上 . 4. EJB 定义了 一个协 议 , 使得不 同供应 商提 供的 构件能 在运行 时互 操作 . ■

9.4 分布对 象主流技术 ---EJB

512

EJB 与 JavaBean 的关 系 1. 客户 端的 JavaBeans 容器 可以根 据 JavaBeans 的属 性 , 方法 , 事件的 定义在 设计 时 或运行 时对 Java Beans 进行 操作 . 一般 JavaBeans 是可 视化的 构件 . 一个 标准的 JavaBeans 是一 个客户 端构 件 , 在运 行时 不能 被其它 客户机 程序 存取或 操作 2. EJB 没有用 户界面 , 并完 全位于 服务 器端 ,EJB 可以由 多个 JavaBeans 组成 . 3. EJB 可以和 远程的 客户 程序端 通信 , 并提供 一 定的功 能 . 如果不 和客户 端程 序交互 ,EJB 一般 不执行 具体的 功能 . 4. EJB 与 JavaBeans 的一个重 要区 别是 EJB 提 ■

9.4 分布对 象主流技术 ---EJB



513

EJB 与 CORBA 的关系

1. 一个 CORBA 客户机 ( 用 CORBA 支持的 语言些 的 程序 ), 可以存取 基于 CORBA 的 EJB 服务器 上的 构 件 2. 一个客户 机在一 个事 务过程 中可 以同时 调用 CORBA 的 EJB 服务器对 象 3. 通过 IIOP 可以使 EJB 系统和 CORBA 系统集成 .

EJB 与客户 机的关 系 ■ 对于 EJB 来说 , 有两种 类型的 客户 机可以 使用 EJB: 1. EJB/CORBA 客户 机 ---- 一个 使用 EJB APIs 的 Java 客户机 . 2. 纯 CORBA 客户机 ---- 用 CORBA IDL 支持 的任 ■

9.4 分布对 象主流技术 ---EJB ■

EJB 的网络 计算 客户 端

在网络计算环境中利用 Java 的最好途径是由 EJB 提供服务 器端的构件 , 而由 JavaBeans 提供客户端构件 .

514

■ ■ ■





9.4 分布 对象处 理技 术 --- 几种构 件模型 的比515 较 组件 :CORBA,JavaBean,ActiveX 协议 :IIOP, RMI, DCOM CORBA 最大的特 点是 语言中 性和 跨平台 . 可 以跨越 不同网 络 , 不同机 器和 不同操 作系 统 , 实现分 布对象 之间 的互操 作 . 整体功 能最 强 . Java 提出了 一个概 念清 晰 , 结构紧 凑的分 布 计算模 型和构 件互 操作方 法 , 为构件 应用 开发 提供了 相当的 灵活 性 , 使用上 比 CORBA 方便 . 但是没 有提供 分布 对象事 物管 理等服 务 . 微软结 合 ActiveX/DCOM 的开 发 , 配套提出 了 自己的 事务服 务器 (MTS) 和消 息队 列服务 器 , 加之 asp 的使 用 , 以及 wizard 的可视 化编程 环境 , 倍受业 界的 欢迎 .







9.4 分布 对象处 理技 术 --- 几种构 件模型 的比516 较 CORBA 的优势在 技术 ,较之 COM/DCOM 领 先至少 2-3 年; COM/ DCOM 的优势 在市场 能力。 目前, 只有 OMG 的技 术能 够支持 大型 异构分 布式应 用的开 发, Microsoft 的技 术尚不 能胜 任。 CORBA 技术与 Java 技术趋 于结合 , CORBA 的互操 作性与 Java 的可移植 可移 动性将 使分 布对象 技术达 到新 的高度 。

9.4 分布 对象处 理技 术 --- 几种构 件模型 的比517 较 CORBA/ORB

ActiveX/DCOM

EJB/RMI

集成性 支持跨语言操作支持 跨平台操作 网络通讯 公共服务构件 可用性 事务处理 消息服务 安全服务 目录服务 容错性 产品成熟性 软件开发商的支持度 可扩 展 性

A A A A

A E A A C C B A

A

D

C B

A

D C B B C C B

A A B B A B C

A

D A

B

A

9.5 分布 对象处 理技 术 --- 发展趋 势 ■ ■

■ ■

■ ■



518

Object web 人们关 注的 新热点 hypertext web-->interactive web-->object web 什么是 object Web? CORBA 和 Java 结合 是 object web 的技术 基础 和 hypertext web,interactive web 比较 : 1. object web 给用 户的突 出感 觉是操 纵实 体不再 局限于 含有 “超链 ”的 文档 2. 作用于 实体 的操作 行为 不再局 限于 “下 载”一 类的文 件传 输 .



9.5 分布 对象处 理技 术 --- 发展趋 势 Object web 的体 系结构

Web browser

Java ORB 第一层

HTTP 文档

HTTP HTTP Server CGI

Java Applet

IIOP

519

Java ORB

Java Applet CORBA Server Object

Internet JDBC 第二层 业务服 务 器

第三层 业务服 务 器

9.5 分布 对象处 理技 术 --- 发展趋 势 ■ ■

中间层 可以由 任何 一种服 务器 来支持 中间层 主要完 成各 种业务 逻辑

520

9.5 分布 对象处 理技 术 --- 发展趋 势 ■ ■

521

从应用 的角度 看 object web (1) 开发 的 internet/Intranet 管理 – agent 能够在 internet 上公 布其 管理功能 的接 口 , 并接受 任何 合法的 maneger 按照标 准的运 城对 象 访问协 议 (corba//iiop) 所进行 的访 问 . – Maneger 能够访 问任 何时刻 加入 Internet 的 Agent, 只要该 agent 支持 internet 上的标准 的远 程 对象访 问协 议 , 并在加 入 internet 时按 照标 准的协 议公布 其接 口 .



(2) Web 文档 组合 – 结合组 合文 档和 web 文档的 双重 功能 .

9.5 分布对 象处理技术 --- 发展趋势 ■ ■

从技术 的角度 看 object web 对象开 发技术 和对 象访问 技术 对象

对象 访问

Hypertext web Object web interactive web COM 对象 Java 对象 超文本 CORBA 对象 各类构 件 HTTP/CGI DCOM CORBA/IIOP RMI

HTTP/CGI

522

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 • 用 RMI 编写 一个 分布式 应用 , 核心 有以下 三 方面: • 定位远 程对象 – 1. 一个应 用可以 利用 RMI 的名 字服 务功能 注册 器 远程对 象。 – 2. 可以象 操作普 通对 象一样 传送并 返回 一个远 程 对象的 引用 ( 指针 ) 。

• 与远程 对象通 信: – 底层的 通信 由 RMI 实现, 对于系 统开 发人员 来说 ,远程 调用 和标准 的 Java 方法调 用没有 什么 区别 。

• 为需要 传递的 对象 装载类 的字 节码 – RMI 允许调 用者向 远程 对象传 递一 个对 象,因 此523 RMI 提供这 种装载 对象 的机制 。

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例

Web Server URL protocol

URL protocol

Client RMI

RMI

Server RMI

Web Server

registry URL protocol

524

北 京 大 学

9.6 一个 RMI 的分布 式应用的实 例 一、问 题的提 出 task

计 算 机 系

Client

代 亚 非

Client

task

Remote object Compute engin

Server

task

Client

525

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 • 分布特 点 : – engin 开发 , 先运行 ,task 后定义 . 写 engin 时不 对执 行什 么任 务作任何 规定 . 任务 可以 是任意 定制 的 .

• 前提条 件 : – 定义 任务 的类 , 要规定 任务的 实现 步骤 , 使得这 个 任务 能够 提交给 engin 去执 行 . 使用 server 上的 CPU 资源 .

• 技术支 持 : – RMI 的动态 装载 功能 .

526

9.6 一个 RMI 的分布 式应用的实 例 A.m1()

北 京 大 学

计 算 机 系

代 亚 非

compute engine

远程方法

远程对 象

client

m1 A server

Comput e

Task

executeTask execute

远程 对象 必须继 承远 程接口 确定 那些 方法是 远程 方法 , 为此定 义远 程接口 远程 接口 只负责 提供 方法名 , 不一共实 现细 节 , 因此 必须 由 一个 对象 来实现 接口

527

北 京 大 学

计 算 机 系

9.6 一个 RMI 的分布 式应用的实 例 • 二、设 计一个 服务 器 • 核心协 议 : 提交 任务 , 执行任 务 , 返回结果 client

client

• 在 java 中远程调用是通过定义远程接口来实现的 , 一个接口只能有一个方法 • 不同类型的任务 , 只要他们实现了 Task 类型 , 就 代 可以在 engin 上运行 . 亚 非 • 实现这个接口的类 , 可以包含任何任务计算需要 的数据以及和任何任务计算需要的方法 . 528

9.6 一个 RMI 的分布 式应用的实 例

( 1 )定义 远程 接口 北 京 • 第一个 接口 :compute

大 学 package compute; import java.rmi.Remote; 计 import java.rmi.RemoteException; 算 机 public interface Compute extends Remote 系 { Object executeTask(Task t) throws RemoteException;}

代 亚 package compute; 非 import java.io.Serializable; public interface Task extends Serializable { Object execute(); }

compute engine Comput e

Task

executeTask execute

第二个接口 : 定义一个 task 类型 , 作为参数传给 executeTask 方法 , 规定了 engin 与它的任务之间的 接口 , 以及如何启动它的任务 529. 它 不是一个远程接口

9.6 一个 RMI 的分布 式应用的实 例

北 • Compute engin 的设 计要考 虑以 下问题 : 京 大 • 1. compute engine 是一个 类 ComputeEngine , 它 学 实现了 Compute 接口 , 只要调 用该类 的方 法

executeTask, 任务就 能提交 上来 . 计 算 • 2. 提交 任务 的 Client 端程 序并不 知道 任务是 被 机 下载到 engin 上执 行的 . 因此 client 在定义 任务 系

时并不 需要包 含如 何安装 的 server 端的 代码 . • 3. 返回 类型 是对象 , 如果 结果 是基本 类型 , 需要 代 转化成 相应的 对等 类 . 亚 非 • 4. 用规 定任务 如何 执行的 代码 填写 execute 方法 . 530

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 ( 2 )实 现远程 接口 • 一般说 来 , 实现一个 远程 接口的 类至 少有以 下 步骤 : • 1. 声明远 程接口 • 2. 为远程 对象定 义构 造函数 • 3. 实现远 程方法 engin 中创 建对象 的工 作可以 在实 现远程 接口 类 的 main 函数 中实现 : • 1. 创建并 安装安 全管 理器 • 2. 创建一 个或更 多的 远程对 象的 实例 • 3. 至少注 册一个 远程 对象 531

package engine; import java.rmi.*; import java.rmi.server.*; import compute.*; 9.6 一个 RMI 的分布 式应用的实 例 public class ComputeEngine extends UnicastRemoteObject 北 implements Compute 京 { public ComputeEngine() throws RemoteException 大 { super(); } 学 public Object executeTask(Task t) 计{ return t.execute(); } 算 public static void main(String[] args) 机 系 { if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } 代String name = "//host/Compute"; 亚try { Compute engine = new ComputeEngine(); 非 Naming.rebind(name, engine); System.out.println("ComputeEngine bound"); } catch (Exception e) { System.err.println("ComputeEngine 532 exception: " + e.getMessage()); e.printStackTrace(); }}

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 • 在构造 函数中 , 通过 super(), a UnicastRemoteObject 被启 动 , 即它 可以 侦听客 户 端来的 请求输 入 • 只有一 个远程 方法 , 参数是 客户 端远程 调用 这个 方法时 传来的 任务 . 这个任 务被 下载到 engin, 远 程方法 的内容 就是 调用客 户端 任务的 方法 , 并把 结果回 送给调 用者 . 实际上 这个 结果是 在客 户的 compute engine 任务的 方法中 体现 的 . callexecuteTask(task)

Comput e

Task

executeTask execute 533

北 京 大 学

计 算 机 系

9.6 一个 RMI 的分布 式应用的实 例 • 参数传 递规则 : • 1. 远程 对象 通常通 过引 用传递 . 一个 远程 对象 的引用 是一个 stub, 它是 客户端 的代 理 . 它实 现远程 对象中 的远 程接口 的内 容 • 2. 本地 对象 通过串 行化 拷贝到 目的 . 如果不 作制定 , 对象的 所有 成员都 将被 拷贝 .

代 亚 非

534

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 • 通过引 用传递 一个 对象, 意味 着任何 由于 远 程调用 引起的 变化 都能反 映在 原始的 对象 中 。 • 当传递 一个远 程对 象时, 只有 远程接 口是 可 用的, 而在 实现 类中定 义的 方法或 者是 非远 程接口 中的方 法, 对接收 者来 说是不 可用 的 • 在远程 方法调 用中 ,参数 ,返 回值, 异常 等 非对象 是值传 送 . 这意 味着对 象的 拷贝被 传 送到接 受方。 任何 在对象 上发 生的变 化不 影 响原始 的对象 • 一旦服 务器用 rmi 注册 了, main 方法就存 在 了,不 需要一 个守 护线程 工作 维护服 务器 的535

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 三、实 现一个 客户 程序 • 目标: 创建一 个任 务,并 规定 如何执 行这 个 任务。 package compute; public interface Task extends java.io.Serializable { Object execute(); }

client Pi

computePi

execute() ExecuteTask()

• task 不是远程接口,但是需要传递到服务器,因 此用串行化 536

北 京 大 学

计 算 机 系

9.6 一个 RMI 的分布 式应用的实 例 • computePi 的作用 装载 安全管理器 生成一 个远程对 象 comp Look up(ComputeEngin), 获得了 stubs 生成 任务对象 调用 ComputeEngin 的远程 方法 获得计算结果

Pi task=new Pi() Comp.executeTask(task)

代 亚 非

537

package client; 9.6 一个 RMI 的分布 式应用的实 例 import java.rmi.*; import java.math.*; import compute.*; public class ComputePi { 北 京 public static void main(String args[]) { 大 学 if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } 计 算 try { String name = "//" + args[0] + "/Compute"; 机 Compute comp = (Compute) Naming.lookup(name); 系 Pi task = new Pi(Integer.parseInt(args[1])); BigDecimal pi = (BigDecimal) (comp.executeTask(task)); System.out.println(pi); 代 亚 } catch (Exception e) { 非 System.err.println("ComputePi exception: " + e.getMessage()); e.printStackTrace(); } 538 }

北 京 大 学

计 算 机 系

9.6 一个 RMI 的分布 式应用的实 例 • Pi 的作 用 实现 Task 接口 实现 execute 算法

rmiregistry computepi Compute engin

代 亚 非

539

package client; 9.6 一个 RMI 的分布 式应用的实 例 import compute.*; import java.math.*; 北 public class Pi implements Task { 京 大 private static final BigDecimal ZERO = 学 BigDecimal.valueOf(0); 计 算 private static final BigDecimal ONE = 机 BigDecimal.valueOf(1); 系 private static final BigDecimal FOUR = BigDecimal.valueOf(4); 代 亚 private static final int roundingMode = 非 BigDecimal.ROUND_HALF_EVEN; public Pi(int digits) 540 { this.digits = digits; }

9.6 一个 RMI 的分布 式应用的实 例

public Object execute() 北 { return computePi(digits); } 京 大 *************************************************** 学 * pi/4 = 4*arctan(1/5) - arctan(1/239) 计 **************************************************** 算 public static BigDecimal computePi(int digits) { 机 系 int scale = digits + 5; BigDecimal arctan1_5 = arctan(5, scale); 代 BigDecimal arctan1_239 = arctan(239, scale); 亚 BigDecimal pi 非 arctan1_5.multiply(FOUR).subtract(arctan1_239).multiply(FOUR); return pi.setScale(digits, BigDecimal.ROUND_HALF_UP); } 541

/** 9.6 一个 RMI 的分布 式应用的实 例 * Compute the value, in radians, of the arctangent of 北 * the inverse of the supplied integer to the speficied 京 * number of digits after the decimal point. The value 大 学 * is computed using the power series expansion for the * arctangent: 计 算 * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + 机 * (x^9)/9 ... 系 */

代 亚 非

542

public static BigDecimal arctan(int inverseX, int scale) 9.6result, 一个 RMI 的分布 式应用的实 例 { BigDecimal numer, term; BigDecimal invX = BigDecimal.valueOf(inverseX); 北 京BigDecimal invX2 = 大BigDecimal.valueOf(inverseX * inverseX); 学numer = ONE.divide(invX, scale, roundingMode); int i = 1; 计result = numer; 算do { numer =numer.divide(invX2, scale, roundingMode); 机 int denom = 2 * i + 1; 系 term = numer.divide(BigDecimal.valueOf(denom), scale, roundingMode); if ((i % 2) != 0) { result = result.subtract(term); } 代 亚 else { result = result.add(term); } 非 i++; } while (term.compareTo(ZERO) != 0); return result; 543 } }

北 京 大 学

计 算 机 系

9.6 一个 RMI 的分布 式应用的实 例 • 由于 Rmi 的存在 ,系统 可以 做到: • 1 、可 以直接 通过 名字定 位远 程方法 的位 置 • 2 、以 参数的 形式 将一个 对象 传递给 一个 远程 方法 • 3 、可 以使一 个对 象到另 外一 个虚拟 机上 运行 • 4 、计 算结果 可以 返回

代 亚 非

544

北 京 大 学

计 算 机 系

9.6 一个 RMI 的分布 式应用的实 例 • 将接口 ,远程 对象 ,客户 代码 分成三 个程 序 包: • 1. compute ( Compute and Task interfaces) • 2. engine ( ComputeEngine implementation class and its stub) • 3. client ( ComputePi client code and Pi task implementation)

代 亚 非

545

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 • 接口 compute 对于 编程双 方都 是需要 的 , 通常 将接口 文件打 成包 , 分发给 server 和 client 的 开发者 : • 假设接 口的开 发者 将写好 的接 口程序 放在 c:\home\waldo\src\compute 目录 下 • cd c:\home\waldo\src • javac compute\Compute.java • javac compute\Task.java • jar cvf compute.jar compute\*.class 546

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 • 类文件 必须是 网络 可访问 的 ,rmi 利用 URL 定 位类文 件 • 假设 ComputeEngine.java 存放 在 c:\home\ann\src\engine • 假设 compute.jar 存放在 c:\home\ann\public_html\classes. • 设置环 境变量 • CLASSPATH=c:\home\ann\src;c:\home\ann\publi c_html\classes\compute.jar

547

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 • 编译 ComputeEngine.java, 产生 一个 stub, 并 使 stub 是网 络可访 问的 . • 产生 stub 的命令 是 rmic, 生成 的文件 形式 为 : className_Stubs.class 和 className_skeleton.class • 命令如 下 : • cd c:\home\ann\src • javac engine\ComputeEngine.java • rmic -d . engine.ComputeEngine • md c:\home\ann\public_html\classes\engine • copy engine\ComputeEngine_*.class c:\home\ann\public_html\classes\engine548

北 京 大 学

计 算 机 系

9.6 一个 RMI 的分布 式应用的实 例 • 展开接 口文件 • cd c:\home\ann\public_html\classes • jar xvf compute.jar

代 亚 非

549

9.6 一个 RMI 的分布 式应用的实 例

北 • 执行程 序 京 大 • 1. 在启动 ‘ compute engine’ 之前 , 首先 要启动 学 RMI 的 registry 。 – unset CLASSPATH 计 算 – start rmiregistry 机 系 • 2. 启动 Server.

• 确认 compute.jar 文件和 实现远 程对 象的类 在指 定 的 class 路径 下 代

亚 非

– set CLASSPATH= c:\home\ann\src;c:\home\ann\public_html\classes\compute.jar 550

北 京 大 学

9.6 一个 RMI 的分布 式应用的实 例 • 启动 compute engine 时 , 要规定 服务器 端的 类 在什么 情况下 是可 用的。 启动 ’ Compute engine’

计 算 机 系

– java -Djava.rmi.server.codebase= file:/c:\home\ann\public_html\classes/ -Djava.rmi.server.hostname= zaphod.east.sun.com -Djava.security.policy=java.policy engine.ComputeEngine

代 亚 非

• 551

北 京 大 学

计 算 机 系

代 亚 非

9.6 一个 RMI 的分布 式应用的实 例 3. 启动 Client 指定类 (pi) 的位置 –set CLASSPATH= c:\home\jones\src; c:\home\jones\public_html\classes\compute.jar -java -Djava.rmi.server.codebase= file:/c:\home\jones\public_html\classes/ -Djava.security.policy=java.policy –client.ComputePi localhost 20

•输入完 上述命 令后 ,得到 结果 • 3.14159265358979323846 552

9.6 一个 RMI 的分布 式应用的实 例

北 京 大 学

计 算 机 系

代 亚 非

553

9.7 基于 CORBA 的分布 式应用系统 的实 例

北 京 大 学

windowsNT ( C++ )

计 算 机 系

Sun ( Java )

代 亚 非

client

Netscape ( COBOL )

554

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例

• CORBA 技术和 Java 技术的 结合 --Java IDL • 什么是 IDL? • IDL 是 CORBA 规范中 的接口 定义 语言 , 不依 赖于任 何具体 的编 程语言 . • CORBA 提供了 到各 种不同 语言 的 IDL 映射 . • Java IDL 是 CORBA 到 Java 的映 射 , 使 Java 也支持 CORBA 规范 • Java IDL 和 Java RMI 非常相 似 ,RMI 只支 持 Java 语言写的 分布 对象 ,Java IDL 可以和 CORBA 支持的 任何 一种语 言编 写的 ORB 对 象进行 交互 • Java RMI 和 Java IDL 目前使 用的通 信协 议不 555 同 , 分别 是 JRMP 和 IIOP.

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例 • 在 Java IDL 中 , 客户 端通过 引用 与远程 的对 象 进行交 互 , 即客 户机 使用 stubs 对远 程服务 器 上的对 象进行 操作 , 但并不 拷贝 服务器 上的 对 象. • Java RMI 即可 以通过 引用 , 也可 以将对 象下 载到本 地机上 运行 ( 因为有 串行化 功能 ). • Java 实现简单 , 但语 言不兼 容 • Java IDL 则可以 充分发 挥语 言无关 的优 势

556

北 京 大 学

计 算 机 系

9.7 基于 CORBA 的分布 式应用系统 的实 例 • IDL Java 的技 术组成 : – – – –

IDL 至 Java 的编 译器 :idltojava 生成客 户端 的 stubs 和服务 器端的 skeleton CORBA API 和 ORB 一个简 单的 名字服 务

代 亚 非

557

9.7 基于 CORBA 的分布 式应用系统 的实 例

北 京 大 学

计 算 机 系

代 亚 非

客户应用

应 用 开 发 者

C++, Java 编译器

客户程序

C++, Java 编译器

服务程序

IDL Stub

IDL 接口

IDL 编译器 ( C++, Java )

Skeleton 对象实现

558

9.7 基于 CORBA 的分布 式应用系统 的实 例

北 京 大 学

计 算 机 系

代 亚 非

从反应式 Agent 的角度看 应用开发者

IDL 文件

IDL 编译器

对象实现

事件处理部分

IDL Skeleton BOA

事件适配部分

ORB 内核

事件感知部分

事件处理分发部分

559

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例 • CORBA 编程实 例 • 运行在 浏览器 中的 客户对 象与 远程的 服务 对 象交互 , 客户端 的表 现是在 浏览 器中点 击一 个 button ,就会 在一个 文本 域中返 回服 务端的 时 间,同 时也在 服务 端的标 准输 出上打 印该 时 间。 时间 是一个 对象 The data in server side is 2000.6.1 12 : 56 : 00

server

button 560

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例 OBJECT REFERENCE CLIENT

SERVER SKELETONS

STUBS ORB

SERVANT

IIOP ORB METHOD REQUEST

561

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例

1. 首先是 定义交 互接 口,在 文件 dateit.idl 中。 – module TheDate (相 当于 包) – { interface DateIt – { string dateit(); }; – };

2. 用 IDL 接口 到 Java 语言 的映射 – – – – – –

jidl dateit.idl 该命令 会生 成几个 相关 的 java 文件: DateIt.java DateItHelper.java DateItHolder.java _DateItImplBase.java StubForDateIt.java 562

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例

3. 编写服 务对象 的程 序,在 文件 DateIt_impl.java 中 package TheDate; // jidl 产生的 Java 文件放 在 TheDate 包中 import org.omg.CORBA.*; import java.io.*; import java.awt.*;import java.util.Date; import java.lang.System; public class DateIt_impl extends _DateItImplBase // 扩展 了 jidl 生成的 抽象类 _DateItImplBase { String date_time; public String dateit() { date_time=(new Date()).toString(); // 获取时 间 System.out.println(date_time); 563 return date_time;// 向客 户端 返回时间 串

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例

4. 编写服 务方的 程序 ,在文 件 Server.java 中。

package TheDate; import org.omg.CORBA.*;import java.io.*; public class Server { public static void main(String args[]) {try { // 创建 ORB 和 BOA 对象实 例 ORB orb = ORB.init(args, new java.util.Properties()); // 生成 服务 对象实 例 BOA boa = orb.BOA_init(args); DateIt_impl p = new DateIt_impl(); // 创建 服务 对象实 例 564

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例

// 保存 引用 try { String ref = orb.object_to_string(p); // 将对 象编 码成字符 串 String refFile = "date.ref"; FileOutputStream file = new FileOutputStream(refFile); PrintStream out = new PrintStream(file); out.println(ref); // 存入 文件 date.ref 中 out.flush(); file.close(); }catch(IOException ex) { System.err.println("Can't write to" +ex.getMessage()); System.exit(1); } 565

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例 // 将引 用存 入 html 文件中, 参见 后面列 出的 date.html 文 try { String ref = orb.object_to_string(p); String refFile = "c:\\Inetpub\\wwwroot\\Docs\\date.html"; FileOutputStream file = new FileOutputStream(refFile); PrintStream out = new PrintStream(file); out.println("");

566

北 京 大 学

计 算 机 系

代 亚 非

//9.7 指由基于 Client.java 编译成的分布 的 class 文件 CORBA 式应用系统 的实 out.println("<param name=ior 例value=\"" + ref + "\">"); // 将由服 务对 象转化 成的 字符 串存入 超文 本文件 中 out.println("<param name=org.omg.CORBA.ORBClass " + "value=com.aic.CORBA.IIOPORB>"); out.println("<param =com.aic.CORBA.ORBSingleton>"); name=org.omg.CORBA.ORBSingletonClass " + "value out.println("
"); out.flush(); file.close(); // 这样 浏览 器调入该 超文本页 面时, 会运 行 Client.class 的 applet ,并将 包含标 识服务 对象 的字符 串由 参数 ior 传递 给 applet 。 } catch(IOException ex) { System.err.println(“Can't write to ”+ex.getMessage()+“”); System.exit(1); } 567

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例 // 服务对 象就 绪,准 备接 受请 boa.impl_is_ready(null); System.exit(0); } catch(SystemException ex) { System.err.println(ex.getMessage()); ex.printStackTrace(); System.exit(1); } } }

568

北 京 大 学

计 算 机 系

代 亚 非

9.7 基于 CORBA 的分布 式应用系统 的实 例 • 5. 编写 客户方 的程 序,在 文件 Client.java 中 。 package TheDate;// 由于 jidl 产生的 JAVA 文件放 在 package TheDate 中,因此 该语 句是必 须的 import org.omg.CORBA.*; import java.io.*; import java.awt.*; import java.util.Date; import java.lang.System;

569

9.6 各种主流技 术的主要 开发过程 -CORBA

北 public class Client extends java.applet.Applet 京 { private DateIt serverdate; 大 private Button button; 学 private TextField outdate; 计 算 机 public void init() 系 { String ior = getParameter("ior");

代 亚 非

//Applet 只能从 包含它的 HTML 文件获取 IOR 串 见 Server.java.

// 产生 ORB 实例 ORB orb = ORB.init(this, null); // Create client object org.omg.CORBA.Object obj = orb.string_to_object(ior);570

北 京 大 学

计 算 机 系

代 亚 非

9.6 各种主流技 术的主要 开发过程 -CORBA // 串到 对象 的转化 if(obj == null) throw new RuntimeException(); serverdate = DateItHelper.narrow(obj); // 产生 对象 实例, 其实 是服务 对象的 映射 // 添加 serverdate 按钮 和文 字域 button = new Button("Dateis"); outdate = new TextField("",30); this.add(button); this.add(outdate); }

571

北 京 大 学

计 算 机 系

代 亚 非

9.6 各种主流技 术的主要 开发过程 -CORBA // 事件处理 // public boolean action(Event event, java.lang.Object arg) { if(event.target == button) { outdate.setText("please wait..."); outdate.setText(serverdate.dateit()); // 调用服 务对 象的函 数, 返回 服务端 的时 间 return true; } else return super.action(event, arg); } }

572