Java1-10_pku

  • July 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: 18,819
  • Pages: 540
1

1.3 Java 的开发环境 • 直接执行 JDK.exe 产生如下目录结构

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

2

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

3

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

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

4

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

5

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

6

1.4 你的第一个 Java 程序

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

7

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

8

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)

9

• • • • • • • • • •

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)); .

10

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”); char ch=s.charAt(6);

11

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

12

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); } }

类的继承 关系 •

13 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); } }

14

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

•方法和变量的定义及修饰字 public 、 protected 、 private [ 变量修饰字 ] 变量数据类型 变量名 1, 变量名 2[= 变量初值 ]…;

[ 方法修饰字 ] 方法的返回值类型 方法名称 ( 参数 1, 参数 2,… { …// 方法的内容 }

15

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

16

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

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

17

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;

18

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

19

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(); } }

20

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

21

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

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

22

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); }}

23

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)

24

• • • • • •

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

25

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

26

• • • • • • • •

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. 为什么不正确 ? 只有对象的方法可以访问 对象的变量 .

27

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

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

28

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

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

29

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

30

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

// 已有两个类 Circle 和 Rectangle, 完成相关参数的计算 class Circle { public int r; Circle(int r) {this.r=r} //this 指 " 这个对象的 " public int area() {return 3*r*r; } // 取近似 } class Rectange { public int width,height; // 这里不需 "this" Rectangle (int w, int h) {width=w,height=h;} public int area() {return width*height; } }

31

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

• for (i=0;i<shape.length;i++) { • area_total+=shape[i].area();

32

abstract class Shape 1.9 Java 中的抽象、接口和程序包 { 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; } }

33

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

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

34

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

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

35

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); }}

36

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;} }

37

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

38

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;} }

39

• • • • • • • •

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

40

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

41

小结 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 {}

42

小结 • 名空间及访问规则 • --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

43

小结 • • • •

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

44

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

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 address sender sender address address receiver receiver

45

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

46

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

47

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

SERVER co n Applet 被下载的 nec

SERVER

t io n

n o i t c e n con

本地程序

applet

file

local

与 applet 无关的 本地方法

48

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 的灵魂

49

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

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

50

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

51

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

Exposure update () { clear arae call paint ()

paint ()

52

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);

53

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); }

54

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”

55

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

56

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

Java applet vspace

其它文字

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

57

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

58

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=“”; }

59

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

60

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); }

61

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

62

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() }

63

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);

64

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()

65

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

66

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

67

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

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

68

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); }}}

69

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

70

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”);

71

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

浏览器

服务器

72

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(), String getRef()

73

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); // 处理例外 } }}

74

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”);

75

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); 规定尺寸

规定背景

76

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

在类中

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

在 init0 中

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

在 paint0 中

77

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);}}

78

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

数学运算

引出最后结果

线程 1

线程 2

79

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); } }

80

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; } }

81

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

82

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

83

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

84

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

85 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){} }}

86

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; }

87

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);

88

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

89

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

paint() {}

clock 类 clock(){} 初始化

Show(){} 换算弧度

drawNiddle(){} 画图

90

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);

91

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); }

92

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))); } }

93

2.6 动态效果 --- 线程的应用 import java.awt.*;import java.util.Date; 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); } }

94

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

paint() {}

clock 类 clock(){} 初始化

Show(){} 换算弧度

drawNiddle(){} 画图

95

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

clock(){} 初始化

stop()

paint()

run()

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

drawNiddle(){} 画图

96

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); Thread thread;}

97

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

98

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;

99

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); } }

100

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

101

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
102

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);}

103

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

104

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();}}

105

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();

106

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

107

2.9 小结

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

public class myapplet extends Applet { init() {…};start() {…}; stop() {…};destroy() {…}; paint(Graphics g){…} } myclass1{…..};class myclass2{…};

applet 主类

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

2.9 小结

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

Classes

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

mymethod1 mymethode2

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

109

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

内存

110

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

111

第三章

事件处理

北京大学计算机系

代亚非

112

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

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

113

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



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

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

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

114

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

115

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

int id

int modifiers

int x

Event

int y

long when

int key

Object target

Object arg

116

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

MouseUp() Event

MouseDown() MouseDrag()

HandleEvent()

MouseMove() MouseEnter()

action()

MouseExit() keyDown() KeyUp()

117

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

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

118

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);

119

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
120

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);} } ❚ [ 练习 ] 对鼠标的点击动作计数

121

3.3 键盘产生的事件 捕获的方法 keyDown(Event evt, int key) Event 类的键常量 常量 键 常量 键 常量 键 DOWN 下箭头键 END End 键 F1 F1 键 F2 F2 键 F3 F3 键 F4 F4 键 F5 F5 键 F6 F6 键 F7 F7 键 F8 F8 键 F9 F9 键 F10 F10 键 F11 F11 键 F12 F12 键 HOME Home 键 LEFT 左箭头键 PGDN PageDown 键 PGUP PageUp 键 RIGHT 右箭头键 UP 上箭头键

122

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); } }

123

3.3 键盘产生的事件

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

124

3.3 键盘产生的事件 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚

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

125

3.4 动作事件 ❚ 凡是由构件产生的事件叫动作事件 ACTION_EVENT, 处理这类事件的方法是 : action().

music

确定

sports

取消

art

126

3.4 动作事件

❚ action(Event evt, Object arg) ❚ evt.target: 指明事件类型 ❚ (button,checkbox,list,...) int clickCount

int id int modifiers

int x

Event

int y

long when

int key

Object target

Object arg

127

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

