You are on page 1of 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 程序


<html><body>
<applet code=“HelloApplet.class” width=200
height=100>
</applet></body></html>
•运行 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();
class Car DemoCar.set_number(3838);
{ int car_number; DemoCar.show_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); }
}
class TrashCar extends Car 13
{ int capacity;
void中的类、方法和变量
类的继承
1.7 Java set_capacity(int trash_car_capacity)
关系

{ capacity=trash_car_capacity;}

void show_capacity();
{ System.out.println(“My capacity is: ”+
class Car capacity);}}
{
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); pro_i=5
def.i=10; def.show(); } 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
{ void print_out() B.data_a=5
{ 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 在变量之前 , 定义一个常量 .
• 属于类的变量和方法 ----static object b
char data
• static 在变量或方法之前 ,
表明它们是属于类的 , object b static int object c
char data share_data char data
class ABCD
{ char data;
static int_share_data; } Object b
char data
class Demo
{ ABCD a,b,c,d}
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 中的抽象类、接口和程序包


• 利用接口可实现多重 继承 interface Stack
( 可同时实现多个接口) { void push(object x);
• 接口的作用和抽象类
object pop();}
类似 , 指定原型 , 不直 class A extends Applet
implements Stack
接定义方法的内容 .
{ void push(object x)
• 关键字 implement 用 来实
{
现方法 , 即在使用时要用
…;// 具体内容
给出方法的实际内容
}
• 接口中的方法和变量
object pop()
• 是 public 的 {
…;// 具体内容 }
}
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
使用 newstatic来做内存配置
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 Circle
class ThisClass
{ int r;
{ public static void main()
Circle(int r)
{ Bank bank=new Bank();
{this.r=r;}
bank.someMethod(this);
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

小结 package
C:\ myclass.calculate
class A{} class B{}
classC{}
java

lib myclass
javasource
bin
classes.zip calculate
myjava.java
javac appletviewer A B C
java
import java.io.*;
让 PATH 包含 … \java\bin import java.awt.Graphics;
SET CLASSPATH import mycalss.calculate.*;
=.; C:\...\myclasses 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; address
address sender
sender
• private String senderState; address
address receiver
receiver
• private String senderZip;
• private String receiverName;
• private String receiverStreet;
• private String receiverCity;
• private String receiverState;
• private String receiverZip;
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

<Html> 2. Browser loads


<Applet code= ….> HTML document
</Applet>
3. Browser loads
Applet class applet classes

Location: http://someLocation/file.html
4. Browser
run applet

Loading...
47
2.1 所有小应用程序的根源
• Applet 的限制
本地程序
SERVER Browser
co
n
Applet 被下载的 nec
t io applet
n
file

ti on
SERVER ne c
con local
与 applet 无关的
本地方法
48
2.1 所有小应用程序的根源
• 2.1.2 applet 的生命周期
Java.applet.Applet

public void init() public void destroy()

public void start() public void stop()

public void paint(Graphics g)

• paint() 虽不在生命周期内 , 但它的作用相


当于 applet 的灵魂
49
2.1 所有小应用程序的根源
• 一个 applet 的可视周期
init

start
离开 web 页面 重新装入或改变页
面大小或返回 Web
stop 页面

destroy
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> </APPLET>
• CODEBASE 的作用
当 class 文件与起始页文件不在同一个目录下
时 , 使用 CODEBASE 说明
<APPLET CODE=“showdate.class”
width=600 height=80>
CODEBASE=“\myjava\class”</APPLET>
55
2.2 小试身手
<APPLET
<APPLET
CODE=“showdate.class”
CODE=“showdate.class” C:\C:\
width=600 height=80>
width=600 height=80>
CODEBASE=“\myjava\class”
</APPLET>
</APPLET> myjava
myjava

public javacode class


public javacode class

Index.html showdate
Index.html showdate
56
2.2 小试身手
• ALIGN,HSPACE,VSPACE
其它文字

hspace
Java applet

vspace
其它文字

<APPLET
CODE=“showdate.class” width=600 height=80>
vspace=100 hspace=100
</APPLET>
57
2.2 小试身手
• 向 applet 传递参数的两个步骤
1. 在起始页中要有 <PARAM> 标签
2. 在 applet 中要有 getParameter 方法
在起始页中有 :
<applet code=showdate width=600 heigt=80>
<param name=rem value=“ 时间是 : ”></applet>
在 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) public void init()


{ g.setFont(msgFont); {title=getParameter
g.setColor(Color.blue); (“rem”);
g.darwString(title+ if (title==null)
timeNow.toString(),5,50); title=“”;
} }
59
2.2 小试身手
• 例 : 利用一个可以显示运行字符串的类 , 显
示自己的字符串 (htmlpara.html)
<applet code=htmlpara.class width=300 heigh=200>
<param name=MESSAGE value=”this is a test">
<param name=FONT value="BOLD">
<param name=POINT_SIZE value=20></applet>
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){}
}
}
import java.applet.*; import java.awt.Graphics; 85
public class2.6MovingCharacter
动态效果 ---extends 线程的应用 Applet
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
2.6 动态效果 --- 线程的应用
class Clock
{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
2.6 动态效果 --- 线程的应用
theta=(double)(hours*60*60+minutes*60+second)/
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 动态效果 --- 线程的应用
主类

生成时间对象,取时间 paint() {}
生成 Clock 对象,将时间
传递给 Clock 对象

clock 类

clock(){} Show(){} drawNiddle(){}

初始化 换算弧度 画图
95
2.6 动态效果 --- 线程的应用
主类

start() stop() paint() run()

启动新线程 停止线 生成 clock 类实例 repaint()



clock 类

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<frame.length;i++)
frame[i]=getImage(getCodeBase(), “pic”+i+ “.gif”);
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<frame.length;i++)
frame[i]=getImage(getCodeBase(), "pic"+i+ ".gif");
}
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{…};
108
init() applet 启动后第一个被执行 , 在此初始化
applet 2.9 小结
主类 start() init() 后被执行 , 程序主要代码写在此
paint() start() 后被执行 , 写与输出有关的代码

stop() 浏览器变换页面时执行 , 可以省略重写

destroy() 浏览器关闭时执行 , 可以省略重写


自定义方法 不能自动被执行 , 可以由前三
Classes 个方法调用 . 例如 : start()
{ mymethod()}
mymethod1
. Classes myclass =new Classes()
.myclass.method1();
mymethode2
109
class Myclass 2.9 小结 test1
{ int v1;
method(int num) v1 20
{v1=num;}
}
public class Demo extends Applet
test2
{ public void init()
v1 10
{ Myclass test1=new Myclass();
test1.method(20);
Myclass test2=new Myclass();
test2.method(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 C 程序
www 浏览器 Web server
url 数据库程序
interact
applet

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

鼠标动作 .
• 响应用户的动作称为处理事件 .
• 在类 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 key


int id

int modifiers Object target


Event

int x int y long when Object arg


116

3.1 什么是事件

如果你不
如果你不 MouseUp()
覆盖你要
覆盖你要 MouseDown()
处理的方 Event
处理的方 MouseDrag()
法,则缺
法,则缺
省的方法 HandleEvent() MouseMove()
省的方法
返回一个
返回一个 MouseEnter()
假值,通
假值,通 MouseExit()
知系统没 action()
知系统没 keyDown()
有处理事
有处理事

件 KeyUp()
117

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

3.2 鼠标产生的事件
❚ 例 : 在鼠标单击的地方显示 “ ”.
(MouseClick.html)
捕获事件 获得参数 (x,y) 在 (x,y) 处画
mouseDown (Event evt, 叉
int x, int 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.*;
3.2 鼠标产生的事件
.public class mark extends Applet
{ 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<i;k++){
g.fillOval(Marks[k].x,Marks[k].y,10,10);
}
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 key
int id

int modifiers Object target


Event

int x int y long when 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)
{ CurrentMarks++; 10
repaint(); return true; }
public void paint(Graphics g)
{ g.drawString(" "+CurrentMarks,10,10);}
}
129

3.4 动作事件
❚ 例 : 根据用户选择画图形
思路 :java.awt.*;
❚import
园 ❚import java.applet.Applet;
1. 设计两个按钮 ( 后面详细讲 )
❚public class drawing
2. 事件处理 action extends Applet
{ boolean circlemark=true;
根据选择
❚ public init() , 分别标记园或方
根据标记画出相应图形
❚ {3 Button b1=new Button(“ 园 ” );
❚ Button b2=new 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; }
131
3.5 通用的事件处理程序 -----
handleEvent
❚ 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);
132
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:
133
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;
}
134
3.5 通用的事件处理程序 ---
handleEvent
❚ 覆盖 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 143
{ 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();
}
}
144

3.7 Java1.1 事件模型

100101 class TestButton


button
color=black;
paint()

class ButtonHandler
new ButtonHandler(this);
a 100101

a.color=black;
public class TestButtoninner extends Applet 145

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

class Example3.7 Java1.1 事件模型


