1
1.3 Java 的开发环境 • 直接执行 JDK.exe 产生如下目录结构
java\bin 的目录下包含义下主要文件 : javac: Java 编译器 , 用来将 java 程序编译成 Bytecode java: Java 编译器 , 执行已经转换成 Bytecode 的 java 应用程序 . jdb: Java 调试器 , 用来调试 java 程序 javap: 反编译 , 将类文件还原回方法和变量 . javadoc :文档生成器 , 创建 HTML 文件 appletviwer : Java 解释器 , 用来解释已经转换成 Bytecode 的 java 小应用程序 .
2
1.3 Java 的开发环境 • \java\lib 子目录 • 库函数 , 其中 classese.zip 不要解开 . • 在 autoexec.bat 中加入以下两条命令 1. PATH c:\…\java\bin 2. SET CLASSPATH=. ; c:\...\lib\classes.zip; c:\myclasses;
3
1.3 Java 的开发环境 • • • • • • •
vj1.1++ 的使用 1. 进入 vj, 创建一个 project 2. 加入 java 文件 3. 编辑 java 文件 4. 编译 java 文件 5. Build 一个 project 文件 6. 运行 java 程序 -- 如果是小应用程序 , 则直接在 VJ 环境下运 行 -- 如果是应用程序 , 则在 dos 下运行
4
1.4 你的第一个 Java 程序 • Java 应用和 Java 小应用程序 class HelloWorldApp { public static void main(String args[]) { System.out.println(“Hi, ”+args[0]); } } • 编辑存盘:主类名 ( 含有 main 方法 ) 和 文件名要一致性 HelloWorldApp.java • 编译程序 : javac HelloWorldApp.java • 运行程序 : java HelloWorldApp “myjava” • 运行结果 : Hi, myjava
5
1.4 你的第一个 Java 程序 Java 小应用程序不能直接执行和使用 , 必须 要在浏览器中执行 import java.applet.Applet; import java.awt.*; public class HelloApplet extends Applet { public void paint(Graphics g) { g.drawString(“Hello World”,0,50); } } 编辑存盘: 主类名 (extends Applet) 和文件名一致 编译代码 : javac HelloApplet.java 编写 HTML 文件 : HelloApplet.html
6
1.4 你的第一个 Java 程序
•运行 applet 程序 : 1. appletviwer HelloApplet.html 2. 在浏览器中运行 • 运行结果 : Hello World!
7
1.5 Java 类型系统 • 能够动态取得的内存有两种 , 一种是对象 实体 , 一种数组 . • Java 用 new 申请内存 , 不需要自己释放不 用的内存 . • 在 Java 中,定义数组时不必给出数组的 大小,因为 Java 的数组是动态分配的。 • 数组变量实际上是一个引用变量,因此要 实例化才能使用。 • 例: int Array[] ; Array=new int[8] ;
8
1.5 Java 类型系统 • 例: String arrayOfString[]=new String[10] ;每个数组元素包含一个对字 符串对象的引用,此时值分配了包含字符 串引用的空间,并没有对字符串本身分配 内存,此时不能访问。 arrayOfString[0] 对 arrayOfString arrayOfString[1] 字 符 arrayOfString[8] 串 arrayOfString[9] 的 引 for(int i=0;i<arrayString.length;i++) 用
arrayOfString[i]=new String(“Hello”+i)
9
• • • • • • • • • •
1.6 Java 中的字符串
Java 中的字符串类 String 和 StringBuffer String 类的定义和实例的生成 例 : String str; str=“this is a string” 或 : String str1=new String(“hello”); 或 : String str2=“aaaa”; String 类与其它类型的转换 例 1: 将实型数转换成字符串 System.out.println(String.valueOf(Math.PI)); .
10
1.6 Java 中的字符串 • • • • • • • • •
有两个字符串 str1,str2 str1=str2; 是指两个引用指向同一个地址 str1.equals(str2) 是指两个字符串内容相等 获取 String 对象的信息 例如: String s=“this is a string”; int len=s.length(); boolean result=s.equal(“this is a test”); char ch=s.charAt(6);
11
1.6 Java 中的字符串 • StringBuffer 的字符串的内容是可以改变 的 • 创建 StringBuffer 对象 • StringBuffer str=new Stringbuffer(“hello”); • 获取 StringBuffer 对象的信息 • 例int len=str.length(); :StringBuffer sb=new StringBuffer(“Drink Java!”); sb.insert(6,”Hot ”); System.out.println(sb.toString()); Drink Hot Java!
System.out.println 接受的是 String
12
1.7 Java 中的类、方法和变量 • 试用一个简单的类 class CarDemo { public static void main(String args[]) { Car Democar=new Car(); DemoCar.set_number(3838); class Car DemoCar.show_number(); { int car_number; }} void set_number(int car_num) { car_number=car_num;} My car number is 3838
void show_number() { System.out.println (“My car No. is :”+car_number); } }
类的继承 关系 •
13 class TrashCar extends Car { int capacity; 1.7 Java void中的类、方法和变量 set_capacity(int trash_car_capacity) { capacity=trash_car_capacity;}
void show_capacity(); { System.out.println(“My capacity is: ”+ capacity);}}
class Car { int car_number; void set_number(int car_num) { car_number=car_num;}
void show_number() {System.out.println (“My car No. is :”+car_number); } }
14
1.7 Java 中的类、方法和变量 • 类的严格定义及修饰字public, abstract, final 或者没有 [ 类的修饰字 ] class 类名称 [extends 父类名称 ] { …. }
•方法和变量的定义及修饰字 public 、 protected 、 private [ 变量修饰字 ] 变量数据类型 变量名 1, 变量名 2[= 变量初值 ]…;
[ 方法修饰字 ] 方法的返回值类型 方法名称 ( 参数 1, 参数 2,… { …// 方法的内容 }
15
1.8 Java 名空间及访问规则 • 每个类都创造了有自己的名字空间,指方法和变 量可以知道彼此的存在,可以使用。 • abstract 类不能直接产生属于这个类的对象 • final 类不能被其他任何类所继承 ( 安全的考虑 ) • public 类不但可以被同一程序包中的其它类使用 , 别的程序包中的类也可以使用 . • friendly( 缺省 ) 类只能被本包中的其它类使用
16
1.8 Java 名空间及访问规则 • 变量和方法的修饰字 public 、 protected 、 private
• public: 任何其它类、对象只要可以看到 这个类的话,那么它就可以存取变量的数 据,或使用方法。 class ABC { public int pub_i=5 ; public void show)() { System.out.println (“pub_i”+pub_i); }
17
1.8 Java 名空间及访问规则 class Demo { public static void main(String args[]) { ABC abc=new ABC(); System.out.println(“abc.pub_i”+abc.pub_i); abc.pub_i=10; abc.show(); } abc.pub_i=5 } pub_i=10;
18
1.8 Java 名空间及访问规则 • protected 变量和方法 • 如果一个类中变量或方法有修饰字 protected ,同一类,同一包可以使用。不 同包的类要使用,必须是该类的子类可以 存取变量或调用 public class ABC { protected int pro_i=5; protected void show() { System.out.println (“pro_i=” +pro_i;) } }
19
1.8 Java 名空间及访问规则 • 不同中的类不能使用 class DEF { public static void main(String args[]) { ABC abc=new ABC(); System.out.println (“abc.pro_i=“+abc.pro_i); abc.pub_i=10; abc.show(); } }
20
1.8 Java 名空间及访问规则 • 不同包但是是子类 import mytest.pack.ABC; class DEF extends ABC { public static void main(String agrs[]) { DEF def=new DEF(); System.out.println(def.i); def.i=10; def.show(); } }
pro_i=5 pro_i=10
21
1.8 Java 名空间及访问规则 • • • •
private 不允许任何其他类存取和调用 friendly( 前边没有修饰字的情况 ) 在同一程序包中出现的类才可以直接使用 它的数据和方法 .
22
1.8 Java 名空间及访问规则 • 当子类中的变量名与父类的相同 , 原来的 变量被遮盖 . class demo { public static void main(String args[]) { C c=new C(); c.println_out(); }} class A { int data_a=3;} data_a=5 class B extends A { int data_a=5;} A.data_a=3 class C extends B B.data_a=5 { void print_out() { System.out.println(“data_a= ”+data_a); System.out.println(“A.data_a= ”+A.data_a); System.out.println(“B.data_a= ”+B.data_a); }}
23
1.8 Java 名空间及访问规则 • 方法的覆盖 (overriding) 和重载 (overloading) • 子孙类中定义的方法和祖先类中某个方法 同名 , 同参数行 , 则祖先类中的该方法被 覆盖 . • 方法的重载是指一个对象的多态性 , 即多 void show(int int_num) 123 个方法用相同的名称 , 但参数行不同 . 类
1.23
void show(double double_num)
123,1.23
void show(nit nit_ um, float float_um)
24
• • • • • •
1.8 Java 名空间及访问规则 到此为止 -----final final 在方法之前 , 防止该方法被覆盖 . final 在类之前 , 标是该类不能被继承 final 在变量之前 , 定义一个常量 . b 属于类的变量和方法 ----static object char data static 在变量或方法之前 , b static int object c 表明它们是属于类的 , object char data share_data class ABCD { char data; static int_share_data; } class Demo { ABCD a,b,c,d}
char data
Object b char data
25
1.8 Java 名空间及访问规则 • 静态变量在各实例间共享 • 如果是 public 静态变量 , 则其它类可以不 通过实例化访问它们 • 静态方法称为类的方法 , 因此不用实例化 即可调用 ( 面向过程 ) • 一个对象的方法可以访问对象的数据成员 , 尽管不属于方法的局部变量 • 一个类的方法只能访问自己的局部变量
26
• • • • • • • •
1.8 Java 名空间及访问规则 例 : 不正确的引用 class StaticError { String mystring=“hello”; public static void main(String args[]) { System.out.println(mystring);} } 错误信息 :can’t make a static reference to nonstatic variable. 为什么不正确 ? 只有对象的方法可以访问 对象的变量 .
27
1.8 Java 名空间及访问规则 • • • • • • •
解决的办法 : 1. 将变量改称类变量 class StaticError { static String mystring=“hello”; public static void main(String args[]) { System.out.println(mystring);} }
28
1.8 Java 名空间及访问规则 • • • • • • • •
2. 先创建一个类的实例 class NoStaticError { public static void main(String args[]) { String mystring=“hello”; System.out.println(mystring);} } }
29
1.9 Java 中的抽象类、接口和程序包 • 抽象类是指不能直接被实例化的类 , 因此一般 作为其它类的超类 , 与 final 类正好相反 • 抽象类中的抽象的方法 - 在该类中定义但不在 该类中提供实现 , 由继承类提供细节 • public abstract class SomeAbstractClass • { void method1() • { System.out.println(“Hi, Java”);} • void method2(); • } • 定义抽象方法的类必须是抽象类 .
30
1.9 Java 中的抽象类、接口和程序包
// 已有两个类 Circle 和 Rectangle, 完成相关参数的计算 class Circle { public int r; Circle(int r) {this.r=r} //this 指 " 这个对象的 " public int area() {return 3*r*r; } // 取近似 } class Rectange { public int width,height; // 这里不需 "this" Rectangle (int w, int h) {width=w,height=h;} public int area() {return width*height; } }
31
1.9 Java 中的抽象类、接口和程序包 • 假设有若干个 Circle, 以及若干个 Rectangle, 希望 计算它们的总面积 , 直截了当的做法是将它们分 别放到两个数组中 , 用两个循环 , 加上一个加法 , 这种做法是不漂亮的 • 如果还有其它形状 ,triangle,ellipses 等 , 上述方法 显得 “累赘 ” , 我们希望有一种统一的表示 , 例如 用一个数组 shape[], 接受所有的形状 , 然后用
• for (i=0;i<shape.length;i++) { • area_total+=shape[i].area();
32
abstract class Shape 1.9 Java 中的抽象、接口和程序包 { abstract float area();} class Circle extends Shape { public int r; Circle(int r) {this.r=r;} public float area() { return 3.14*r*r; } } class Square extends Shape {public int width,height; Rectangle (int w, int h){ width=w,height=h;} public float area() {return width*height; } }
33
1.9 Java 中的抽象类、接口和程序包 • 利用接口可实现多重 继承 ( 可同时实现多个接口) • 接口的作用和抽象类 类似 , 指定原型 , 不直 接定义方法的内容 . • 关键字 implement 用 来实 现方法 , 即在使用时要用 给出方法的实际内容 • 接口中的方法和变量 • 是 public 的
interface Stack { void push(object x); object pop();} class A extends Applet implements Stack { void push(object x) { …;// 具体内容 } object pop() { …;// 具体内容 } }
34
1.9 Java 中的抽象类、接口和程序包 • 程序包:相当于其它语言中的库函数 • 打包 package Graphics; class Square {…;} class Circle {…;} class Triangle {…;}
• 使用程序包中的类要用 import 命令 . 表示路径 ,* 表示使用包中的所有类 java • import java.io.FileInputStream ; io • import java.io.* ; FileInputStream
35
1.10 对象的构造函数
class Demo1 // 数组的配置 •{ public 使用 new static来做内存配置 void main(String args[]) { int array[][]; array=new int[5][5]; array[1][4]=5; } class myClass // 对象实体的产生 } { int data=5;} class Demo2 { public static void main(String args[]) { myClass obj;// 建立一个引用 obj=new myClass(); System.out.println(obj.data); }}
36
1.10 对象的构造函数 • Constructor 和 finalizer • constructor( 构造函数 ), 在一个类中和类同名 的方法叫构造函数 . 系统在产生对象时会自 动执行 . class UsePoint
class Point { Point point_A=new Point(); { int x,y; Point point_B=new Point(5,7); Point() } { x =0;y=0;} Point(int new_x,int new_y) { x=new_x,y=new_y;} }
37
1.10 对象的构造函数 • 构造函数应包含哪些内容 ? • 构造函数多半定义一些初值或内存配置工 作 • 一个类可以有多个构造函数 ( 重载 ), 根据 参数的不同决定执行哪一个 . • 如果程序中没有定义构造函数 , 则创造实 例时使用的是缺省函数 , 它是一个无内容 的空函数 .
38
1.10 对象的构造函数 • this • this 指自己这个对象 • this 的作用是要将自己这个对象当作参数 , 传 送给别的对象中的方法 . class ThisClass { public static void main() { Bank bank=new Bank(); bank.someMethod(this); } }
class Circle { int r; Circle(int r) {this.r=r;} public area() { return r*r*3;} }
39
• • • • • • • •
1.10 对象的构造函数 super 指这个对象的父类 super 用来引用父类中的方法及变量数据 . public class apple extends fruits { public apple(int price) { super(price); } } 以上句子表示使用超类的构造函数生成实例 super 必须是子类构造器的第一条语句
40
小结 • 一个 Java 程序 ( 包括小应用程序 ) 由若干个类 组成 , 它们分布在由 CLASSPATH 指定的一个 目录中 • 一个 Java 程序中只能由一个主类 , 并且与文件 同名 . • CLASSPATH 指定多个目录 , 它们和起来被看 成是这个目录的根 , 根中的类构成缺省包 • 目录树中每个子目录中的类的集合对应 Java 一个包 (package), 子目录的层次与包的记法的 层次对应
41
小结 C:\
package myclass.calculate class A{} class B{} classC{}
java lib bin javac
classes.zip appletviewer
java 让 PATH 包含 … \java\bin SET CLASSPATH =.; C:\...\myclasses
javasource
myjava.java
myclass calculate A
B
C
import java.io.*; import java.awt.Graphics; import mycalss.calculate.*; public class test {}
42
小结 • 名空间及访问规则 • --package,class/object,member, 局部变量 • --public ,private,protected,default 成员的可视性描述 public protected 缺省 private 同一类中 同一包中 不同包的 子类中 非子类 也不同包
yes yes
yes yes
yes yes
yes no
yes
yes*
no
no
yes
no
no
no
43
小结 • • • •
对象模型 ---Java 的类和对象 ( 一般 , 抽象 , 接口 ) -- 继承机制 ( 单继承 , 多实现 ) static, import, final 的含义和作用
44
小结 • 避免在类中使用太多的基本类型 • • • • • • • • • •
private String senderName; private String senderStreet; private String senderCity; private String senderState; private String senderZip; private String receiverName; private String receiverStreet; private String receiverCity; private String receiverState; private String receiverZip;
address address sender sender address address receiver receiver
45
2.1 所有小应用程序的根源 • 2.1.1 小应用的特点 • 回忆一下小应用程序的书写格式 import java.applet.*; public class MyApplet extends Applet { ;} • applet 都继承自 java.applet.Applet 类 , 由 Sun 公司事先定义好了 . • 每个小应用都有一个主程序类 , 之前必须加 上 public.
46
2.1 所有小应用程序的根源 http://someLocation/file.html 1. Browser loads URL <Applet code= ….> Applet class
2. Browser loads HTML document 3. Browser loads applet classes
Location: http://someLocation/file.html
Loading...
4. Browser run applet
47
2.1 所有小应用程序的根源 • Applet 的限制 Browser
SERVER co n Applet 被下载的 nec
SERVER
t io n
n o i t c e n con
本地程序
applet
file
local
与 applet 无关的 本地方法
48
2.1 所有小应用程序的根源 • 2.1.2 applet 的生命周期 Java.applet.Applet public void init() public void start()
public void destroy() public void stop()
public void paint(Graphics g)
• paint() 虽不在生命周期内 , 但它的作用相 当于 applet 的灵魂
49
2.1 所有小应用程序的根源 • 一个 applet 的可视周期 init start 离开 web 页面 stop destroy
重新装入或改变页 面大小或返回 Web 页面
50
2.1 所有小应用程序的根源 • 有关 paint() 方法 • Applet 本身是一个容器 , 因此任何输出都必须用 图形方法 paint() • 当小应用首次被装载,以及每次窗口放大、缩 小、刷新时都要调用 paint 方法 • paint() 是由浏览器调用的 , 而不是由程序调用 ,当程序希望调用 paint 方法时,用 repaint 命 令 • paint 方法的参数是 Graphics 类的对象 g ,它在 java.awt.Graphics 内 • paint ( Graphicd g ) { 。。。 }
51
2.1 所有小应用程序的根源 AWT thread ( waiting ) repaint()
Exposure update () { clear arae call paint ()
paint ()
52
2.2 小试身手 • 2.2.1 起始页上的时间和日期 0Mon Dec 07 14:23:50 GMT+08:00 1998
• 介绍两个类 : 1. 类名 :Date 创建一个实例 Date timeNow=new Date(); 2. 类名 Font 创建一个实例 Font msgFont=new Font(“TimesRoman”,Font.ITALIC,30);
53
2.2 小试身手 看下面的例子 , 想一想生命周期的四个方法哪去了 ? import java.awt.*; import java.util.Date; public class showDate extends java.applet.Applet { Date timeNow=new Date(); Font msgFont=new Font(“TimesRoman”,Font.ITALIC,30); public void paint(Graphics g) { g.setFont(msgFont); g.setColor(Color.blue); g.darwString(timeNow.toString(),5,50); }
54
2.2 小试身手 2.2.2 在起始页中加入 applet • html 中有关的代码 <APPLET CODE=“showdate.class” width=600 height=80> • CODEBASE 的作用 当 class 文件与起始页文件不在同一个目录下 时 , 使用 CODEBASE 说明 <APPLET CODE=“showdate.class” width=600 height=80> CODEBASE=“\myjava\class”
55
2.2 小试身手 <APPLET <APPLET CODE=“showdate.class” CODE=“showdate.class” width=600 height=80> height=80> width=600 CODEBASE=“\myjava\class” public public Index.html Index.html
C:\C:\
myjava myjava javacode class javacode class
showdate
showdate
56
2.2 小试身手 • ALIGN,HSPACE,VSPACE 其它文字 hspace
Java applet vspace
其它文字
<APPLET CODE=“showdate.class” width=600 height=80> vspace=100 hspace=100
57
2.2 小试身手 • 向 applet 传递参数的两个步骤 1. 在起始页中要有
标签 2. 在 applet 中要有 getParameter 方法 在起始页中有 :
在 applet 中有 : string title=getParameter(rem); 在显示时间的命令中加入 title: g.drawString(title+timeNow.toString(),5,50);
58
2.2 小试身手 import java.awt.*; import java.util.Date; public class showDate extends java.applet.Applet { Date timeNow=new Date(); String title; Font msgFont=new Font(“TimesRoman”,Font.ITALIC,30); public void paint(Graphics g) { g.setFont(msgFont); g.setColor(Color.blue); g.darwString(title+ timeNow.toString(),5,50); }
public void init() {title=getParameter (“rem”); if (title==null) title=“”; }
59
2.2 小试身手 • 例 : 利用一个可以显示运行字符串的类 , 显 示自己的字符串 (htmlpara.html)
60
2.2 小试身手 public void init() { String paramete; parameter=getParameter("MESSAGE"); if (parameter!=null) message=parameter; parameter=getParameter("FONT"); if (parameter!=null) font_to_use=parameter; parameter=getParameter("POINT_SIZE"); if (parameter!=null) point_size=Integer.parseInt(parameter); }
61
2.3 图形处理 2.3.1 图形坐标系统 0 任何与绘图有关的操作 x 第一个要用的是 java.awt.Graphics 类 Graphics 类的对象不是 y 由 new 产生的 , 而是由系 统或其他方式直接将生好的 Graphics 对象当作 方法的参数 , 再交给程序设计者去处理 . 例如 : paint(Graphics g)
62
2.3 图形处理 • Graphics 的方法 paint(Graphics g) { g.clearRect(); g.copyArea(); g.drawAre() ; g.drawLine(); g.drawOval();g.drawRect(); g.drawPolygon(); g.fillArc(); g.fillOval(); g.fillPolygen(); g.fillRect(); g.getColor(); g.getFont() g.setFont(); g.setColor(); g.getFontMetrics() g.fillRoundRect() }
63
2.3 图形处理 2.3.2 字型和颜色的设置 2.3.2.1 字型设置的方法 Font font=new Font(“TimesRoman”,Font.ITALIC,24); g.setFont(font); • 在小应用程序中显示输出的方法 g.drawString(String, int x, int y); g.drawChars(char data[], int offset, int length, int x, int y);
64
2.3 图形处理 g.drawBytes(byte data[],int offset, int length, int x, int y); 例 :g.drawString(“This is a test”,5,10); • 获取字体的属性 Font font=g.getFont(); • Font 类中常用的方法 GetFamily() getName() getSize() getStyle() isItalic() isPlain() isBold() toString()
65
2.3 图形处理 import java.awt.Graphics; import java.awt.Font; public class drawtext extends java.applet.Applet { Font fn=new Font("TimesRoman",Font.ITALIC,20); public void paint(Graphics g) { g.setFont(fn); g.drawString(”Font demo”,5,10); } }
Font demo
66
2.3 图形处理 •获取更详细的数据 请查阅有关 FontMetrics 类的方法 fontMetrics=getFontMetrics(font); •FontMetrics 中比较重要的方法有 : stringWidth, charWidth, getAscent, getDescent, getLeading, getHeigh
67
2.3 图形处理 2.3.2.2 颜色的调整 • Color 对象的使用 创造自己的颜色 : Color mycolor=new Color(int red, int blue, int green);
• g.setColor(Color.yellow) • g.setColor(mycolor); • 例 : 随机产生颜色 , 并画圆
68
2.3 图形处理 import java.awt.Graphics; import java.awt.Color; public class drawcircle extends java.applet.Applet { public void paint(Graphics g) { int red,green,blue,x; for (x=0;x<370;x+=30){ red=(int)Math.floor(Math.random()*256); green=(int)Math.floor(Math.random()*256); blue=(int)Math.floor(Math.random()*256); g.setColor(new Color(red,green,blue)); g.fillOval(x,0,30,30); }}}
69
2.4 URL 类 2.4.2 构造 URL 类 ( 全名 java.lang.URL) • 绝对 URL 的构造方法 : URL(String spec) 例 : URL url=new URL (http://www.hit.edu.cn/cv/index.html”) • 相对 URL 的构造方法 : 某绝对地址 :http://rainy.hit.edu.cn/test.html 在该目录下有两个文件 mywork.html myfamily.html
70
2.4 URL 类 URL base=new URL(“http://rainy.hit.edu.cn”);
URL url1=new (base, “mywork.html”); URL url2=new (base, “mywork.html”); • 其他 URL 的构造方法 : URL url=new URL (“http”, “www.hit.edu.cn”,“/~dyf/test.html”);
71
2.4 URL 类 2.4.3 获取小应用程序 HTML 页面的 URL 和 小应用程序本身的 URL • • • •
URL html=getDocumentBase(); System.out.print(html); URL codebase=getCodeBase(); System.out.print(codebase);
web page
html applet
浏览器
服务器
72
2.4 URL 类 • 2.4.4 URL 异常 (MalformedURLException) 当创建 URL 时发生错误 , 系统会产生异常 try{ URL url=new URL(str); }catch(MalformedURLException( e) { DisplayErrorMessage();} • 2.4.5 URL 类的基本方法 String getProtocol(), String getHost(), ing getPort(), String getFile(), String getRef()
73
2.4 URL 类 • 构造 URL 的实例 import java.net.URL; import java.net.MalformedURLException; public class Test { URL url1,url2,url3; void test() { try { url1= new URL(“file:/D:/image/example.gif”); url2= new URL(“http://www.hit.edu.cn/cv/”); url1= new URL(url2, “hit.gif”); }catch (MalformedURLException e); // 处理例外 } }}
74
2.5 载入现有图像文件 Image 类 • java 支持 gif 和 jpg 两种格式的图像 • 图像文件的 URL: URL picurl= new URL (“http://xxx.yyy.edu/Applet/img1.gif”); • 取一幅图像构成图像对象 Image img1 = getImage(picurl); Image img2 = getImage(getCodeBase(), “img2.gif”);
75
2.5 载入现有图像文件 • 显示一幅图像 : g.drawImage(img1, x, y, this); g.drawImage(img1, x, y,Color.red, this); g.drawImage(image1, x, y,x2,y2,Color.red, this); 规定尺寸
规定背景
76
2.5 载入现有图像文件 • 完整的过程 不要忘记 AWT 包 定义 Image 对象了 吗?
在类中
指定图像的 URL 了 吗? 把图像取出来吧 .
在 init0 中
还记得画图像用什么方法和命令 吗?
在 paint0 中
77
2.5 载入现有图像文件 import java.applet.*;import java.awt.*; public class image extends Applet { Image img; public void init() { img=getImage(getCodeBase(),"img0001.gif");} public void paint(Graphics g) { int width=img.getWidth(this); int height=img.getHeight(this); g.drawRect(52,52,width+30,height+30); g.drawImage(img,57,57,width+20,height+20,this);}}
78
2.6 动态效果 --- 线程的应用 2.4 动态效果 --- 线程的应用 • 什么是线程 ? 线程是执行中的程序中的单个顺序控制 开始 流. • Java 支持多线程 显示进度
数学运算
引出最后结果
线程 1
线程 2
79
2.6 动态效果 --- 线程的应用 • 静态的情况 import java.applet.*; import java.awt.Graphics; public class maguee extends Applet { public void paint(Graphics g) { g.drawString("Hello, Java!",0,0); } }
80
2.6 动态效果 --- 线程的应用 • 动态的情况 ( 不是多线程 ) public void init() { x=size().width; y=size().height/2; width=x; } public void paint(Graphics g) { while(true) { g.drawString("Hello, Java!",x,y); x-=10; if(x<0) x=width; } }
81
2.6 动态效果 --- 线程的应用 • 实现一个线程 让 Applet 类去实现 Runable 接口 , 创建一个线程 类 改写方法 start, 在其中产生一个新的线程来 工作 改写 stop 方法 , 在其中编写结束线程的程 序代码 引入新的方法 , 将分给线程的工作写到 run 中
82
2.6 动态效果 --- 线程的应用 第一步:实现 Runable 接口 public class xc extends java.applet.Applet implements Runnable { Thread smallthread=null; … } Thread 是一个类 , 只有是它的实例才能具有线 程的功能 主函数中要定义一个线程变量
83
2.6 动态效果 --- 线程的应用 第二步:改写方法 start public void start () { if ( smallthread == null ) { smallthread= new Thread ( this ); smallthread.start(); // 从现在开始程序由两个线程在执行 }} 第三步:改写 stop 方法 public void stop () { smallthread.stop(); // 停止线程 smallthread = null; // 释放线程对象 }
84
2.6 动态效果 --- 线程的应用 第四步 : 新的方法 run 将让线程要做的事放 run 中 public void run() { while (true) { repaint(); try {Thread.sleep(1000);} catch(InterruptedException e){} } }
85 import java.applet.*; import java.awt.Graphics; public class Applet 2.6MovingCharacter 动态效果 ---extends 线程的应用 implements Runnable { int x=200; Thread my_thread=null; //------------------------------------------------public void start() { my_thread=new Thread(this); my_thread.start(); } public void run() { while(true) { repaint(); try { Thread.sleep(100); } catch(InterruptedException e){} }}
86
2.6 动态效果 --- 线程的应用 • .
public void stop() { my_thread.stop(); }
public void paint(Graphics g) { g.drawString("Hello, Java!",x,y); x-=10; if(x<0) x=200; }
87
2.6 动态效果 --- 线程的应用 • 跳动的小球
up=false; x=x-10; if(x<0) x=width; if (up) y=y+10;else y=y-10; if (y<0) up=true; if (y>height) up=false; g.setColor(Color.red); g.fillOval(x,y,30,30);
88
2.6 动态效果 --- 线程的应用 例 : 起始页上的小时钟 一个必须用到的类 ----Date 类 , 给出系统时 间 Date NowTime=new Date(); NowTime.getHours(), NowTime.getMinutes() 自己需要写什么样的类 ? (Hour*60*60+minute*60+second)/43200*2.0*PI Clock--把数字时间成图形表示 (minute*60+second)/3600*2.0*PI second/60*2.0*PI
89
2.6 动态效果 --- 线程的应用 主类 取时间
paint() {}
clock 类 clock(){} 初始化
Show(){} 换算弧度
drawNiddle(){} 画图
90
class Clock 2.6 动态效果 --- 线程的应用 {int hours,minutes,second,radius; Clock(int hrs,int min,int sec) { hours=hrs%12; minutes=min; second=sec; } void show(Graphics g, int x, int y,int redius) { int hrs_len=(int)(radius*0.5); int min_len=(int)(radius*0.7); int sec_len=(int)(radius*0.85); double theta; g.drawOval(x ,y, radius*2, radius*2);
91
theta=(double)(hours*60*60+minutes*60+second)/ 2.6 动态效果 --- 线程的应用 43200.0*2.0*Math.PI; drawNiddle(g,Color.blue, x, y, hrs_len, theta); theta=(double)(minutes*60-second)/3600.0*2.0*Math.PI; drawNiddle(g,Color.blue, x, y, min_len,theta); theta=(double)second/60.0*2.0*Math.PI; drawNiddle(g,Color.red, x, y, sec_len, theta); }
92
2.6 动态效果 --- 线程的应用 private void drawNiddle(Graphics g, Color c, int x, int y, int len, double theta) { g.setColor(c); g.drawLine(x,y,(int)(x+len*Math.sin(theta)), (int)(y-len*Math.cos(theta))); } }
93
2.6 动态效果 --- 线程的应用 import java.awt.*;import java.util.Date; public class ClockDemo extends java.applet.Applet { public void paint() { Date timeNow = new Date(); Clock myClock = new Clock(timeNow.getHours(), timeNow.getMinutes(), timeNow.getSeconds()); myClock.show(g,100,100,100); } }
94
2.6 动态效果 --- 线程的应用 主类 生成时间对象,取时间 生成 Clock 对象,将时间 传递给 Clock 对象
paint() {}
clock 类 clock(){} 初始化
Show(){} 换算弧度
drawNiddle(){} 画图
95
2.6 动态效果 --- 线程的应用 主类 start() 启动新线程
clock(){} 初始化
stop()
paint()
run()
停止线 生成 clock 类实例 repaint() 程 clock 类 Show(){} 换算弧度
drawNiddle(){} 画图
96
2.6 动态效果 --- 线程的应用 例 : 在主页上显示 字符串并且颜色从左至右不断变化 让我们来想一想 : 需要那些数据成员 ? String msg, Font fnt, Color clr, spot_clr; Thread thread; String Msg="Welcome to HIT"; 需要哪些方法 ? init, start, stop, run, paint; public void init() { fnt= new Font("TimeRoman",Font.PLAIN,30); clr=new Color(255,0,0); spot_clr=new Color(0,0,255); Thread thread;}
97
2.6 动态效果 --- 线程的应用 run() 中做什么 ? 反复调用 repaint public void run() { while(true) { repaint(); try{Thread.sleep(50);} catch(InterruptedException e) {} } }
98
2.6 动态效果 --- 线程的应用 paint() 中做什么 ? 输出两次字符串 , 第一次用一种颜色 , 第二次用另一 种颜色 ( 该颜色只作用于指定的区域 )
You are Welcome to HIT g.clipRect(x,y,width,height) public void paint(Graphics g) { FontMetrics fntM=g.getFontMetrics(); int font_height=fntM.getHeight(); int base_line=size().height/2+font_height/2;
99
2.6 动态效果 --- 线程的应用 g.setFont(fnt); g.setColor(clr); g.drawString(Msg,0,base_line); g.clipRect(strPt-50,0,str_bk_size,size().height);
g.setColor(spot_clr); g.drawString(Msg,0,base_line); strPt=(strPt+1)%(size().width+100); } }
100
2.6 动态效果 --- 线程的应用 在 Java 中播放动画 1. 需要多张图片 2 调用图片的方法 ? getImage, 3. 将多幅图像存入图像对象数组 Image frame[]=new Image[10]; for (int i=0;i
4. 显示图像 drawImage(x,y,0,0,this),
101
2.6 动态效果 --- 线程的应用 import java.awt.*; public class nina extends java.applet.Applet implements Runnable {Image frame[]; Thread threadNina; int frame_i; int delay_time; public void init() { frame=new Image[10]; threadNina=null; frame_i=0; for (int i=0;i
102
2.6 动态效果 --- 线程的应用 public void run() { while(true) { repaint(); try{ Thread.sleep(100);} catch(InterruptedException e) {} frame_i=(frame_i+1)%frame.length; } } public void paint(Graphics g) { g.drawImage(frame[frame_i],0,0,this);}
103
2.7 播放声音 java 支持 au 格式的声音 两个方法 : void play(URL url) void play(URL url, String name) 例 :play(getCodeBase(), “boing.au”); ( 注 : 它是一次性的 ) 如果想反复播放怎么办 ? 借用类 AudioClip(loop(),play(),stop())
104
2.7 播放声音 例 :AudioClip bg_sound= getAudioClip(getCodeBase(), “boing.au”); bg_sound.play(); 或 : bg_sound.loop(); import java.applet.AudioClip; public class audio extends java.applet.Applet {AudioClip sound=getAudioClip(getCodeBase(),"boing.au"); public void start() { my_sound.loop(); } public void stop(){ { if(my_sound!=null) my_sound.stop();}}
105
2.7 播放声音 • 图像加声音岂不是更有吸引力 1. 在 init 中既取图像也取声音片断 frame[i]=getImage(getCodeBase(), "img000"+i+".gif"); SoundClip=getAudioClip(getCodeBase(),"boing.au");
2. 在 init 中加入 SoundClip.loop(); 3. 在 stop 中加入 if (SoundClip!=null) SoundClip.stop();
106
2.8 可通用的代码 • 同时包含 main() 方法和 init() 方法 • 由于 application 本身不是图形环境 , 因此需 要在程序中加入图形环境 , 以便可以作为普 通的 application 使用 • import java.applet.Applet; import java.awt.*; • import java.awt.event.*; • import java.util.*;
107
2.9 小结
• 小应用程序是在浏览器中运行的 , 每个小应用程 序中必须有一个主类 , 冠以 public, 并且继承自 java.applet. • 小应用程序包括生命周期的四个环节和 paint() • 根据程序要求 , 用户可以在主类中定义其它方法 , 或定义其它类 . • • • • • •
public class myapplet extends Applet { init() {…};start() {…}; stop() {…};destroy() {…}; paint(Graphics g){…} } myclass1{…..};class myclass2{…};
applet 主类
108 init() applet 启动后第一个被执行 , 在此初始化
2.9 小结
start() init() 后被执行 , 程序主要代码写在此 paint() start() 后被执行 , 写与输出有关的代码 stop() 浏览器变换页面时执行 , 可以省略重写 destroy() 浏览器关闭时执行 , 可以省略重写
Classes
自定义方法 不能自动被执行 , 可以由前三 个方法调用 . 例如 : start() { mymethod()}
mymethod1 mymethode2
. Classes myclass =new Classes() .myclass.method1();
109
class Myclass { int v1; method(int num) {v1=num;} }
2.9 小结
public class Demo extends Applet { public void init() { Myclass test1=new Myclass(); test1.method(20); Myclass test2=new Myclass(); test2.method(10); } }
test1 v1 20
test2 v1 10
内存
110
2.9 小结 • 线程是实现动态效果的核心 , 运行线程必须继承 Thread 类或者实现 Runable 接口 . • run 是线程的主体 , 它反复调用 repaint() 方法 , 其中必须有休眠 sleep(). • sleep() 语句要捕获中断异常 ( 右面讲 ) • try{Thread.sleep(100);} • catch(InterruptedException e) {} • 有线程的小应用 ,start(),stop() 方法必须重写 . • 需要获取网络资源时 ( 包括本地资源 ), 要用 URL 类
111
第三章
事件处理
北京大学计算机系
代亚非
112
第 3 章 事件处理 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚
3.1 什么是事件 3.2 鼠标产生的事件 3.3 键盘产生的事件 3.4 动作事件 3.5 通用事件处理程序 3.6 发送自己的事件 3.7 Java 1.1 事件处理模型 3.9 小结
113
3.1 什么是事件 ❚ CGI 之外的选择 form cgi www 浏览器 Web server url interact applet
•
外部程序 C 程序 数据库程序
什么是事件 ? 用户用于交互而产生的键盘或
鼠标动作 . • 响应用户的动作称为处理事件 . • 在类 Event 中 , 定义了所有的事件处理方法 , 小应用已经继承了这些方法 .
114
3.1 什么是事件 ❚ import java.awt.Event; ❚ Event 在 java.awt 包中 , 无论哪个键按下或者 释放 , 还是鼠标移动 , 按下鼠标或释放鼠 标 ,AWT 都会捕获 , 并产生一个事件 . ❚ 处理事件的核心是重写处理事件的方法 ❚ 通用方法 : ❚ handEvent(Event evt, Object arg) ❚ 专用方法 : ❚ MouseDown(Event evt, Object arg)
115
3.1 什么是事件 ❚ Event 类的数据域 int clickCount
int id
int modifiers
int x
Event
int y
long when
int key
Object target
Object arg
116
3.1 什么是事件 如果你不 如果你不 覆盖你要 覆盖你要 处理的方 处理的方 法,则缺 法,则缺 省的方法 省的方法 返回一个 返回一个 假值,通 假值,通 知系统没 知系统没 有处理事 有处理事 件 件
MouseUp() Event
MouseDown() MouseDrag()
HandleEvent()
MouseMove() MouseEnter()
action()
MouseExit() keyDown() KeyUp()
117
3.2 鼠标产生的事件 ❚ ❚ ❚ ❚ ❚ ❚ ❚
鼠标事件的三个参数 : 1. 事件的类型 ( 是移动 ? 托拽 ) 2. 是按下还是放开 ? 3. 鼠标的位置 (x,y) 方法的重写 : public boolean mouseDown(Event evt,int x,int y) {….}
118
3.2 鼠标产生的事件 ❚ 例 : 在鼠标单击的地方显示 “ (MouseClick.html) 捕获事件 mouseDown
❚ ❚ ❚ ❚ ❚
获得参数 (x,y) (Event evt, int x, int y)
”. 在 (x,y) 处画 叉 paint() drawLine
思路 : 记忆鼠标点过的所有点 1.Point marks[]=newPoint[20];( 在 init 方法中 ) 2.marks[i++]=new Point(x,y); (MouseDown 方法中 ) 3. 将所有的点画出来 ( 在 paint 方法中 ) g.fillOval(x,y,10,10);
119
import java.awt.*;import java.applet.*; extends Applet .public class mark 3.2 鼠标产生的事件 { int i; Point Marks[]; public void init() {Marks[] =new Point[20]; i=20;} boolean mouseDown(Event evt, int x, int y) { Marks[i++]=new Point(x,y); repaint(); } public void paint(Graphics g) { int k; for (k=0;k
120
3.2 鼠标产生的事件 import java.awt.*;import java.applet.Applet; public class CountClick extends Applet {int CurrentMarks=0; public boolean mouseDown(Event evt,int x,int y) { CurrentMarks++; repaint(); return true; } public void paint(Graphics g) { g.drawString(" "+CurrentMarks,10,10);} } ❚ [ 练习 ] 对鼠标的点击动作计数
121
3.3 键盘产生的事件 捕获的方法 keyDown(Event evt, int key) Event 类的键常量 常量 键 常量 键 常量 键 DOWN 下箭头键 END End 键 F1 F1 键 F2 F2 键 F3 F3 键 F4 F4 键 F5 F5 键 F6 F6 键 F7 F7 键 F8 F8 键 F9 F9 键 F10 F10 键 F11 F11 键 F12 F12 键 HOME Home 键 LEFT 左箭头键 PGDN PageDown 键 PGUP PageUp 键 RIGHT 右箭头键 UP 上箭头键
122
3.3 键盘产生的事件 ❚ 例题 : 显示用户按下的字母键内容 import java.applet.Applet;import java.awt.*; { char Presskey; public boolean keyDown(Event evt, int key) { Presskey=(char)key; repaint(); return true; } public void paint(Graphics g) { g.drawString(Presskey,10,10); } }
123
3.3 键盘产生的事件
❚ 键盘事件处理通常包括 : 显示字符 , 光标移动 ❚ 特殊键 public boolean keyDown(Event evt, int key) { switch(key) { case Event.F1: {….}; case Event.PGUP: {…} } } ❚ 修正键 if(evt.shiftDown()) if(evt.controlDown());
124
3.3 键盘产生的事件 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚
练习题 : 在屏幕上显示用户输入的字符串 在前一题的基础上 , 将字符串起来 , 但是不能用 : String s; s=s+presskey; 应该用 StringBuffer 对象的 append 方法 StringBuffer sb; sb.appned(presskey); sb 的内容不能直接用 g.drawString() 应该用 g.drawString(sb.toString(),x,y);
125
3.4 动作事件 ❚ 凡是由构件产生的事件叫动作事件 ACTION_EVENT, 处理这类事件的方法是 : action().
music
确定
sports
取消
art
126
3.4 动作事件
❚ action(Event evt, Object arg) ❚ evt.target: 指明事件类型 ❚ (button,checkbox,list,...) int clickCount
int id int modifiers
int x
Event
int y
long when
int key
Object target
Object arg
127
3.4 动作事件 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚
判断组件类型 ( 如是 button 或 checkbox) if(evt.target instanceof Button) if(evt.target instanceof Checkbox) 判断是哪多个同类组件中的哪一个 if(evt.target==button1) if(evt.target=button2) 或者通过判断标签内容 if(arg==“ 确定 ” ) if(arg==“ 取消 ” )
128
3.4 动作事件
例 : 记录按下按钮的次数 , 并显示出来 . import java.awt.*; import java.applet.Applet; public class CountClick extends Applet { int CurrentMarks=0; public init() { Button b1=new Button(“ 按钮 ” ); 按钮 add.b1; } public boolean action(Event evt,Object arg) 10 { CurrentMarks++; repaint(); return true; } public void paint(Graphics g) { g.drawString(" "+CurrentMarks,10,10);} }
129
3.4 动作事件 ❚ 例 : 根据用户选择画图形 ❚import 思路 :java.awt.*; 园 ❚import java.applet.Applet; 1. 设计两个按钮 ( 后面详细讲 ) class drawing ❚public 2. 事件处理 action extends Applet { boolean circlemark=true; ❚ public 根据选择 init() , 分别标记园或方 ❚ {3 Button 根据标记画出相应图形 b1=new Button(“ 园 ” ); b2=new Button(“ 方 ” ); ❚ Button g.drawCirlce(50,50,50,50); add.b1; add.b2; ❚ } g.drawRect(25,25,75,75);
130
3.4 动作事件 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚
public void paint(Graphics g) { if (circlemark) g.filloval(10,10,30,30); else g.fillRect(20,20,40,40}; } public boolean action(Event evt,Object arg) { if (evt.target==b1) circlrmark=true; else circlrmark=false; repaint(); return true; }
3.5 通用的事件处理程序 ----handleEvent
131
❚ handleEvent 处理所有的小应用程序所接受 的事件 , 由它将事件送给相对应的方法 . ❚ 让我们看一下 handleEvent 的缺省实现 public boolean handleEvent(Event evt) { switch(evt) { case Event.MOUSE_ENTER: return mouseEnter(evt,evt.x,evt.y); case Event.MOUSE_EXIT: return mouseExit(evt,evt.x,evt.y); case Event.MOUSE_MOVE: return mouseMove(evt,evt.x,evt.y);
3.5 通用的事件处理程序 ----handleEvent case Event.MOUSE_DRAG: return mouseDrag(evt,evt.x,evt.y); case Event.MOUSE_UP: return mouseUp(evt,evt.x,evt.y); case Event.MOUSE_DOWN: return mouseDown(evt,evt.x,evt.y); case Event.KEY_PRESS: case Event.KEY_ACTION: return keyDown(evt,evt.key); case Event.KEY_RELEASE:
132
3.5 通用的事件处理程序 --handleEvent
}
case Event.KEY_ACTION_RELEASE: return keyUp(evt,evt.key); case Event.ACTION_EVENT: return action(evt,evt.arg); case Event.GOT_FOCUS: return gotFocus(evt,evt.arg); case Event.LOST_FOCUS: return lostFocus(evt,evt.arg); } return false;
133
3.5 通用的事件处理程序 --handleEvent
134
❚ 覆盖 handleEvent 的情况 ( 原来的 handleEvent 不 被执行 ): 只处理我们感兴趣的事 :
public boolean handleEvent(Event evt) { switch(evt.id) { case Event.MOUSE_ENTER: //doing something; case Event.MOUSE_EXIT: //doing something; default:return super.handelEvent(evt); return false;
}}
135
.
3.7 Java1.1 事件模型 ❚ Java 1.0 的事件管理机制 ❚ 在 Java1.0 中,多数事件发生在类 component 里, 并通过 handleEvent() 方法将事件传递给相应的处 理方法 , 如果没有这样的方法 , 则沿着包含层次传 给上一级容器 , 直到最顶层容器仍没有处理 , 则合 理遗弃 , 不适于重用 . 例如一个发生在按钮上的事 件 , 处理它的 action 方法通常属于包含按钮的父类 容器 , 这不符合面向对象的设计原则 ❚ Java 1.1 的事件管理机制 ❚ 在 Java 1.1 中,一个事件常常被其它对象处理,这 些对象称为事件监听器,当特定事件发生时,相 应的监听器能够知道。 AWT 共有 11 个监听器类, 每个都有处理事件的方法。
136
3.7 Java1.1 事件模型 ❚ ❚ ❚ ❚ ❚ ❚ ❚ ❚
在 Java 1.1 中如何捕获事件 ? 三个步骤 : 1. 必须定义一个类来实现接口 class ButtonTracker implements ActionListener{…} 2. 定义方法 3. 向构件注册该类的一个实例 button1.addActionListener(this); checkbox.addActionListener(this);
137
3.7 Java1.1 事件模型 ❚ Java1.0 和 Java1.1 事件模型的区别 ? ❚ 在 Java1.1 中 , 事件不再沿着包含层次向上传 递 , 而是传给一个监听器 , 因此在 Java1.1 中 , 你希望发生在构件事件由哪个监听器处理 , 就 将该监听器注册到构件上 .
138
3.7 Java1.1 事件模型 窗口
panel
窗口或面板 的监听器
button
按钮的监听器
139
3.7 Java1.1 事件模型
❚ 常用的监听器及其方法
❚ 键盘监听器 :KeyListener, 方法 : ❚ keyPressed, KeyReleased, keyTyped. ❚ 鼠标监听器 : MouseListener, 方法 : ❚ mouseClicked, mousePressed, mouseReleased, ❚ mouseEntered, mouseExited ❚ 构件监听器 :ActionListener, 方法 : ❚ actionPerformed(); ❚ 如果一个监听器有若干各方法 , 则必须将这些方法全 部覆盖
140
3.7 Java1.1 事件模型 ❚ ❚ ❚ ❚ ❚ ❚ ❚
例: import java.awt.event.*;import java.awt.*; import java.applet.*; public class TestButton extends Applet { Button button1; Color color;
❚ }
141
3.7 Java1.1 事件模型
public void init() { button1 = new Button("Test Button"); button1.addActionListener (new ButtonHandler(this)); add(button1); Font font = new Font("TimesRoman", Font.PLAIN, 48); g.setFont(font); color = Color.black; resize(400, 200); }
142
3.7 Java1.1 事件模型 public void paint(Graphics g) { g.setColor(color); g.drawString("TEST COLOR", 55, 120); }
public class ButtonHandler implements ActionListener { TestButton a; 3.7 Java1.1tb)事件模型 ButtonHandler(TestButton { a=ts;} public void actionPerformed(ActionEvent event) { String arg = event.getActionCommand(); if (arg == "Test Button") { if (a.color == Color.black) a.color = Color.red; else a.color = Color.black; a.repaint(); } }
143
144
3.7 Java1.1 事件模型 100101
class TestButton button color=black; paint()
class ButtonHandler a 100101 a.color=black;
new ButtonHandler(this);
public class TestButtoninner extends Applet { Button button1; Color color; Font font; 3.7 Java1.1 事件模型 class ButtonHandler implements ActionListener { public void actionPerformed(ActionEvent event) { String arg = event.getActionCommand(); if (arg == "Test Button") { if (color == Color.black) color = Color.red; else color = Color.black; repaint(); } } }
145
146
class Example3.7 Java1.1 事件模型 {public void init() ❚ 在某个类的内部定义的类叫内部类 button1=new Button(“button”); ❚{ Button 内部类的特点 : button1.addActionListener(new ButtonHandler() ❚ 内部类可以访问外部类的数据和方法 { public void actionPerformed(ActionEvent event) ❚ 内部类的匿名形式是指 : { String arg = event.getActionCommand(); ❚ 在定义一个新类的同时创建一个实例 if (arg == "Test Button") . { if (color == Color.black) color = Color.red; else color = Color.black; repaint(); } . };
147
3.7 Java1.1 事件模型 ❚ 适配器 ❚ 简化代码 ❚ 不用适配器时 , 必须对某个适配器的多个方法进行重 写 , 加入适配器后可以为这些方法提空实现 class MouseAdapter implements MousListener { public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} }
148
3.7 Java1.1 事件模型 ❚ 继承某个适配器的子类 , 则程序中只需覆盖你 需要的方法 , 其余方法则从父类继承 ❚ class MouseTrack extends MouseAdapter ❚ { public void mouseClicked(MouseEvent e) ❚ { //…handle mouse click event; ❚ } ❚ }
149
3.8 小结 ❚ 事件由用户的使用了鼠标和键盘动作引起的 ❚ 事件由事件处理器来处理 ❚ handleEvent() 方法是 Applet 从 component 继承而来的 . ❚ 重写 handleEvent() 方法时注意返回 false 指 明有些情况被忽略 , 将其传给上层对象 . ❚ 在组件上产生的动作叫动作事件 ,action 方法 类处理
150
3.10 作业 ❚ 1. 在鼠标按下的两点之间画一条线
第 4 章 异常
北京大学计算机系 代亚非
第 4 章 异常 ■ ■ ■ ■ ■ ■ ■
4.1 4.2 4.3 4.4 4.5 4.6 4.7
异常的概念 异常的分类 捕获异常 声明异常 抛出异常 创造自己的异常 总结
152
153 4.1 异常的概念 ■ 什么是异常 ? 异常实际上是程序中错误导致中断了 正常的指令流的一种事件 . ■ 没有处理错误的程序 : read-file { openTheFile; determine its size; allocate that much memory; closeTheFile; }
■
4.1 异常的概念 以常规方法处理错误
openFiles; if (theFilesOpen) { determine the lenth of the file; if (gotTheFileLength){ allocate that much memory; if (gotEnoughMemory) { read the file into memory; if (readFailed) errorCode=-1; else errorCode=-2; }else errorCode=-3; }else errorCode=-4 ; }else errorCode=-5;
154
4.1 异常的概念 ■
■
■ ■
155
观察前面的程序你会发现大部分精力花 在出错处理上了 . 只把能够想到的错误考虑到 , 对以外的 情况无法处理 程序可读性差 出错返回信息量太少
4.1 异常的概念 ■
用异常的形式处理错误
read-File; { try { openTheFile; determine its size; allocate that much memory; closeTheFile; }catch(fileopenFailed) { dosomething; } catch(sizeDetermineFailed) {dosomething;} catch(memoryAllocateFailed){ dosomething;} catch(readFailed){ dosomething;} catch(fileCloseFailed) { dosomething; } }
156
4.1 异常的概念
157
和传统的方法比较异常的优点 : 1. 把错误代码从常规代码中分离出来 2. 把错误传播给调 method4 产生异常 用堆栈 method3 传 method2 3. 按错误类型和 递 method1 处理异常 错误差别分组 4. 系统提供了对于一些无法预测的错误的 捕获和处理 5. 克服了传统方法的错误信息有限的问题 ■
4.1 异常的概念
158
class ■ . ExcepTest { public void main(String args[]) { int b=0; int a; try { a=4/b;} catch(ArithmeticException e) { System.out.println(“divided by 0”);} }} try{ URL url=new URL(http://www.hit.edu.cn/”,”hit.gif”);} catch(MalformedURLEception e) { badURL=true; repaint();}
4.2 异常的分类 ■
■
■
■
■
159
异常是一个对象 , 它继承自 Throwable 类 , 所有的 Throwable 类的子孙类所产生的对象都是例外 . Error: 由 Java 虚拟机生成并抛出 ,Java 程序不做处 理. Runtime Exception( 被 0 除等系统错误 , 数组下标超 范围 ): 由系统检测 , 用户的 Java 程序可不做处理 , 系统将它们交给缺省的异常处理程序 . Exception( 程序中的问题 , 可预知的 ): Java 编译器 要求 Java 程序必须捕获或声明所有的非运行时异常 throw: 用户自己产生异常
4.2 异常的分类 ■
.
160
Throwable
用户自己产生的异常 Exception 要处理
Error RuntimeException
不做处理 由用户捕获或 声明并处理 缺省的异常 处理程序
4.3 捕获异常 捕获并处理异常 try { // 接受监视的程序块 , 在此区域内发生 // 的异常 , 由 catch 中指定的程序处理 ; }catch( 要处理的异常种类和标识符 ) { // 处理异常 ; }catch( 要处理的异常种类和标识符 ) { // 处理异常 ; } ■
161
4.3 捕获异常 ■
常见的异常
■
ArithmeticException ArrayIndexOutOfBandsException ArrayStoreException 如果在使用能 IOException 够产生异常的 FileNotFoundException 方法而没有捕 NullPointerException 获和处理,将 MalformedURLException 不能通过编译 NumberFormatException OutOfMemoryException
■ ■ ■ ■ ■ ■ ■ ■
162
4.3 捕获异常 ■ ■ ■ ■
例 : 编写 Java 程序 , 包含三种异常 算术异常 , 字符串越界 , 数组越界 观察输出信息 : 每个异常对象可以直接给出信息
163
class first_exception 4.3 捕获异常 { public static void main(String args[]) { char c; int a,b=0;int[] array=new int[7]; String s="Hello"; try {a=1/b;} catch(ArithmeticException ae) { System.out.println(“Catch “+ae));} try {array[8]=0;} catch(ArrayIndexOutOfBoundsException ai) { System.out.println((“Catch “+ai);} try{ c=s.charAt(8));} catch(StringIndexOutOfBoundsException se) { System.out.println((“Catch “+se);}}}
164
4.3 捕获异常 165 一定会执行的程序块 ---finally 异常处理的统一出口 try { // 常规的代码 ; } catch() { // 处理异常 } finally { // 不论发生什么异常 ( 或者不发生任何 异常 ), 都要执行的部分 ; }
4.3 捕获异常 ■ ■ ■ ■ ■ ■ ■ ■
finally 在文件处理时非常有用 try { 对文件进行处理的程序 ; }catch(IOException e) { // 对文件异常进行处理 ; }finally { 不论是否发生异常 , 都关闭文件 ; }
166
4.4 声明异常
167
一个方法不处理它产生的异常 , 而是沿着调用 层次向上传递 , 由调用它的方法来处理这些异 常 , 叫声明异常 . 声明异常的方法 在产生异常的方法名后面加上要抛出 (throws) 的异常的列表 ■ void compute(int x)throws ArithmeticException {…} ■ returnType methodName([parameterlist]) throws exceptionList ■
4.4 声明异常 例 : 若因 某种原 因不想 在创建 URL 的 方法中 处理异 常
168
public int compute(int x) throws ArithmeticException e) { return z=100/x;} public method1() { int x; try { x=System.in.read(); compute(x);} catch(IOException ioe) { System.out.println(“read error”); } catch(ArithmeticException e) { System.out.println(“devided by 0”); } }
4.4 声明异常
method1 处理
computer
抛出
169
4.4 声明异常
170
例 : 说出程序执行结果 public class exception1 { void Proc(int sel) throws ArithmeticException, ArrayIndexOutOfBoundsException { System.out.println(“In Situation" + sel ); if (sel==0) { System.out.println("no Exception caught"); return; }else if(sel==1) {int iArray[]=new int[4]; iArray[10]=3; } }
4.4 声明异常
171
public static void main(String args[]) { try { Proc(0); Proc(1); }catch(ArrayIndexOutOfBoundsException e){ System.out.println("Catch"+e); } } c:>jview throwsException In Situation 0 no Exception caught In Situation 1 Catch java.lang.ArrayIndexOutOfBoundsExce ption:10
4.5 抛出异常 抛弃异常
■ ■ ■
:
不是出错产生 , 而是人为地抛
出 throw ThrowableObject; throw new ArithmeticException(); 例 : 编写程序人为抛出 (JavaThrow.prj) ArithmeticException, ArrayIndexOutOfBoundsException StringIndexOutOfBoundsException
A method
Exception throw
172
Another method caught
class JavaThrow 4.5 抛出异常 173 { public static void main(String args[]) { try{ throw new ArithmeticException();} catch(ArithmeticException ae) { System.out.println(ae); } try{ throw new ArrayIndexOutOfBoundsException();} catch(ArrayIndexOutOfBoundsException ai) { System.out.println(ai); } try { throw new StringIndexOutOfBoundsException();} catch(StringIndexOutOfBoundsException si){ { System.out.println(si); }
■
■
■
4.6 创造自己的异常 174 不是由 Java 系统监测到的异常 ( 下标越 界 , 被 0- 除等 ), 而是由用户自己定义的 异常 . 用户定义的异常同样要用 try--catch 捕 获 , 但必须由用户自己抛出 throw new MyException. 异常是一个类 , 用户定义的异常必须继 承自 Throwable 或 Exception 类 , 建议 用 Exception 类 .
4.6 创造自己的异常
175
■
形如 :
■
class MyException extends Exception {….}; 例 1 : 计算两个数之和 , 当任意一个数超出范 围时 , 抛出自己的异常
■ ■
public class NumberRangeException extends Exception { public NumberRangeException(String msg) { super(msg); } }
4.6 创造自己的异常 ■public .
boolean action(Event evt, Object arg)
{ try { int answer = CalcAnswer(); answerStr = String.valueOf(answer); }catch (NumberRangeException e) { answerStr = e.getMessage(); } repaint(); return true; }
176
public int CalcAnswer() throws NumberRangeException 创造自己的异常 177 { int int1, int2; 4.6 int answer = -1; String str1 = textField1.getText(); ■ . str2 = textField2.getText(); String try { int1 = Integer.parseInt(str1); int2 = Integer.parseInt(str2); if ((int1 < 10) || (int1 > 20) || (int2 < 10) || (int2 > 20)) { NumberRangeException e = new NumberRangeException (”Numbers not within the specified range."); throw e; } answer = int1 + int2; }catch (NumberFormatException e) { answerStr = e.toString(); } return answer;
4.6 创造自己的异常 ■
■
■
■
■
178
例 2 : 在定义银行类时 , 若取钱数大于 余额则作为异常处理 (InsufficientFundsException). 思路 : 产生异常的条件是余额少于取额 , 因此是否抛出异常要判断条件 取钱是 withdrawal 方法中定义的动作 , 因此在该方法中产生异常 . 处理异常安排在调用 withdrawal 的时 候 , 因此 withdrawal 方法要声明异常 , 由上级方法调用 要定义好自己的异常类
class Bank 4.6 创造自己的异常 { double balance; public ■ . void deposite(double dAmount) { if(dAmount>0.0) {balance+=dAmount;}}
179
public void withdrawal(double dAmount) throws InsufficientFundsException { if (balance
4.6 创造自己的异常 public class ExceptionDemo { public static void main(String args[]) { try { Bank ba=new Bank(50); ba.withdrawal(100); System.out.println(“Withdrawal successful!”); }catch(Exception e) { System.out.println(e.toString()); } }
180
4.6 创造自己的异常
181
■ .class InsufficientFundsException extends Exception public { private Bank excepbank; private double excepAmount;
InsufficientFundsException(Bank ba, double dAmount) { excepbank=ba; excepAmount=dAmount; } public String excepMesagge() { String str=“The balance”+ excepbank.showBalance()+ “The withdrawal was”+excepAmount; return str; }
4.7 小结 1. 一般格式 : 正常程序和出错处理分离开来 try { Java statement; }catche(ExceptionType1 ExceptionObject) { Exception1 handling; } catche(ExceptionType2 ExceptionObject) { Exception2 handling; }…. }finally { final handling; // ( 统一的出口 , 最终必定要执行 ) }}
182
4.4 小结 ■
183
2. 把异常传播给堆栈 , 沿着被调用的顺序 往前寻找 , 只要找到符合该异常种类彻底 异常处理程序 , 就交给这部分程序去处理 call
Method1
throws
Method2
call Method3 call Read-file
throws
throws 产生异常
Try catch
4.4 小结 3. 异常可以人为地抛出 , 用 throw new 语句 4. 异常可以是系统已经定义好的 , 也可 以是用户自己定义的 5. 用户自己定义的异常一定继承自 Throwable 或 Exception 类
184
作业 ■
当用户按 “ T” 或 “ t” 时 , 人为抛出一个 算术异常 , 处理 方式为打印出错信息
185
import java.awt.*; import java.applet.*; 186 public class keyException extends Applet { final int LOWERCASE_T = 116; final int UPPERCASE_T = 84; String str; int count=0; public void paint(Graphics g) { g.drawString(" "+str+count, 5,20); } public boolean keyDown(Event evt, int key) {if ((key == LOWERCASE_T) || (key == UPPERCASE_T)) { try{ throw new ArithmeticException();} catch(ArithmeticException e) { count++; str=e.toString(); repaint();} return true; } return false; }
第 5章 构成用户界面的窗口环境 北京大学计算机系 代亚非
187
构成用户界面的窗口环境
188
✔ 5.1 使用 AWT(Abstract Window Toolkit) 构件 ✔ 5.2 包含构件的构件 ---- 构件容器
( container ) ✔ 5.3 事件的传递 ✔ 5.4 各种构件的应用实例 ( 一 ) ✔ 5.5 外观的管理与控制 ✔ 5.6 各种构件的应用实例 ( 二 ) ✔ 5.7 总结
5.1 使用 AWT 构件
189
✔ 在 AWT 的概念中,窗口系统所显示的各种
对象统称为构件: Button , Menu , List 等等都是构件。 ✔ Component 是代表构件最基本的类。 ✔ Component 类中定义了各种窗口对象中最基
本、最重要的方法和性质。随时随处都有 可能利用 Component 定义的方法。
5.1 使用 AWT 构件
190
✔ 已经学过的:
✔ 没有学过的:
✔ getFont ,
✔ action, disable , enable ,
✔ handleEvent,keyUp
✔ getBackground ,
✔ keyDown,mouseUp ,
✔ getForeground , hide ,
✔ mouseDown ,
✔ inside , isEnable ,
✔ mouseMove ,
✔ isShowing , isVisible ,
✔ mouseEnter ,
✔ locate , location ,
✔ mouseExit , ✔ mouseDrag, repaint ✔ setFont , resize
✔ move , setBackground , ✔ setForeground , show , ✔ size
5.2 包含构件的构件 ---- 构件容器 (container )
列表
按钮
菜单
窗口,对话框 另一个窗口
container container
191
5.2 包含构件的构件 ---- 构件容器 (container ) import java.awt.*; import java.applet.Applet; public class CountClick extends Applet { int CurrentMarks=0; public init() { Button b1=new Button(“ 按钮” ); add.b1; } } 按钮
192
5.2 包含构件的构件 ---- 构件容器 (container )
193
✔ AWT 使用 Container 类来定义最基本的构件
容器 , 它有两个子类 :Window 类和 Panel 类 . ✔ 在 Window 类还有两个子类 ✔ 1. 定义对话框 , 用 Dialog 子类 ; ✔ Java 还提了一个 Dialog 的子类 ---FileDialog,
用它生成文件对话框 ✔ 2. 定义一般意义的窗口 , 用 Frame 类 .
5.2 包含构件的构件 ---- 构件容器 (container )
194
✔ Panel 干什么用的呢 ? ✔ 使你更方便的组织你的构件 , 得到赏心悦目
的布局 ✔ Applet 是 Panel 的子类 , 因此在小应用程序
里可以直接加入构件 , 而一般的应用程序必 须先定义构件容器 . ✔ 小应用程序在浏览器中所显示的区域就是
Panel, 所占的尺寸就是缺省得 Panel 尺寸 .
5.2 包含构件的构件 ---- 构件容器 (container )
195
Component Container Button
Panel Applet
Menu Textfield Checkbox
Window Frame Dialog FileDialog
5.3 事件的传递 (1.02)
196
当动作发生在按钮上 窗口
panel
时 , 首先看按钮这个 类有没有 action 方法 , 如果没有则看包含按 钮的容器类 ( 即 panel) 有没有 action 方法 , 如果没有事件就传递 窗口 , 如果没有就传 递给 component 的通
按钮
用处理方法 , 如果程 序中没有定义任何 action 方法 , 实际上事
class MyClass extends Frame { MyPanel p=new Mypanel(); add(p); Button b=new Button(exit); add(b); boolean action() {…….;} }
窗口
197
Panel
ok exit
class MyPanel extends Panel class MyBtn extends { Button MyBtn b=new MyBtn(“ok”); { add(b); boolean action() boolean action() { …….; {…….;} return true;} } }
5.3 事件的传递 (1.02)
198
✔ 事件由包含层次由内向外传递 . ✔ 每个处理事件的方法要有一个返回值 , 通知
是否继续向上传递 boolean action(Event evt, Object arg) { ……; return true; }
5.3 事件的传递 (1.1) 窗口
199
panel
窗口获面板 的监听器
button
按钮的监听器
5.4 各种构件的应用实例 -- 按钮
200
✔ Button 类 ✔ 功能 : 创建按钮事件 ✔ 创建一个 Button ✔ Button myButton = new Button(str); ✔ 将 button 放到窗口系统中 :
add(new Button(“ 确定 ” ); 确定 ✔ add(new Button(“ 取消 ” ); ✔ Button 类的常用方法 ✔ getLabel setLabel ✔
取消
5.4 各种构件的应用实例 -- 按钮
201
✔ 处理 button 产生的事件 ✔ 例 : 创建一个按钮 , 每当按下它时 , 在屏幕
显示文字 (singlebutton.html) ✔ 想一想 : ✔ 应该有哪些类 ? Button ,Font; ✔ 应有哪些方法 ? init--- 建立 button action--- 接受动作事件 , 调用 repaint paint--- 显示文 字
5.4 各种构件的应用实例 -- 按钮 import java.awt.*;import java.applet.Applet; public class button extends Applet { Font font; Button b1; public void init() { font= newFont("TimesRoman",Font.BOLD,20); b1=new Button("push"); add(b1); setFont(font); }
202
5.4 各种构件的应用实例 -- 按钮 ✔ boolean action(Event evt,Object arg) ✔ { y+=5; repaint(); ✔
return true;
✔} ✔ paint(Graphics g) ✔ { g.drawString("Button”, 10,y);}
203
5.4 各种构件的应用实例 -- 按钮
204
import java.awt.*;\\ 例 : 不在 applet 中的按钮 class ButtoninFrame { public static void main(String args[]) { Frame myframe=new Frame(); myframe.setTitle("Button in Frame"); myframe.resize(200,200); myframe.show(); Button b1=new Button("Button1"); Button b2=new Button("Button2"); myframe.add(b1);myframe.add(b2); } }
.
5.4 各种构件的应用实例
205
✔ 一般步骤 :
创建 new
加入 add
响应 action
处理
5.4 Computer 各种构件的应用实例 Computer
206
MusicTextField(20); Music New 常用的方法 new TextArea( “this is a test”,20,40); Sports getText();setText(); Sports setEchoCharacter(char c) Scrollbar(VERTICAL,50,0,1,100); Art Art CheckboxGroup gr=new CheckboxGroup(); 1 Checkbox(label, New Checkbox(label, false); New gr,null, false); New Checkbox(label,gr,true);
min
start
max
5.4 各种构件的应用实例 --CheckBox
207
✔ 应用举例 ✔ 建立三个复选框 , 被选中者的标签内容变成
“ changes” – 应有的类 :Checkbox – 应有的方法 : – init: 建立复选框 – action: 接受动作事件 – setLabel(“*****”);
5.4 各种构件的应用实例 --CheckBox import java.awt.*; public class checkbox extends java.applet.Applet { Checkbox b1,b2,b3; public void init() { b1=new Checkbox("Label1",null,false); b2=new Checkbox("Label2",null,false); b3=new Checkbox("Label3",null,false); add(b1); add(b2); add(b3); }
208
5.4 各种构件的应用实例 --- CheckBox
209
public boolean action(Event evt, Object arg) { if (evt.target instanceof Checkbox){ Checkbox selectedbox=(Checkbox)evt.target; String str=selectedbox.getLabel(); if (str=="Label1") selectedbox.setLabel("Chnage1"); else if (str=="Label2") selectedbox.setLabel("Chnage2"); else if (str=="Label3") selctedbox.setLabel("Change3"); } repaint();return true; }
5.4 各种构件的应用实例 --- TextField
210
✔ 例 : 在文本行中输入 , 并用字符串接受 , 显
示出来 ✔ 类的数据构成 :Textfield,Button, String ✔ 类的方法构成 : ✔ init(), ✔ action(): 接受按钮事件 , 调用 paint() ✔ paint(): 用 getText() 方法得到输入内容 , 并显 示.
5.4 各种构件的应用实例 --- TextField
211
import java.awt.*; public class textfieldkey extends java.applet.Applet { TextField t;String s;Button button; public void init() { t=new TextField("",25); add(t); button=new Button("getText"); add(button); }
5.4 各种构件的应用实例 --- TextField public boolean action(Event evt, Object arg) { if(evt.target instanceof Button) { repaint();} return true; } public void paint(Graphics g) { s=t.getText(); g.drawString(s,40,80); }
212
5.5 外观的管理与制
0 1 5
2
3
4
213
0
1
3
4
4
5
5.5 外观的管理与制
214
✔ Panel 类 ( 面板 ) ✔ 功能 : 容纳其他对象 , 安排合理布局 ✔ 创建面板 :
Panel myPanel=new Panel(); ✔ add(myPanel); ✔ 将面板作为容器 : ✔ mypanel.add(button) button1 ✔
button3
button2 button4
import java.awt.*; 215 5.5 外观的管理与制 public class Panel extends java.applet.Applet { ✔Panel panel1,panel2; 例 :(panel.htm) Button button1,button2,button3,button4; public void init() { panel1=new Panel(); panel2=new Panel(); add(panel1); add(panel2); button1=new Button("Button1"); button2=new Button("Button2"); button3=new Button("Button3"); button4=new Button("Button4"); panel1.add(button1); panel1.add(button2); panel2.add(button3); panel2.add(button4); }}
5.5 外观的管理与制
216 北
✔ BorderLayout 类 ✔ 功能 :Applet 分成五个区 ✔ 创建
西
✔ setLayout(new BorderLayout()); ✔ 将其他构件加入 ✔ add(“East”, new Button(“ 东 ” ); ✔ add(“South”, new Button(“ 南 ” ); ✔ add(“West”, new Button(“ 西 ” ); ✔ add(“North”, new Button(“ 北 ” ); ✔ add(“Center”, new Button(“ 中 ” );
中 南
东
5.5 外观的管理与制 ✔ FlowLayout 类 ✔ 缺省的输出管理器 ✔ GridLayout 类 ✔ GridLayout mylayout = new ✔ GridLayout(3,3,0,0)
rows
cols
hspace
✔ setLayout();
vspace
217
5.5 外观的管理与制
218
✔ GridBagLayout 类和
GridBagConstraints 类 ✔ 功能 : 借助于 GridBagConstraints 类 , 实现 更灵活的外观管理 ✔ 每个构件后都跟随一个 GridBagLayout 对象 实体 , 来决定构件的外观 . ✔ 创建 ✔ GridBagLayout myLayout=new ✔ GridBagLayout(); ✔
5.5 外观的管理与制
219
✔ GridBagConstraints 类的约束条件 gridwidth,
gridheight, gridx, gridy, ✔ weightx, weighty, ipadx, ipady, insets ✔ fill 及其设置 ✔ GridBagConstraints.NONE ✔ GridBagConstraints.HORIZONTAL ✔ GridBagConstraints.VERTICAL ✔ GridBagConstraints.BOTH ✔ GridBagConstraints.RELATIVE
5.5 外观的管理与制
220
✔ 例 :(GridBagApplet.html)
button1
button2
button3
button4 button5 button7
button6 button8 button9
public init() 221 5.5 void 外观的管理与制 { GridBagLayout layout=new GridBagLayout(); setLayout(layout); GridBagConstraints GBC = new GridBagConstraints(); Button button1=new Button("button1"); Button button2=new Button("button2"); Button button3=new Button("button3"); Button button4=new Button("button4"); Button button5=new Button("button5"); Button button6=new Button("button6"); Button button7=new Button("button7"); Button button8=new Button("button8"); Button button9=new Button("button9");
GBC.fill=GridBagConstraints.BOTH; 222 5.5 外观的管理与制 ( 按钮可以在水平和垂直两个方向扩展 ) layout.setConstraints(button1,GBC); add(button1); GBC.gridwidth=GridBagConstraints.RELATIVE; (BOTH 依然起作用 , 紧挨着最后一个按钮 ,) layout.setConstraints(button2,GBC); add(button2); GBC.gridwidth=GridBagConstraints.REMAINDER; ( 填充剩余部分 ) layout.setConstraints(button3,GBC); add(button3); But1 But2 But3
5.5 外观的管理与制
223
✔ GBC.gridwidth=
GridBagConstraints.REMAINDER; ✔ ( 表示该按钮独占一行 ) ✔ layout.setConstraints(button4,GBC); ✔ add(button4); But1
But2 But4
But3
5.5 外观的管理与制
224
✔ GBC.gridwidth=2; ✔ ( 表示该按钮占两个单元 ) ✔ layout.setConstraints(button5,GBC); ✔ add(button5);
But1 ✔ GBC.gridwidth=
But2 But4 But5
GridBagConstraints.REMAINDER; ✔ layout.setConstraints(button6,GBC); ✔ add(button6); ✔
But3 But6
5.5 外观的管理与制
225
✔ GBC.gridwidth=1; ✔ GBC.gridheight=2; ✔ ( 高度为两个单元 ) ✔ layout.setConstraints(button7,GBC); ✔ add(button7); But1
But2 But4 But5
But7
But3 But6
5.5 外观的管理与制
226
✔ GBC.gridwidth=
GridBagConstraints.REMAINDER; ✔ GBC.gridheight=1; ✔ layout.setConstraints(button8,GBC); ✔ add(button8); But1 But2 But3 ✔ layout.setConstraints But4 ✔ (button9,GBC); But5 But6 ✔ add(button9); But7
But8 But9
5.6 各种构件的应用实例 ---Canvas
227
✔ 5.6.2 Canvas 类 ( 画布 ) ✔ 功能 : 制作其他构件 , 通常用来放置图形图
像 , 或绘图 . ✔ 画图可以直接在 applet 区域上进行 , 定义了 Canvas 对象后将 paint() 语句作为该对象的 方法 , 这些动作就自动发生在画布区 . ✔ 通常不需要处理画布上发生的事件 ✔ 创建 ✔ Canvas canvas=new Canvas(); ✔ add(canvas);
5.6 各种构件的应用实例 ---Canvas
228
✔ 例 : 根据程序说出运行结果 ✔ 注意一个程序中生成一个 canvas 类的实例 ,
另一个程序没有
import java.awt.*; import java.applet.*; ---Canvas public 5.6 class各种构件的应用实例 canvas_test_2 extends Applet { public void init() { setLayout(new BorderLayout()); add("North", new Button("button1")); add("South", new Button("button2")); add("West", new Button("button3")); add("East", new Button("button4")); } public void paint(Graphics g) { g.setColor(Color.red); g.fillRect(0,0,50,100); g.setColor(Color.blue); g.fillRect(30,0,100,40);} }
229
5.6 各种构件的应用实例 ---Canvas import java.awt.*; import java.applet.*; public class canvas_test extends Applet { MyCanvas mycanvas=new MyCanvas(); public void init() { setLayout(new BorderLayout()); add("Center",mycanvas); add("North",new Button("button1")); ….; add("East",new Button("button4")); } }
230
5.6 各种构件的应用实例 ---Canvas class MyCanvas extends Canvas { public void paint(Graphics g) { g.setColor(Color.red); g.fillRect(0,0,50,100); g.setColor(Color.blue); g.fillRect(30,0,100,40); } }
231
5.6 各种构件的应用实例 ---Canvas
232
5.6 各种构件的应用实例 ---Canvas
233
✔ 例 : 按动鼠标改变画布的颜色
(CanvasApplet) ✔ 有哪些类 ? Canvas, Button, Color; ✔ 哪些方法 ? init(), action(),swapColor(),paint() color
利用输出管理 器按钮和画布
color
color
按钮接收 鼠标事件
变换颜色
执行重画
5.6 各种构件的应用实例 ---Canvas class CanvasApplet extends Applet { MyCanvas mycanvas=new MyCanvas(); public void init() { setLayout(new BorderLayout()); Button button = new Button("Color"); add("North",button); add(“Center”,mycanvas); resize(200,250); } .
234
5.6 各种构件的应用实例 ---Canvas ✔ boolean action(Event evt, Object arg) ✔{ ✔ if(arg=="Color") mycanvas.swapColor(); ✔ return true; ✔}
235
class MyCanvas extends Canvas 5.6 各种构件的应用实例 ---Canvas { Color color; MyCanvas() { color=Color.red; } public void paint(Graphics g) { g.setColor(color); g.fillRect(20,20,100,100); g.setColor(color.white); g.drawString("CANVAS",40,40);} public void swapColor() { if(color==Color.black) color=Color.red; else if(color==Color.red) color=Color.green; else color=Color.black; repaint(); } . }
236
5.6 各种构件的应用实例 ---Frame ✔ 5.6.3 Frame 类 ✔ 功能 : 制作一般的独立窗口 , 它是构件容
器 ✔ 创建 ✔ Frame fmInstance=new Frame(); ✔或 Frame fmInstance= ✔ new Frame(“The window for test”); ✔ 将其显示到屏幕上 ✔ fmInstance.show() ✔ 注意 : 不用 add()
237
5.6 各种构件的应用实例 ---Frame ✔ 常用的方法
dispose,getCursorType,getIconImage, ✔ getMenuBar,getTitle,isResizable, ✔ setCursor,setIconImage,setMenuBar, ✔ setResizable,setTitle ✔ 窗口的相关事件 : ✔ Event.WINDOW_DEICONIFY, ✔ _DESTROY ✔ _EXPOSE, ✔ _ICONIFY, ✔ _MOVED ✔
238
5.6 各种构件的应用实例 ---Frame ✔ 例 : 创建一个窗口 , 并用按钮控制它的显示或 ✔ 隐藏 FrameApplet
Frame Window This is CustomFrame window
hide Show window hidewindow Show window
239
5.6 各种构件的应用实例 ---Frame ✔ 在 applet 中 action 处理 Button 的事件 button.label is show
action 捕获 button button.label is hide
Frame.show Frame.hide
240
5.6 各种构件的应用实例 ---Frame
241
为了将字符显 Frame Window 示在自定义窗 口中 , 包含输 This is CustomFrame window 出语句的方法 必须在自定义 的窗口类中 关闭窗口的事件在窗口类本身处理 .
✔.
关闭窗口的事件在窗口类本身处理 . 注 注 :: 处理窗口中的事件用 处理窗口中的事件用 handelEvent() handelEvent() public public boolean boolean handleEvent(Event handleEvent(Event evt) evt) {{ switch(evt.id) switch(evt.id) {{ case caseEvent.WINDOW_DESTROY: Event.WINDOW_DESTROY: dispose();System.exit(0); dispose();System.exit(0); default: default: return return super.handleEvent(evt); super.handleEvent(evt); }} }}
public class FrameApplet extends Applet 242 5.6 各种构件的应用实例 ---Frame { CustomFrame frame; Button button; public void init() { frame=new CustomFrame ("Custom Frame Window"); button=new Button("Show Window"); add(button); } } public boolean action(Event evt, Object arg) { boolean visible=frame.isShowing(); if(visible){ frame.hide(); button.setLabel("Show window");} else {frame.show();button.setLabel("Hide Window"); return true; } }
class CustomFrame extends Frame 5.6 各种构件的应用实例 ---Frame { CustomFrame(String title) { super(title);}
243
public boolean handleEvent(Event evt) { switch(evt.id) { case Event.WINDOW_DESTROY: dispose();System.exit(0); default: return super.handleEvent(evt); } } public void paint(Graphics g) { resize(200,100); g.drawString("this is a custom window.",30,30); } }
5.6 各种构件的应用实例 ---Frame
244
✔ 多窗口 (FrameApplet\ButtonsFrames.class-f1.bat)
button1
button1 Button1 button2 button2
10 1
Button1 button2 button2
0 7
5.6 各种构件的应用实例 ---Frame
245
1. 一个窗口类创建两个实例 2. 由于有不同的事件发生 ( 按钮 , 关窗口 ), 因此事件先由 通用事件处理程序来接收 , 然后再根据情况做相应的 处理 . switch(evt.id) { case Event.WINDOW_DESTROY: dispose(); return true; case Event.ACTION_EVENT: return action(evt, evt.arg); default: return super.handleEvent(evt);} }
5.6 各种构件的应用实例 ---Frame
246
✔ 3. 任何时候只有一个窗口是活动的 (active)
的因此不必考虑那个判断是哪一个窗口发 生的事件 ✔ 4. 一般的结构 ✔ 在 main() 中 , 只做与窗口有关的事情 : 创建 窗口 , 显示窗口 ✔ 在构造方法中 , 安排窗口中的构件
import java.awt.*; 5.6 各种构件的应用实例 ---Frame class ButtonsInFrames extends Frame { int a1=0,a2=0; public static void main(String args[]) { ButtonsInFrames myframe1=new ButtonsInFrames(); myframe1.setTitle("Button in Frame1"); myframe1.resize(200,200); myframe1.show(); ButtonsInFrames myframe2=new ButtonsInFrames(); myframe2.setTitle("Button in Frame2"); myframe2.resize(200,200); myframe2.show(); }
247
ButtonsInFrames() 5.6 各种构件的应用实例 { setLayout(new BorderLayout());---Frame Button b1=new Button("Button1"); Button b2=new Button("Button2"); add("North",b1);add("South",b2); } public boolean handleEvent(Event evt) { switch(evt.id) { case Event.WINDOW_DESTROY: dispose(); return true; case Event.ACTION_EVENT: return action(evt, evt.arg); default: return super.handleEvent(evt);} }
248
5.6 各种构件的应用实例 ---Frame ✔ public boolean action(Event evt, Object arg) ✔{ ✔ ✔ ✔
if(evt.target instanceof Button) if(arg=="Button1") a1++; else a2++; repaint(); return true;
✔} ✔ public void paint(Graphics g) ✔{ ✔ ✔ }}
g.drawString("button1 "+a1,5,80); g.drawString("button2 "+a2,5,100); .
249
与 List 类有关的事件 public 5.6 各种构件的应用实例 --- 练习 evt) public boolean boolean handleEvent(Event handleEvent(Event evt) 250 Event.LIST_DESELECT, {{ switch(evt.id){ switch(evt.id){ Event.LIST_SELECT case Event.WINDOW_DESTROY: case例 Event.WINDOW_DESTROY: :(FontDisplay.class---f3.bat)
List add
Panel 类 add
List add
}}
dispose(); dispose(); System.exit(0); System.exit(0); default: default: return return super.handleEvent(evt); super.handleEvent(evt);
arae }} TextArea 类 add
area.setfont( Frame 类 handleEvent LIST_SELECT
FontDisplayer Arial FontDisplay! Courier New Courier New You can input Times New Roman something here.
18 20 字型 22 , 字体 24 26
, 字号 )
handleEvent WINDOW_DESTROY
.
import java.awt.*; class FontDisplay extends Frame { TextArea FontShower; public static void main(String args[]) { FontDisplay myFrame=new FontDisplay(); myFrame.setTitle("FontDisplayer"); myFrame.resize(450,300); myFrame.show(); } public FontDisplay() { setLayout(new BorderLayout(5,5)); FontShower=new TextArea("Font Display! ”); add("West", new FontPanel(FontShower)); add("Center",FontShower);}
5.6 各种构件的应用实例 --- 练习
251
5.6 各种构件的应用实例 --- 练习 public boolean handleEvent(Event evt) { switch(evt.id){ case Event.WINDOW_DESTROY: dispose(); System.exit(0); default: return super.handleEvent(evt); } }
对窗口来说只有一个事 件
252
5.6 各种构件的应用实例 --- 练习
253
在 panel 中创建两个列表 往列表中加入条目用 additem(str) 得到列表选项用 getItem 事件处理用用 handleEvent, 得到两个参数 -- 字 型 , 字号 . 对右边的文本区设置属性 , 利用引用传递 . void void updateFontShower() updateFontShower() {{ area.setFont(new area.setFont(new Font(CurrentFontName, Font(CurrentFontName, Font.PLAIN, Font.PLAIN, CurrentSize));}} CurrentSize));}} .
5.6 各种构件的应用实例 --- 练习 ✔ public boolean handleEvent(Event evt) ✔ { switch(evt.id){ ✔ ✔ ✔ ✔ ✔ ✔ ✔
case Event.LIST_SELECT: List target=(List)evt.target; String itemName= target.getItem(((Integer)evt.arg).intValue()); if(target==FontSelector) CurrentFontName=itemName; else CurrentSize=Integer.parseInt(itemName);
254
5.6 各种构件的应用实例 --- 练习 ✔ String FontNames[]={"Arial", "Courier
New", "Times New Roman"}; ✔ List FontSelector=new List(); ✔ for (i=0;i
255
5.6 各种构件的应用实例 --- 练习
256
class FontPanel extends Panel { int CurrentSize=20; String CurrentFontName; TextArea area;; List FontSelector,SizeSelector;
updateFontShower() { area.setFont(param1,param2,param3)
FontPanel(TextArea FS) // 把另一个对象做参数 257 各种构件的应用实例 --- "Courier 练习 New", { int i;5.6String FontNames[]={"Arial", "Times New Roman"}; setLayout(new GridLayout(2,1,5,5)); FontSelector=new List(); for (i=0;i
public boolean handleEvent(Event evt) 258 5.6 各种构件的应用实例 --- 练习 { switch(evt.id){ case Event.LIST_SELECT: List target=(List)evt.target; String itemName= target.getItem(((Integer)evt.arg).intValue()); if(target==FontSelector) CurrentFontName=itemName; else CurrentSize=Integer.parseInt(itemName); updateFontShower(); return true; default: return super.handleEvent(evt); }} void updateFontShower() { area.setFont(new Font(CurrentFontName, Font.PLAIN, CurrentSize));}}
5.6 各种构件的应用实例 ---menu ✔ 5.6.6 菜单系统 类 MenuBar
菜单系统 一般菜 单
菜单容 器 类 Menu 类 MenuItem 非菜单容 器
可撕下菜单 帮助菜 单
选项 #1 选项 #2 菜单中的菜单 选项 #3 选项 #4
选项 #1 选项 #2 第三层菜 单
选项 #3
选项 #1 选项 #2
259
5.6 各种构件的应用实例 ---menu ✔ 创建菜单条 ✔ mb=new MenuBar(); ✔ setMenuBar(mb);( 类 Frame 中的方法 ) file
edit
✔ 创建菜单 ✔ menu1=new Menu(“file”); ✔ menu2=new Menu(“edit) ✔ mb.add(menu1);mb.add(menu2);
260
5.6 各种构件的应用实例 ---menu ✔ 创建菜单项 ✔ ✔ ✔ ✔ ✔
mi1=new MenuItem(“new”); mi2=new MenuItem(“open”); mi3=new MenuItem(“save”); mi4=new MenuItem(“close”); menu1.add(mi1); menu1.add(mi2); File New open Save Close
edit
261
5.6 各种构件的应用实例 ---menu 如何处理事件 public boolean action(Event e, Object arg) { if (e.target instanceof MenuItem) { MenuItem selected=(MenuItem)e.trget; tring s=selected.getLabel(); switch(s) { case “new”: ….; case “open”: ….; case “save”: ….; case “close”: ….; } } }
262
5.6 各种构件的应用实例 ---menu
263
✔ 在处理菜单事件 时应该注意的事
情是 : 判断层次
A
A
✔ MenuContainer uplevel;( 定义一个菜单容器 ) ✔ MenuItem target=(MenuItem)evt.target;( 当前被
选中的对象 ) ✔ uplevel=target.getParent(); ✔ strMessage=uplevel.getLabel()( 得到上一级容器 的标签 )
public boolean action(Event evt, Object arg) 264 5.6strMessage; 各种构件的应用实例 ---menu { String if(evt.target instanceof MenuItem){ MenuItem target=(MenuItem)evt.target; MenuContainer uplevel; uplevel=target.getParent(); while(uplevel instanceof Menu) { strMessage=((Menu)uplevel).getLabel()+strMessage; uplevel=((Menu)uplevel).getParent(); } strMessage="you selected"+strMessage; taMessage.appendText(strMessage); return true; } else return false;}
5.6 各种构件的应用实例 ---menu
265
✔ Java1.1 处理菜单的方法 ✔ 两个主要的策略 : ✔ 1. 让每个菜单项有一个唯一的监听器 ✔ 2. 将一个大的监听器用于用于所有菜单项
5.6 各种构件的应用实例 ---menu class Example { class MenuFileOpen implements ActionListener { public void actionPerformed(ActionEvent e) { openFile(e.getActionCommand());} } class MenuFileSave implements ActionListener { public void actionPerformed(ActionEvent e) { saveFile(e.getActionCommand());} }
266
5.6 各种构件的应用实例 ---menu
267
public void init() { MenuItem OpenItem=new MenuItem(“Open…”); OpenItem.addActionListener (new MenuFileOpen()); MenuItem SaveItem= new MenuItem(“Save…”); SaveItem.addActionListener (new MenuFileSave()); }
5.6 各种构件的应用实例 -- 综合练习 ✔ 设计用户界面可根据用户选择办理银行业
268
务 ✔ Bankapp\Bankapp.class---f2.bat ✔ 控制流程
5.6 各种构件的应用实例 -- 综合练习 ✔ class Bank ✔ { long balance; ✔ public Bank() ✔ { balance=50; } ✔ public void deposite(long amount) ✔ { if(amount>0.0) {balance+=amount;}} ✔ void withdrawal(long amount) ✔ { if(amount>0.0 &&amount <= balance) ✔ { balance-=amount;} ✔ } ✔ public long show_balance() ✔ { return (long)balance; } ✔}
269
5.6 各种构件的应用实例 -- 综合练习 public class BankDemo { public static void main(String agrs[]) { Bank account1=new Bank(); Bank account2=new Bank(); account1.deposite(100); account2.withdrawal(50); account1.show_balance(); account2.show_balance(); } }
270
5.6 各种构件的应用实例 -- 综合练习
WelCome to Bank 100 Create account Show nbalance Deposit Withdrawal
271
5.6 各种构件的应用实例 -- 综合练习 辅类 Bank
主类 Bankapp (Frame 的子类 )
Bank() 创建账户
main() 定义并显示窗口
show_balance diposite() withdrawal()
Bankapp() 布局安排 handleEvent() 关闭窗口 action() 根据按钮做处理
满足 Bank 要求
272
辅类 Warningbox Warningbox(String str) 布局安排 , 显示对话框 显示警告信息 action() 关闭对话框 它是模态的 不满足 Bank 要求
5.6 各种构件的应用实例 -- 综合练习
public public static static void void main(String main(String args[]) args[]) {{ Bankapp Bankapp frame=new frame=new Bankapp(); Bankapp(); frame.setTitle("Bank frame.setTitle("Bank Application"); Application"); frame.resize(200,200); frame.resize(200,200); frame.show(); frame.show(); }}
273
5.6 各种构件的应用实例 -- 综合练习 public publicBankapp() Bankapp() {setLayout(new {setLayout(newGridLayout(6,1)); GridLayout(6,1)); Label Labellb=new lb=new Label("welcome Label("welcometo toBank",Label.CENTER); Bank",Label.CENTER); tf=new tf=newTextField("0",15); TextField("0",15); add(lb);add(tf); add(lb);add(tf); b1=new b1=newButton("create Button("createaccount"); account");add(b1); add(b1); b2=new b2=newButton("show Button("showbalance"); balance");add(b2); add(b2); b3=new b3=newButton("diposite"); Button("diposite");add(b3); add(b3); b4=new b4=newButton("withdrawal"); Button("withdrawal");add(b4); add(b4); resize(450,100); resize(450,100); }}
274
5.6 各种构件的应用实例 -- 综合练习 public public boolean boolean handleEvent(Event handleEvent(Event evt) evt) {{ switch(evt.id) switch(evt.id) {{ case case Event.WINDOW_DESTROY: Event.WINDOW_DESTROY: dispose(); dispose(); System.exit(0); System.exit(0); return return true; true; case case Event.ACTION_EVENT: Event.ACTION_EVENT: return return action(evt, action(evt, evt.arg); evt.arg); default: default: return return super.handleEvent(evt); super.handleEvent(evt); }} }}
275
public boolean action(Event evt,Object arg) 5.6 各种构件的应用实例 -- 综合练习 276 {if(evt.target instanceof Button) { String targetLabel=tf.getText(); long amount=Long.valueOf(targetLabel).longValue(); if (arg=="diposite") { if (!haveAccount) {wb=new WarningBox(this,"create a account first!"); wb.show(); } else { if(amount==0) { wb=new WarningBox(this,”need the amount"); wb.show(); } else { bank.deposite(amount); tf.setText("0"); }} return true; } return false; }
public WarningBox(Frame parent,String str) 5.6 各种构件的应用实例 -- 综合练习 { super(parent,"Warning!",true); Panel panel=new Panel(); panel.setLayout(new GridLayout(2,1)); panel.add(new Label(str,Label.CENTER)); panel.add(new Button("OK")); add(info_panel); } public boolean action(Event evt,Object arg) { if (evt.target instanceof Button) { this.dispose(); return true; } return false; }
277
class Bank 各种构件的应用实例 -- 综合练习 { long5.6 balance; public Bank() { balance=50; } public void deposite(long amount) { if(amount>0.0) {balance+=amount;}} void withdrawal(long amount) { if(amount>0.0 &&amount <= balance) { balance-=amount;} } public long show_balance() { return (long)balance; } }
278
5.7 总结
279
✔ 5.8 总结 ✔ 1 使用 AWT 构件的应用实例 ✔ 2 事件的传递 ✔ 3 外观的管理与控制 了解类及其常用方法 设计好容 器层次
创建输出管理器 setLayout
定义事件的处 理 action
创建类的实 例 new
加到上一级容器中 add
作业
280
✔ 将文本行的输入加入到文本域中
追加
第 6 章 数据流的运用
北京大学计算机系 代亚非
282
第 6 章 数据流的运用 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
6.1 输入输出方法 6.2 输入输出流的基类 6.3 File I/O 6.4 数据输入输出流 6.5 随机存取文件 6.6 文件的其它操作 6.7 java 中的 unicode 6.8 管道流 6.9 对象流 6.10 流的分类 6.11 小结
283
6.1 输入输出方法 ■
什么是数据流 ? 文件
起 点
文件 , 字符串 存储区
文件
程序
程序
网络端点
终端
终 点
网络端点 ■
数据流是指所有的数据通信通道
■
在 java 中有关流的操作使用 java.io.*
■
出于安全的考虑 , 小应用不能实现文件 I/O 流
284
6.1 输入输出方法 ■ ■ ■ ■ ■ ■ ■ ■
System 类管理标准输入输出流和错误流 System.out: 把输出送到缺省的显示 ( 通常是显示器 ) System.in 从标准输入获取输入 ( 通常是键盘 ) System.err 把错误信息送到缺省的显示 每当 main 方法被执行时 , 就自动生成上述三 个对象
285
6.1 输入输出方法 public class ReadHello { public static void main(String args[]) { char inchar; System.out.println(“Enter a character:”); try{ inChar=(char)System.in.read(); Syste.out.println(“ “+ inChar); }catch(IOException e) { Sytem.out.println(“Error reading from user”);
}
}
}
286
6.1 输入输出方法 import java.io.*; class IOTest { public statics void main(String args[]) {try { byte bArray[]=new byte[128]; System.out.println(“Enter something:”); System.in.read(bArray); System.out.print(“You entered:”); System.out.println(bArray); }catch(IOException ioe) { System.out.println(ioe.toString()); } } } stream\Iostream.class---f4.bat
287
6.1 输入输出方法 ■ ■
■ ■ ■ ■ ■ ■ ■
为什么输入的是字符 , 输出是乱码 ? 原因 :System.out.println(bArray) 输出的是数 组的地址而不是字符 改进 : 将字符数组变换成字符串 原来是 :System.out.println(bArray); 现在为 :String s=new String(bArray,0); System.out.println(s); System.in 是属于 BufferedInputStream 类型 System.out 是属于 PrintStream 类型 System.err 也是属于 PrintStream 类型
288
6.2 输入输出流的基类 ■
■ ■ ■ ■ ■ ■ ■ ■ ■
Java 中每一种流的基本功能依赖于基本类 InputStream 和 OutputStream 它们是抽象类 , 不能直接使用 属于 InputStream 类的方法有 : read(): 从流中读入数据 skip(): 跳过流中若干字节数 available(): 返回流中可用字节数 mark(): 在流中标记一个位置 reset(): 返回标记过得位置 markSupport(): 是否支持标记和复位操作 close(): 关闭流
289
6.2 输入输出流的基类 ■ ■ ■ ■ ■ ■ ■ ■
方法 read() 提供了三种从流中读数据的方法 . int read(): 读一个整数 int read(byte b[]): 读多个字节到数组中 int read(byte,int off,int len); 属于 OutputStream 类的方法有 : write(int b): 将一个整数输出到流中 write(byte b[]): 将数组中的数据输出到流中 write(byte b[], int off,int len): 将数组 b 中从 off 指 定的位置开始 len 长度的数据输出到流中
290
6.2 输入输出流的基类 ■ ■ ■ ■
■ ■ ■
■
flush(): 将缓冲区中的数据强制送出 close(): 关闭流 . PrintStream 类 println() 不属于 OutputStream 类 , 它是 PrintStream 类的子类 , 能提供复杂的输出 PrintStream 类的方法有 : write, flush, checkError,print, println,close. 其中 println 可以输出多种形式的数据 . 例如 : println(String s), println(char c) 等
291
6.3 File I/O ■ ■ ■ ■ ■
文件对象的建立 File fp=new File(“tempfile.txt”); 对文件操作要定义文件流 FileInputStream 类用来打开一个输入文件 FileOutputStream 类用来打开一个输出文件 write
输出文件
输入文件
read
292
6.3 File I/O ■ ■ ■
■ ■ ■
文件流的建立 FileInputStream in=new FileInputStream(fp); FileOutputStream out=new FileOutputStream(fp); 例 : 文件拷贝 ( 注意要捕获文件异常 ) 输入流的参数是用于输入的文件名 输出流的参数是用于输出的文件名 file1.txt
输入流
输出流
file2.txt
import java.io.*; 293 class filestream 6.3 File I/O { public static void main(String args[]) { try{ File inFile=new File("file1.txt"); File outFile=new File("file2.txt"); FileInputStream fis=new FileInputStream(inFile); FileOutputStream fos=new FileOutputStream(outFile); int c; while((c=fis.read())!=-1) fos.write(c); fis.close(); fos.close(); }catch(FileNotFoundException e) { System.out.println("FileStreamsTest: "+e); }catch(IOException e) { System.err.println("FileStreamsTest: "+e); }}}
294
6.3 File I/O ■
增加缓冲区流 , 减少访问硬盘的次数 , 提高 效率
file1.txt
输入缓冲区 输出缓冲区
输入流
file2.txt 输出流
文件 文件流
缓冲区流
6.3 File I/O ■ ■
■ ■ ■ ■ ■ ■ ■ ■
295
缓冲区流 : BufferedInputStream 和 BufferedOutputStream 将它们与文件流相接 FileInputStream in=new FileInputStream(“file1.txt”); BufferedInputStream bin= new BufferedInputStream(in,256) int len; byte bArray[]=new byte[256]; len=bin.read(bArray); len 中得到是长度 , bArray 中得到的是数据
296
6.3 File I/O ■ ■
■
只有缓冲区满时 , 才会将数据送到输出流 . Java 在输出数据流中 , 当对方尚未将数据取 走时 , 程序就会被阻塞 . 有时要人为地将尚未填满的缓冲区中的数据 送出 , 使用 flush() 方法 .
文件
297
6.4 数据输入输出流 ■ ■
■ ■
■ ■ ■
什么时候需要数据输入输出流 ? 文件流和缓冲区流的处理对象是字节或字节数 组,利用数据输入输出流可以实现对文件的不 同数据类型的读写 . DataInputStream 、 DataOutputStream 一种较为高级的数据输入输出方式 , 除了字节 和字节数组 , 还可以处理 int,float,boolean 等类型 . 还可以用 readLine 方法读取一行信息 可使用的方法 : write,writeBoolean…,read,readByte… 等
298
6.4 数据输入输出流 ■ ■ ■ ■ ■
■
■
数据流的建立 FileOutputStream fos= new FileOutputStream(”file2.txt")); DataInputStream dis= new DataInputStream(fos) 数据输出流可以是一个已经建立好的输入数 据流对象 , 例如网络的连结 , 文件等 . 下面的例子显示如何利用数据输入输出流往 文件中写不同类型的数据
299 class datainput_output 数据输入输出流 { public6.4 static void main(String args[]) throws IOException { FileOutputStream fos=new FileOutputStream(“a.txt”); DataOutputStream dos=new DataOutputStream (fos); try{ dos.writeBoolean(true); dos.writeByte((byte)123); dos.writeChar('J'); dos.writeDouble(3.141592654); dos.writeFloat(2.7182f); dos.writeInt(1234567890); dos.writeLong(998877665544332211L); dos.writeShort((short)11223); }finally{ dos.close(); }
300 DataInputStream dis=new DataInputStream( 6.4 数据输入输出流 new FileInputStream("a.txt")); try{ System.out.println("\t "+dis.readBoolean()); System.out.println("\t "+dis.readByte()); System.out.println("\t "+dis.readChar()); System.out.println("\t "+dis.readDouble()); System.out.println("\t "+dis.readFloat()); System.out.println("\t "+dis.readInt()); System.out.println("\t "+dis.readLong()); System.out.println("\t "+dis.readShort()); }finally{dis.close();} }}
Stream\datainputandoutput--->f5.bat
301
6.4 数据输入输出流 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
DateLine(InputStream in)( 计算字符和行数 ) { DataInputStream data=new DataInputStream(in); String currentLine; int lineCount=0; int charCount=0; while((currentLine=dataIn.readLine())!=null) { ++lineCount; charCount+=currentLine.length(); } return (charCount/(float)lineCount); }
302
6.5 随机存取文件 ■ ■ ■
■ ■ ■ ■
类 RandomAccessFile zip 文件需要用随机方法处理 文件目录给出个文件的入口 , 可以随机读取 . 创建一个随机文件 new RandomAccessFile(“file1.txt”, “r”); new RandomAccessFile(“file2.txt”, “rw”); 随机文件可以同时完成读和写操作 . zip 文件
文件目录
303
6.5 随机存取文件 ■ ■ ■ ■ ■ ■ ■ ■
支持随机文件操作的方法 : readXXX() 或 writeXXX() skipBytes(); 将指针乡下移动若干字节 seek(): 将指针调到所需位置 getFilePointer(): 返回指针当前位置 length(): 返回文件长度 pos 利用 seek(long pos) 方法查找随机文件中的信息 例 : 把若干个 32 位的整数写到一个名为 “ temp.dat” 的文件中 , 然后利用 seek 方法 , 以相 反的顺序再读取这些数据
304 public class random_file 6.5 随机存取文件 { public static void main(String args[]) { int data_arr[]={12,31,56,23,27,1,43,65,4,99}; try { RandomAccessFile randf=new RandomAccessFile(“temp.dat”); for (int i=0;i>data_arr.length;i++) randf.writeInt(data_arr[i]); for(int i=data_arr.length-1;i>=0;i--) { randf.seek(i*4); System.out.println(randf.readInt()); } randf.close(); }catch (IOException e) { System.out.println(“File access error: “+e);} } }
305
6.6 文件的其它操作 ■
使用文件类获取文件的路径信息
■
设 f 是一个文件对象
■
File f=new File(“data”,temp.dat”);
■
f.getName(): 返回文件名
■
f.getParent(): 返回文件所在目录名
■
f.getPath(): 返回文件路径
■
f.getAbsolutePath(): 返回绝对路 c:\myprog\data\temp.dat
temp.dat data
data\temp.dat
306
6.6 文件的其它操作 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
例 : 获取当前目录下所有文件名和文件的尺寸 : import java.io.*; public class file_size { public static void main(String args[]) { File files=new File(“.”); String file_list[]=files.list(); for(int i=0;i
307
6.7 java 中的 unicode ■ ■
在 java 中用 unicode 表示字符和字符串 DatainputStream 的 readLine 方法 , 以字节形式 读入 , 以 unicode 形式输出
byte + 00000000 8 bit
❚ ❚
Unicode 16bit
DataInputStream 不适合输入是 unicode 的形式 处理字符用 InputStreamReader 类和 BufferedReader 类 (jdk1.1)
6.7 java 中的 unicode import java.io;( 从键盘上读一个字符串 ) public class CharInput { public static void main(String args[]) {String s; throws IOException InputStreamReader ir; BufferedReader in; ir=new InputStreamReader(System.in); in=new BufferedReader(ir); 可以将字符 串转换成整 String s=in.readLine(); 数加以运算 int i=Integer.parseInt(s); i=i*2; 123 System.out.println(“the result is” +i); } }
308
309
6.7 java 中的 unicode ■ ■
■ ■ ■
问题 : 如果字符流不是来自本地 , 有可能编码不一 样 , 直接读取会读出不正确字符 处理方法 : ir=new InputStreamReader(is,”8859_1”); 采用的是 iso8859_1 编码方式 , 在不同平台 之间正确转换字符 .
310 import java.io.*; 6.7filetounicode java 中的 unicode class { public static void main(String args[]) { try{ FileInputStream fis=new FileInputStream("toyamei.txt"); InputStreamReader dis=new InputStreamReader(fis); BufferedReader reader=new String s; BufferedReader(dis); while((s=reader.readLine())!=null) { System.out.println("read: "+s);} dis.close(); }catch(IOException e) { } } }
6.8 使用管道流 输出流 ■ ■ ■
■
■ ■
■
311
输入流
PipedInputStream 和 PipedOutputStream 创建管道流 : PipedInputStream pis=new PipedInputStream(); PipedOutputStream pos=new PipedOutputStream(pis); 或: PipedOutputStream pos=new PipedOutputStream(); PipedInputStream pis=new PipedInputStream(pos);
6.8 使用管道流 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
312
管道流一定是输入输出并用 例 : 将数据从输出管道进 , 从输入管道出 import java.io.*; class pipedstream { public static void main(String args[]) throws IOException { byte aByteData1=123, aByteData2=111; PipedInputStream pis= new PipedInputStream(); PipedOutputStream pos= new PipedOutputStream(pis); System.out.println("PipedInputStream");
313
6.8 使用管道流 ■ ■ ■ ■ ■ ■ ■ ■ ■
try{ pos.write(aByteData); pos.write(aByteData2); System.out.println((byte)pis.read()); System.out.println((byte)pis.read()); } finally { pis.close(); pos.close(); }
314
6.9 对象流 ■ ■
■ ■ ■ ■ ■ ■
在 java.io 包中什么是对象的持续性 ? 能够纪录自己的状态一边将来再生的能力 , 叫对象的持续性 什么是串行化 ? 对象通过写出描述自己状态的的数值来记 录自己的过程叫串行化 . 什么是对象流 ? 能够输入输出对象的流 . 两者什么关系 ? 将串行化的对象通过对象输入输出流写入 文件或传送到其它地方 .
6.9 对象流 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
315
一个相关的例子 : 从一个源读入一个简单的对象 import java.net;import java.io public class GetString { public String getStringFromUrl(URL inURL) { InputStream in; try { in =inURL.openStream(); }catch(IOException ioe) {System.out.printlin(“URL error;”+ioe); return null; } return getString(in); } 通过 url 得到一个字符串
316
6.9 对象流 ■
■ ■ ■ ■ ■ ■ ■
public String getStringFromSocket(Socket inSocket) { inputStream in; try{ in=inSocket.getInputStreamSream(); }catch(IOEception ioe) { System.out.println(“Socket error:”+ioe); return null; } return getString(in); } 通过 socket 得到一个字符串
6.9 对象流
317
public String getString(inputStream inStream) { String readString = new String(); DataInputStream in =new DataInputSream(inStream); char inChar; try{ while(true) { inChar=in.readByte(); readString=readString+inChar; } }catch(EOFException eof) { System.out.println(readString);} }catch(IOException ioe) { { System.out.println(“error:”+ieo);} return readString; }
318
6.9 对象流 ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
下面的对象能读吗 ? Class testObject { int x; int y; float angle; String name; public testObject(int x,int y,float angle, String name); { this.x=x;this.y;this.angle;this.name=name;} } 这仍然是一个简单的对象
6.9 对象流
319
对象流是怎样工作的 ? 允许可串行化的对象在流中传输 1. 只有实现 serializable 接口的类才能被串行化 public class Student implements Serializable { int id;String name; int age; String department; public Student(int id, String name,int age, String department) { this.id=id; this.name=name; this.age=age; this.department =departmernt; } }
6.9 对象流 ■
■ ■ ■ ■ ■ ■
320
2. 构造对象的输入输出流 ( 将对象保存到文 件中 , 或者通过网络传送到其他地方 ) 相应的类 :ObjectInput 对象的输出 : ObjectOutputStream 相应的方法 :writeObject() 对象的输入 :ObjectInputStream 相应的方法 :readObject() 注 :jdk1.1 以上版本支持对象流操作
6.9 对象流 对象流举例 : 将 Student 类的一个实例写到文件中 import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class Objectser {public static void main(String args[]) Student stu=new Student(981036,“Li Ming”,16,“CSD”); {try {FileOutputStream fo=new FileOutputStream(“date.ser”); ObjectOutputStream so=new ObjectOutputStream(fo); os.writeObject(stu);so.close(); }catch(Exception e) {;} }
321
322
6.9 对象流 import java.io.FileInputStream; import java.io.ObjectInputStream; public class ObjectRecov { public static void main(String args[]) { Student stu; try {FileInputStream fi=new FileInputStream(“date.ser”); ObjectInputStream si=new ObjectInputStream(fi); stu=(Student)si.readObject();si.close(); }catch(Exception e) {System.out.println(e);} System.out.println(“ID: ”+stu.id+“name:”+ stu.name+“age:”+age+“dept.:”+stu.department); } }
323
6.10 流的分类 ■
InputStream 和 OutputStream 是所有输入输 出流的祖先 , 它们是一个抽象类 .System.in 和 System.out 是它们的子类
InputStream
PushbackInputStream
FileInputStream PipedIntputStream FilterInputStream ByteArrayInputStream SequencedInputStream StringBufferInputStream
DataInputStream BufferedInputStream LineNumberInputStream
324
6.10 流的分类 FileOutputStream DataOutputStream OutputStream
PipeOutputStream
BufferedOutputStream
FilterOutputStream
PrintStream
ByteArrayOutputStream
InputStream 中的基本方法包括 : ■ read, available, mark, skip, reset, markSuppposed, close ■ OutputStream 中的基本方法包括 : write, flush, close ■
325
6.11 小结 ■ ■
■
■
文件
在 Java 中有数据传输的地方都用到 I/O 流 ( 通常是文件 , 网络 , 内存和标准输入输出 等) InputStream 和 OutputStream 是所有 I/O 流 的祖先 ( 只有 RandomAccessFile 类是一个例 外 ),read 和 write 是它们最基本的方法 , 读 写单位是字节 . 在众多的流对象中 , 并不是每一种都单独使 用 , 其中过滤流的子类在数据送出去之前做 必要的处理 . 目
文件输入流
缓冲输入流
行号输入流
数据输入流
的
326
6.11 小结 ■
■
■
■
File, File(Input/Output)Stream, RandomAccessFile 是处理本地文件的类 Data(Input/Output)Stream 是一个过滤流的子 类 , 借此可以读写各种基本数据 , 在文件和 网络中经常使用 . 如 : readByte, writeBoolean 等. Buffered(Input/Output)Stream 的作用是在数 据送到目的之前先缓存 , 达到一定数量时再 送到目的 , 已减少阻塞次数 . Piped(Input/Output)Stream 适合与一个处理的 输出作为另一个处理的输入的情况
327
作业 作业 : 将键盘上输入的一串字符写到文本文 件中
第 7 章 多线程
北京大学计算机系 代亚非
第 7 章 多线程 ■ ■ ■ ■ ■
7.1 7.2 7.3 7.4 7.5
多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题 小结
329
330
7.1 多线程基本概念 文件
各种系统资源 输入输出装置 文件
各种系统资源 输入输出装置
数据区段
数据区段
程序区段
程序区段
只有一个地方在执行
传统的进程
同时有数个地方在执行
多线程的任务
7.1 多线程基本概念 ■ ■
■ ■
331
多线程的优势 : 减轻编写交互频繁、涉及面多的程序的困 难. 程序的吞吐量会得到改善 . 由多个处理器的系统 , 可以并发运行不同 的线程 .( 否则 , 任何时刻只有一个线程在 运行 )
7.1 多线程基本概念 ■ ■
■
线程与进程的区别 : 多个进程的内部数据和状态都是完全独 立的 , 而多线程是共享一块内存空间和一 组系统资源 , 有可能互相影响 . 线程本身的数据通常只有寄存器数据, 以及一个程序执行时使用的堆栈,所以 线程的切换比进程切换的负担要小。
332
7.1 多线程基本概念 ■
对线程的综合支持是 Java 技术的一个重 要特色 . 它提供了 thread 类、监视器和条 件变量的技术 .
■
虽然 Macintosh,Windows NT,Windows 9 等 操作系统支持多线程 , 但若要用 C 或 C+ + 编写多线程程序是十分困难的,因为它 们对数据同步的支持不充分 .
333
7.2 创建线程的方式 1. public class mythread extends Applet implements Runnable ( 小应用或已经是某个类的子类时 ) 2. 继承类 Thread public class mythread extends Thread 3. 上述两种方法中都可用类 Thread 产生线 程的对象 Thread newthread; 4. 创建并启动线程 newthread=new Thread(this); newthread.start();
334
7.2 创建线程的方式 ■
■
■
■ ■
■
335
5. run 方法是运行线程的主体 , 启动线程时 , 由 java 直接调用 public void run() 6. 停止线程 , 由小应用程序的 stop 调用线程的 stop newthread.stop() 7 sleep 方法的作用 , 暂停线程的执行 , 让其它线程 得到机会 ,sleep 要丢出异常 , 必须抓住 . Try{sleep(100)}catch(InterruptedException e){} 例 : 小应用程序中不用 Runnable 接口仍然可以使 用线程 ( 不调用主类的方法和调用主类的方法 )
7.2 创建线程的方式 import java.applet.*; public class thread extends Applet { mythread t1=new mythread(); public init() { t1.start();} class mythread extends Thread { public void run() { for (int i=0;i<4;i++) System.out.println( “”+i); { try{sleep(400); } catch(InteruptedException e){ } } }
336
public class mainclass extends Applet 7.2 创建线程的方式 { C t1=new C(this); public void init() { t1.start();} public void paint(Graphics g) { g.drawString("Hello,java",10,50);}} class C extends Thread { mainclass a; C(mainclass b) { a=b; } public void run() { while(true){ a.repaint(); try{sleep(400);} catch(InterruptedException e){} }}
337
7.2 创建线程的方式
338
8. 其它常用的方法 isAlive : 判断线程目前是否正在执行状态中 if(newthread.isAlive()) newthread.stop(); resume: 要求被暂停得线程继续执行 suspend: 暂停线程的执行 join: 等待线程执行完毕 thatThread.join(); 被等待的那个线程不结束 , 当前线程就一直等待 . yield: 将执行的权力交给其它线程 , 自己到 队列的最后等待 .
7.2 创建线程的方式
339
9. 线程的优先权 ■ 某一时刻只有一个线程在执行 , 调度策略为固 定优先级调度 . newthread.setPriority(Thread.MIN_PRIORITY) ■ 级别有 :MIN-PRIORITY ■ NOM_PRIORITY ■ MAX-PRIORITY 10. 自私的线程 : 有很高的优先权的线程 , 不主动 睡眠或让出处理器控制权 .
340
7.2 创建线程的方式 11. 线程的状态 yield()
new Thread() New Thread
start()
stop()
suspend() sleep() wait()
. . Runnable . stop() or run()exit Dead
Not Runnable resume() stop()
7.2 创建线程的方式 ■
■
341
当一个线程执行完所有语句后就自动终止 ,调用线程的 stop() 方法,也可以强制终止 线程。 如果希望线程正常终止,可采用标记来使 线程中的 run ()方法退出。
7.2 创建线程的方式 public class Xyz implements Runnable { private boolean timeToQuit=false ; public void run () { while (! timeToQuit ) {…..} //clean up before run () ends ; } public void stopRunning() { timeToQuit=true;} }
342
7.2 创建线程的方式 ■ ■ ■
public class ControlThread { private Runnable r=new Xyz(); private Thread t=new Thread(r);
■
public void startThread() { t.start(); }
■ ■ ■
publi void stopThread() { r.stopRunning();}
■ ■ ■
}
343
7.3 线程的挂起与唤醒 ■ ■ ■ ■
■
暂停线程的执行等待条件满足再执行 . 下面的例子显示线程的挂起和唤醒 小应用程序第一次开始时 , 线程被启动 浏览器改变页面时 , 小应用程序的 stop() 方法被调用 , 线程被挂起 . 浏览器回到原来的页面时 , 线程被唤醒 .
344
7.3 线程的挂起与唤醒
345
public void start() { if (mythread==null) {mythread=new Thread(); mythread.start();} else { mythread.resume();} } public void run() { while(true){ try{sleep(100);} catch(InterruptedException e) {}} } public void stop() { mythread.suspend(); }.
7.4 多线程问题 --- 执行的顺序
346
多个线程运行时 , 调度策略为固定优先级调度 . 级别相同时 , 由操作系统按时间片来分配 ■ 下面给出的例子中 , 共运行三个线程 , 它们做同 样的事 , 每次打印循环次数和自己的序列号 , 运 行结果表明 , 它们并不是连续运行的 . ■ 在上例中如果给某个线程赋予较高的优先权 , 则 发现这个进程垄断控制权 thread.setPriority(Thread.MAX_PRIORITY) ■ thread\multithread.class--f1.bat ■ thread\Priority.class---f2.bat ■
// 多个进程运行时执行顺序是交叉的 347 7.3 多线程问题 class multithread extends Thread { int threadNum; public static void main(String args[]) { multithread array[]=new multithread[3]; for (int i=0;i<3;i++) array[i]=new multithread(i); for (int i=0;i<3;i++) array[i].start(); } multithread(int SerialNum) { super(); threadNum=SerialNum; } public void run() { for(int j=0;j<5;j++) System.out.println(“<"+j+"> +MySerialNum); System.out.println("thread "+threadNum+ "bye.");}}
7.4 多线程问题 --- 如何写多线程 1. 分别定义不同的线程类 , 在各自的 run 方法中定义线程的工作 class mythread1 extends Thread { public void run{….} } class mythread2 extends Thread { public void run{….} } 2. 在主类中实例化各线程类 , 并启动线程 . public class demo extends Applet { public void init() { mythread t1=new mythread1(); mythread t2=new mythread2(); t1.start(); t2.start();} }
348
7.4 多线程问题 --- 如何写多线程 ■练习
: 将窗口分为上下两个区 , 分别运行 两个线程 , 一个在上面的区域中显示由右 向左游动的字符串 , 另一个在下面的区域 从左向右游动的字符串 . ■方法一 : 一个线程 , 在 paint 方法中使用 两个输出字符串的语句 public void paint(Graphics g) { if y1<0 y1=200 else y1=y1-10; if y2>200 y2=0 else y2=y2+10; g.drawString(“hello, Java!”,20,y1,); g.drawString(“hello, Java!”,40,y2,); }
349
7.4 多线程问题 --- 如何写多线程 ■
■
350
方法二 : 定义两个类 , 运行各自的线程 , 各 自有自己的 paint() 方法 . 注意 : 两个小应用程序必须是 panel 类或者 是 canvas 类 , 将小应用的区域分成两块 , 否 则不能运行 paint 语句 .
7.4 多线程问题 --- 线程间的通信
351
1. 线程间的通信可以用管道流 ,. 线程 1 PipedOutputStream PipedInputStream 输出流 outStream 输入流 inStream
线程 2
创建管道流 : PipedInputStream pis=new PipedInputStream(); PipedOutputStream pos=new PipedOutputStream(pis); 或: PipedOutputStream pos=new PipedOutputStream(); PipedInputStream pis=new PipedInputStream(pos);
352
7.4 多线程问题 --- 线程间的通信 管道 流不能直 读写 printStream
接
■
DataInputStream
PrintStream p = new PrintStream( pos ); p.println(“hello”); DataInputStream d=new DataInputStream(pis); d.readLine(); ■ 2. 通过一个中间类来传递信息 . s s 线程 1
中间类 m
线程 2 s=m.read()
m.write(s) write()
read()
7.4 多线程问题 -- 线程间的通信
353
管道流可以连接两个线程间的通信 ■ 下面的例子里有两个线程在运行 , 一个往外 输出信息 , 一个读入信息 . ■ 将一个写线程的输出通过管道流定义为读线 程的输入 . outStream = new PipedOutputStream(); inStream = new PipedInputStream(outStream); new Writer( outStream ).start(); new Reader( inStream ).start(); ■
354
7.4 多线程问题 -- 线程间的通信 主类 Pipethread 作为参数传给 Writer 辅类 Writer Writer( outStream ) 线 程 将数据写 类 到输出流
输入流
辅类 Reader 线 程 类 从流中读数据
■
(thread\Pipethread.class--f3.bat)
355 public class Pipethread 7.4static 多线程问题 -- 线程间的通信 { public void main(String args[]) { Pipethread thisPipe = new Pipethread(); ■ . thisPipe.process(); } public void process() { PipedInputStream inStream; PipedOutputStream outStream; PrintStream printOut; try{ outStream = new PipedOutputStream(); inStream = new PipedInputStream(outStream); new Writer( outStream ).start(); new Reader( inStream ).start(); }catch( IOException e ){ } } }
class Reader extends Thread 356 { private inStream;// 从中读数据 7.4 PipedInputStream 多线程问题 --- 线程间的通信 public Reader(PipedInputStream i) { inStream = i; } public void run() { String line; DataInputStream d; boolean reading = true; try{ d = new DataInputStream( inStream ); while( reading && d != null){ try{line = d.readLine(); if( line != null ){ System.out.println( ”Read: " + line ); } else reading = false; }catch( IOException e){ } } catch( IOException e ){ System.exit(0); } try{ Thread.sleep( 4000 );} catch( InterruptedException e ){}}}
357 class Writer extends Thread 7.4 PipedOutputStream 多线程问题 -- 线程间的通信 { private outStream;// 将数据输出 private ■ . String messages[ ]= { "Monday", "Tuesday ", "Wednsday", "Thursday","Friday :", "Saturday:","Sunday :"}; public Writer(PipedOutputStream o) { outStream = o; } public void run() { PrintStream p = new PrintStream( outStream ); for (int i = 0; i < messages.length; i++) { p.println(messages[ i ]); p.flush(); System.out.println("WrIte:" + messages[i] ); } p.close(); p = null;
7.3 多线程问题 --- 资源协调
358
1. 数据的完整性 线程 1 线程 2
取过
来
加1 后送 回去
变量
线程 10
withdrwal()
余额 透支
withdrwal()
359
7.3 多线程问题 --- 资源协调 ■ ■ ■ ■
对共享对象的访问必须同步 , 叫做条件变量 . Java 语言允许通过监视器 ( 有的参考书称其为管 程 ) 使用条件变量实现线程同步 . 监视器阻止两个线程同时访问同一个条件变量 . 它的如同锁一样作用在数据上 . 线程 1 进入 withdrawal 方法时 , 获得监视器 ( 加 锁 ); 当线程 1 的方法执行完毕返回时 , 释放监视 器 ( 开锁 ), 线程 2 的 withdrawal 方能进入 .
线程 1
监视 器
withdrawal()
线程 2
360
7.3 多线程问题 --- 资源协调 ■
■
用 synchronized 来标识的区域或方法即为监 视器监视的部分。 一个类或一个对象由一个监视器 , 如果一个 程序内有两个方法使用 synchronized 标志 , 则他们在一个监视器管理之下 . 线程 1
read ■
监 视 器
线程 2
write
一般情况下,只在方法的层次上使用关键区
7.3 多线程问题 --- 资源协调
361
此处给出的例子演示两个线程在同步限制下工作的情况 . class Account { statics int balance=1000; // 为什么用 static? statics int expense=0; public synchronized void withdrawl(int amount) { if (amount<=balance) { balance-=amount; expense+=amount;} else { System.out.println(“bounced: “+amount);} } }
7.3 多线程问题 --- 资源协调 ■
362
2. 等待同步数据 生产者
write.
. 共享对象
消费者
read
可能出现的问题 : • 生产者比消费者快时 , 消费者会漏掉一些数 据没有取到 • 消费者比生产者快时 , 消费者取相同的数据 . • notify() 和 wait () 方法用来协调读取的关系 . • notify() 和 wait () 都只能从同步方法中的调
7.3 多线程问题 --- 资源协调
363
■
notify 的作用是唤醒正在等待同一个监视器 的线程 .
■
wait 的作用是让当前线程等待 信息版例子 read() 方法在读信息之前先等待 , 直到信息可 读 , 读完后通知要写的线程 . write() 方法在写信息之前先等待 , 直到信息 被取走 , 写完后通知要读的进程 . DemoWait.class--->f4.bat
■ ■
■
■
7.3 多线程问题 --- 资源协调
writer aaaa aaaa aaaa aaaa aaaa bbbbb cccccccccccc cccc cccc
364
reader aaaa bbbbb cccc
aaaa bbbbb
7.3 多线程问题 --- 资源协调 ■ ■ ■ ■ ■ ■ ■ ■ ■
365
class WaitNotifyDemo { public static void main(String[] args) { { MessageBoard m = new MessageBoard(); Reader readfrom_m = new Reader(m); Writer writeto_m=new Writer(m); readfrom_m.start(); writeto_m.start(); } }
class MessageBoard { 366 { private message; --- 资源协调 7.3String 多线程问题 private boolean ready = false;( 信号灯 ) public synchronized String read() { while (ready == false) { try { wait(); } catch (InterruptedException e) { } } ready = false; notify(); // 起始状态先写后读 return message; } public synchronized void write(String s) { while (ready == true) { try { wait(); } catch (InterruptedException e) { } } message = s; ready = true; notify(); }}
class Reader extends Thread 7.3 MessageBoard 多线程问题 mBoard; --- 资源协调 { private public Reader(MessageBoard m) { mBoard = m; } public void run() { String s = " "; boolean reading = true; while( reading ){ s = mBoard.read(); System.out.println("Reader read: " + s); if( s.equals("logoff") ) reading = false; } System.out.println("Finished: 10 seconds..."); try{ sleep( 10000 ); } catch (InterruptedException e) { } } }
367
class Writer extends Thread { private 7.3MessageBoard 多线程问题mBoard; --- 资源协调 private String messages[ ]= { "Monday :------------------------", “…..”, "Sunday : ----------------------"};
368
public Writer(MessageBoard m) { mBoard = m; } public void run() { { for (int i = 0; i < messages.length; i++) { mBoard.write(messages[ i ]); System.out.println("Writer wrote:" + messages[i] ); try { sleep((int)(Math.random() * 100)); } catch (InterruptedException e) { } }
369
7.3 多线程问题 --- 资源协调 多线成问题 --- 资源的协调和锁定 1. 死锁问题 ■
线程 1
note
把 “ pen” 给我 , 我 才能给你 “ note” 把 “ note” 给我 , 我 才能给你 “ pen”
线程 2
pen
如果你的持有一个锁并试图获取另一个锁时 , 就有死锁的危险 . 解决死锁问题的方法 : 给条件变量施加排序
7.3 多线程问题 ---daemon 线程
370
■
什么是 daemon( 守护 )?
■
在客户 / 服务器模式下 , 服务器的作用是等待用户 发来请求 , 并按请求完成客户的工作
客户端 ■ ■
■
request
服务器端
守护线程是为其它线程提供服务的线程 daemon 守护线程一般应该是一个独立的线程 , 它的 run() 方法是一个无限循环 . 守护线程与其它线程的区别是 , 如果守护线程是 唯一运行着的线程 , 程序会自动退出
7.4 小结
371
1. 实现线程有两种方法 : ■ 实现 Ruannable 接口 ■ 继承 Thread 类 2. 在小应用中通常在 start 中创建线程 3. 当新线程被启动时 ,java 调用该线程的 run 方 法 , 它是 Thread 的核心 . 4. 线程由四个状态 : 新生 , 运行 , 暂停 , 死亡 5. 线程间的通信方式由三种 : 完全共享数据 , 通过监视器 , 通过 join.
7.4 小结
372
6. 两个或多个线程竞争资源时 , 需要用同步 的方法协调资源 . 7. 多个线程执行时 , 要用到同步方法 , 即使用 synchronized 的关键字设定同步区 8. wait 和 notify 起协调作用 9. 守护进程的特点是当程序中制胜它自己时 , 会自动中止 .
作业 ■
373
创建两个线程的实例 , 分别将一个数组从小 大大和从达到小排列 . 输出结果 .
第 8 章网络功能
北京大学计算机系 代亚非
第 8 章网络功能 ■ ■ ■ ■ ■ ■ ■ ■ ■
8.1 Java 与 internet 8.2 使用 URL 8.3 访问 cgi 8.4 URL 连接 8.5 Socket 8.6 internet 应用 8.7 数据报 8.8 JDBC 8.9 小结
375
8.1 Java 与网络 ■ ■ ■
■
■
■
376
Java 语言取得成功的领域之一就是网络 ( 其他语言 ) 数页代码 ---->(Java) 一条语句 TCP/IP( 传输控制协议 / 网间协议 ) 是 internet 的 主要协议 , 定义了计算机和外设进行通信所使用 的规则 ( 应用层 , 传输层 , 网络层 , 链路层 ). 大多数基于 internet 的应用程序被看作 TCP/IP 协 议的上一层 . 如 : ftp, http, smtp, pop3, telnet, nntp 等 IP 地址 :TCP/IP 网络中的每台计算机都有唯一的 地址 --IP 地址 . 在 Java 中 , 有一个用来存储 internet 地址的类叫 InetAddress.
8.1 Java 与网络 例 : 获取本机的 IP 地址 import java.net.*; public class getLocalHostTest { public static void main() { InetAddress myIP=null; try {myIP=InetAddress.getLocalHost();} catch{UnknowHostException e){} System.out.println(myIP); } } 创建 inetAddress 类不用构造函数 ( 不用 new) ■
377
8.1 Java 与网络
378
下面的例子演示 java 如何根据域名自动到 DNS 上查找 IP 地址 ( 与 DNS 服务器的连接减至一行 ) import java.net.*; public class getIP { public static void main(String args[]) { InetAddress pku=null; try{ pku= InetAddress.getByName(“www.pku.edu.cn”); }catch(UnknowHostException e) {} System.out.println(pku); } }
8.1 Java 与网络
379
■
Java 提供的网络功能有三大类 : URL, Socket, Datagram.
■
URL 是三大功能中最高级的一种 , 通过 URL Java 程序可以直接送出或读入网络上的数据 .
■
Socket 是传统网络程序最常用的方式 , 可以想象为 两个不同的程序通过网络的通信信道 .
■
Datagram 是更低级的网络传输方式 , 它把数据的 目的纪录在数据包中 , 然后直接放在网络上 .
380
8.2 使用 URL ■ ■ ■
■ ■ ■ ■ ■
8.2.3 通过 URL 读取 WWW 服务器上的数据 将 URL 位置的数据转成一个数据流 URL url=new (http://www.pku.edu.cn/index.html” DataInputStream data=new DataInputStream(url.openStream()); 从数据流中读出数据 String line=data.readLine() while ( line ! =null ) line=data.readLine() DataInputStream URL
Java 程序
8.2 使用 URL
381
http://www.pku.edu.cn/
<...>
例 : 从给定的位置中读数据 (ReadURLData.prj) connect TextField a
Frame 主类
str=a.getText()
action
URL url=new URL(str) URL url
TextArea c
Button
b=url.openStream()
line_str=b.readLine() c.appendText(line_str); DataInputStream b
public boolean action(Event evt, Object arg) { try{ 8.2 使用 URL String line; String str=textfield.getText(); url=new URL(str); data=new DataInputStream(url.openStream()); while((line=data.readLine())!=null){ textarea.appendText(line); } data.close(); }catch(MalformedURLException me){ System.out.println("Error URL"); }catch(IOException ie){ System.out.println("Error IO"); } return true; }
382
383
8.3 访问 cgi ■
起始页上的计数器及 cgi 程序 (script) 欢迎来访 , 你是第
1 2 7
个来访者 !
你是第
个来访者 客户端 HTML
cgi 程序名 img
服务器端 CGI 程序
num+ +
384
8.3 访问 cgi Your name form send
Web 服务器
cgi 程序
数据库 服务器
reset