判断组件类型 ( 如是 button 或 checkbox) if(evt.target instanceof Button) if(evt.target instanceof Checkbox) 判断是哪多个同类组件中的哪一个 if(evt.target==button1) if(evt.target=button2) 或者通过判断标签内容 if(arg==“ 确定 ” ) if(arg==“ 取消 ” )

128

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);} }

129

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

130

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; }

3.5 通用的事件处理程序 ----handleEvent

131

❚ handleEvent 处理所有的小应用程序所接受 的事件 , 由它将事件送给相对应的方法 . ❚ 让我们看一下 handleEvent 的缺省实现 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);

3.5 通用的事件处理程序 ----handleEvent 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:

132

3.5 通用的事件处理程序 --handleEvent

}

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;

133

3.5 通用的事件处理程序 --handleEvent

134

❚ 覆盖 handleEvent 的情况 ( 原来的 handleEvent 不 被执行 ): 只处理我们感兴趣的事 :

public boolean handleEvent(Event evt) { switch(evt.id) { case Event.MOUSE_ENTER: //doing something; case Event.MOUSE_EXIT: //doing something; default:return super.handelEvent(evt); return false;

}}

135

.

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

136

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

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

137

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

138

3.7 Java1.1 事件模型 窗口

panel

窗口或面板 的监听器

button

按钮的监听器

139

3.7 Java1.1 事件模型

❚ 常用的监听器及其方法

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

140

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

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

❚ }

141

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); }

142

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

public class ButtonHandler implements ActionListener { TestButton a; 3.7 Java1.1tb)事件模型 ButtonHandler(TestButton { 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(); } }

143

144

3.7 Java1.1 事件模型 100101

class TestButton button color=black; paint()

class ButtonHandler a 100101 a.color=black;

new ButtonHandler(this);

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(); } } }

145

146

class Example3.7 Java1.1 事件模型 {public void init() ❚ 在某个类的内部定义的类叫内部类 button1=new Button(“button”); ❚{ Button 内部类的特点 : 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(); } . };

147

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) {} }

148

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

149

3.8 小结 ❚ 事件由用户的使用了鼠标和键盘动作引起的 ❚ 事件由事件处理器来处理 ❚ handleEvent() 方法是 Applet 从 component 继承而来的 . ❚ 重写 handleEvent() 方法时注意返回 false 指 明有些情况被忽略 , 将其传给上层对象 . ❚ 在组件上产生的动作叫动作事件 ,action 方法 类处理

150

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

第 4 章 异常

北京大学计算机系 代亚非

第 4 章 异常 ■ ■ ■ ■ ■ ■ ■

4.1 4.2 4.3 4.4 4.5 4.6 4.7

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

152

153 4.1 异常的概念 ■ 什么是异常 ? 异常实际上是程序中错误导致中断了 正常的指令流的一种事件 . ■ 没有处理错误的程序 : read-file { openTheFile; determine its size; allocate that much memory; closeTheFile; }



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

openFiles; if (theFilesOpen) { determine the lenth of the file; if (gotTheFileLength){ allocate that much memory; if (gotEnoughMemory) { read the file into memory; if (readFailed) errorCode=-1; else errorCode=-2; }else errorCode=-3; }else errorCode=-4 ; }else errorCode=-5;

154

4.1 异常的概念 ■



■ ■

155

观察前面的程序你会发现大部分精力花 在出错处理上了 . 只把能够想到的错误考虑到 , 对以外的 情况无法处理 程序可读性差 出错返回信息量太少

4.1 异常的概念 ■

用异常的形式处理错误

read-File; { try { openTheFile; determine its size; allocate that much memory; closeTheFile; }catch(fileopenFailed) { dosomething; } catch(sizeDetermineFailed) {dosomething;} catch(memoryAllocateFailed){ dosomething;} catch(readFailed){ dosomething;} catch(fileCloseFailed) { dosomething; } }

156

4.1 异常的概念

157

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

4.1 异常的概念

158

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 异常的分类 ■









159

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

4.2 异常的分类 ■

.

160

Throwable

用户自己产生的异常 Exception 要处理

Error RuntimeException

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

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

161

4.3 捕获异常 ■

常见的异常



ArithmeticException ArrayIndexOutOfBandsException ArrayStoreException 如果在使用能 IOException 够产生异常的 FileNotFoundException 方法而没有捕 NullPointerException 获和处理,将 MalformedURLException 不能通过编译 NumberFormatException OutOfMemoryException

■ ■ ■ ■ ■ ■ ■ ■

162

4.3 捕获异常 ■ ■ ■ ■

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

163

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);}}}

164

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

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

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

166

4.4 声明异常

167

一个方法不处理它产生的异常 , 而是沿着调用 层次向上传递 , 由调用它的方法来处理这些异 常 , 叫声明异常 .  声明异常的方法  在产生异常的方法名后面加上要抛出 (throws) 的异常的列表 ■ void compute(int x)throws ArithmeticException {…} ■ returnType methodName([parameterlist]) throws exceptionList ■

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

168

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 声明异常

method1 处理

computer

抛出

169

4.4 声明异常

170

例 : 说出程序执行结果 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 声明异常

171

public static void main(String args[]) { try { Proc(0); Proc(1); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Catch"+e); } } c:>jview throwsException In Situation 0 no Exception caught In Situation 1 Catch java.lang.ArrayIndexOutOfBoundsExce ption:10

4.5 抛出异常  抛弃异常

■ ■ ■

:

不是出错产生 , 而是人为地抛

出 throw ThrowableObject; throw new ArithmeticException(); 例 : 编写程序人为抛出 (JavaThrow.prj) ArithmeticException, ArrayIndexOutOfBoundsException StringIndexOutOfBoundsException

A method

Exception throw

172

Another method caught

class JavaThrow 4.5 抛出异常 173 { 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 创造自己的异常 174 不是由 Java 系统监测到的异常 ( 下标越 界 , 被 0- 除等 ), 而是由用户自己定义的 异常 . 用户定义的异常同样要用 try--catch 捕 获 , 但必须由用户自己抛出 throw new MyException. 异常是一个类 , 用户定义的异常必须继 承自 Throwable 或 Exception 类 , 建议 用 Exception 类 .

4.6 创造自己的异常

175



形如 :



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; }

176

public int CalcAnswer() throws NumberRangeException 创造自己的异常 177 { 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 创造自己的异常 ■









178

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

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

179

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()); } }