{public void init()
❚ 在某个类的内部定义的类叫内部类
❚{ Button
内部类的特点 button1=new
: 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 章 异常 152

■ 4.1 异常的概念
■ 4.2 异常的分类
■ 4.3 捕获异常
■ 4.4 声明异常
■ 4.5 抛出异常
■ 4.6 创造自己的异常
■ 4.7 总结
4.1 异常的概念 153
■ 什么是异常 ?

异常实际上是程序中错误导致中断了
正常的指令流的一种事件 .
■ 没有处理错误的程序 :

read-file {
openTheFile;
determine its size;
allocate that much memory;
closeTheFile;
}
4.1 异常的概念 154
■ 以常规方法处理错误
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;
4.1 异常的概念 155

■ 观察前面的程序你会发现大部分精力花
在出错处理上了 .
■ 只把能够想到的错误考虑到 , 对以外的
情况无法处理
■ 程序可读性差
■ 出错返回信息量太少
4.1 异常的概念 156
■ 用异常的形式处理错误
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; }
}
4.1 异常的概念 157

■ 和传统的方法比较异常的优点 :
1. 把错误代码从常规代码中分离出来
2. 把错误传播给调 method4 产生异常
用堆栈 method3 传
3. 按错误类型和 method2

错误差别分组 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 捕获异常 161

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

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

■ 例 : 编写 Java 程序 , 包含三种异常
■ 算术异常 , 字符串越界 , 数组越界
■ 观察输出信息 :
■ 每个异常对象可以直接给出信息
class first_exception
4.3 捕获异常 164
{ 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);}}}
4.3 捕获异常 165
 一定会执行的程序块 ---finally

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

■ finally 在文件处理时非常有用
■ try {
■ 对文件进行处理的程序 ;
■ }catch(IOException e) {
■ // 对文件异常进行处理 ;
■ }finally {
■ 不论是否发生异常 , 都关闭文件 ;
■ }
4.4 声明异常 167
■ 一个方法不处理它产生的异常 , 而是沿着调用
层次向上传递 , 由调用它的方法来处理这些异
常 , 叫声明异常 .
 声明异常的方法

 在产生异常的方法名后面加上要抛出
(throws) 的异常的列表
■ void compute(int x)throws
ArithmeticException {…}
■ returnType
methodName([parameterlist]) throws
exceptionList
4.4 声明异常 168

例 : 若因
某种原
public int compute(int x) throws
因不想 ArithmeticException e)
在创建 { return z=100/x;}
URL 的
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 声明异常 169

method1
处理

抛出
computer
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 抛出异常 172

 抛弃异常 : 不是出错产生 , 而是人为地抛



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

throw caught
class JavaThrow
4.5 抛出异常
{ public static void main(String args[]) 173

{
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 创造自己的异常 176

.
■public boolean action(Event evt, Object arg)
{ try {
int answer = CalcAnswer();
answerStr = String.valueOf(answer);
}catch (NumberRangeException e)
{ answerStr = e.getMessage(); }
repaint();
return true;
}
public int CalcAnswer() throws NumberRangeException
{ int int1, int2; 4.6 创造自己的异常
int answer = -1; 177
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
{ double balance;4.6 创造自己的异常 179

public
■ . void deposite(double dAmount)
{ if(dAmount>0.0) {balance+=dAmount;}}

public void withdrawal(double dAmount)


throws InsufficientFundsException
{ if (balance<dAmout) {
throw new InsufficientFundsException(this,dAmount);
}
balance=balance-dAmount;
}
public void show_balance()
{ System.out.println("The balance is "+(int)balance);}
}
4.6 创造自己的异常 180

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

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

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

throws throws throws

产生异常
Try catch
4.4 小结 184

3. 异常可以人为地抛出 , 用 throw new


语句
4. 异常可以是系统已经定义好的 , 也可
以是用户自己定义的
5. 用户自己定义的异常一定继承自
Throwable 或 Exception 类
作业 185

■ 当用户按 “ T” 或 “ t” 时 , 人为抛出一个
算术异常 , 处理 方式为打印出错信息
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 , ✔ move , setBackground ,


✔ setForeground , show ,
✔ mouseDrag, repaint
✔ size
✔ setFont , resize
5.2 包含构件的构件 ---- 构件容器
191
(container )

列表 按钮 菜单

窗口,对话框 container

另一个窗口 container
5.2 包含构件的构件 ---- 构件容器
192
(container )
import java.awt.*;
import java.applet.Applet;
public class CountClick extends Applet
{ int CurrentMarks=0;
public init()
{ Button b1=new Button(“ 按钮” );
add.b1;
}
} 按钮
5.2 包含构件的构件 ---- 构件容器
193
(container )
✔ AWT 使用 Container 类来定义最基本的构件
容器 , 它有两个子类 :Window 类和 Panel 类 .
✔ 在 Window 类还有两个子类

✔ 1. 定义对话框 , 用 Dialog 子类 ;

✔ Java 还提了一个 Dialog 的子类 ---FileDialog,


用它生成文件对话框
✔ 2. 定义一般意义的窗口 , 用 Frame 类 .
5.2 包含构件的构件 ---- 构件容器
194
(container )
✔ Panel 干什么用的呢 ?

✔ 使你更方便的组织你的构件 , 得到赏心悦目
的布局
✔ Applet 是 Panel 的子类 , 因此在小应用程序
里可以直接加入构件 , 而一般的应用程序必
须先定义构件容器 .
✔ 小应用程序在浏览器中所显示的区域就是
Panel, 所占的尺寸就是缺省得 Panel 尺寸 .
5.2 包含构件的构件 ---- 构件容器
195
(container )
Component

Container

Button Panel
Applet
Menu Window

Textfield Frame

Dialog
Checkbox
FileDialog
5.3 事件的传递 (1.02) 196
当动作发生在按钮上
窗口 panel 时 , 首先看按钮这个
类有没有 action 方法 ,
如果没有则看包含按
钮的容器类 ( 即 panel)
有没有 action 方法 ,
如果没有事件就传递
窗口 , 如果没有就传
递给 component 的通
按钮 用处理方法 , 如果程
序中没有定义任何
action 方法 , 实际上事
class MyClass extends Frame 窗口 197
{ Panel
MyPanel p=new Mypanel();
add(p);
Button b=new Button(exit);
add(b); ok
boolean action()
{…….;}
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 各种构件的应用实例 -- 按钮 202

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

✔ boolean action(Event evt,Object arg)


✔ { y+=5; repaint();
✔ return true;
✔}
✔ paint(Graphics g)
✔ { g.drawString("Button”, 10,y);}
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();


New1 Checkbox(label,
New Checkbox(label,gr,null, false);
false);
New Checkbox(label,gr,true);

min start max


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

✔ 应用举例
✔ 建立三个复选框 , 被选中者的标签内容变成
“ changes”
– 应有的类 :Checkbox
– 应有的方法 :
– init: 建立复选框
– action: 接受动作事件
– setLabel(“*****”);
5.4 各种构件的应用实例 --CheckBox 208

import java.awt.*;
public class checkbox extends java.applet.Applet
{ Checkbox b1,b2,b3;
public void init()
{ b1=new Checkbox("Label1",null,false);
b2=new Checkbox("Label2",null,false);
b3=new Checkbox("Label3",null,false);
add(b1); add(b2); add(b3);
}
5.4 各种构件的应用实例 --- CheckBox 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 212

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); }
5.5 外观的管理与制 213

0
0 1 3
1 2 3 4
4 4 5
5
5.5 外观的管理与制 214

✔ Panel 类 ( 面板 )
✔ 功能 : 容纳其他对象 , 安排合理布局
✔ 创建面板 :
✔ Panel myPanel=new Panel();
✔ add(myPanel);
✔ 将面板作为容器 :
✔ mypanel.add(button) button1 button2

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

✔ FlowLayout 类
✔ 缺省的输出管理器

✔ GridLayout 类
✔ GridLayout mylayout = new
✔ GridLayout(3,3,0,0)

rows cols hspace vspace


✔ setLayout();
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 button6
button8
button7
button9
public 外观的管理与制
5.5 void init() 221
{ 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;
5.5 外观的管理与制
( 按钮可以在水平和垂直两个方向扩展 ) 222

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 But3


But4
5.5 外观的管理与制 224

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

But1 But2 But3


But4
✔ GBC.gridwidth= But5 But6
✔ GridBagConstraints.REMAINDER;
✔ layout.setConstraints(button6,GBC);
✔ add(button6);
5.5 外观的管理与制 225

✔ GBC.gridwidth=1;
✔ GBC.gridheight=2;
✔ ( 高度为两个单元 )
✔ layout.setConstraints(button7,GBC);
✔ add(button7);
But1 But2 But3
But4
But5 But6
But7
5.5 外观的管理与制 226

✔ GBC.gridwidth=
GridBagConstraints.REMAINDER;
✔ GBC.gridheight=1;
✔ layout.setConstraints(button8,GBC);
✔ add(button8);
✔ layout.setConstraints But1 But2 But3
✔ (button9,GBC); But4
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.*;
class各种构件的应用实例
public 5.6 canvas_test_2 extends Applet ---Canvas 229

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

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

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

✔ 例 : 按动鼠标改变画布的颜色
(CanvasApplet)
✔ 有哪些类 ? Canvas, Button, Color;
✔ 哪些方法 ? init(), action(),swapColor(),paint()
color color color

利用输出管理 按钮接收
变换颜色 执行重画
器按钮和画布 鼠标事件
5.6 各种构件的应用实例 ---Canvas 234

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

✔ boolean action(Event evt, Object arg)


✔{
✔ if(arg=="Color") mycanvas.swapColor();
✔ return true;
✔}
class MyCanvas extends Canvas
5.6 各种构件的应用实例 ---Canvas
{ Color color; 236
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();
}
} .
5.6 各种构件的应用实例 ---Frame 237

✔ 5.6.3 Frame 类
✔ 功能 : 制作一般的独立窗口 , 它是构件容

✔ 创建
✔ Frame fmInstance=new Frame();
✔或 Frame fmInstance=
✔ new Frame(“The window for test”);
✔ 将其显示到屏幕上
✔ fmInstance.show()
✔ 注意 : 不用 add()
5.6 各种构件的应用实例 ---Frame 238

✔ 常用的方法
✔ dispose,getCursorType,getIconImage,
✔ getMenuBar,getTitle,isResizable,
✔ setCursor,setIconImage,setMenuBar,
✔ setResizable,setTitle
✔ 窗口的相关事件 :
✔ Event.WINDOW_DEICONIFY,
✔ _DESTROY
✔ _EXPOSE,
✔ _ICONIFY,
✔ _MOVED
5.6 各种构件的应用实例 ---Frame 239

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

Frame Window

This is CustomFrame window

hide
hidewindow
Show
Show window
window
5.6 各种构件的应用实例 ---Frame 240

✔ 在 applet 中 action 处理 Button 的事件

button.label is show

Frame.show
action 捕获
button
Frame.hide
button.label is hide
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
5.6 各种构件的应用实例
{ CustomFrame ---Frame
frame; Button button; 242
public void init()
{ frame=new CustomFrame
("Custom Frame Window");
button=new Button("Show Window");
add(button); }
}
public boolean action(Event evt, Object arg)
{ boolean visible=frame.isShowing();
if(visible){
frame.hide(); button.setLabel("Show window");}
else {frame.show();button.setLabel("Hide Window");
return true;
}
}
class CustomFrame extends Frame
5.6 各种构件的应用实例
{ CustomFrame(String title) ---Frame 243
{ super(title);}

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 10 Button1 0
button2 1 button2 7

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

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

✔ 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);
✔ }} .
与 List 类有关的事件
public
public boolean
5.6 各种构件的应用实例 boolean handleEvent(Event
--- 练习 evt)
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)
dispose();
dispose(); System.exit(0);
System.exit(0);
List List default:
default: return
return super.handleEvent(evt);
super.handleEvent(evt);
}} FontDisplayer
add add
arae }} FontDisplay!
Arial
Courier New
Courier New You can input
Panel TextArea 类 Times New Roman
类 something here.
18
add add 20
area.setfont(
Frame 类 字型 22
, 字体
24
, 字号 )
26

