Professional Documents
Culture Documents
{ 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
•方法和变量的定义及修饰字
public 、 protected 、 private
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.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
小结
• 对象模型
• ---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
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
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 起始页上的时间和日期
• 介绍两个类 :
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
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);
浏览器 服务器
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
包
还记得画图像用什么方法和命令 在 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 动态效果 --- 线程的应用
• 实现一个线程
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 类
初始化 换算弧度 画图
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 类
初始化 换算弧度 画图
95
2.6 动态效果 --- 线程的应用
主类
初始化 换算弧度 画图
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() 中做什么 ?
输出两次字符串 , 第一次用一种颜色 , 第二次用另一
种颜色 ( 该颜色只作用于指定的区域 )
第三章 事件处理
北京大学计算机系 代亚非
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 类的数据域
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
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 的缺省实现
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
窗口 panel 窗口或面板
的监听器
button 按钮的监听器
139
❚ }
141
class ButtonHandler
new ButtonHandler(this);
a 100101
a.color=black;
public class TestButtoninner extends Applet 145
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”);} }}
■ . 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 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); }
■ 形如 :
■ class MyException extends Exception
■ {….};
■ 例 1 : 计算两个数之和 , 当任意一个数超出范
围时 , 抛出自己的异常
.
■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;}}
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
产生异常
Try catch
4.4 小结 184
■ 当用户按 “ 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
✔ 在 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 ,
列表 按钮 菜单
窗口,对话框 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 子类 ;
✔ 使你更方便的组织你的构件 , 得到赏心悦目
的布局
✔ 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
}
✔ 事件由包含层次由内向外传递 .
✔ 每个处理事件的方法要有一个返回值 , 通知
是否继续向上传递
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
.
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
✔ 应用举例
✔ 建立三个复选框 , 被选中者的标签内容变成
“ 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
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)
✔ GridBagLayout 类和
✔ GridBagConstraints 类
✔ 功能 : 借助于 GridBagConstraints 类 , 实现
更灵活的外观管理
✔ 每个构件后都跟随一个 GridBagLayout 对象
实体 , 来决定构件的外观 .
✔ 创建
✔ GridBagLayout myLayout=new
✔ GridBagLayout();
5.5 外观的管理与制 219
✔ 例 :(GridBagApplet.html)
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);
✔ GBC.gridwidth=2;
✔ ( 表示该按钮占两个单元 )
✔ layout.setConstraints(button5,GBC);
✔ add(button5);
✔ 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
✔ 例 : 按动鼠标改变画布的颜色
(CanvasApplet)
✔ 有哪些类 ? Canvas, Button, Color;
✔ 哪些方法 ? init(), action(),swapColor(),paint()
color color color
利用输出管理 按钮接收
变换颜色 执行重画
器按钮和画布 鼠标事件
5.6 各种构件的应用实例 ---Canvas 234
✔ 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
hide
hidewindow
Show
Show window
window
5.6 各种构件的应用实例 ---Frame 240
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);}
✔ 多窗口 (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
.
handleEvent handleEvent
LIST_SELECT WINDOW_DESTROY
import java.awt.*;
class FontDisplay extends Frame
5.6 各种构件的应用实例 --- 练习
{ TextArea FontShower;
251
对窗口来说只有一个事
件
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
updateFontShower()
{ area.setFont(param1,param2,param3)
FontPanel(TextArea FS) // 把另一个对象做参数
各种构件的应用实例
{ int i;5.6String 练习 New",
--- "Courier
FontNames[]={"Arial", 257
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
✔ 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
✔ 设计用户界面可根据用户选择办理银行业
务
✔ 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
WelCome to Bank
100
Create account
Show nbalance
Deposit
Withdrawal
5.6 各种构件的应用实例 -- 综合练习 272
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; }
✔ 5.8 总结
✔ 1 使用 AWT 构件的应用实例
✔ 2 事件的传递
✔ 3 外观的管理与控制
了解类及其常用方法
定义事件的处 加到上一级容器中
理 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
write
read
输出文件 输入文件
292
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
文件
文件流 缓冲区流
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
文件
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 数据输入输出流
■ 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
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 中的基本方法包括 :
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 多线程基本概念
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 创建线程的方式
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);
■
multithread(int SerialNum)
{ super(); threadNum=SerialNum; }
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
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
线程 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; }
如果你的持有一个锁并试图获取另一个锁时
, 就有死锁的危险 .
解决死锁问题的方法 : 给条件变量施加排序
370
7.3 多线程问题 ---daemon 线程
■ 什么是 daemon( 守护 )?
■ 在客户 / 服务器模式下 , 服务器的作用是等待用户
发来请求 , 并按请求完成客户的工作
request 服务器端
客户端
■ 守护线程是为其它线程提供服务的线程
daemon
■ 守护线程一般应该是一个独立的线程 , 它的 run()
方法是一个无限循环 .
■ 守护线程与其它线程的区别是 , 如果守护线程是
唯一运行着的线程 , 程序会自动退出
371
7.4 小结
1. 实现线程有两种方法 :
■ 实现 Ruannable 接口
■ 继承 Thread 类
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);
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)
Your name
Web 服务器
form 数据库
cgi 程序 服务器
send reset
返回结果
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 程
序结合一起工作
Server Client
OutputStream OutputStream
InputStream InputStream
读 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
定义数据成员 创建服务器 ( 端口号 ) 服务器等待
网络连接
通知客户连接完毕
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
读 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)
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
等待请求报文 发出请求
创建接收包
获得对方地址
等待接收
构成信息包
发送出去
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 分布对象技术要解决的基本问题
分布式系统的客户 / 服务器模型
.
439
9.1 分布对象技术要解决的基本问题
一个分布式系统的例子 ( 原始的方法 )
电话订购 WEB
客户 1 服务器 1
...
...
电话订购
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 分布对象技术概论
■ 分布计算是近二十年来影响计算机急速发展的最活跃因素
之一
■ 经历了两种不同的技术路线
经典的客户 / 面向对象的多
体系结构 自主的多 agent 模型
服务器模型 层客户服务器
模型
( 允许时间 , 空间 , 语言 , 操作平台的差异性 )
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 是一条软总线 , 把分布式系统中各类
对象和应用连接成相互作用的整体
Ó¦ÓöÔÏó ͨÓù¦ ÄÜ
¶ÔÏó· þÎñ
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)
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
系统中,而这个接口规范是独立于任何实现语言和环境。。
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 与客户机的关系
客户端
支持跨语言操作支持 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 分布对象处理技术 --- 发展趋势
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 的分布式应用的实例
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
远程对象必须继承远程接口
确定那些方法是远程方法 , 为此定义远程接口
代 远程接口只负责提供方法名 , 不一共实现细节 , 因此必须由
亚
非 一个对象来实现接口
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
( 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 的作用
装载安全管理器
代
亚
非
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 的分布式应用的实例
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 的分布式应用系统的实
例
应用开发者
对象实现 事件处理部分
代 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 的分布式应用系统的实
例
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;
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