180

4.6 创造自己的异常

181

■ .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; // ( 统一的出口 , 最终必定要执行 ) }}

182

4.4 小结 ■

183

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

Method1

throws

Method2

call Method3 call Read-file

throws

throws 产生异常

Try catch

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

184

作业 ■

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

185

import java.awt.*; import java.applet.*; 186 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章 构成用户界面的窗口环境 北京大学计算机系 代亚非

187

构成用户界面的窗口环境

188

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

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

5.1 使用 AWT 构件

189

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

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

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

5.1 使用 AWT 构件

190

✔ 已经学过的:

✔ 没有学过的:

✔ getFont ,

✔ action, disable , enable ,

✔ handleEvent,keyUp

✔ getBackground ,

✔ keyDown,mouseUp ,

✔ getForeground , hide ,

✔ mouseDown ,

✔ inside , isEnable ,

✔ mouseMove ,

✔ isShowing , isVisible ,

✔ mouseEnter ,

✔ locate , location ,

✔ mouseExit , ✔ mouseDrag, repaint ✔ setFont , resize

✔ move , setBackground , ✔ setForeground , show , ✔ size

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

列表

按钮

菜单

窗口,对话框 另一个窗口

container container

191

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; } } 按钮

192

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

193

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

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

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

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

194

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

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

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

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

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

195

Component Container Button

Panel Applet

Menu Textfield Checkbox

Window Frame Dialog FileDialog

5.3 事件的传递 (1.02)

196

当动作发生在按钮上 窗口

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() {…….;} }

窗口

197

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)

198

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

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

5.3 事件的传递 (1.1) 窗口

199

panel

窗口获面板 的监听器

button

按钮的监听器

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

200

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

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

取消

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

201

✔ 处理 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); }

202

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

return true;

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

203

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

204

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); } }

.

5.4 各种构件的应用实例

205

✔ 一般步骤 :

创建 new

加入 add

响应 action

处理

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

206

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, false); New gr,null, false); New Checkbox(label,gr,true);

min

start

max

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

207

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

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

5.4 各种构件的应用实例 --CheckBox 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); }

208

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

209

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

210

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

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

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

211

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); }

212

5.5 外观的管理与制

0 1 5

2

3

4

213

0

1

3

4

4

5

5.5 外观的管理与制

214

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

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

button3

button2 button4

import java.awt.*; 215 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 外观的管理与制

216 北

✔ 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

217

5.5 外观的管理与制

218

✔ GridBagLayout 类和

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

5.5 外观的管理与制

219

✔ GridBagConstraints 类的约束条件 gridwidth,

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

5.5 外观的管理与制

220

✔ 例 :(GridBagApplet.html)

button1

button2

button3

button4 button5 button7

button6 button8 button9

public init() 221 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; 222 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 外观的管理与制

223

✔ GBC.gridwidth=

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

But2 But4

But3

5.5 外观的管理与制

224

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

But1 ✔ GBC.gridwidth=

But2 But4 But5

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

But3 But6

5.5 外观的管理与制

225

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

But2 But4 But5

But7

But3 But6

5.5 外观的管理与制

226

✔ 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

227

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

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

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

228

✔ 例 : 根据程序说出运行结果 ✔ 注意一个程序中生成一个 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")); } 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);} }

229

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")); } }

230

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); } }

231

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

232

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

233

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

(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); } .

234

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

235

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(); } . }

236

5.6 各种构件的应用实例 ---Frame ✔ 5.6.3 Frame 类 ✔ 功能 : 制作一般的独立窗口 , 它是构件容

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

237

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

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

238

5.6 各种构件的应用实例 ---Frame ✔ 例 : 创建一个窗口 , 并用按钮控制它的显示或 ✔ 隐藏 FrameApplet

Frame Window This is CustomFrame window

hide Show window hidewindow Show window

239

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

action 捕获 button button.label is hide

Frame.show Frame.hide

240

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

241

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

✔.

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

public class FrameApplet extends Applet 242 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 各种构件的应用实例 ---Frame { CustomFrame(String title) { super(title);}

243

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

244

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

button1

button1 Button1 button2 button2

10 1

Button1 button2 button2

0 7

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

245

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

246

✔ 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(); }

247

ButtonsInFrames() 5.6 各种构件的应用实例 { setLayout(new BorderLayout());---Frame 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);} }

248

5.6 各种构件的应用实例 ---Frame ✔ 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); .

249

与 List 类有关的事件 public 5.6 各种构件的应用实例 --- 练习 evt) public boolean boolean handleEvent(Event handleEvent(Event evt) 250 Event.LIST_DESELECT, {{ switch(evt.id){ switch(evt.id){ Event.LIST_SELECT case Event.WINDOW_DESTROY: case例 Event.WINDOW_DESTROY: :(FontDisplay.class---f3.bat)

List add

Panel 类 add

List add

}}

dispose(); dispose(); System.exit(0); System.exit(0); default: default: return return super.handleEvent(evt); super.handleEvent(evt);

arae }} TextArea 类 add

area.setfont( Frame 类 handleEvent LIST_SELECT

FontDisplayer Arial FontDisplay! Courier New Courier New You can input Times New Roman something here.

18 20 字型 22 , 字体 24 26

, 字号 )

handleEvent WINDOW_DESTROY

.

import java.awt.*; class FontDisplay 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 各种构件的应用实例 --- 练习

251

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

对窗口来说只有一个事 件

252

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

253

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

5.6 各种构件的应用实例 --- 练习 ✔ 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);