.
handleEvent handleEvent
LIST_SELECT WINDOW_DESTROY
import java.awt.*;
class FontDisplay extends Frame
5.6 各种构件的应用实例 --- 练习
{ TextArea FontShower;
251

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 各种构件的应用实例 --- 练习 252

public boolean handleEvent(Event evt)


{ switch(evt.id){
case Event.WINDOW_DESTROY:
dispose(); System.exit(0);
default: return super.handleEvent(evt);
}
}

对窗口来说只有一个事

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 各种构件的应用实例 --- 练习 254

✔ public boolean handleEvent(Event evt)


✔ { switch(evt.id){
✔ case Event.LIST_SELECT:
✔ List target=(List)evt.target;
✔ String itemName=
✔ target.getItem(((Integer)evt.arg).intValue());
✔ if(target==FontSelector)
✔ CurrentFontName=itemName;
✔ else CurrentSize=Integer.parseInt(itemName);
5.6 各种构件的应用实例 --- 练习 255

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


New", "Times New Roman"};
✔ List FontSelector=new List();
✔ for (i=0;i<FontNames.length;i++)
✔ FontSelector.addItem(FontNames[i]);
✔ add(FontSelector);
✔ List SizeSelector=new List();
✔ for (i=6;i<64;i+=2)
✔ SizeSelector.addItem(String.valueOf(i));
✔ add(SizeSelector);
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) // 把另一个对象做参数
各种构件的应用实例
{ int i;5.6String 练习 New",
--- "Courier
FontNames[]={"Arial", 257

"Times New Roman"};


setLayout(new GridLayout(2,1,5,5));
FontSelector=new List();
for (i=0;i<FontNames.length;i++)
FontSelector.addItem(FontNames[i]);
add(FontSelector); SizeSelector=new List();
for (i=6;i<64;i+=2)
SizeSelector.addItem(String.valueOf(i));
add(SizeSelector); area=FS;
CurrentFontName=FontNames[0];
updateFontShower(); }
public boolean handleEvent(Event evt)
5.6 各种构件的应用实例 --- 练习
{ switch(evt.id){ 258

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 259

✔ 5.6.6 菜单系统

类 MenuBar 菜单系统
一般菜 可撕下菜单 帮助菜
单 单
选项
菜单容 #1
选项
器 #2
菜单中的菜单 选项
类 #1
选项
选项
Menu #2
第三层菜
#3
选项 选项
类 MenuItem #4

选项 #1
选项
#3 #2

非菜单容

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

✔ 创建菜单条
✔ mb=new MenuBar();
✔ setMenuBar(mb);( 类 Frame 中的方法 )
file edit
✔ 创建菜单
✔ menu1=new Menu(“file”);
✔ menu2=new Menu(“edit)
✔ mb.add(menu1);mb.add(menu2);
5.6 各种构件的应用实例 ---menu 261

✔ 创建菜单项
✔ mi1=new MenuItem(“new”);
✔ mi2=new MenuItem(“open”);
✔ mi3=new MenuItem(“save”);
✔ mi4=new MenuItem(“close”);
✔ menu1.add(mi1); menu1.add(mi2);
File edit
New
open
Save
Close
5.6 各种构件的应用实例 ---menu 262

如何处理事件
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”: ….;
}
}
}
5.6 各种构件的应用实例 ---menu 263

✔ 在处理菜单事件 时应该注意的事
情是 : 判断层次

A
✔ MenuContainer uplevel;( 定义一个菜单容器 )
✔ MenuItem target=(MenuItem)evt.target;( 当前被
选中的对象 )
✔ uplevel=target.getParent();
✔ strMessage=uplevel.getLabel()( 得到上一级容器
的标签 )
public boolean action(Event evt, Object arg)
{ String 各种构件的应用实例 ---menu
5.6strMessage; 264

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 266

class Example
{ class MenuFileOpen implements ActionListener
{ public void actionPerformed(ActionEvent e)
{ openFile(e.getActionCommand());}
}
class MenuFileSave implements ActionListener
{ public void actionPerformed(ActionEvent e)
{ saveFile(e.getActionCommand());}
}
5.6 各种构件的应用实例 ---menu 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 各种构件的应用实例 -- 综合练习 269

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

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(); }
}
5.6 各种构件的应用实例 -- 综合练习 271

WelCome to Bank

100

Create account

Show nbalance

Deposit

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

辅类 Bank 主类 Bankapp 辅类 Warningbox


(Frame 的子类 )
Bank() main() Warningbox(String str)
创建账户 定义并显示窗口 布局安排 , 显示对话框
show_balance 显示警告信息
Bankapp()
diposite() 布局安排 action()
关闭对话框
withdrawal() handleEvent()
它是模态的
关闭窗口

action() 不满足 Bank 要求


根据按钮做处理
满足 Bank 要求
5.6 各种构件的应用实例 -- 综合练习 273

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(); }}
5.6 各种构件的应用实例 -- 综合练习 274

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

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);
}}
}}
public boolean action(Event evt,Object arg)
5.6 各种构件的应用实例
{if(evt.target instanceof Button) -- 综合练习 276
{ 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); 277
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;
}
class Bank
{ long5.6 各种构件的应用实例 -- 综合练习
balance; 278
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; }
}
5.7 总结 279

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

设计好容 创建输出管理器 创建类的实


器层次 setLayout 例 new

定义事件的处 加到上一级容器中
理 action 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
输入流 输出流

文件
文件流 缓冲区流
295
6.3 File I/O
■ 缓冲区流 :
■ 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)

■ 数据输出流可以是一个已经建立好的输入数
据流对象 , 例如网络的连结 , 文件等 .
■ 下面的例子显示如何利用数据输入输出流往
文件中写不同类型的数据
class datainput_output 299

{ 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(); }
DataInputStream dis=new DataInputStream( 300

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 方法 , 以相
反的顺序再读取这些数据
public class random_file 304

{ public6.5 随机存取文件
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(): 返回文件名 temp.dat
■ f.getParent(): 返回文件所在目录名 data
■ f.getPath(): 返回文件路径 data\temp.dat
■ f.getAbsolutePath(): 返回绝对路
c:\myprog\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<file_list.length;i++)
■ { File current_file=new File(file_list[i]);
■ System.out.println(file_list[i]+
■ current_file.length());
■ }
■ }
307

6.7 java 中的 unicode


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

byte + 00000000 Unicode


16bit
8 bit
❚ DataInputStream 不适合输入是 unicode 的形式
❚ 处理字符用 InputStreamReader 类和
BufferedReader 类 (jdk1.1)
308
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);
}
309

6.7 java 中的 unicode


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