254

5.6 各种构件的应用实例 --- 练习 ✔ String FontNames[]={"Arial", "Courier

New", "Times New Roman"}; ✔ List FontSelector=new List(); ✔ for (i=0;i
255

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

256

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

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

FontPanel(TextArea FS) // 把另一个对象做参数 257 各种构件的应用实例 --- "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) 258 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

259

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);

260

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

261

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”: ….; } } }

262

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

263

✔ 在处理菜单事件 时应该注意的事

情是 : 判断层次

A

A

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

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

public boolean action(Event evt, Object arg) 264 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

265

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

5.6 各种构件的应用实例 ---menu 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());} }

266

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

267

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

5.6 各种构件的应用实例 -- 综合练习 ✔ 设计用户界面可根据用户选择办理银行业

268

务 ✔ 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; } ✔}

269

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(); } }

270

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

WelCome to Bank 100 Create account Show nbalance Deposit Withdrawal

271

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

主类 Bankapp (Frame 的子类 )

Bank() 创建账户

main() 定义并显示窗口

show_balance diposite() withdrawal()

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

满足 Bank 要求

272

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

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

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

273

5.6 各种构件的应用实例 -- 综合练习 public publicBankapp() Bankapp() {setLayout(new {setLayout(newGridLayout(6,1)); GridLayout(6,1)); Label Labellb=new lb=new Label("welcome Label("welcometo toBank",Label.CENTER); Bank",Label.CENTER); tf=new tf=newTextField("0",15); TextField("0",15); add(lb);add(tf); add(lb);add(tf); b1=new b1=newButton("create Button("createaccount"); account");add(b1); add(b1); b2=new b2=newButton("show Button("showbalance"); balance");add(b2); add(b2); b3=new b3=newButton("diposite"); Button("diposite");add(b3); add(b3); b4=new b4=newButton("withdrawal"); Button("withdrawal");add(b4); add(b4); resize(450,100); resize(450,100); }}

274

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

275

public boolean action(Event evt,Object arg) 5.6 各种构件的应用实例 -- 综合练习 276 {if(evt.target instanceof Button) { 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; }

277

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; } }

278

5.7 总结

279

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

创建输出管理器 setLayout

定义事件的处 理 action

创建类的实 例 new

加到上一级容器中 add

作业

280

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

追加

第 6 章 数据流的运用

北京大学计算机系 代亚非

282

第 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 小结

283

6.1 输入输出方法 ■

什么是数据流 ? 文件

起 点

文件 , 字符串 存储区

文件

程序

程序

网络端点

终端

终 点

网络端点 ■

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



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



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

284

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

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

285

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”);

}

}

}

286

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

287

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 类型

288

6.2 输入输出流的基类 ■

■ ■ ■ ■ ■ ■ ■ ■ ■

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

289

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 长度的数据输出到流中

290

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

■ ■ ■



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

291

6.3 File I/O ■ ■ ■ ■ ■

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

输出文件

输入文件

read

292

6.3 File I/O ■ ■ ■

■ ■ ■

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

输入流

输出流

file2.txt

import java.io.*; 293 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); }}}

294

6.3 File I/O ■

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

file1.txt

输入缓冲区 输出缓冲区

输入流

file2.txt 输出流

文件 文件流

缓冲区流

6.3 File I/O ■ ■

■ ■ ■ ■ ■ ■ ■ ■

295

缓冲区流 : 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 中得到的是数据

296

6.3 File I/O ■ ■



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

文件

297

6.4 数据输入输出流 ■ ■

■ ■

■ ■ ■

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

298

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





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

299 class datainput_output 数据输入输出流 { public6.4 static void main(String args[]) throws IOException { 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(); }

300 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

301

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); }

302

6.5 随机存取文件 ■ ■ ■

■ ■ ■ ■

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

文件目录

303

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

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

304 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);} } }

305

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

306

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
307

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); } }

308

309

6.7 java 中的 unicode ■ ■

■ ■ ■

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

310 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 使用管道流 输出流 ■ ■ ■



■ ■



311

输入流

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

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

312

管道流一定是输入输出并用 例 : 将数据从输出管道进 , 从输入管道出 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");

313

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(); }

314

6.9 对象流 ■ ■

■ ■ ■ ■ ■ ■

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

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

315

一个相关的例子 : 从一个源读入一个简单的对象 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 得到一个字符串

316

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 对象流

317

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; }

318

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 对象流

319

对象流是怎样工作的 ? 允许可串行化的对象在流中传输 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 对象流 ■

■ ■ ■ ■ ■ ■

320

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

6.9 对象流 对象流举例 : 将 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) {;} }

321

322

6.9 对象流 import java.io.FileInputStream; import java.io.ObjectInputStream; public class ObjectRecov { public static void main(String args[]) { Student stu; try {FileInputStream fi=new FileInputStream(“date.ser”); ObjectInputStream si=new ObjectInputStream(fi); stu=(Student)si.readObject();si.close(); }catch(Exception e) {System.out.println(e);} System.out.println(“ID: ”+stu.id+“name:”+ stu.name+“age:”+age+“dept.:”+stu.department); } }

323

6.10 流的分类 ■

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

InputStream

PushbackInputStream

FileInputStream PipedIntputStream FilterInputStream ByteArrayInputStream SequencedInputStream StringBufferInputStream

DataInputStream BufferedInputStream LineNumberInputStream