java 中的 unicode
6.7filetounicode
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);
312
6.8 使用管道流
■ 管道流一定是输入输出并用
■ 例 : 将数据从输出管道进 , 从输入管道出
■ 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 得到一个字符串
317
6.9 对象流
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;}
■ }
■ 这仍然是一个简单的对象
319
6.9 对象流
对象流是怎样工作的 ?
允许可串行化的对象在流中传输
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;
}
}
320
6.9 对象流
■ 2. 构造对象的输入输出流 ( 将对象保存到文
件中 , 或者通过网络传送到其他地方 )
■ 相应的类 :ObjectInput
■ 对象的输出 : ObjectOutputStream
■ 相应的方法 :writeObject()
■ 对象的输入 :ObjectInputStream
■ 相应的方法 :readObject()
■ 注 :jdk1.1 以上版本支持对象流操作
321
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) {;}
}
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
DataInputStream
PipedIntputStream
FilterInputStream BufferedInputStream

ByteArrayInputStream LineNumberInputStream

SequencedInputStream

StringBufferInputStream
324

6.10 流的分类
FileOutputStream
DataOutputStream
PipeOutputStream
OutputStream 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 章 多线程

北京大学计算机系
代亚非
329
第 7 章 多线程
■ 7.1 多线程基本概念
■ 7.2 创建线程的方式
■ 7.3 线程的挂起与唤醒
■ 7.4 多线程问题
■ 7.5 小结
330
7.1 多线程基本概念
文件 各种系统资源 输入输出装置 文件 各种系统资源 输入输出装置

数据区段 数据区段

程序区段 程序区段

同时有数个地方在执行
只有一个地方在执行

传统的进程 多线程的任务
331
7.1 多线程基本概念
■ 多线程的优势 :
■ 减轻编写交互频繁、涉及面多的程序的困
难.
■ 程序的吞吐量会得到改善 .
■ 由多个处理器的系统 , 可以并发运行不同
的线程 .( 否则 , 任何时刻只有一个线程在
运行 )
332
7.1 多线程基本概念
■ 线程与进程的区别 :
■ 多个进程的内部数据和状态都是完全独
立的 , 而多线程是共享一块内存空间和一
组系统资源 , 有可能互相影响 .
■ 线程本身的数据通常只有寄存器数据,
以及一个程序执行时使用的堆栈,所以
线程的切换比进程切换的负担要小。
333
7.1 多线程基本概念

■ 对线程的综合支持是 Java 技术的一个重


要特色 . 它提供了 thread 类、监视器和条
件变量的技术 .
■ 虽然 Macintosh,Windows NT,Windows 9 等
操作系统支持多线程 , 但若要用 C 或 C+
+ 编写多线程程序是十分困难的,因为它
们对数据同步的支持不充分 .
334
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();
335
7.2 创建线程的方式
■ 5. run 方法是运行线程的主体 , 启动线程时 , 由
java 直接调用 public void run()
■ 6. 停止线程 , 由小应用程序的 stop 调用线程的 stop
newthread.stop()
■ 7 sleep 方法的作用 , 暂停线程的执行 , 让其它线程
得到机会 ,sleep 要丢出异常 , 必须抓住 .
■ Try{sleep(100)}catch(InterruptedException e){}
■ 例 : 小应用程序中不用 Runnable 接口仍然可以使
用线程 ( 不调用主类的方法和调用主类的方法 )

336
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){ }
}
}
public class mainclass extends Applet 337
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){} }}
338
7.2 创建线程的方式

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

9. 线程的优先权
■ 某一时刻只有一个线程在执行 , 调度策略为固
定优先级调度 .
newthread.setPriority(Thread.MIN_PRIORITY)
■ 级别有 :MIN-PRIORITY

■ NOM_PRIORITY
■ MAX-PRIORITY
10. 自私的线程 : 有很高的优先权的线程 , 不主动
睡眠或让出处理器控制权 .
340
7.2 创建线程的方式

11. 线程的状态 suspend()


sleep()
new Thread() yield() wait()

start() . .
New Thread Runnable . Not Runnable
stop() or
run()exit resume()
stop() stop()
Dead
341
7.2 创建线程的方式
■ 当一个线程执行完所有语句后就自动终止
,调用线程的 stop() 方法,也可以强制终止
线程。
■ 如果希望线程正常终止,可采用标记来使
线程中的 run ()方法退出。
342
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;}
}
343
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();}
■ }
344
7.3 线程的挂起与唤醒
■ 暂停线程的执行等待条件满足再执行 .
■ 下面的例子显示线程的挂起和唤醒
■ 小应用程序第一次开始时 , 线程被启动
■ 浏览器改变页面时 , 小应用程序的 stop()
方法被调用 , 线程被挂起 .
■ 浏览器回到原来的页面时 , 线程被唤醒 .
345
7.3 线程的挂起与唤醒
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(); }.
346
7.4 多线程问题 --- 执行的顺序
■ 多个线程运行时 , 调度策略为固定优先级调度 .
级别相同时 , 由操作系统按时间片来分配
■ 下面给出的例子中 , 共运行三个线程 , 它们做同
样的事 , 每次打印循环次数和自己的序列号 , 运
行结果表明 , 它们并不是连续运行的 .
■ 在上例中如果给某个线程赋予较高的优先权 , 则
发现这个进程垄断控制权
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.");}}
348
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();} }
349
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,);
}
350
7.4 多线程问题 --- 如何写多线程
■ 方法二 : 定义两个类 , 运行各自的线程 , 各
自有自己的 paint() 方法 .
■ 注意 : 两个小应用程序必须是 panel 类或者
是 canvas 类 , 将小应用的区域分成两块 , 否
则不能运行 paint 语句 .
351
7.4 多线程问题 --- 线程间的通信

1. 线程间的通信可以用管道流 ,.
线程 1 PipedOutputStream PipedInputStream 线程 2
输出流 outStream 输入流 inStream
创建管道流 :
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

m.write(s) s=m.read()
write() read()
353
7.4 多线程问题 -- 线程间的通信

■ 管道流可以连接两个线程间的通信
■ 下面的例子里有两个线程在运行 , 一个往外
输出信息 , 一个读入信息 .
■ 将一个写线程的输出通过管道流定义为读线
程的输入 .
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)
public class Pipethread 355

{ public 多线程问题
7.4static -- 线程间的通信
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 多线程问题 --- 线程间的通信
7.4 PipedInputStream inStream;// 从中读数据
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 ){}}}
class Writer extends Thread 357

{ private 多线程问题 -- 线程间的通信


7.4 PipedOutputStream 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;
358
7.3 多线程问题 --- 资源协调
1. 数据的完整性

线程 1 取过

加1
后送
回去
线程 2 变量

线程 10

withdrwal() 余额 withdrwal()

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

线程 2
监视
线程 1 器
withdrawal()
360
7.3 多线程问题 --- 资源协调
■ 用 synchronized 来标识的区域或方法即为监
视器监视的部分。
■ 一个类或一个对象由一个监视器 , 如果一个
程序内有两个方法使用 synchronized 标志 ,
则他们在一个监视器管理之下 .

线程 1 线程 2

read 视 write

■ 一般情况下,只在方法的层次上使用关键区
361
7.3 多线程问题 --- 资源协调

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

■ 2. 等待同步数据
.
生产者 共享对象 消费者

write. read
可能出现的问题 :
• 生产者比消费者快时 , 消费者会漏掉一些数
据没有取到
• 消费者比生产者快时 , 消费者取相同的数据
.
• notify() 和 wait () 方法用来协调读取的关系 .
• notify() 和 wait () 都只能从同步方法中的调
363
7.3 多线程问题 --- 资源协调
■ notify 的作用是唤醒正在等待同一个监视器
的线程 .
■ wait 的作用是让当前线程等待
■ 信息版例子
■ read() 方法在读信息之前先等待 , 直到信息可
读 , 读完后通知要写的线程 .
■ write() 方法在写信息之前先等待 , 直到信息
被取走 , 写完后通知要读的进程 .
■ DemoWait.class--->f4.bat
364
7.3 多线程问题 --- 资源协调

writer reader
aaaa
aaaa
aaaa
aaaa aaaa aaaa aaaa
bbbbb bbbbb bbbbb
cccccccccccc
cccc
cccc cccc
365
7.3 多线程问题 --- 资源协调
■ 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
多线程问题
7.3String
{ private message; --- 资源协调
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 367
多线程问题 mBoard;
7.3 MessageBoard
{ 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) { } } }
class Writer extends Thread 368
多线程问题mBoard;
7.3MessageBoard
{ private --- 资源协调
private String messages[ ]= {
"Monday :------------------------",
“…..”,
"Sunday : ----------------------"};

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. 死锁问题
把 “ pen” 给我 , 我
线程 1 才能给你 “ note”
线程 2

note 把 “ note” 给我 , 我 pen


才能给你 “ pen”

如果你的持有一个锁并试图获取另一个锁时
, 就有死锁的危险 .
解决死锁问题的方法 : 给条件变量施加排序
370
7.3 多线程问题 ---daemon 线程
■ 什么是 daemon( 守护 )?
■ 在客户 / 服务器模式下 , 服务器的作用是等待用户
发来请求 , 并按请求完成客户的工作

request 服务器端
客户端

■ 守护线程是为其它线程提供服务的线程
daemon
■ 守护线程一般应该是一个独立的线程 , 它的 run()
方法是一个无限循环 .
■ 守护线程与其它线程的区别是 , 如果守护线程是
唯一运行着的线程 , 程序会自动退出
371
7.4 小结
1. 实现线程有两种方法 :
■ 实现 Ruannable 接口

■ 继承 Thread 类

2. 在小应用中通常在 start 中创建线程


3. 当新线程被启动时 ,java 调用该线程的 run
方 法 , 它是 Thread 的核心 .
4. 线程由四个状态 : 新生 , 运行 , 暂停 , 死亡
5. 线程间的通信方式由三种 : 完全共享数据
, 通过监视器 , 通过 join.
372
7.4 小结
6. 两个或多个线程竞争资源时 , 需要用同步
的方法协调资源 .
7. 多个线程执行时 , 要用到同步方法 , 即使用

synchronized 的关键字设定同步区
8. wait 和 notify 起协调作用
9. 守护进程的特点是当程序中制胜它自己时
, 会自动中止 .
373
作业
■ 创建两个线程的实例 , 分别将一个数组从小
大大和从达到小排列 . 输出结果 .
第 8 章网络功能

北京大学计算机系
代亚非
375
第 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 小结
376
8.1 Java 与网络
■ Java 语言取得成功的领域之一就是网络
■ ( 其他语言 ) 数页代码 ---->(Java) 一条语句
■ TCP/IP( 传输控制协议 / 网间协议 ) 是 internet 的
主要协议 , 定义了计算机和外设进行通信所使用
的规则 ( 应用层 , 传输层 , 网络层 , 链路层 ).
■ 大多数基于 internet 的应用程序被看作 TCP/IP 协
议的上一层 . 如 : ftp, http, smtp, pop3, telnet, nntp

■ IP 地址 :TCP/IP 网络中的每台计算机都有唯一的
地址 --IP 地址 .
■ 在 Java 中 , 有一个用来存储 internet 地址的类叫
InetAddress.
377
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)
378
8.1 Java 与网络
下面的例子演示 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); }
}
379
8.1 Java 与网络
■ 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/
<html>
<head><title><...></title></head>
<body>
例 : 从给定的位置中读数据 (ReadURLData.prj)
connect
TextField a Frame 主类 TextArea c

str=a.getText() action
line_str=b.readLine()
URL url=new URL(str) Button c.appendText(line_str);

URL url b=url.openStream() DataInputStream b


public boolean action(Event evt, Object arg)
{ try{ 8.2 使用 URL
382

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;
}
383
8.3 访问 cgi
■ 起始页上的计数器及 cgi 程序 (script)

欢迎来访 , 你是第 1 2 7 个来访者 !

你是第 <img SRC=“/cgi-bin/perl/counter.cgi”>


个来访者

客户端 cgi 程序名 服务器端 num+


+
HTML CGI 程序
img
384
8.3 访问 cgi

Your name
Web 服务器
form 数据库
cgi 程序 服务器

send reset

<form action="/scripts/test/query.idc" method=get>


<form action="/scripts/test/query.idc" method=post>
385
8.3 访问 cgi
■ cgi( 公共网关 ) 程序可以用任何一种变成语
言来写 .
■ cgi 将 web 与外部程序连接起来(数据库查
询等)
■ cgi 程序要放在指定的目录下 cgi-bin 目录 .
■ cgi 通常以 GET 或 POST 接收数据 ,
386
8.3 访问 cgi
■ 从小应用中运行 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://< 机器名 >/<cgi 程序路径 >?< 查询串 >
387
8.3 访问 cgi
■ get 方法把数据作为命令行中的参数传递给
此 cgi ( 适用于少量参数的情况 )
■ Java cgi 程序通过 main 方法的串参数接受
命令行 .
■ C cgi 程序通过访问环境变量中的查询串
QUERY_STRING, 来获得查询串中的信息 .
■ 例如在 C 程序中 : char *str;
■ str=getevn(QUERY_STRING);
import java.io.*; import java.net.*; 388
访问 cgi
8.3 test_cgi
public class
{ public static void main(String[] args) throws Exception
{ URL url = new URL
("http://pact518.hit.edu.cn/~wangqb/CGI-
BIN/test_2.cgi?012345678");
URLConnection connection = url.openConnection();
}
}
#include "stdio.h”
main()
{ char *Query_String;
Query_String = getenv("QUERY_STRING");
/*treatment*/
}
389
8.3 访问 cgi
■ 从小程序中运行 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 程序
390
8.3 访问 cgi
■ 通知发送的数据类型
■ ds.println(“Content-type:plain/text”);
■ 通知发送的数据长度
■ String data=“Hello world”
■ ds.println(“Content-
length”+data.length+”\n”);
■ 发送数据
■ ds.println(data);
391
8.3 访问 cgi
■ 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 392

客户端 URL 连接 服务器端

java 程序 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”);
393
8.4 URL 连接
■ 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);
394
8.4 URL 连接
3. 从服务器读数据
DataInputStream inStream=new
DataInputStream(con.getInputStream());
inStream.readLine();
■ 从 URL 中得到的输出流通常与一个 CGI 程
序结合一起工作

客户端 STDIN CGI 程序


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

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();
397
8.4 URL 连接
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" 398
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
}
399
8.5 Socket
8.3.1 什么是 socket?
■ Socket 是网络上运行的程序之间双向通信链
路的最后终结点 网络服务
IP,port ftp
20
21 telnet
Socket
客户程序 23 Mail
finger
端口
IP 与端口的组合得出
数据结构
一个套接字 , 可以完全 I/O 缓冲区
分辨 internet 上运行的
程序
400
8.5 Socket
■ 端口号 :TCP/IP 协议为每种服务定义了一个
端口 , 当一台计算机上运行不同服务器程序
时 ,, 根据端口号不同提供相应的服务 .
■ 端口号不是计算机上的物理连接器 , 它只是
具有软件意义的假想端口

internet 服务 端口号 internet 服务 端口号


ftp 21 www 80
telnet 23 nntp 119
DNS 53 POP3 110
401
8.5 Socket
■ 在服务器端通过指定一个用来等待的连接
的端口号创建一个 ServerSocket 实例 .
■ 在客户端通过规定一个主机和端口号创建
一个 socket 实例 , 连到服务器上 .
■ ServerSocket 类的 accept 方法使服务器处于
阻塞状态 , 等待用户请求
■ Socket 类和 ServerSocket 是基于 TCP 协议的
,TCP 协议提供 64 个端口 ,256 以内位保留端
口.
402
8.5 Socket
■ 在本地机器上测试网络程序用回送地址
Socket socket=new Socket(“127.0.0.1”,2525);
■ 如果网络的一端已经关闭 , 另一端读到 null.
■ 建立 socket 连接后 , 还应该建立输入输出数
据流 .
403
8.5 Socket

Server Client

ServerSocket(port #) Socket(host, port #)


Server Socket.accept() Attempt the connection
Socket()

OutputStream OutputStream
InputStream InputStream

Close Socket Close Socket


404
8.5 Socket
■ 下面的例子演示一个简单的服务器与客户
的交互 , 即服务器等待 , 客户访问 , 相互通
一次信息 .
. 8.5 Socket
定义数据成员 定义数据成员 405
8.5 Socket
创建服务器 ( 端口号 )1111 创建 Socket 实例
1 1 1
1 1
waiting for user .0 .
7 . 0
服务器等待 12 建立 socket 流
网络连接

读 socket 流
建立 socket 流 ( 接收并显示 )
connetcting client...
i n:
向用户发出一个字符串 log 送用户名给
a 服务器
j av
读客户 端信息
s s f ul
User :java s uc ce 读 socket 流
Log in
提示用户登录成功 关闭流
■ 例 : 显示服务器与客户机间的通信 ( 服务器端 ) 406
8.5 Socket
定义数据成员 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");}
409
8.5 Socket
■ 例 : 显示服务器与客户机间的通信 ( 客户
定义数据成员 端) PrintStream output;
DataInputStream input;
创建 Socket 实例 String string;
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());
410
. 8.5 Socket
定义数据成员
System.out.println(input.readLine());
■. 实例
创建 Socket
System.in.read(bArray);
String s=new String(bArray,0);
建立 socket 流 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 定义数据成员 411
. 8.5 Socket
创建服务器 ( 端口号 )1111 创建 Socket 实例
1 1 1
1 1
waiting for user .0 .
7 . 0
服务器等待 12 建立 socket 流
网络连接

读 socket 流
建立 socket 流 ( 看到提示 )
connetcting client...
i n:
向用户发送字符串 log 送用户名给
a 服务器
j av
读客户 端信息
s s f ul
User :java s uc ce 读 socket 流
Log in
提示用户登录成功 关闭流
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
■ 第二种方案 : serverthreadserverthread
■ ( 支持多客户 )
client1
Server

client2

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

客户 1 线程
线程( () )
服务器
客户 2
线程线程 2
( )
. 8.5定义数据成员
Socket 定义数据成员 415

创建服务器 ( 端口号 )1111 创建 Socket 实例


1 1 1
1 1
waiting for user .0 .
7 . 0
服务器等待 12 建立 socket 流
网络连接

读 socket 流
建立 socket 流 ( 看到提示 )
connetcting client...
i n:
提示用户输入客户名 log 送用户名给
a 服务器
j av
读客户 端信息
s s f ul
User :java s uc ce 读 socket 流
Log in
提示用户登录成功 关闭流
public static void main(String args[]) 416
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();
}}
class serverThread extends Thread 417
8.5 Socket
{ DataInputStream input; PrintStream output;
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; } }}
418
8.5 Socket
■ 例 : 通过服务器交换信息 (exchangebyserver)

Hello 1071 Hello 1076

abc def

server