324

6.10 流的分类 FileOutputStream DataOutputStream OutputStream

PipeOutputStream

BufferedOutputStream

FilterOutputStream

PrintStream

ByteArrayOutputStream

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

325

6.11 小结 ■ ■





文件

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

文件输入流

缓冲输入流

行号输入流

数据输入流



326

6.11 小结 ■







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

327

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

第 7 章 多线程

北京大学计算机系 代亚非

第 7 章 多线程 ■ ■ ■ ■ ■

7.1 7.2 7.3 7.4 7.5

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

329

330

7.1 多线程基本概念 文件

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

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

数据区段

数据区段

程序区段

程序区段

只有一个地方在执行

传统的进程

同时有数个地方在执行

多线程的任务

7.1 多线程基本概念 ■ ■

■ ■

331

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

7.1 多线程基本概念 ■ ■



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

332

7.1 多线程基本概念 ■

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



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

333

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();

334

7.2 创建线程的方式 ■





■ ■



335

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

7.2 创建线程的方式 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){ } } }

336

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){} }}

337

7.2 创建线程的方式

338

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

7.2 创建线程的方式

339

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

340

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 创建线程的方式 ■



341

当一个线程执行完所有语句后就自动终止 ,调用线程的 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;} }

342

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();}

■ ■ ■

}

343

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



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

344

7.3 线程的挂起与唤醒

345

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 多线程问题 --- 执行的顺序

346

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

// 多个进程运行时执行顺序是交叉的 347 7.3 多线程问题 class multithread extends Thread { 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();} }

348

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

: 将窗口分为上下两个区 , 分别运行 两个线程 , 一个在上面的区域中显示由右 向左游动的字符串 , 另一个在下面的区域 从左向右游动的字符串 . ■方法一 : 一个线程 , 在 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,); }

349

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



350

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

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

351

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);

352

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 多线程问题 -- 线程间的通信

353

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

354

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

输入流

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



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

355 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 356 { 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 ){}}}

357 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 多线程问题 --- 资源协调

358

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

取过



加1 后送 回去

变量

线程 10

withdrwal()

余额 透支

withdrwal()

359

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

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

线程 1

监视 器

withdrawal()

线程 2

360

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



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

read ■

监 视 器

线程 2

write

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

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

361

此处给出的例子演示两个线程在同步限制下工作的情况 . 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 多线程问题 --- 资源协调 ■

362

2. 等待同步数据 生产者

write.

. 共享对象

消费者

read

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

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

363



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



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

■ ■





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

writer aaaa aaaa aaaa aaaa aaaa bbbbb cccccccccccc cccc cccc

364

reader aaaa bbbbb cccc

aaaa bbbbb

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

365

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 { 366 { 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) { } } }

367

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

368

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) { } }

369

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

线程 1

note

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

线程 2

pen

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

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

370



什么是 daemon( 守护 )?



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

客户端 ■ ■



request

服务器端

守护线程是为其它线程提供服务的线程 daemon 守护线程一般应该是一个独立的线程 , 它的 run() 方法是一个无限循环 . 守护线程与其它线程的区别是 , 如果守护线程是 唯一运行着的线程 , 程序会自动退出

7.4 小结

371

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

7.4 小结

372

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

作业 ■

373

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

第 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 小结

375

8.1 Java 与网络 ■ ■ ■







376

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

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) ■

377

8.1 Java 与网络

378

下面的例子演示 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 与网络

379



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



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



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



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

380

8.2 使用 URL ■ ■ ■

■ ■ ■ ■ ■

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() while ( line ! =null ) line=data.readLine() DataInputStream URL

Java 程序

8.2 使用 URL

381

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; }

382

383

8.3 访问 cgi ■

起始页上的计数器及 cgi 程序 (script) 欢迎来访 , 你是第

1 2 7

个来访者 !

你是第 个来访者 客户端 HTML

cgi 程序名 img

服务器端 CGI 程序

num+ +

384

8.3 访问 cgi Your name form send

Web 服务器

cgi 程序

数据库 服务器

reset



8.3 访问 cgi ■



■ ■

385

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

8.3 访问 cgi ■



386

从小应用中运行 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 ■





■ ■

387

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

import java.io.*; import java.net.*; 388 8.3 test_cgi 访问 cgi public class { 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 ■ ■

■ ■ ■ ■ ■ ■ ■

389

从小程序中运行 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);

390

8.3 访问 cgi ■



■ ■ ■ ■ ■

391

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 规定服务器与外部程序交换信息的协议 .

8.3 访问 cgi 客户端 java 程序

392

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 连接

393

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 连接

394

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 连接 ■

■ ■





395

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

8.4 URL 连接

396

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/cgi-bin/test.cgi"); URLConnection connection = url.openConnection(); connection.setDoOutput(true); PrintStream out = new PrintStream(connection.getOutputStream()); out.println("0123456789"); out.close();

8.4 URL 连接

397

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 }

398

399

8.5 Socket

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

Socket

20 21 23

ftp telnet Mail finger

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

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

400

8.5 Socket ■



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

internet 服务

端口号

ftp telnet

21 23

www nntp

80 119

DNS

53

POP3

110

8.5 Socket ■







401

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

8.5 Socket ■

■ ■

402

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

403

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 ■

404

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

. 8.5 Socket 定义数据成员

定义数据成员

8.5 Socket

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

建立 socket 流 connetcting client... 向用户发出一个字符串 读客户 端信息 User :java 提示用户登录成功

: n i log

a v a j

Log

送用户名给 服务器

l u f s s 读 socket 流 ce c u s in

关闭流

405



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

8.5 Socket

406

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);}

407

8.5 Socket 定义数据成员 ■

.

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

建立 socket 流