WAIT GO…def
WAIT GO…abc
419
8.5 Socket 服务器
(1111)
■ . IP client1 IP client2
客 accept 客
户 socket 户

线程 线程

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

•服务器端
ServerSocket server Socket
client1 accept()

client2
serviceRequest
read_net_input
getInputStream
write_net_output() getOutputStream
reader.start(
)
reader.run reader.run
421
8.5 Socket
socket client()
客户端 getInputStream

getOutputStream getLocalPort()
writer.start()
write_net_output()
write.run 服


read_net_input()

paint()

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

datagram
server client
423
8.7 Datagram
■ TCP 提供高可靠性服务 , 适用于一次要传输
交换大量报文的情况 , 信道上传递的包不需
要源地址和目的地址
■ UDP 提供高效率服务 , 适用于依次传输交换
少量报文的情形 ( 如数据库查询 ), 每个数
据包要包含目的地址和端口号 .
■ 数据报文的使用以包为中心 : 打包 , 拆包 .
■ Java.net 包支持两种不同的在网络上送数据
的方法 : 一般套接字和数据报文套接字 .
424
8.7 Datagram
■ 发出报文的标准步骤如下 :
■ 1. 定义数据成员
■ DatagramSocket socket;
■ DatagramPacket packet;
■ InetAddress address;( 用来存放接收方的地址 )
■ int port; ;( 用来存放接收方的端口号 )
■ 2. 创建数据报文 Socket 对象
■ try {socket=new DatagramSocket(1111);}
■ catch(java.net.SocketException e) {}
■ socket 绑定到一个本地的可用端口 , 等待接
收客户的请求 .
425
8.7 Datagram
■ 3. 分配并填写数据缓冲区 ( 一个字节类型的数
组)
■ byte[] Buf=new byte[256];
■ 存放从客户端接收的请求信息 .
■ 4. 创建一个 DatagramPacket
■ packet=new DatagramPacket(buf, 256);
■ 用来从 socket 接收数据 , 它只有两个参数
■ 5. 服务器阻塞
■ socket.receive(packet);
■ 在客户的请求报道来之前一直等待
426
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();
427
8.7 Datagram
■ 客户端接收包的步骤如下 :
1. 定义数据成员
int port; InetAddress address;
DatagramSocket socket;
DatagramPacket packet;
byte[] sendBuf=new byte[256];
2. 建立 socket
socket=new DatagramSocket();
428
8.7 Datagram
■ 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);( 如果没有到就一直等待 ,
因此实用程序要设置时间限度 )
429
8.7 Datagram
5. 处理接收到的数据
String received=new String(packet.getData(),0);
■ System.out.println(received);

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

建立一个报文包 packet 建立一个请求包

等待请求报文 发出请求

创建接收包
获得对方地址

等待接收
构成信息包

发送出去
431
8.8 小结
■ 实现网络功能要靠 URL 类 , URLConection
类 , Socket 类和 DatagramSocket 类
■ 网络上的数据传送是将网络连接转换成输
入输出流
■ DataInputStream 和 DataOutputStream
(PrintStream) 是网间流的载体 .
■ URL 适用于 web 应用 , 如访问 http 服务器
是高层服务
■ URLConection 的另一边通常是 cgi 程序
■ cgi 程序完成客户端与外部程序的交互
432
8.6 小结
■ 向 cgi 传送数据有 get 和 post 两种方法
■ cgi 通过访问环境变量或读标准输入获得数据
■ 回绕地址可用于在本地机器上调试网络程序
■ Socket 适用于面向连接的 , 可靠性要求高的应用
■ Datagram 适用于效率要求高的应用
■ Socket 是由 IP 和端口构成的一种网上通信链路的一端
■ Socket 通信要分别运行服务器和客户程序
■ 服务器程序是多线程的 , 可处理多个客户的请求
433
作业
■ 编写一个会话程序
■ 要求 :
■ 会话双方可以自由通话 , 看到对方发
来 “ bye” 则退出
分布对象技术

北京大学计算机系
代亚非

哈尔滨工业大学计算机系代亚非 434
435
第九章 分布对象技术
■ 9.1 分布对象技术要解决的基本问题
■ 9.2 分布对象技术概论
■ 9.3 分布对象的核心概念
■ 9.4 分布对象主流技术介绍
■ 9.5 分布对象处理技术 --- 发展趋势
■ 9.6 分布对象主流技术开发过程
436
9.1 分布对象技术要解决的基本问题
■ 以下情况是不是分布式?
■ 1 、浏览
■ 2 、两个及其之间的通信
■ 3 、计算引擎
437
9.1 分布对象技术要解决的基本问题

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

C++Main
C++ Object
Program
同一地址空间

A
机 B机

就是支持访问异地对象
438
9.1 分布对象技术要解决的基本问题
分布式系统的客户 / 服务器模型

两层客户 / 服务器模型 ( 胖客户 )


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

.
439
9.1 分布对象技术要解决的基本问题
一个分布式系统的例子 ( 原始的方法 )

电话订购 WEB
客户 1 服务器 1

电话订购 中心订购 WEB


客户 2 服务器 服务器 2 Internet

...
...

电话订购
WEB
客户 n
服务器 m
440
9.1 分布对象技术要解决的基本问题
■ 使用多个服务器分割用户
电话订购 WEB
客户 1 中心订购 服务器 1
服务器 1
电话订购 WEB
客户 2
服务器 2

电话订购 WEB
客户 1 中心订购 服务器 1 Internet
服务器 1
电话订购 WEB
客户 2
服务器 2

电话订购 WEB
客户 1 中心订购 服务器 1
服务器 1
电话订购 WEB
客户 2
服务器 2
441
9.1 分布对象技术要解决的基本问题

订购
服务器 1
调度程序
Internet

...
订购
服务器 2

WEB
...

服务器 1

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

客户
Sybase

Informix
443
9.1 分布对象技术要解决的基本问题
■ 问题案例二 -- 如何管理不断接入的新应用

N1 N2 ... Nk

管理器
444
9.1 分布对象技术要解决的基本问题

单机应用开发环境 分布式应用开发环境

Socket API

Fortran 语言 RPC

OOP 语言
分布对象技术
软构件技术
445
9.1 分布对象技术要解决的基本问题
■ 我们都听到了哪些词汇 ?

ActiveX Controls
CORBA/IIOP
RPC
COM/DCOM
构件模型
Java/RMI
分布对象 EJB

还有还有 … ...
446
9.2 分布对象技术概论
■ 分布计算是近二十年来影响计算机急速发展的最活跃因素
之一
■ 经历了两种不同的技术路线

1. 理想的技术路线 (80 年代学术界普遍追求的目标 )


试图在互连的计算机硬件上部署全新的分布式操
作系统
2. 现实的技术路线
(90 年代工业界普遍遵守的路线 ) 集中式
在网络计算平台上部署分布计算环境
提供开发工具和公共服务
支持分布式应用
资源共享和协同工作
客户 / 服务器
447
9.2 分布对象技术概论
■ 分布式系统比想象的要复杂 :
1. 异构环境下的应用互操作的问题
2. 系统管理问题
3. 系统安全的问题
4. 传统的面向过程的技术在开发大型软件系统的局限

■ 面向对象技术用于分布式 ( 分布对象技术 ) 标志第二
代分布式计算技术进入成熟和蓬勃发展时期 .
■ 分布式对象技术始于 90 年代初,已经发展成为当今
分布异构环境下建立应用系统集成框架和标准构件
的核心技术
448
9.2 分布对象技术概论
第三代
第一代 第二代
( 从现在开
(80 年代中 ~90 年代初 ) (90 年代 )
始)
异构环境下的信
面向的主要问题 信息共享 智能化的协同工作
息互操作

经典的客户 / 面向对象的多
体系结构 自主的多 agent 模型
服务器模型 层客户服务器
模型

沿用传统的计 将面向对象技 面向 agent 的


算概念和设施 术应用到分布
关键技术的特点 拟人化交互环
( 如过程调用和 计算 境
文件设施 )

能够提供丰富的分 已经成为建立 概念验证系统


布式系统服务 , 良 集成构架和软 令人鼓舞 , 尚
好的分布式系统管 构件标准的核 未达到广泛应
理和典型的分布系 心技术 用与协同工作
统应用 的成熟程度
449
9.2 分布对象技术概论
■ 分布对象技术的核心概念有那些 ?
1 分布对象的位置对客户透明
2 可以被远程客户应用以方法调用的形式访问
3 分布对象所使用的语言要对客户透明
4. 分布对象运行的系统平台对客户透明
5. 分布对象具有移动性

( 允许时间 , 空间 , 语言 , 操作平台的差异性 )
450
9.2 分布对象技术概论
C++

B机

VB
一个应用逻辑
一个应用逻辑
C机

VB
Java A 机 Java
A机

D机
451
9.2 分布对象技术概论
■ 分布式系统 , 分布对象与软构件是什么关系 ?
1. 独立于特定程序设计语言和应用系统 , 可重用
和自包含的软件成分称为软构件 .
2. 分布对象是一种典型的软构件
3. 分布式系统是分散在两个或更多地方的计算机
上为了实现一个共同的目标而共同工作的软件
实体集合 .
■ 分布式应用系统中的成员 :