向客户发出登录要求

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

408

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

409

例 : 显示服务器与客户机间的通信 ( 客户 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 定义数据成员

410

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 流 ( 看到提示 )

建立 socket 流 connetcting client... 向用户发送字符串 读客户 端信息 User :java 提示用户登录成功

: n i log

a v a j

Log

送用户名给 服务器

l u f s s 读 socket 流 ce c u s in

关闭流

411

412

8.5 Socket ■ ■ ■

■ ■

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

client(1111)

Server2(2222)

client(2222)

Computer 1

413

8.5 Socket ■ ■

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

client1

serverthreadserverthread

Server

client2

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

414

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

Log

送用户名给 服务器

l u f s s 读 socket 流 ce c u s in

关闭流

415

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(); }}

416

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; } }}

417

418

8.5 Socket ■

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

Hello 1076

Hello 1071 abc

def server

WAIT GO…def

WAIT GO…abc

8.5 Socket . IP client1 客

服务器 (1111)



户 线程

419

accept socket

IP client2

客 户

线程

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

420

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

reader.run

reader.run

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

421

8.5 Socket 客户端

socket getInputStream

client()

getOutputStream writer.start()

getLocalPort()

write_net_output()

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

服 务 器

422

8.7 Datagram ■ ■



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

server datagram server

client

8.7 Datagram ■



■ ■

423

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

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

424

发出报文的标准步骤如下 : 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 ■

■ ■ ■ ■ ■ ■ ■ ■

425

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();

426

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

427

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

428

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

429

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

System.out.println(received);



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





430

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

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

获得对方地址

创建接收包

构成信息包

等待接收

发送出去

8.8 小结 ■







■ ■

431

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

8.6 小结 ■ ■ ■ ■ ■ ■ ■ ■

432

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

作业 ■ ■ ■

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

433

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

哈尔滨工业大学计算机系代亚非

434

第九章 分布对象技术 ■ ■ ■ ■ ■ ■

9.1 9.2 9.3 9.4 9.5 9.6

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

435

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

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

436

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

A 机

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

就是支持访问异地对象

437

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

438

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

.

.

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

439

一个分布式系统的例子 ( 原始的方法 ) 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

440

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

订购 服务器 1

调度程序

...

订购 服务器 2

... 订购 服务器 n

WEB 服务器 1

Internet

441

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

客户

Oracle

Sybase

Informix

442

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

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

N2

管理器

...

Nk

443

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

分布式应用开发环境

Socket API Fortran 语言

RPC

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

444

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

我们都听到了哪些词汇 ? ActiveX Controls CORBA/IIOP COM/DCOM 构件模型 Java/RMI 分布对象

EJB 还有还有 … ...

RPC

445

9.2 分布对象技术概论

446

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

客户 / 服务器

9.2 分布对象技术概论

447

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

448

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

体系结构

关键技术的特点

第二代 (90 年代 )

信息共享

异构环境下的信 息互操作

经典的客户 / 服务器模型

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

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

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

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

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

面向 agent 的 拟人化交互环 境 概念验证系统 令人鼓舞 , 尚 未达到广泛应 用与协同工作 的成熟程度

9.2 分布对象技术概论 ■

449

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

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

9.2 分布对象技术概论

450

C++

B机 VB

一个应用逻辑 一个应用逻辑

C机 VB A机

Java

A 机 Java D机

9.2 分布对象技术概论

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

451

9.2 分布对象技术概论

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

452

9.2 分布对象技术概论 ■ ■

软构件模型的例子 ? OMG 的 CORBA 、 Microsoft 的 COM/DCOM 和 SUN 的 Java/EJB 。

453

454

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

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

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

Object A Object Object A A

C机

Agent 2

A机

ORB D机

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

客户

客户构造 构架

服务器

相同的接口

相同的接口 应用逻辑

服务器构 造构架

分布对象的实现 ➏







对象请求分发器 ORB

分布对象 代理





对象请求适配器 ➑

对象请求感知器 ➒

网络传输访问 ➋



455

456

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

O1 O2

虚拟 图书馆

客户

客户

预约服务

ORB

ORB

客户

还书

O1R O2R O3R O4R O5R O6R

O3

还 / 借处理

O5

O4

O6

Oracle

Sybase

Informix

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

457

对象服务 : 支持分布式系统正常工作的各类基 本的系统级服务 Ó¦ Óö ÔÏ ó Í ¨ Óù ¦ ÄÜ 1. 名字管理 2. 事件通告 3. 对象事务管理 ¶ ÔÏ ó Çë Çó ´ ú Àí (O b ject R e q u est B ro k er) 4. 对象生命周期

¶ ÔÏ ó · þ Î ñ

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

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

■ ■ ■ ■ ■

458

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

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



459

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

ͨÓù¦ ÄÜ

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

¶ÔÏó· þÎñ

9.4 分布对象主流技术 ■



460

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

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

461

CORBA ■









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

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

462

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

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

IDL Stubs

¶ÔÏ óÊµÏ Ö (Server)

ORB ½Ó¿Ú

¾² Ì ¬IDL Skeletons

ORBÄÚº Ë

¶¯Ì ¬ Skeleton

¶ÔÏ ó ÊÊÅäÆ÷

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







463

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

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

464

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

465

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 结构一次请求的过程

466

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



467

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

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

468

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

469

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

从程序员的角度看 OLE

OLE 控件 属性 为 将 来 而 建 造

OLE 文档

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

事件

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

链接

嵌入

结构化存 储

跟踪器 ( 永久命名 )

组件对象模型 (COM)

OLE 自动 化

470

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





■ ■ ■

471

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

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





■ ■

472

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

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







473

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

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







474

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

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

475

Java/RMI ■







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

476

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

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

477

RMI 系统的一般原理 : Web Server Client 1. 定位远程对象 : URL protocol RMI RMI 的命名工具注册远程 对象 . RMI 2. 和远程对象通信 : URL protocol Server RMI 传输层处理通信细节 RMI 3. 装载被串对象的字节码 : registry RMI 提供了传递数据和 装 Web Server URL protocol 载一个对象代码 的必要机 制 ■



■ ■



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

478

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

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

479

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

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

480

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

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

481

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 提供客户端构件 .

482

9.4 分布对象处理技术 --- 几种构件模型的比483 较

■ ■ ■





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







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

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

ActiveX/DCOM

EJB/RMI

支持跨语言操作支持 跨平台操作

A A A

A

D

C B

A A

公共服务构件 可用性 事务处理 消息服务 安全服务 目录服务 容错性 产品成熟性 软件开发商的支持度 可扩展 性

A

D

A

A E A A C C

C B B C

B

C B A

A

B

B B A B C D A A

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



486

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

487

Java ORB

Java Applet CORBA Server Object

Internet JDBC 第二层 业务服务 器

第三层 业务服务 器

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

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

488

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

489

从应用的角度看 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 interactive web

HTTP/CGI

Object web COM 对象 Java 对象 CORBA 对象 各类构件 DCOM CORBA/IIOP RMI

HTTP/CGI

490

9.6 一个 RMI 的分布式应用的实例 • 用 RMI 编写一个分布式应用 , 核心有以下三方面: • 定位远程对象 – 1. 一个应用可以利用 RMI 的名字服务功能注册器远程对象。 – 2. 可以象操作普通对象一样传送并返回一个远程对象的引用 ( 指针 ) 。

• 与远程对象通信: – 底层的通信由 RMI 实现,对于系统开发人员来说,远程调用 和标准的 Java 方法调用没有什么区别。

• 为需要传递的对象装载类的字节码

代 亚 非

– RMI 允许调用者向远程对象传递一个对象,因此 RMI 提供这 种装载对象的机制。

491

9.6 一个 RMI 的分布式应用的实例

Web Server URL protocol

URL protocol

代 亚 非

Client RMI

RMI

Server RMI

Web Server

registry URL protocol

492

9.6 一个 RMI 的分布式应用的实例 一、问题的提出 task

Client task

代 亚 非

Client

Remote object Compute engin

Server

task

Client

493

9.6 一个 RMI 的分布式应用的实例 • 分布特点 : – engin 开发 , 先运行 ,task 后定义 . 写 engin 时不对执 行什么任务作任何规定 . 任务可以是任意定制的 .

• 前提条件 : – 定义任务的类 , 要规定任务的实现步骤 , 使得这个 任务能够提交给 engin 去执行 . 使用 server 上的 CPU 资源 . 代 亚 非

• 技术支持 : – RMI 的动态装载功能 .

494

9.6 一个 RMI 的分布式应用的实例 A.m1()

compute engine

远程方法

远程对象

client

代 亚 非

m1 A server

Compute

Task

executeTask execute

远程对象必须继承远程接口 确定那些方法是远程方法 , 为此定义远程接口 远程接口只负责提供方法名 , 不一共实现细节 , 因此必须由 一个对象来实现接口

495

9.6 一个 RMI 的分布式应用的实例

client

client

• 在 java 中远程调用是通过定义远程接口来实现的 , 一个接口只能有一个方法 代 • 不同类型的任务 , 只要他们实现了 Task 类型 , 就 亚 可以在 engin 上运行 . 非 • 实现这个接口的类 , 可以包含任何任务计算需要 的数据以及和任何任务计算需要的方法 . 496

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 Compute

Task

executeTask execute

第二个接口 : 定义一个 task 类型 , 作为 参数传给 executeTask 方法 , 规定了 engin 与它的任务之间的接口 , 以及如何启动它的任务 . 它 不是一个远 程接口 497

9.6 一个 RMI 的分布式应用的实例 • Compute engin 的设计要考虑以下问题 : • 1. compute engine 是一个类 ComputeEngine , 它实 现了 Compute 接口 , 只要调用该类的方法 executeTask, 任务就能提交上来 . • 2. 提交任务的 Client 端程序并不知道任务是被 下载到 engin 上执行的 . 因此 client 在定义任务时 并不需要包含如何安装的 server 端的代码 . • 3. 返回类型是对象 , 如果结果是基本类型 , 需要 代 转化成相应的对等类 . 亚 非 • 4. 用规定任务如何执行的代码填写 execute 方法 . 498

9.6 一个 RMI 的分布式应用的实例

代 亚 非

( 2 )实现远程接口 • 一般说来 , 实现一个远程接口的类至少有以下步骤 : • 1. 声明远程接口 • 2. 为远程对象定义构造函数 • 3. 实现远程方法 engin 中创建对象的工作可以在实现远程接口类的 main 函数中实现 : • 1. 创建并安装安全管理器 • 2. 创建一个或更多的远程对象的实例 • 3. 至少注册一个远程对象 499

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 500 exception: " + e.getMessage()); e.printStackTrace(); }}

9.6 一个 RMI 的分布式应用的实例

代 亚 非

• 在构造函数中 , 通过 super(), a UnicastRemoteObject 被启动 , 即它可以侦听客户 端来的请求输入 • 只有一个远程方法 , 参数是客户端远程调用这个 方法时传来的任务 . 这个任务被下载到 engin, 远 程方法的内容就是调用客户端任务的方法 , 并把 结果回送给调用者 . 实际上这个结果是在客户的 compute engine 任务的方法中体现的 . callexecuteTask(task)

Compute

Task

executeTask execute 501