1. 创建和维护分布对象的实体的应用称为服务器
2. 按照接口访问该对象的应用称为客户
3. 服务对象同时可以是其它服务对象的客户
452
9.2 分布对象技术概论
■ 软构件模型的开发的目的是什么 ?
重用 , 高层开发 , 通过工具进行自动化开发 , 简
化开发过程等 .
■ 软构件模型的开发的手段是什么 ?

软构件开发者 :
1. 处理底层的实现细节以及与此相关的类 .
2. 定义构件的基本的体系结构 , 构件界面的结构
, 与 其它构件及容器相互作用的机制
软构件应用者 :
在可视的开发环境将以存在的构件进行简单的
组装。
453
9.2 分布对象技术概论
■ 软构件模型的例子 ?
■ OMG 的 CORBA 、 Microsoft 的
COM/DCOM 和 SUN 的 Java/EJB 。
454
9.3 分布对象技术 ---- 基本工作原理
■ 什么是对象请求代理 ?
■ 支持客户访问异地分布对象的核心机制称为
对象请求代理 ORB(Object Request Broker)

B机

一个应用逻辑 络
C++Main Object
Object A
Object A
计 对象适配器
一个应用逻辑
Program
A 的代理
算 A
C机

Agent 1 境 Agent 2
A机
A机
ORB
D机
455
9.3 分布对象技术 ---- 基本工作原理

客户 相同的接口 服务器
相同的接口

客户构造 应用逻辑 分布对象的实现 服务器构


构架 ➊ ➏ 造构架

➓ ➎
对象请求分发器
分布对象

ORB
➐ ➍
代理 对象请求适配器
➑ ➌

对象请求感知器

网络传输访问


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

如何管理客户应用
客户 O1
Oracle
O2

虚拟 O1R
客户
图书馆 O2R
O3R O3
ORB

ORB
O4R Sybase
O5R O4

还书 预约服务 O6R
客户

还 / 借处理 O5

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

¶ ÔÏ ó · þ Î ñ

利用这些服务创建一个新的对象而不必知道对象在哪
458
9.3 分布对象技术 ---- 基本工作原理
■ 通用功能 : 支持分布式系统高效开发和有效
工作的各类面向领域的常规服务和工具
■ GUI Ó¦ Óö ÔÏ ó Í ¨ Óù ¦ ÄÜ
■ 数据库服务
■ 电子邮件服务
■ 系统管理服务 ¶ ÔÏ ó Çë Çó ´ ú Àí (O b ject R e q u est B ro k e
■ 面象应用领域的领域构架

¶ ÔÏ ó · þ Î ñ
459
9.3 分布对象技术 ---- 基本工作原理
■ 应用对象 : 涉及各种应用软件 , 它在对象服
务和公共设施帮助下完成相应的应用逻辑
■ ORB 是一条软总线 , 把分布式系统中各类
对象和应用连接成相互作用的整体
Ó¦ÓöÔÏó ͨÓù¦ ÄÜ

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

¶ÔÏó· þÎñ
460
9.4 分布对象主流技术
■ 分布对象技术的实质性进步 :
1. 使面向对象技术能够在异构的网络环境中得以
全面 , 彻底 , 方便的实施
2. 有效地控制系统的开发 , 管理 , 维护的复杂性
■ 分布对象的主流技术
1. OMG的CORBA
2. Microsoft的ActiveX/DCOM
3. SUN的Java/RMI
461
9.4 分布对象主流技术 ---CORBA
CORBA
■ OMG 是一个非盈利性国际组织 , 致力于使 CORBA
成为 “无所不在的中间件 ”
■ 1989 年成立 , 当时仅有 8 家成员 , 目前有 900 家成

■ OMG 制定的分布对象计算标准规范 , 按照这些规范
设计的开发的分布式计算软件环境可以在几乎所有
的主流硬件平台和操作系统上运行
■ CORBA 的通信协议是 IIOP(Internet Inter-ORB
Protocol).
■ CORBA 结构集中围绕着两个主要元素 :ORB 和
IDL,CORBA 的语言独立性和平台独立性主要来源
于这两个方面 .
462
9.4 分布对象主流技术 ---CORBA
■ ORB 负责定位服务器对象
■ IDL 是建立接口的基础

¿Í »§ ¶ÔÏ óÊµÏ Ö
(Client) (Server)

¶¯Ì ¬ IDL ORB ¾² Ì ¬IDL ¶¯Ì ¬ ¶ÔÏ ó


¼¤»î Stubs ½Ó¿Ú Skeletons Skeleton ÊÊÅäÆ÷
½Ó¿Ú

ORBÄÚº Ë
463
9.4 分布对象主流技术 ---CORBA
■ IDL Stubs 、 IDL Skeleton 是由 IDL 编译器编译产生的,用于静态
调用,有较好的性能和效率。
■ 动态调用接口和动态 Skeleton 用于动态调用,使得即使在编译时不
知道可用的服务器和接口消息,也能完成客户与服务器的作用
■ CORBA 中的说明语言,称作 OMGIDL ,用以描述对象的 接口。它
本身不是一个可编程语言,但它为程序员提供了语言的独立性,他
们不必知道调用者所采用的语言。
■ IDL 的词法,语法规则遵从 C++ 的规定,但加入了一些关键词支持
分布概念。用接口描述语言( Interface Description Language )编
写的对象接口,使得与语言无关的独立性成为可能。
464
9.4 分布对象主流技术 ---CORBA
■ CORBA 规范的特点 :
1. 互操作性: CORBA 在客户和服务器之间提
供了两层可操作性。一方面它将客户与服务
器都抽象为对象,所有功能都封装在对象内
部,对外提供简单的接口,可被其它对象以
动态或静态方式调用。另一方面,对象间的
通信是通过 ORB 代理实现,对象不必关心细
节,由 ORB 定址发送请求。是 ORB 对象间
的 “通信总线 ”。
465
9.4 分布对象主流技术 ---CORBA
2. 提供了软总件机制:所谓软总线是指 CORBA 规范定义了
一组接口规范,任何应用程序,软件系统或工具只要具有与
该接口规范相符合的接口定义,就能方便地集成对 CORBA
系统中,而这个接口规范是独立于任何实现语言和环境。。

3. 跨平台性:由于 CORBA 规范中定义了 IDL 语言这一与


平台无关的语言,并引入代理 (ORB) 概念,因此
CORBA 应用具有较好的跨平台性。
466
9.4 分布对象主流技术 ---CORBA
■ CORBA 结构一次请求的过程
OBJECT REFERENCE SERVANT

CLIENT SERVER

STUBS SKELETONS
IIOP
ORB ORB

METHOD REQUEST

CORBA 结构一次请求的过程
467
9.4 分布对象主流技术 ---COM/DCOM
COM,DCOM,OLE 和 ActiveX
■ OLE 对象链接和嵌入 (Object Linking and
Embedding)
■ 从用户角度看 OLE
1. OLE 文档 : 以文档为中心 ,OLE 集成数据
2. OLE 自动化
3. OLE 控件 : 可以被嵌入应用程序中的自包含
的,
可重用的组件 .
468
9.4 分布对象主流技术 ---COM/DCOM
469
9.4 分布对象主流技术 ---COM/DCOM
470
9.4 分布对象主流技术 ---COM/DCOM
从程序员的角度看 OLE
OLE 控件
属性 事件

将 OLE 文档
就地激活

( 可视编程 ) OLE 自动
而 链接 化
建 剪贴板 拖放 嵌入

一致数
据传输 结构化存 跟踪器
器 储 ( 永久命名 )
(UDT)
组件对象模型 (COM)
471
9.4 分布对象主流技术 ---COM/DCOM
■ 构件对象模型 COM (Component Object
Model ) 是一个用于在交互平台上开发客户
/ 服务器应用程序开放结构 .
■ COM 是微软的构件对象模型 , 是构件之间
实现互操作的二进制标准 .
■ COM 对象通过接口来显示功能。接口是
COM 对象与外部世界的一个绑定约定。
■ COM 可以用不同语言实现 .
■ COM 只能运行在 WINDOWS 平台上
■ COM 实现了 OLE 对象的底层通信 , 其作用
类似于 CORBA/ORB
472
9.4 分布对象主流技术 ---COM/DCOM
■ ActiveX 是 OLE 技术和 COM 技术在 Internet 网上的
一个扩展 . 但是它的内容更多 , 它还组成了一系列用
来产生丰富的 Internet 网和多媒体服务 .
■ ActiveX 文档:
将 OLE 文档扩展到 internet ,能够由浏览器打开的
word , powerpoint 文件
■ ActiveX 控件:
将 OLE 控件扩展到 internet 。如一个页面包含
ActiveX 控件,该控件可以自动下载到用户端。
■ ActiveX 脚本:
■ 将 OLE 自动化带进 internet 。可以利用 VBScript ,
JavaScriot , Perl ,等语言编写。
473
9.4 分布对象主流技术 ---COM/DCOM
■ 分布式组件对象模型 (DCOM 或 ActiveX/DCOM ):
被称为网络 OLE, 是二进制的 COM 对象在局域 , 广域
网和 Internet 上的扩展 .
■ COM 能够使客户在本地处理中透明地访问库中的对
象, DCOM 允许在远程处理中透明地访问对象。
( DCOM 的功能实现使得程序员不必编写网络代码 ,
或仅仅知道如何编写网络代码)
■ DCOM 基于 (RPC--- remote procedure call ) 工作,
它不是一种编程语言,而是一种规范,一种服务,是
一种高级网络协议。
■ DCOM 是用 COM 实现的。
474
9.4 分布对象主流技术 ---COM/DCOM
■ ActiveX 控件通过底层 DCOM 进行通信,允
许控件互相访问对方公布了的方法,而不必
考虑控件是否在一个容器或同一台机器上。
■ ActiveX 控件由事件连接在一起 , 开发环境支
持开发者创建方法,当任何一个构件出发了
一个事件 , 这些方法都被调用 .
■ 目前有十几家公司提供大量的控件 , 你可以访
问这些公司的 WEB 站点 , 下载你需要的
ActiveX 控件 , 进行编程 : www.activex.com.
■ Windows98 和 WindowsNT5.0 都把 DCOM 作
为操作系统的一部分 .
475
9.4 分布对象主流技术 ---Java/RMI
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 就是代表远程对象的客户
方代理 , 定义远程对象版本所支 Application
持的所有接口 .
Client Server
2. Skeletons 是与服务器方的
RRL 接口的服务器方构件
3. RRL 负责维护不与具体 Stub 或 Stubs Skeletons
Skeleton 模型相关的独立引用 Remote Reference Layer
协议 . 这个灵活性使 RRL 的改 Transport
变不会影响另外两层 .
4. 传输层由 4 个抽象构成 : RMI System

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

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

设施 .
EJB 服务器
479
9.4 分布对象主流技术 ---EJB
■ 为什么要有服务器端构件模型 EJB?
1. EJB 将成为用 Java 语言开发分布式的、面向对象的企
业应用系统的标准构件体系结构, EJB 使得通过组合
构件得到分布式应用成为可能
2. EJB 不需要应用开发人员了解底层的事务处理细节 , 状
态管理 , 多线程 , 资源共享管理 , 以及其它底层 API 细节
.
3. EJB 遵循 Java 的 “ write once, run anywhere” 的原则 .
一个 EJB 可以部署在任何 EJB 平台上 .
4. EJB 定义了一个协议 , 使得不同供应商提供的构件能在
运行时互操作 .
5. EJB 体系结构和已有的服务器平台 , 其它的 Java APIs,
CORBA 兼容
480
9.4 分布对象主流技术 ---EJB
■ EJB 与 JavaBean 的关系
1. 客户端的 JavaBeans 容器可以根据 JavaBeans 的
属性 , 方法 , 事件的定义在设计时或运行时对 Java
Beans 进行操作 . 一般 JavaBeans 是可视化的构件
. 一个标准的 JavaBeans 是一个客户端构件 , 在运
行时不能被其它客户机程序存取或操作
2. EJB 没有用户界面 , 并完全位于服务器端 ,EJB 可以
由多个 JavaBeans 组成 .
3. EJB 可以和远程的客户程序端通信 , 并提供一定的
功能 . 如果不和客户端程序交互 ,EJB 一般不执行具
体的功能 .
4. EJB 与 JavaBeans 的一个重要区别是 EJB 提供了
网络功能 .
481
9.4 分布对象主流技术 ---EJB
■ 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 支持的任何语言些的客户机 .
482
9.4 分布对象主流技术 ---EJB
■ EJB 的网络计算

客户端

在网络计算环境中利用 Java 的最好途径是由 EJB 提供服务


器端的构件 , 而由 JavaBeans 提供客户端构件 .
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 D
跨平台操作 A C A
A B A
公共服务构件 A D A
可用性
事务处理 A C B
消息服务 E B B
安全服务 A B A
目录服务 A C B
容错性 C C C
产品成熟性 C B D
软件开发商的支持度 B A A
可扩展 B
性 A A
486
9.5 分布对象处理技术 --- 发展趋势

■ Object web 人们关注的新热点


■ hypertext web-->interactive web-->object web
■ 什么是 object Web?
■ CORBA 和 Java 结合是 object web 的技术基础
■ 和 hypertext web,interactive web 比较 :
■ 1. object web 给用户的突出感觉是操纵实体不再
局限于含有 “超链 ”的文档
■ 2. 作用于实体的操作行为不再局限于 “下载 ”一类
的文件传输 .
487
9.5 分布对象处理技术 --- 发展趋势
■ Object web 的体系结构

HTTP 文档
Web browser HTTP
HTTP
Java Applet Java Applet
Server
CGI
CORBA
IIOP Java
Java ORB Server
ORB Object
第一层 Internet
JDBC
第二层 业务服务 第三层 业务服务
器 器
488
9.5 分布对象处理技术 --- 发展趋势

■ 中间层可以由任何一种服务器来支持
■ 中间层主要完成各种业务逻辑
489
9.5 分布对象处理技术 --- 发展趋势
■ 从应用的角度看 object web
■ (1) 开发的 internet/Intranet 管理
– agent 能够在 internet 上公布其管理功能的接口 , 并
接受任何合法的 maneger 按照标准的运城对象访问
协议 (corba//iiop) 所进行的访问 .
– Maneger 能够访问任何时刻加入 Internet 的 Agent,
只要该 agent 支持 internet 上的标准的远程对象访
问协议 , 并在加入 internet 时按照标准的协议公布其
接口 .
■ (2) Web 文档组合
– 结合组合文档和 web 文档的双重功能 .
490
9.5 分布对象处理技术 --- 发展趋势
■ 从技术的角度看 object web
■ 对象开发技术和对象访问技术
Hypertext web Object web
interactive web
COM 对象 Java 对象
CORBA 对象 各类构件
HTTP/CGI DCOM CORBA/IIOP
RMI HTTP/CGI
9.6 一个 RMI 的分布式应用的实例
• 用 RMI 编写一个分布式应用 , 核心有以下三方面:
• 定位远程对象
– 1. 一个应用可以利用 RMI 的名字服务功能注册器远程对象。
– 2. 可以象操作普通对象一样传送并返回一个远程对象的引用
( 指针 ) 。
• 与远程对象通信:
– 底层的通信由 RMI 实现,对于系统开发人员来说,远程调用
和标准的 Java 方法调用没有什么区别。
• 为需要传递的对象装载类的字节码
– RMI 允许调用者向远程对象传递一个对象,因此 RMI 提供这
代 种装载对象的机制。

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

Web Server Client

URL protocol RMI

RMI
URL protocol Server
代 RMI

非 Web Server registry
URL protocol

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

Client Remote
object

task Compute
engin

代 Client
亚 Server

task

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

• 分布特点 :
– engin 开发 , 先运行 ,task 后定义 . 写 engin 时不对执
行什么任务作任何规定 . 任务可以是任意定制的 .
• 前提条件 :
– 定义任务的类 , 要规定任务的实现步骤 , 使得这个
任务能够提交给 engin 去执行 . 使用 server 上的
CPU 资源 .
• 技术支持 :

亚 – RMI 的动态装载功能 .

494
9.6 一个 RMI 的分布式应用的实例
A.m1()
compute
远程方法 engine

m1
远程对象
Compute Task
A

client server executeTask execute

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

非 一个对象来实现接口

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

client client

• 在 java 中远程调用是通过定义远程接口来实现的
, 一个接口只能有一个方法
代 • 不同类型的任务 , 只要他们实现了 Task 类型 , 就
亚 可以在 engin 上运行 .

• 实现这个接口的类 , 可以包含任何任务计算需要
的数据以及和任何任务计算需要的方法 .
496
9.6 一个 RMI 的分布式应用的实例
( 1 )定义远程接口
• 第一个接口 :compute
package compute; compute
import java.rmi.Remote; engine
import java.rmi.RemoteException;
public interface Compute extends Remote Compute Task
{ Object executeTask(Task t)
throws RemoteException;} executeTask execute

代 第二个接口 : 定义一个 task 类型 , 作为


亚 package compute; 参数传给 executeTask 方法 ,
非 import java.io.Serializable;
public interface Task extends 规定了 engin 与它的任务之间的接口 ,
以及如何启动它的任务 . 它 不是一个远
Serializable 程接口
{ Object execute(); } 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;
9.6 一个
import java.rmi.*; RMI
import 的分布式应用的实例
java.rmi.server.*; import compute.*;
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 的分布式应用的实例

三、实现一个客户程序
• 目标:创建一个任务,并规定如何执行这个任务。

client
package compute;
public interface Task extends
java.io.Serializable { Pi computePi
Object execute();
}
代 execute() ExecuteTask()

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

504
9.6 一个 RMI 的分布式应用的实例
• computePi 的作用
装载安全管理器

生成一个远程对象 comp Look up(ComputeEngin), 获得了 stubs

生成任务对象 Pi task=new Pi()


调用 ComputeEngin 的远程方法
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 的作用 rmiregistry
实现 Task 接口 computepi
Compute
实现 execute 算法 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)
{ this.digits = digits; } 508
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)
{ BigDecimal 一个
9.6result, RMI
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++ )

Sun
client
( 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 编译器 客户程序
IDL Stub


IDL 编译器
开 IDL 接口
( C++, Java )


代 Skeleton
亚 C++, Java 编译器 服务程序
非 对象实现

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 SERVANT

CLIENT SERVER

STUBS SKELETONS
IIOP
ORB 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("<applet codebase=
\"http://202.118.243.55/docs\" ”+ "code=
\"TheDate/Client.class\" " +”
代 width=500 height=300>");

534
// 9.7 基于 CORBA
指由 Client.java 编译成的 class的分布式应用系统的实
文件
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("</applet>");
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

You might also like