9.6 一个 RMI 的分布式应用的实例 • 参数传递规则 : • 1. 远程对象通常通过引用传递 . 一个远程对象 的引用是一个 stub, 它是客户端的代理 . 它实 现远程对象中的远程接口的内容 • 2. 本地对象通过串行化拷贝到目的 . 如果不 作制定 , 对象的所有成员都将被拷贝 . 代 亚 非

502

9.6 一个 RMI 的分布式应用的实例

代 亚 非

• 通过引用传递一个对象,意味着任何由于远程调 用引起的变化都能反映在原始的对象中。 • 当传递一个远程对象时,只有远程接口是可用的 , 而在实现类中定义的方法或者是非远程接口中 的方法,对接收者来说是不可用的 • 在远程方法调用中,参数,返回值,异常等非对 象是值传送 . 这意味着对象的拷贝被传送到接受 方。任何在对象上发生的变化不影响原始的对象 • 一旦服务器用 rmi 注册了, main 方法就存在了, 不需要一个守护线程工作维护服务器的工作状态 ,只要有一个 computer engin 的引用在另一个虚 拟机, computer engin 就不会关闭 503

9.6 一个 RMI 的分布式应用的实例 三、实现一个客户程序 • 目标:创建一个任务,并规定如何执行这个任务。

package compute; public interface Task extends java.io.Serializable { Object execute(); } 代 亚 非

client Pi

computePi

execute() ExecuteTask()

• task 不是远程接口,但是需要传递到服务器,因 此用串行化 504

9.6 一个 RMI 的分布式应用的实例 • computePi 的作用 装载安全管理器 生成一个远程对象 comp

Look up(ComputeEngin), 获得了 stubs

生成任务对象 调用 ComputeEngin 的远程方法 获得计算结果

Pi task=new Pi() Comp.executeTask(task)

代 亚 非

505

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(); } } 506 }

9.6 一个 RMI 的分布式应用的实例 • Pi 的作用 实现 Task 接口 实现 execute 算法

rmiregistry computepi Compute engin

代 亚 非

507

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) 508 { 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); } 509

/** 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 ... */ 代 亚 非

510

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); result = numer; int i = 1; 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; } }

511

9.6 一个 RMI 的分布式应用的实例 • 由于 Rmi 的存在,系统可以做到: • 1 、可以直接通过名字定位远程方法的位置 • 2 、以参数的形式将一个对象传递给一个远程 方法 • 3 、可以使一个对象到另外一个虚拟机上运行 • 4 、计算结果可以返回 代 亚 非

512

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) 代 亚 非

513

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 514

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 非

515

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\engine 516

9.6 一个 RMI 的分布式应用的实例 • 展开接口文件 • cd c:\home\ann\public_html\classes • jar xvf compute.jar

代 亚 非

517

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\com pute.jar 518

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 •

519

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 •

520

9.6 一个 RMI 的分布式应用的实例

代 亚 非

521

9.7 基于 CORBA 的分布式应用系统的实 例 windowsNT ( C++ ) client

代 亚 非

Sun ( Java ) Netscape ( COBOL )

522

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 目前使用的通信协议不同 , 分 别是 JRMP 和 IIOP.

523

9.7 基于 CORBA 的分布式应用系统的实 例

代 亚 非

• 在 Java IDL 中 , 客户端通过引用与远程的对象 进行交互 , 即客户机使用 stubs 对远程服务器 上的对象进行操作 , 但并不拷贝服务器上的对 象. • Java RMI 即可以通过引用 , 也可以将对象下 载到本地机上运行 ( 因为有串行化功能 ). • Java 实现简单 , 但语言不兼容 • Java IDL 则可以充分发挥语言无关的优势

524

9.7 基于 CORBA 的分布式应用系统的实 例 • IDL Java 的技术组成 : – – – –

IDL 至 Java 的编译器 :idltojava 生成客户端的 stubs 和服务器端的 skeleton CORBA API 和 ORB 一个简单的名字服务

代 亚 非

525

9.7 基于 CORBA 的分布式应用系统的实 例

客户应用

应 用 开 发 者

代 亚 非

C++, Java 编译器

客户程序

C++, Java 编译器

服务程序

IDL Stub

IDL 接口

IDL 编译器 ( C++, Java )

Skeleton 对象实现

526

9.7 基于 CORBA 的分布式应用系统的实 例

从反应式 Agent 的角度看 应用开发者

IDL 文件

代 亚 非

IDL 编译器

对象实现

事件处理部分

IDL Skeleton BOA

事件适配部分

ORB 内核

事件感知部分

事件处理分发部分

527

9.7 基于 CORBA 的分布式应用系统的实 例 • CORBA 编程实例 • 运行在浏览器中的客户对象与远程的服务对 象交互 , 客户端的表现是在浏览器中点击一个 button ,就会在一个文本域中返回服务端的时 间,同时也在服务端的标准输出上打印该时 间。 时间是一个对象 代 亚 非

The data in server side is 2000.6.1 12 : 56 : 00

server

button 528

9.7 基于 CORBA 的分布式应用系统的实 例 OBJECT REFERENCE CLIENT

SERVER SKELETONS

STUBS ORB

代 亚 非

SERVANT

IIOP ORB METHOD REQUEST

529

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 530

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); return date_time;// 向客户端返回时间串 } } 531

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(); // 创建服务对象实例

532

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); }

533

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("");

534

代 亚 非

// 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); }

535

9.7 基于 CORBA 的分布式应用系统的实 例

代 亚 非

// 服务对象就绪,准备接受请 boa.impl_is_ready(null); System.exit(0); } catch(SystemException ex) { System.err.println(ex.getMessage()); ex.printStackTrace(); System.exit(1); } } } 536

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;

537

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); 538

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); }

539

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); } } 540