第1章 计算机语言概述

Download Report

Transcript 第1章 计算机语言概述

JAVA程序设计
第一部分











第一章
第二章
第三章
第四章
第五章
第六章
第七章
第八章
第九章
第十章
第十一章
JAVA程序设计基础
计算机语言概述
Java的开发环境
Java语言基础
Java面向对象的程序设计
异常处理
Java的可重用类
Java小应用程序Applet
用户界面
Java的多线程机制
Java网络编程基础
Java与数据库的连接—JDBC技术
第1章 计算机语言概述







1.1 程序设计语言
1.2 语言翻译程序
1.3 虚拟机、Java虚拟机与Java运行环境
1.4 Java程序的运行
1.5 Java程序的开发、执行流程
小结
习题
1.1 程序设计语言
从程序设计语言发展过程来分,将程序设计语言
分为机器语言、汇编程序设计语言、高级程序
设计语言(如Basic语言、Fortran语言、Pascal
语言、Prolog语言、Java语言等等)和数据库
管理语言等。
 1.1.1 机器语言与汇编语言
不须翻译即由计算机直接执行的指令叫做机器
指令。这些指令的集合叫做机器语言。
汇编语言是一种面向机器的程序设计语言,它
用符号表示机器指令

1.1.2 高级程序设计语言
由于汇编语言仍然没有解决对硬件的依赖,
人们希望使用的程序语言能够独立于机
器进行数据描述,采用与英语语法相类
似的语法结构。这样的程序语言更直观、
更方便,便于移植。于是产生了高级语
言。
1.2 语言翻译程序
如何使高级程序设计语言程序映射成等
价的机器语言程序成为关键,语言翻译
程序就是起这种作用(见图1-1)。
程序设计师
println(“Hello”);
中高级程序语言代码
语言翻译程序
:
00110111 11010010 11100000
11111000 10001001 00100011
11110011 11000011 11000110
:
计算机
按语言程序的翻译方式不同,语言翻译程序分为
解释型翻译程序和编译型翻译程序。
解释型翻译程序在语言程序翻译时,它读入源程
序一句,翻译一句,执行一句,这样反反复复
直到最终完成。Basic语言是典型的解释型语
言。
编译型翻译程序也叫编译程序,它在翻译语言
程序时,加工整个源程序,最终翻译成机器语
言,然后交给计算机执行。编译程序有利于目
标程序的优化,有利于提高目标程序的运行速
度。Fortran语言、C语言、Pascal语言都是编
译型语言。
1.3 虚拟机、Java虚拟机与Java
运行环境
编译程序与操作系统和计算机硬件有关,为了
提高编译程序的可移植性,人们提出了虚拟机
的理论。虚拟机好似通用的计算机,有自己的
指令系统,但本身没有实际的硬件。为了虚拟
机代码可以执行,必须有虚拟机实时运行支持
系统把虚拟机代码转换成相应硬件机器的代码,
然后加以执行。有了虚拟机,编译程序首先把
语言程序翻译成虚拟机代码,这样的编译程序
可移植性就大大提高了。
Java虚拟机(JVM-Java Virtual Machine)
是一种虚拟机,从结构上看它与实际的
计算机相似,它的作用是使得一台实际
的机器能够运行Java字节码程序。Java
字节码是Java源程序编译后的程序,它
不能被计算机直接执行,但它可以被所
有的Java虚拟机执行。这就是Java字节
码程序可以在网络上移植的原因。


Java程序必须有自己的运行环境(Java
平台)。一个Java运行环境包括实际计
算机、适配器、Java虚拟机、Java基本
软件和Java应用程序接口 。
利用Java语言可以开发两种形式的应用
程序:Java应用程序(Java Application)
和Java小应用程序(Java Applet)。Java
小应用程序是借助浏览器运行的程序。
1.4 Java程序的运行
Java程序是半编译半解释型语言,其程序
的运行过程与编译型和解释型语言都不
同。
首先,编译程序将Java源程序编译成与实
际计算机无关的字节码,然后Java运行
系统解释并执行字节码。
1.5 Java程序的开发、执行流程

Java的源程序可以用任何编辑程序(如
记事本)进行编辑,然后以纯文本方式
存储在文件中,文件必须以Java作为后
缀。Java源程序由Java编译器编译成字
节码,存储在以class为后缀的文件中,
最后由运行系统解释和执行
小结
用户可以通过图标、菜单和命令使用计算机,这
是最简单的人机交互方式。语言程序必须由该
语言的翻译程序映射成机器语言才能执行。语
言翻译程序分为解释型翻译程序和编译型翻译
程序。
 Java是纯面向对象的程序设计语言,是半编译
半解释型语言。Java由于其可移植性、安全性、
分布性和高性能性,成为网络开发的主流语言。
JDK1.2、JBuilder6是典型的Java程序开发环境,
可视化集成开发环境JBuilder6主要适用于开发
大型Java软件。
习题




1. 叙述人们使用计算机的主要接口。
2.叙述计算机语言的主要分类。
3.叙述语言翻译程序的分类。
4.叙述Java语言特点、Java平台的构成
及Java程序开发的步骤。
第3章 Java语言基础











3.1 一个简单的Java程序实例
3.2 标识符、注释和分隔符
3.3 变量和常量
3.4 基本类型
3.5 运算符及表达式
3.6 运算符的优先级
3.7 数据类型转换
3.8 简单语句和复合语句
3.9 控制语句
3.10 综合应用举例
小结
3.1 一个简单的Java程序实例
例3-1 Point类
//源程序清单3-1
public class Point
{
int x;
int y;
public Point() {
相同
x=0;
0)
y=0;
}
//类首部
//类体开始
//构造方法,方法名与类名保持
//无方法参数列表时,构造原点(0,
3.1 一个简单的Java程序实例
publiv Point (int X, int Y) {
//构造方法,与上一构
造方法不同
x=X;
//它有参数列表按给定的X、Y值
构成一个点(X,Y)
y=Y;
}
public double distance() {
//方法distance(),求点
到坐标原点的距离
3.1 一个简单的Java程序实例
return Math.sqrt(x*x+y*y);
//调用Math类的方法
sqrt()
}
public void draw() {
//方法draw(), 画点,但未
完整实现
}
//方法体是空语句
public static void main(String args[]) { //main()方法,
类运行时的入口
System.out.println(“Point Calss! “) //在屏幕上显示
Point Calss!
}
}
//类体结束
上述程序是由类首部、类体两大部分组成,类体
描述类的成员变量和方法。
3.1.1类首部class Point
类首部定义类的名字Point。保留字class和类名
之间应至少留有一个空格。
3.1.2类体
类体位于类名后面左大括号“{”和结束类体的右
大括号“}”之间。类体定义了类的成员变量和
方法。
3.2 标识符、注释和分隔符

3.2 标识符、注释和分隔符
程序中要用到许多名字,诸如类、变量、
方法的名字。标识符就是用来标记它们
的名字的。Java标识符的规则是:标识
符是以字母、下划线或美元符$开头的字
母、下划线、数字的序列。标识符的长
度是任意的。标识符分为两类:保留字
及用户定义标识符。
3.2.1.1 保留字
保留字是Java预定义的标识符,这些标识符都
是具有特定的含义。保留字又称关键字。
 3.2.1.2用户定义标识符
用户定义的标识符用来为程序设计者根据自己的
需要定义的类、变量、方法、接口等取名。用
户在定义标识符时应注意:
1.禁止用保留字作为用户标识符;
2.遵守先定义后使用的原则。即只有定义了的
标识符,才可在语句部分使用。

3.2.2 注释
注释用来对程序中的代码作出解释。注释的内
容在程序编译时,不产生目标码
Java有三种注释形式:
1.“//”单行注释。表示从此向后,直到行尾都是
注释。
2.“/*……*/“ 块注释。在“/*”和“*/”之间都是注
释,块注释不能嵌套。
3.“/**……*/”文档注释。所有在“/**”和“*/”之
间的内容可以用来自动形成文档。
上面程序例中,使用单行注释“//“和文档注释
/**……*/

3.2.3 分隔符
空格、逗号、分号及行结束符称为分隔符,规定
任意两个相邻标识符、数、保留字或两个语句
之间必须至少有一个分隔符,以便编译程序能
识别。为便于阅读,程序也需要如同自然语言
一样,恰当地使用分隔符。值得指出的是,这
些分隔符不能互相代用,即该用空格的地方只
能用空格,该用逗号的地方只能用逗号。

3.3 变量和常量
3.3.1 变量
变量是用来存放指定类型的数据,其值在程序
运行过程中是可变的。Java的变量有两种:局
部变量、类成员变量。局部变量是类方法中使
用的变量。在使用Java中的每个变量之前,都
必须对它进行说明。变量的说明形式如下:
类型 变量名表
其中类型说明了变量能存放什么格式的数据。变
量名是用来标识该变量的,我们存取变量的内
容(变量的值)都是通过变量名来实现的。当
说明多个变量为同一类型时,变量名之间用逗
号分隔。

3.3.1.1变量名
在Java中用标识符来表示
变量名
 3.3.1.2变量的类型
变量类型定义了变量的值
所属的类型。
Java中的数据类型可分为
两大类,一类称为基本数
据类型,另一类则是构造
数据类型。下表列出了
Java中的基本数据类型


 整型


基本类型浮点型


布尔型


字符型
数据类型

数组


 构造类型 类

 ......



3.3.1.3 变量的作用域
变量的作用域是指什么范围的程序可以访问该
变量。
变量按作用域分为:类成员变量、方法参数、
局部变量、异常处理变量。
局部变量是方法体内说明的变量,按局部变量
说明的部位,它的作用域可以是整个说明它的
方法体,也可以是方法体内一程序段。
3.3.2 常量
常量是指在程序运行过程中其值不变的量。
常量在表达式中用文字串表示,它区分不
同类型,如整形常量123、-15,实型常量
12.1f,字符常量’x’,布尔常量true,字
符串类型常量 “Test”。

3.4 基本类型

3.4.1 整数
整型是那些没有小数部分的数据类型,它又分
为下列不同的整数数据类型:byte、short、int、
long。这些不同整数数据类型的意义在于它们
所需的内存空间大小不同,这也确定了它们所
能表示数值的范围不同。
采用不同的整数类型表达同一数值,在存储单
元中的存储情况是不同的。
3.4.1.1整数常量
整数常量有三种表示形式:
(1)十进制整数,如 33,58,-90。
(2)八进制整数,以0当头,如010表示十进制
的8,-015表示十进制的-13。
(3)十六进制整数,以0x或0X当头,如0x10表
示十进制的16,-0X15表示十进制的-21。
Java缺省整数常量类型为int类型,用4个字节表
示。如果要表示long 类型整数需要在整数后加
上后缀L或l,表示它为长整数。例如:
1234567L、987654320l。

3.4.1.2整数变量
整型变量类型有int、short、long、byte,由于byte
和short类型表示的数范围较小,不常使用。整
型变量说明如下:
 例3-1
int anIntVar; //说明变量anIntVar 是int整型;
long aLongVar =12345; //说明变量aLongVar 是
long整型,并赋以初值12345;
short aShortVar ; //说明变量aShortVar 是short整
型;
byte aByteVar; //说明变量aByteVar 是byte整型;

3.4.2 浮点型
浮点型是带有小数部分的数据类型,也叫实型。
Java包括两种不同的实型:float和double。一
个float类型的变量占用4个字节(32位),而double
类型的数据则需要8个字节(64位)内存。
Java缺省的浮点型常数是double 型,如果要表
示float型,要在数字后加后缀F或f。如果要表
示double型,在数字后加后缀D或d。



3.4.2.1浮点型常量
浮点型有两种表示法:
(1)浮点型十进制表示法
( 2) 浮点型科学表示法
3.4.3布尔型
布尔数据类型用于表达两个逻辑状态之一的值:
true(真)或false(假)。Java不可将布尔类型看
做整型值。
布尔型变量说明如下:
boolean aBooleanVar; //说明变量aBooleanVar 是
boolean 型。
boolean b=false //说明变量b是boolean 型, 并
赋以初值false。

3.4.4字符型
Java的字符使用Unicode编码,用16位来表示,
它可以支持世界上所有语言。一般计算机语言
通常使用ASCII码,用8位表示一个字符。
ASCII码是Unicode码的一个子集, Unicode表
示ASCII码时,其高位为0,它是其前255个字
符。Unicode字符通常用十六进制。
 3.4.4.1字符型常量
字符常数就是用两个单引号括起来的一个
ASCII码字符集中的字符。例如:′A′;′a′;′2′
“\”称为转义符,意思是转变了其后继字符的含
义。

3.4.4.2字符型变量
字符型变量的类型是char, 计算机用16位来表示。
其值范围0-65535。字符型变量说明如下:
char ch=’a’; //说明变量ch类型是char,并赋以
初值’a’
 3.4.4.3字符串常量
一个字符串常量是括在两个双引号之间的字符
序列。若两个双引号之间没有任何字符,则为
空串。Java允许在字符串中出现转义字符

3.5 运算符及表达式


3.5.1 表达式
表达式是由操作数和运算符按一定语法形式组
成的符号序列每个表达式经过运算后都会产生
一个确定的值。一个常量或一个变量是最简单
的表达式
3.5.2运算符
运算符标明对操作数的运算方式。
运算符按其要求的操作数个数分为:一元运算符、
二元运算符、三元运算符
运算符按其功能分为七类:算术运算符 、关系
运算符 、逻辑运算符 、位运算符 、条件运算
符 、赋值运算符 、其他
3.5.3 算术运算符
算术运算符用于对整型数和实型数的运算。按其
要求的操作数的个数分为单元运算符和双元运
算符两类。
 3.5.3.1单元运算符
单元运算符++、--可以位与操作数的前面,如
++x 或-x, 也可以位于操作数的后面,如x++、
x--;无论单元运算符位于操作数的前面或后面,
操作数完成单元运算后,并把结果赋于操作数
变量。但是,如把++x或x++整体参加表达式
运算时,++x和x--的整体值是不一样的。

3.5.3.2 双元运算符
双元运算符+、-、*、/,如两个操作数都是整
型,结果是整型。否则是实型。%运算符求整
数除的余数,它要求两边操作数均为整型,结
果也为整型。
 3.5.4.关系运算符
关系运行算符有==(等于),!=(不等于),),
<(小于), <=(小于等于),>(大于),
>=(大于等)及instanceof (对象运算符)七
种,整型和实型可以混合出现在关系运算符两
边外,在一个关系运算符两边的数据类型应一
致,一个关系表达式的结果类型为布尔型,即
关系式成立为true,不成立为false。

3.5.5 逻辑运算符
逻辑运算符有四个,它们是:!(非),&
(与),|(或),^(异或)。这些运算符要
求的操作数和结果值都是布尔型。
 3.5.6位运算符
位运算符用来对二进制的位进行运算。
 3.5.7赋值运算符
赋值运算符“=”用来把右边表达式的值赋给左边
的变量,即将右边表达式的值存放在变量名所
表示的存储单元中,这样的语句又叫赋值语句。
它的语法格式如下:
变量名=表达式;
注意:赋值运算符“=”与数学的等号含义不同。

3.5.8 条件运算符
条件运算符是“?:”,它是Java 中唯一的三元
运算符。它要求三个操作数,其格式如下。

变量 = <布尔表达式> ? <表达式1> : <表达式2>
第一个操作数必须是布尔表达式,其他两个操作
数可以是数值型或布尔型表达式。
条件运算符的含义是:当<布尔表达式>为真时,
变量的值为<表达式1>的值,否则为<表达式
2>的值。
3.6 运算符的优先级
表达式的运算次序取决于表达式中各种运
算符的优先级。优先级高的先运算,优
先级低的后运算,另外还可用括号
“()”改变表达式的运算次序。
如果在表达式中有括号(表达式中只使
用圆括号),又有函数,则优先计算括
号中的内容,其次再进行函数运算。同
一优先级的运算顺序是从左到右。
3.7 数据类型转换
3.7.1自动类型转换
整型、实型、字符型数据可以混合运算。运算过
程中,不同类型的数据会自动转换为同一类型,
然后进行运算。自动转换按低级类型数据转换
成高级类型数据的规则进行,转换规则为:
(1)(byte 或short )op int→int
(2)(byte或short或int)op long→long
(3)(byte或short或int或long)op float→float
( 4 ) (byte 或 short 或 int 或 long 或 float)op
double→double
(5)char op int→int

3.7.2强制类型转换
高级数据类型要转换成低级数据类型,需要用到
强制类型转换。其一般形式为:
(类型名)表达式
 例如:
int i;
byte b;
i=356;
b=(byte)i; //强制转换后丢失一部分数据,使得
b的值为100
一般使用强制类型转换可能会导致数值溢出或
精度的下降,应尽量避免。

3.8 简单语句和复合语句
3.8.1 变量说明语句
变量说明语句用来声明一个变量,变量说明语句
的格式为:
类型 变量名1, 变量名2…;
 3.8.2赋值语句
赋值语句是将表达式的值赋给变量,其格式为:
变量=表达式;
 3.8.3方法调用语句
方法是一系列相关的程序语句的集合,能实现一
定的功能。方法调用语句的一般形式为:
方法名(参数列表);

3.8.4空语句
空语句是什么都不做的语句,其形式为:
; //这是一条空语句
 3.8.5标准输入输出(I/O)
输入和输出是Java程序的重要组成部分,它提供
了人机交互的手段,它们属于方法调用语句,
由于常用,对它们专门介绍。输入是指把需要
加工的数据放到计算机中,输出则把计算机处
理的结果呈现给用户。Java程序中需要用户提
供的信息可通过输入操作来完成,而程序执行
的结果需通过输出操作呈现给用户。在Java中,
通过Syetem.in和System.out对象分别与键盘和
显示器发生联系而完成程序信息的输入和输出。


3.8.5.1 标准输出System.out对象
System.out对象包含着多个从键盘输入数据或
向显示器输出数据的方法。System.out对象中
包含的最常用的方法是println()方法和print()方
法。println()方法允许向标准输出设备(显示
器)打印一行文本。
print()方法与println()方法非常相似,两者的唯
一不同在于println()方法完成打印后开始一个
新行,而Print()方法完成打印后不换行。
3.8.5.2 标准输入System.in对象
System.in对象可在命令行应程序中用于
从键盘接收输入。
 3.8.6 复合语句
将相关语句组合在一起就构成复合语句。
复合语句{}括起来,{}内的每条语句的
语法与上面介绍的一样。

3.9 控制语句
3.9.1. 选择语句
选择语句提供了一种控制机制,使得程序根据相应的
条件去执行对应的语句。
 3.9.1.1. if-else语句(条件语句)
if-else语句根据判定条件的真假来执行两种操作中的一种。
简单形式if-else语句的语法形式为:
if( 布尔表达式)
语句1;
[else
语句2;
]
其中,用“[ ]”括起的else部分是可选的



若无else部分,语句的执行过程是:执行if语
句时,首先对表达式的值进行判断,若布尔表
达式的值为true,则程序执行语句1,否则就什
么也不做,转去执行if语句的后续语句。
若有else部分,语句的执行过程是:执行if语句
时,首先对表达式的值进行判断,布尔表达式
的值为true,则程序执行语句1,否则执行语句2。

3.9.1.2 if-else语句的嵌套
if-else语句中内嵌的语句1或语句2又是if-else语句的情况
称为if-else语句的嵌套。如:
if(布尔表达式1)
语句1;
else if(布尔表达式2)
语句2;
…
else if (布尔表达式m)
语句m;
else 语句n;
Jave规定,else总是与离它最近的if配对。如果需要,
可以通过使用花括号“{}”来改变配对关系。

3.9.2 switch语句

switch语句根据表达式的结果来执行多个可能操作中
的一个,它的语法形式如下:
switch(表达式)
{
case常量1:语句1
[break;]
case常量2:语句2
[break;]
…
case常量n:语句n
[break;]
[default:缺省处理语句
break;]
}













switch语句中的每个“case常量:”称为
一个case子句,代表一个case分支的入口。

3.9.3 循环语句
循环语句的作用是反复执行同一段代码直到满足结束条
件。Java语言中常用循环语句来完成。Java循环语句
有while、while-do和for三种。
 3.9.3.1 while语句
while语句形式如下:
while <布尔表达式> DO <语句>
其中:
while是while语句的关键字;布尔表达式是循环条件;语
句为循环体,当循环体为多个语句时应构成复合语句。
while语句执行的过程为:执行while语句时,首先判断布
尔表达式的值,当布尔表达式的值为true,则执行循
环体,然后再判断条件,直到布尔表达式的值为false,
停止执行语句。
3.9.3.2 do-while语句
Java还提供了另一个与while语句类似的语句—
—do-while语句。do-while语句的语法形式如
下:
do
语句
while(布尔表达式);
do-while语句执行的过程为:先执行一次循环体
中的语句,然后测试布尔表达式的值,如果布
表达式的值为真,那就继续执行循环体,dowhile语句将不断地测试布尔表达式的值并执
行循环体中的内容,直到布尔表达式的值为假
为止。

3.9.4 for语句
for语句是循环的另一种表示形式。for语句
的语法形式为:
for(表达式1;表达式2;表达式3)
语句

for是for语句的关键字,语句为for语句的循环体,若有多
个语句,需构成复合语句。
for语句中循环控制变量必须是有序类型,常用的有整型、
字符型、布尔型。循环控制变量初值和终值通常是与
控制变量类型相一致的一个常量,也可以是表达式。
循环次数由初值和终值决定。
for语句的执行过程如下:
(1)按表达式1将初值赋给循环控制变量。
(2)按表达式2判断循环是否成立,即判断控制变量
的值是否越过终值(未越过终值为条件成立,越过终
值为条件不成立),若条件不成立、转步骤(6)。
(3)若条件成立,则执行循环体。
(4)按表达式3修改控制变量。对于递增型为原控制
变量值的后续;对于递减型为原控制变量值的前导。
(5)返回步骤(2)。
(6)结束循环。
3.9.6 break和continue语句
break除了可以用在switch语句中之外,亦可以用
于循环语句中,这时它与continue语句一样,
对循环的执行起限定转向的作用。
 3.9.6.1 break语句
对于Jave中的三种类型的循环:while、do-while、
for来说,正常的退出循环的方法是当测试条
件变为false时,但有时即使测试的条件为true,
也希望循环立即终止,这时可以用break语句
实现此功能。break语句的一般语法格式为:
break [标号];
其中,用“[ ]”括起的标号部分是可选的。

break语句不能用于循环语句和switch语句之外的任何其
他语句中。break语句通常有两种使用情况:
(1)不带标号的情况。
此时,break语句的功能是终止break所在的循环,转达去
执行其后的第一条语句。对于不带标号的break语句,
在执行时有两个特点:一是在有多重循环时,它只能
使循环从本层的循环跳出来;二是此时程序一定转移
到本层循环的下一个语句。
(2)带标号的情况。
此时,break语句的功能是终止由标号指出的语句块的执
行。它的一种典型用法是实现从其所处的多重循环的
内部直接跳出来,只要在欲跳出的循环开始处加上标
号即可。
标号就是加在语句前面的一个合法的标识符(即标号),
并在标号的后边跟一个冒号(:),即如下所示:
标号:语句
3.9.6.2 continue语句
continue语句只能在循环中使用。它和break
语句的区别是continue语句只结束本次循
环,而不是终止整个循环的执行;而
break语句则是结束整个循环语句的执行。
continue语句的一般语法格式为:
continue[标号];
其中,用“[ ]”括起的标号部分是可选的。

continue语句通常两种使用情况。
(1)不带标号的使用情况。
此时,continue语句用来结束本次循环,即跳过
循环体中continue语句后面的语句,回到循环
体的条件测试部分继续执行。
不带标号continue语句和不带标号的break语句类
似,只能跳过本次循环的剩余语句。
(2)带标号的使用情况。
此时,continue语句跳过标号所指语句块中的所
有余下部分语句,回到标号所指语句块的条件
测试部分继续执行。

3.10 综合应用举例
例3.21编写程序,输出加法表和乘法表
分析 加法表如下:
 1
2 3 4 5 6 7 8 9 (行号)
1 2 3 4 5 6 7 8 9 10
2 3 4 5 6 7 8 9 10 11
3 4 5 6 7 8 9 10 11 12
4 5 6 7 8 9 10 11 12 13
5 6 7 8 9 10 11 12 13 14
6 7 8 9 10 11 12 13 14 15
7 8 9 10 11 12 13 14 15 16
8 9 10 11 12 13 14 15 16 17
9 10 11 12 13 14 15 16 17 18
列号












//源程序清单3-21
public class Add
{
public static void main(String[ ]args)
{int j; //列循环变量
int i; //行循环变量
int k; //存i+j
System.out.println(" 1 2 3 4
9");
for(i=1;i<=9; i++)
{
5
6
8

for(j=1;j<=9; j++)
{k=i+j;
if (j==1)
System.out.print(i+" ");
else
if (k>9)
System.out.print(k+" ");
格,使加法表对齐
else
System.out.print(k+" ");
}
System.out.println();
}
}

}












//调整两数之间空格,一位数多一空
小结
这一章主要介绍了Java语言的基本要素:标识符
的规则、常数规则、局部变量说明格式。介绍
了Java的基本数据类型及其运算符和表达式。
Jave程序设计语言提供多种基本语句:变量说明
语句、赋值语句、方法调用语句、空语句。另
外还提供了以下几种流程控制语句:分支语句
( if-else,switch-case ) 、 循 环 语 句 (while,dowhile,for) 、 转 向 控 制 语 句
(break,continue,label)。
第4章 Java面向对象的程序设计











4.1 面向对象的理论基础
4.2 类与对象的基本概念
4.3 Java的类
4.4 继承
4.5 对象的创建及使用
4.6 null,this和super
4.7 接口
4.8 包
4.9 综合应用示例
小结
习题
4.1 面向对象的理论基础

面向对象的概念是近年来广泛被人们接受,这
里重要的原因是它以辨证唯物的认识论作为自
己的理论基础。现实世界本质上是由物质构成
的,物体是物质的载体,各种各样的物体相互
联系地构成大千世界。客观存在的物体就是最
基本的对象。面向对象分析就是根据人们研究
的现实世界中复杂的对象是如何由许多小的、
简单的对象组成的原理建立的。面向对象的理
论同样认为,不同的物体由共性,共性存在与
个性之中。物体的共性是人们对世界认识的抽
象, 物体的个性又继承了共性。世界上的物体
不是一盘散沙,它门之间存在各种各样的联系,
这种联系导致物体状态的变化。 所有这些,
构成了面向对象分析的核心思想。
4.2 类与对象的基本概念
4.2.1对象
对象在我们的现实生活中很普通的概念。所有的
物体都可以被视为对象,大到宇宙,小到原子,
都可以将其看作是对象。一个对象具有本身的
属性即特征,这些特征决定对象的状态,对象
还通过自己的行为,不断改变自己的状态。
 4.2.2类
类是具有相同特征的多个对象的模板,它封装了
这些对象的所有共同特征。对象是类的实例。
类只是一个抽象的模型,利用这个抽象的模型
可以构造具体的实例。

4.3 Java的类

类是组成Java程序的基本要素。它描述
了一类对象的属性和行为。一个Java类
形式上包含两个部分:
类首说明
类体
4.3.1类首说明
类首说明的形式如下:

[修饰符] class 类名 [extends父类名][implements
接口名列表]
类的修饰符有private、public、abstract 、final。
一个类可以同时有多个修饰符,但是不能有相
同的修饰符。当一个类有多个修饰符时,这些
修饰符无先后顺序之分,可以任意的顺序排列
它们,但是建议按照public、abstract、final的
顺序写。
类名是所创建的类的名字,类名可以是任意的
Java标识符,根据Java命名的约定,类名的每
个有意义的单词的首字母要大写,其余字母小
写。
extends(继承)保留字用来表明新创建的类继承
哪个类,被继承的类称为此类的父类。extends
后面只能跟一个父类名称。
implements(实现)用来表明这个类实现了哪些接
口,接口名可以有多个


4.3.2类体
类体中定义了该类所有的成员变量和该类所支持的方法,
其形式说明如下:
类体
{
[成员变量说明]
[构造方法说明]
[静态初始化说明]
[方法说明]
}
类体中由成员变量说明、构造方法说明、静态初始化说
明和方法说明构成。它们都是可选的。类体中的这四
种说明没有先后顺序之分,但是为了类的可读性,建
议按照上面出现的顺序说明它们。

4.3.2.1成员变量说明及其使用
1.成员变量说明
成员变量的说明类似于方法的局部变量说明,所不同的
是,成员变量定义在类中,是类成员的一部分,整个
类都可以访问它。Java中成员变量说明形式如下:
[修饰符] 成员变量类型 成员变量名
成员变量的修饰符有以下几种:缺省访问修饰符、public、
protected、private、final、static、transient、volatile 。
2.成员变量使用
类的成员变量在定义它的类内部,直接通过成员变量名
来访问。如从类的外部访问,类变量和类对象变量的
使用方法是不同。使用类变量的格式如下:
类名 . 类变量名
访问类对象变量的格式如下:
类对象名 . 类对象变量名
4.3.2.2构造方法说明
每当由类构造对象时都要调用该类特定的构造方
法,在Java中,每个类都至少有一个构造方法。
构造方法可以确保用户正确地构造类的对象,
同时,构造方法也会对对象作初始化工作。构
造方法说明形式如下:
[构造方法修饰符] 方法名([形式参数列表])
[throws异常列表]
方法体
构造方法修饰符与一般方法修饰符相同
构造方法不能像一般的方法那样被直接调用,
它是在构造类的实例的时候被Java编译器自动
调用的。

一个类的构造方法可以有多个,它们都具有相同的方法
名,即类名。编译器根据参数的类型来决定调用哪个
构造方法。构造方法分为缺省的构造方法(不带参数)
和带参数的构造方法。
(1)缺省的构造方法
如果类的定义没有编制构造方法,Java语言会自动为用
户提供。这个由Java自动提供的构造方法就是所谓的
缺省构造方法。缺省的构造方法确保每个Java类都至
少有一个构造方法,该方法应符合类的定义。
(2)带参数的构造方法
带有参数的构造方法能够实现这样的功能;当构造一
个新对象时,类构造方法可以按需将一些指定的参数
传递给构造方法。

4.3.3.2方法体
方法体是实现了这个方法的代码段,也可以是一个分号
“;”,表示无方法体,该类没有实现。当且仅当方
法的修饰符中有abstract或native时,方法才无方法体。
方法体是由各种语句构成的。
 4.3.3.3 方法的调用
类方法调用形式如下:
类名 . 类静态方法名(实型参数列表)
对象方法调用形式如下:
类对象名 . 类非静态方法名(实型参数列表)
关于类方法的使用,有如下一些限制:
(1) 在类方法中不能引用对象变量。
(2) 在类方法中不能使用super、this关键字。(super、this
介绍见后)。
(3) 类方法不能调用类中的对象方法。


4.3.3.4 递归方法
1)直接递归方法与间接递归方法
一个方法体中又调用自身,这种方法叫
递归方法,或更准确的说叫直接递归方
法。如方法体中调用的虽然不是自身,
但是它间接地调用自身。这叫间接递归
方法。



递归方法调用涉及两个概念:
1)栈,一种数据结构。它提供这样的功能:
数据依次入栈,最先进栈的数据最后出栈,最
近入栈的数据最先出栈。
2)递归方法调用数据的保留
每调用一次过程,便分配一次局部变量,待调用
结束局部变量就释放。但从递归过程的执行可
以看出,上一次调用的执行尚未结束,下一次
调用又开始,所以递归调用就存在数据的保留
问题,这个数据的保留就是采栈解决的。

4.3.3.5 方法的final修饰符
方法的final修饰符表明方法不得被子类覆盖。
带有final修饰符的方法称为最终方法。Java的
方法除非把说明为最终方法,否则方法是可以
覆盖的。Java之所以这样规定,主要是因为
Java的纯面向对象特性,它把覆盖当作面象对
象的重要特性,给予了最大限度的实现。
把方法声明为最终方法有时可增加代码的安全
性。

4.3.3方法说明
在Java中,方法总是Java类的一个组成部分。通过类的方
法,改变对象的状态。方法说明的格式如下:
方法首部说明
方法体
方法说明分为方法首部说明和方法体两部分。

4.3.3.1方法首部说明
方法首部说明的形式如下:
[方法修饰符] 返回值类型
方法名([形参列表])
[throws异常列表]
方法名是Java中任意的标识符。
形式参数列表是可选的。如果方法没有形式参数,就用
一对小括号“()”表示。形式参数列表的形式如下:
(类型 形参名,类型 形参名,……)。
4.4 继承
在Java编程语言中,通过继承可利用已有的类,并扩
展它的属性和方法。这个已有的类可以是语言本身提
供的、其他程序员编写的或程序员原来编写的。

4.4.1类继承语法形式
类继承语法形式如下:
class SubClassName extends SupperClassName
extends是继承关键词。SubClassName是新的子类名,
SupperClassName是继承的父类名。父类名是必需的,
而且只能有一个。子类可以对父类的方法覆盖或重载。
所谓方法覆盖是子类的方法首部与父类的方法首部相
同,但是子类重新实现了该方法。所谓方法重载是子
类的方法名与父类的方法名相同,但是子类该方法的
形式参数列表与父类的方法的形式参数列表不同,并
且重新实现了该方法。


如果子类声明了一个与父类的成员变量同名的
成员变量,则称父类的该成员变量被隐藏
(Hiding),如果子类声明了一个与父类的成
员方法同名的成员变量,则称父类的该成员方
法被重写(Overriding)。关键字this和super分
别用来指明子类和父类中同名的成员变量和方
法。当父类的成员变量、子类的成员变量和类
中方法使用的局部变量三者同名,或其中两个
同名时,可以使用关键字this和super来指定所
要使用的变量。
4.4.2类的层次结构
在Java中,java.lang.Object类是Java所有类的父类。继承
关系形成的这种所谓的树形层次结构。
继承带来最大的好处是类的可重用。当创建自己的Java
类时,应尽可能将它作为某个类的子类,在自己的子
类加入一些特殊的内容就行了,而不必重新定义这个
类所需的全部的属性和行为。
 4.4.3抽象类和方法
对象高级抽象的需要,希望类只需要说明方法首部,而
不需要或不可能说明其方法体,其方法体只能由其子
类来完成。使用abstract关键字的抽象类可以达到这个
目的。但是,抽象类不能创建任何对象,抽象类只是
用来由其子类继承。继承抽象类的子类比它的父类更
加具体化、特殊化。



4.4.4 最终类(final类)
final修饰符可以定义一个不能被继承的类,即
不能再有子类。java.lang.System类就是final类
的一个例子,它是最终的类。 final修饰符通
常是出于安全的目的而使用的,因为不能继承
final类,人们就不能重载或覆盖它的任何方法。
如果允许一个类被继承,其被重载或覆盖的方
法可能会被改写。保证某个特定的方法在类层
次关系上的某层以后只有一个定义,这对于那
些安全性非常关键的类是非常必要的。
无论是用于修饰变量、方法还是类,final关键
字的作用都是一样的:增强代码的安全性。
4.5 对象的创建及使用
创建对象包括三步:对象的说明、对象的实例
化和对象的初始化。
 4.5.1对象说明
对象说明就是类成员变量的说明或局部变量的说
明,但是,对象说明强调变量的类型是类,而
不是基本类型。对象说明的语法形式为:
类名 对象名;
该语句从指定的类创建对象。类名和对象名都
必须是合法的Java标识符,分别标识所用的类
和所创建的对象。对象说明语句可以出现在类
定义中,也可以出现在方法体中。

4.5.2 对象的实例化和初始化
在说明一个对象时,并没有为该对象分配存储空
间。对象的实例化完成对象的空间分配,这由
new操作完成。构造方法的调用确定了对象的
初始状态。对象构造的语法形式为:
对象名=new 类构造方法名([实参表]);
 4.5.3 对象的成员变量及方法的的访问
构造了对象,才能访问对象的成员变量及方法。

4.6 null,this和super
Java语言规范中,每个类均有这样3个变
量值:null、this和super。null为"空"变量,
用于指代空对象,但这个对象不存在相
应的实例,如下面的例子:
Rectangle rect=null;
这个例子变量不创建相应的对象。本质
上讲,这类似与创建一个"空壳"。
this引用表示的是对象本身。
4.7 接口
4.7.1接口的概念
接口和类不同,一个接口可以是一个或多个其他
它接口的直接继承,也就是说,接口是支持多
继承的。同时,一个类也可以实现一个或多个
接口,这意味着在此类中必须实现接口中的所
有抽象方法。
接口和类之间的区别:
(1)类只能继承一个类,而对于接口,类可以
实现多个接口。
(2)对于继承性,类继承了父类的方法:子类
可以选择是否覆盖父类的方法。接口的方法没
有实现,因此,类必须实现接口中的每个方法。

4.7.2接口的说明
接口类型的说明类似于类的说明,它由两部分组
成:接口说明和接口体。接口说明的形式如下:

[修饰符]interface接口名[extends] [接口序列]
像类一样,编译好的接口保存在Class文件中。
4.7.2.1接口说明
interface前的修饰符是可选的。当没有修饰符的
时候,表示此接口的访问只限于同包的类。如
果使用修饰符,则只能用public修饰符,表示
此接口是公有的,在任何地方都有可以引用它,
这一点和类是相同的。
接口说明中的extedns关键词和类说明中的
extends一样,用来定义直接的父接口。和类不
同,一个接口可以继承多个父接口,当extedns
后面有多个父接口时,他们之间用逗号隔开

4.7.2.2接口体
接口体就是用大括号括起来的那部分。接口体
说明接口的成员,包括常量和抽象方法。
像类一样,接口可以有两种成员:成员变量和
成员方法。
接口中所有的成员变量都隐含的是public、static、
final的,因此接口中的成员变量都是静态最终
变量。在接口中成员变量声明的形式如下:
[修饰符] 类型名 变量名;
接口中说明的方法都是抽象方法,其声明的形式
为:
[修饰符] 方法返回值类型 方法名(参数列表);

4.8.3 包的使用
系统提供的包可以直接引用
 4.8.4 Java包
Java自带了一些包。这些包以“java.”开
头,作为java 的一个标准部分发布的。
学习Java,必须学习Java 常用包中的类。

4.9 综合应用举例
4.9.1 单向链表的概念
单向链表是动态数据结构中最基本的形
式。一个链表是一组结点的序列。为了
表示每个结点Ai与其后续结点Ai+I的关
系,对结点Ai来说,除存贮结点保存的
信息之外,还需要有一个指示其后续结
点的信息。
 4.9.2 单向链结点类描述

4.9.2.单向链表的生成
单向链表首先从建立只有一个元素的链表开始。
然后通过一个结点一个结点的插入而形成的。根据结
点插入及结点取出方式的不同,链表可分为栈形式或
队形式两种。在不断生成链表的过程中,若每一个新
结点插入至已生成链表头之前,而取出时取最近插入
的。这样的链表具有“后进先出”的特性,所以称为
栈式链表。若每个新结点插入至已生成的表尾,而取
出时取最先插入的。这样的链表具有“先进先出”的
特性,便称为队式链表。
对于链表的操作主要可分为链表的生成,插入一个元
素,删除一个元素,遍历链表的每个元素及对链表的
查找等等。
4.9.3 单向链表中插入或删除一个结点
 4.9.3.1先讨论插入一个结点
为实现插入目的,要使用两个类型为链结点类的
临时变量finger 、previous。设指针previous 用
于指向插入位置的前一个结点,临时变量
finger用于建立一个新结点)。
4.9.3.2 单向链表中删除一个结点
为实现删除目的,要使用两个类型为链结点类的
临 时 变 量 finger 、 previous 。 设 临 时 变 量
previous 用于指向删除位置的前一个结点,临
时变量finger用于指向删除的结点

小结
本章主要介绍如何定义和使用类,类是构造面向
对象程序的基本单位。类的使用主要表现为以
下两方面:一是通过类创建和使用类的对象;
二是用一个类去定义另一个类,即类的继承与
引用,而多态性则为类的使用提供了更大的灵
活性。
应注意区分类与对象这两种个基本概念。而类是
抽取了同类对象的共同属性和行为形成的对象
或实体的“模版”, 类是静态概念。对象是
现实世界中实体的描述,对象要创建才存在。
有了对象才能对对象进行操作,不再使用的对
象由系统撤消。
接口是面向对象的一个重要思想,利用接口使设计与实
现分离,使利用接口的用户程序不受不同接口实现的
影响,不受接口实现改变的影响。接口在Java中还起
着另一个非常重要的作用,弥补Java只支持单继承的
不足,它用来完成多继承的一些功能。Java接口反映
了对象分析中较高层次的抽象,为描述相互似乎没有
关系的对象的共性提供的一种有效的手段。
包是为了解决一个大的问题、设计较大规模的程序而引
入的概念。使用包可以提供一定的访问控制。类、方
法缺省访问权限就是可以在它所属的包中被访问。说
明为public的类、方法才可以在其他它包中被访问。
如果类和方法被说明为protected, 则其他它包中只有它
的子类才可以访问它们。
本章最后描述了单向链结点类、单向链类、队类和栈类,
这些是Java.编程中的重要可重用类。
习题







1.什么是类?类的结构是怎样的?设计类应考虑哪些
因素?
2.什么是对象?对象和类是什么关系?
3.什么是方法?方法的结构是怎样的?设计方法应考
虑哪些因素?
4.解释下列名词:
继承 多态性 隐藏 重写 重载 抽象类 抽象方法 最终类
包
5.创建一个类,它的构造方法没有参数,方法体输出
一些信息。再创建一个该类的对象。
6.为上题的类增加一个重载的构造方法,该构造方法
有一个String类型的参数,其方法体输出这一参数值。






7.创建一个有两个方法的类,其中第一个方法两次调
用第二个方法:第一次不使用this,第二次使用this。
8.创建一个类,该类含有int和char类型的成员变量且
未被初始化,然后编程输出这些成员变量的值,查看
Java是否执行了默认的初始化操作。
9.创建一个类,该类含有public、private、protected以
及friendly的成员变量和成员方法。创建一个该类的对
象,查看当试图访问这些成员时编译程序会给出哪些
信息。(注意:在同一目录下的类都属于一个默认的
包。)
10.创建两个类A和B,它们的构造方法没有参数。创
建一个A的子类C,C中含有B类型的成员变量。不要
为C创建构造方法。创建一个类C的对象,观察其结果。
11.将上题中类A、B的构造方法改为有参数的。为类C
编写一个构造方法,在该构造方法中执行所有的初始
化操作。
12.用递归方法求第n个Fibonacci数。

















13.用递归方法打印如下形式的数字塔。
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
//提示
void prints(int k)
{ if (k<>0)
{ prints(k-1);
for(i=0;i=k;i++)
System.out.print(i+” “);
System.out.println();
}
}
14.利用单向链表构造队的类(队要求先进先出)
15.利用单向链表构造栈的类(栈要求先进后出)
第5章 异常处理







5.1 异常类的层次
5.2 throws抛出异常
5.3 throw抛出异常
5.5 try-catch异常处理
5.6 正确地使用异常
小结
习题
5.1 异常类的层次
异常在Java中也是作为类的实例的形式出
现 的 。 Java 中 的 所 有 的 异 常 类 都 是 从
Throwable类派生出来的 。Throwable类
有 两 个 直 接 子 类 : java.lang.Error 和
java.lang.Exception。
Error类及其子类主要用来描述一些Java运行时刻
系统内部的错误或资源枯竭导致的错误。普通
的程序不能从这类错误中恢复,也无法抛出这
种类型的错误,这类错误出现的几率是很小的。
另一个异常类的子类是Exception类和它的子类。
在编程中错误的处理主要是对这类错误的处理,
如除数为零、数组下标越界等。类Exception是
普通程序可以从中恢复的所有规范了的异常的
父类。
Exception类又有两个子类:RuntimeException和
Non_RuntimeException。
RuntimeException类处理的错误包括:错误的强
制类型转换、数组越界访问、空引用。
RuntimeException是程序员编写程序不正确所
导致的异常,是在Java系统运行过程中出现的
异常,如除数为零等。由于运行时异常可能会
出现在程序的任何地方,而且出现的可能性非
常大,因而由程序本身去检测运行异常出现与
否,将会使程序的开销过大,所以编译器并不
要求程序去说明或捕获运行时异常。
RuntimeException类的异常的产生是程序员的
过失,理论上,程序员经过检查和测试可以查
出这类错误。
N0_RuntimeException类的异常包括:格式不正
确的URL、试图为一个不存在的类找到一个代
表它的类的对象。
Non_RuntimeException是指可以由编译器在编译
时检测到的、可能会发生在方法执行过程中的
异常,如找不到指定名字的类或者界面,不是
程序本身的错误,如果这些异常情况没有发生,
程序本身仍然是完好的。如果程序不能适当地
处理可能会引发运行异常的语句,则程序将不
能通过编译器的编译。
Java提供了两种不同的异常抛出机制:throws
抛出异常和 throw 抛出异常。throws抛出异常
人们又叫间接抛出异常,throw抛出异常又叫
直接抛出异常
5.2 throws抛出异常
throws异常说明总是和方法说明出现在一起。这
样,方法说明告诉编译器该方法可能会产生哪
些异常,从而要求它的调用者必须考虑处理这
些异常。throws异常说明形式如下:
方法说明 [ throws 异常类列表]
方法抛出异常是throws子句中指定的异常类或其
子类。比如在方法的说明中指明方法可能产生
IOException,但是实际上可能抛出的异常或
许是EOFException类的实例,这些异常都是
IOException的子类。
在下列情况下Java方法抛出异常:
(1)调用的方法抛出了异常;
(2)检测到了错误并使用throw语句抛出异常;
(3)程序代码有错误,从而异常,比如数
组越界错误。
5.3 throw抛出异常
在程序中使用throw语句(注意,这里是throw而
不是throws)来抛出异常,该异常可以在该程
序中进行捕获、处理,也可以在调用该方法者
捕获、处理。throw语句的形式为:
throw 异常类的实例;
抛出异常有这样三步:
(1)确定异常类;
(2)创建异常类的实例;
(3)抛出异常。

利用try-catch语句可以说明抛出异常的部位,同时又说明捕获、
处理的办法。
try…catch语句形式如下:
try
{
语句; //说明抛出异常的部位,该部位含有抛出异常的语句,如
调用抛出异常的方法。
}
catch (异常类1 变量名) //按抛出的异常类进行捕获,并加处
理
{
catch 处理
}
catch (异常类2 变量名)//按抛出的异常类进行捕获,并加处
理
5.5 try-catch异常处理










{
catch 处理
}
。。。。。。
[finally 语句]
当catch紧前面的try块中发生了一个异常,try-catch语句
就会自动在try块后面的各个catch块中,找出与该异常
类相匹配的参数。当参数符合以下三个条件之一时,
就认为这个参数与产生的异常相匹配:
(1)参数与产生的异常的属于一个类;
(2)参数是产生的异常的父类;
(3)参数是一个界面时,产生的异常实现了这一界面。
当产生的异常找到了第一个与之相匹配的参数时,就执
行包含这一参数的catch语句中的Java代码,执行完
catch语句后,程序恢复执行,但不会回到异常发生处
理继续执行,而是执行try-catch结构后面的代码。
5.5.3 finally子句
finally语句可以说是为异常处理事件提供的一个
清理机制,一般是用来关闭文件或释放其他的
系统资源,作为try-catch-finally结构的一部分,
可以没有finally语句,如果存在finally语句,
不 论 try 块 中 是 否 发 生 了 异 常 , 是 否 执 行 过
catch语句,都要执行finally语句。
try-catch-finally语句的形式如下:





















try
{
//正常执行的代码,可能产生异常
….
}
catch (异常类1e)
{
//异常类1的处理代码
……
}
catch (异常类2e)
{
//异常类2的处理代码
……
{
……
catch (异常类ne)
{
//异常类n的处理代码
……
}
finally
{
//执行清除工作的语句
…..




(1) try块中的语句没有产生异常。在这种情况下,Java
首先执行try块中的所有的语句,然后执行finally子句
中的代码,最后执行try…catch..finally块后面的语句;
(2) try块中的语句产生了异常,而且此异常在方法内
被捕获。在这种情况下,Java首先执行try块中的语句,
直到产生异常处,然后跳过此try块中剩下的语句,执
行捕获此异常的catch子句的处理代码;然后执行
finally子句中的代码;
(3)如果catch子句中没有重新抛出异常,那么Java将执
行try…catch…finally块后面的语句。如果在catch子句
又重新抛出了异常,那么Java将这个异常抛出给方法
的调用者;
(4)try块中产生了异常,而此异常在方法内没有被捕获。
在这种情况下,Java将执行try块中的代码直到产生异
常,然后跳过try块中的代码而转去执行finally子句中
的代码,最后将异常抛出给方法的调用者。
5.6 正确地使用异常
由于异常使用起来非常方便,以至于在很
多情况下可能会滥用异常。但是,使用
异常处理会降低程序运行的速度,如果
您在程序中过多地使用异常处理,您的
程序的执行速度会显著地降低。在这里
我们给出几点建议,来帮助您掌握好使
用异常处理的尺度。
(1) 在可以使用简单的测试就能完成的检查中,
不要使用异常来代替它。
(2) 不要过细地使用异常。最好不要到处使用异
常,更不要在循环体内使用异常处理,您可以
将它包裹在循环体外面。
(3) 不要捕获了一个异常而又不对它做任何的
处理。
(4)将异常保留给方法的调用者并非不好的做法。
对于有些异常,将其交给方法的调用者去处理
是一种更好的处理办法。

小结
异常处理把Java程序各种可能出现的错误都看作异常,
集中起来统一处理。程序员只需要说明何处理可能的
出现异常,如何处理即可。
throws抛出异常和方法说明联系在一起,是针对以下情
况:调用的方法抛出了异常、检测到了错误并使用
throw语句抛出异常、程序代码有错误从而异常。方法
中出现的异常由catch语句捕获,进行处理。
try-catch异常处理机制,当try体中发生了一个异常,trycatch语句就会自动在try体后面的各个catch语句中,找
出与该异常类相匹配的参数,就执行包含这一参数的
catch语句中的java代码,执行完catch语句后,程序恢
复执行,但不会回到异常发生处理继续执行,而是执
行try-catch结构后面的代码。
finally语句可以说是为异常处理事件提供的一个清理机
制。
习题




1.什么是异常?简述Java的异常处理机制。
2.系统定义的异常与用户自定义的异常
有何不同?如何使用这两类异常?
3. 编写从键盘读入10个字符放入一个字
符数组,并在屏幕上显示它们的程序。
请处理数组越界异常。
第六章
Java的可重用类
第一节
第二节
第三节
第四节
小结
Java可重用类的结构
Java语言包(Java.lang)
Java.util包
输入输出包java.io





6.1 java可重用类的结构
面向对象技术是迄今为止提高软件可重用性的最有
效的途径之一,其中类和类的继承机制为提高程序的可重
用性提供了强有力的支持。
Java有一系列功能强大的可重用类, 异常类也是java可重
用类。功能相关的可重用类组织成包。可重用类的继承层
次和包的组织呈树型结构。
6.1 java可重用类的结构
Java可重用类按功能划分为:
按使用方法可分为两大类:
语言包java.lang(lang意为
language)、输入/输出包java.io、
实用程序包java.util(util意为
utility)、小应用程序包java.applet
图形用户接口包java.swing、网络
包java.net
前三种包称为基础包,后三种包
称为应用包。
适用于所有Java解释器的
包和仅适用于浏览器环境
的包,
Applet包是只适用于浏览
器环境的包。
图6-1给出了Jave资源的层次结构,其中,矩形框表示包,
椭圆框表示包中的类,本章其余的图均如此表示。
6.1 java可重用类的结构
Java常用的包的简单介绍如下:
(1)java.lang包
主要含有与语言相关的类。
(2)jave.io包 主要含有与输入输出相关的类。
(3)java.util包 包括许多具有特定功能的类。
(4) java.swing包和java.awt包
提供了创建图形用户
界面元素的类。
(5) java.net包 含有与网络操作相关性的类。
(6)java.applet包 含有控制Hotjava浏览器的类。
(7)java.text——为国际化的应用程序提供类。
(8)java.beans——Java Beans定义了应用程序编程接口
(API);
Java
Java.lang.math
Java.lang
Java.lang.system
…..
Java.net
Java.io.file
Java.io
图6-1
…..
Java.util
…..
Java.applet
…..
Java.swing
…..
Java工具包层次图
6.2 Java语言包(Java.lang)



6.2.1 字符串类
6.2.2 数组
6.2.3 Math类
6.2.1 字符串类


1 String类
2 StringBuffer类
6.2.1 字符串类
Java语言的核心部分是java. lang, 它定义了Java中的大多数基
本的类。
java.lang 包含了Object(目标)类,java.lang.Object类
是Java中整个类层次结构的根节结点,这个软件包还定义了基
本数据类型的类:String、Boolean、Character、Byte、
Integer、Short、Long、Float、Double。这些类支持数字型
的转换和字符串操作。
下面是java.lang中的其他类:
Class——为运行时搜集的信息,如对instanceof操作符提
供支持。
Math——提供像pi 和e 这样的数学常数及各种函数。
6.2.1 字符串类
System——提供对操作系统的访问,包括默认的I/O流
环境变量、自动垃圾收集、系统时间和系统属性;许多
System方法可访问Runtime类的方法。
Runtime——提供了操作系统的访问:使用
java.lang.System可以更容易地访问大多数Runtime方法;
唯一的例外是exec方法,它开始一个新进程。
Thread——和java.lang.Runnable接口协同作用提供
Java中的多线程的支持。
Throwable——它是Java中所有异常(Exception)的基
类,是java.lang.Exception、java.lang.Error和
java.lang.RuntimeException的父类。应用程序运行时发生
意外时,异常和错误类就发出信号。它在第65章作了介绍。
1.
string类
java.lang提供了两种字符串类:
String类
StringBuffer类。
String类提供几种字符串创建方法:
String s=”Hello!”;使用字符串常量自动创建
String实例。
String s= new String(String s);通过String对
象或字符串常量传递给构造方法。
public String(char value[]);整个字符数组赋给
String构造方法。
public String(char value[],int offset,int
count);字符数组一部分赋给String构造方法,offset为
起始下标,
1.
string类
String类提供了丰富的字符串操作方法,其中重要的列举如下:
public int length();返回字符串的长度。
public char charAt(int index); 返回字符串位置
index处的字符。
public boolean equals(Object o);比较两个字符
串对象,相等返回True,反之,返回False。
public int compareTo(String s);比较两个字符
串字典顺序,相等返回0,s大于当前串返回一个负值,s小
于当前串返回一个正值。
public boolean regionMatches(int toffset,String
Other,int ooffset,int len);从当前字符串位置
toffset开始
1.
string类
寻找字符串Other、起始位置为ooffset、长度为 len的子串。
如发现匹配,返回True,否则,返回False。
public boolean startsWith(String prefix);从当
前字符串位起始位置t开始寻找字符串 prefix。如发现匹配,
返回True,否则,返回False。
public boolean startsWith(String prefix);从当
前字符串位起始位置t开始寻找字符串 prefix。如发现匹配,
返回True,否则,返回False。
public boolean endsWith(String suffix);如当前
字符串的结尾子串与suffix匹配,返回 True,否则,返回
False。
public int indexOf(String str);在当前字符串中
寻找与str匹配的子串,返回首次匹配的起始下标值,无匹
配返回-1。
1. string类
public String substring(int beginIndex,int
endIndex);在当前字符串中,求从起始位置 beginIndex
到结束位置endIndex的子串。
public String concat(String str);将当前字符
串与str连接,返回连接后的字符串。
public String toLowertCase();将当前字符串全
转换为小写形式。
public String toUpperCase();将当前字符串全
转换为大写形式。
public Char toCharArray();将当前字符串转换
为字符数组。
public Static String valueOf(type variable);
//把variable转换为字符串。
6.2.2 数组
数组是相同类型的数据元素按顺序组成的一种复合数
据类型,元素在数组中的相对位置由下标来指明。数组中
的每个元素通过数组名加下标进行引用。数组作为一种特
殊的数据类型具有以下特点:
(1)一个数组中所有的元素应该是同一类型;
(2)数组中的元素是有顺序的;
(3)数组中的一个元素通过数组名和数组下标来确定。
数组分为一维数组和多维数组。


1 一维数组
2 多维数组
2. stringbuffer类
String类实现一种不能改变的静态字符串
StringBuffer类实现一种动态可变的字符串。
StringBuffer类可以用于创建String类,StringBuffer一
旦建好,用toString()方法将其转换为String类。以后,
就可以使用String类方法来操作。
StringBuffer类提供三种创建方法:
public StringBuffer();创建一个空的
StringBuffer类。
public StringBuffer(int length);创建一个大小
为length的StringBuffer类。
public StringBuffer(String str);按str创建一个
动态可变的StringBuffer类。
2. stringbuffer类
StringBuffer类提供的方法主要用于把输入数据转
换为String类。输入数据可来自键盘或其它数据源。类型
可以是字符、字符数组、整数、浮点数、和Object类型等。
下面介绍StringBuffer的主要方法:
public int length(); 返回缓冲区的字符数。
public int capacity();返回缓冲区剩余空间。
public synchronized StringBuffer append(type variable);
把variable转换为字符串,然后与当前串连
public synchronized StringBuffer
append(Char(char
ch));把字符ch连接到当前串尾。
public synchronized StringBuffer insert(int offset,type
variable);把variable转换为字符串,然后插入到当前串由offset指定
的位置。
2 . stringbuffer类
public synchronized StringBuffer insert(int offset,char
ch);把字符ch插入到当前串由 offset指定的位置。
public synchronized String toString();把StringBuffer
转换为字符串String。
例6-1 利用StringBuffer类将键盘输入的数据建立
一个字符串实例。
import java.io.*;
public class StringBufferToString
{
public static void main(String args[])
{
try
2. stringbuffer类
{
System.out.println(“Enter Width!”);
int length =System.in.read();
StringBuffer strb=new StringBuffer(length);
while (ch=System.in.read())!=’\n’)
{
strb.append(ch)
}
} catch (IOExceptio e) { }
String str=strb.toString();
}
}
1. 一维数组
1} 数组的说明与构造
数组声明的格式为:
类型
数组名[ ];
数组名为Java标识符。“[ ]”部分指明该变量是一
个数组类型变量。
数组说明之后尚不能立即被访问,因为还没有为数
组元素分配内存空间。
需要使用用new操作来构造数组,即在数组说明之
后为数组元素分配存储空间,同时对数组元素进行初始化。
其格式如下:
数组名=new类型[数组长度];
例如,
list=new int[3];
1. 一维数组
它为整型数据组list分配3个整数元素的内存空间,
并使每个元素初值为0。
为简化起见,还可以把数组的说明和构造合在一起,
其格式如下:
类型 数组名[ ]= new类型[数组长度];
2} 数组的初始化
数组初始化就是为数组元素指定初始值。通常在构
造数组时,Jave会使每个数组元素初始化为一个默认值。
但在许多情况下,并不希望数组的初值为默认值,此时,
就需要用赋值语句来对数组进行初始化。
数组的初始化方式有两种:一种方式是像初始化简
单类型一样自动初始化数组,即在说明数组的同时进行初
始化。
1. 一维数组
另一种方式是在声明之后再创建数组,然后为每个元素赋
值。(这一句是从下面提上来的,请殷老师考虑是否合适)
由上可见,数组初始化可由花括号“{}”括起的一串由
逗号分隔的表达式组成,逗号(,)分隔数组元素中的值。
在语句中不必明确指明数组的长度,因为它已经体现在所
给出的数据元素个数之中了,系统会自动根据所给的元素
个数为数组分配一定的内存空间,如上例中数组a的长度自
动设置为5。应该注意的是,“{}”里的每一个数组元素的
数据类型必须是相同的。
3) 数组元素的使用
声明了一个数组,并用new语句为它分配了内存空
间后,就可以在程序中像使用任何变量一样来使用数组元
素,数组元素的标识方式为:
1. 一维数组
3) 数组元素的使用
声明了一个数组,并用new语句为它分配了内存空
间后,就可以在程序中像使用任何变量一样来使用数组元
素,数组元素的标识方式为:
数组名[下标]
其中下标为非负的整型常数或表达式,其数据类型
只能为byte,short,int, 而不能为long。
若在Java程序中超出了对数组下标的使用范围
(例如,数组的最大下标为5,但是在程序中存取了下标为
8或-4的元素),则在运行此程序时将出现如下错误信息:
Exception in thread″main″
java.lang.ArrayIndexOutOfBoundsException
2 . 多维数组
在Java中并不直接支持多维数组,所以,多维数组
的声明是通过对一维数组的嵌套式声明来实现的,即用
“数组的数组”来声明多维数组。例如,二维数组为一个
特殊的一维数组,其每个元素又是一个一维数组。
1) 二维数组的说明与创建
二维数组说明的格式为:
类型 数组名[ ] [ ];
例如: int intArray[ ] [ ];
对二维数组来说,分配内存空间有下面几种方法。
(1)直接为每一维分配空间,如:
int a[ ] [ ] = new int[2] [3];
该语句创建了一个二维数组a,其较高一维含两个元
素,图6-2为该数组的示意图。
2. 多维数组
A[0][0]
A[1][0]
a[0][1]
a[1][1]
a[0][2]
a[1][2]
(图6-2 )
从最高维开始,分别为每一维分配空间,如:
int b[ ][ ] = new int[2] [ ];//最高维含2个元
素,每个元素为一个整型数组
b[0] = new int[3];//最高维第一个元素是一个长
度为3的整型数组
b[1] = new int[5];//最高维第二个元素是一个长
度为5的整型数组
图6-3为该数组的示意图。
2. 多维数组
b[0][0] b[0][1] B[0][2]
b[1][0] b[1][1] B[1][2] b[1][3] b[1][4]
注意 在使用运算符new来分配内存时,对于多维数
组至少要给出最高维的大小。
如果在程序中出现 Int a2[ ] [ ] =new int[ ]
[ ];
编译器将要提示如下错误:
Array dimension missing
2) 二维数组元素的初始化
二维数组元素的初始化有两种方式:
(1)直接对每个元素进行赋值;
(2)在说明数组的同时进行初始化。
2. 多维数组
3)二数组元素的引用
对二维数组中每个元素,其引用方式为:
数组名[下标1] [下标2]
其中下标1、下标2为非负的整型常数或表达式,如:
a[2] [3] 、cc[i+2][j*3 ](i,j为整数)等。同样,每一维
的下标取值都从0开始。
6.2.3 math类
java.lang.Math类是标准的数学类,封装了一些数学
函数和常量。在这个类中,数的大小范围跟具体的操作平台
有关。与System类相似,java.lang.Math类也不能被子类
化或实例化,因为它的所有方法和变量也都是静态的。




1
2
3
4
三角函数
乘方
舍入
其他
1. 三角函数
下面的三个方法接受一个double类型的且以弧度为单位
的角度值,并返回相应的运算结果。
(1) sin(double a) 返回角度a的sin值。
(2) cos(double a) 返回角度a的cos值。
(3) tan(double a) 返回角度a的tan值。
下面的方法是反三角函数:
(1) asin(double r) 返回 sin值为r的角度值。
(2) acos(double r) 返回 cos值为r的角度值。
(3) atan(double r) 返回 tan值为r的角度值。
2. 乘方
(1) pow(double y,double x)返回y的x次方。例如,
Math.pow(2.0,3.0)返回结果为8.0。
(2) exp(double x)返回x的底数
(3) log(double x)返回x的自然对数。
(4) sqrt(double x)返回x的平方根。
3.舍入
(1)
(2)
(3)
(4)
(5)
值。
ceil(double a) 返回大或等于a的最小整数值。
floor(double a)返回小于或等于a的最大整数值。
rint(double a) 返回舍入尾数后接近a的整型值。
round(float a)返回舍入尾数后最接近a的整型值。
round(double a)返回舍入尾数后最接近a的长整型
4.其他
(1) abs(a)返回a绝对值。
(2) max(a,b)返回a和b的最大值。
(3) min(a,b)返回a和b的最小值。
6.3 java包
java.util包提供了许多实用的类,如:日期、向量、
哈希表、锁和堆栈等,





6.3.1
6.3.2
6.3.3
6.3.4
6.3.5
Java.util包的构成
日期时间类
向量类及其使用
哈希表类及其应用
栈类
6.3.1 java.until包的构成
java.util包由一些实用类组成的,有些类还是Java
语言所不可缺少的。java.util包的大致层次结构如图6-4
所示
这里还需要说明几点:图中Dictionary是抽象类,
Enumeration和Observer是接口,其余属于普通类;
Hashtable除继承Dictionary类外,还是java.lang类库中
Cloneable的一个实现;类似地,BitSet和Vector除继承了
类Object外,同时也是java.lang.Cloneable的实现;BitSet
是最终类,不能用以创建子类。
图6-4
java.until包层次结构
6.3.2 日期时间类
日期时间类是一个相对简单、但使用频繁的类,它提供了
独
立于具体系统的日期/时间的表示形式。
日期时间类的实例可通过如下几种方法予以创建:
1) public Date()
这个构造方法将把当前日期和时间保存于所创建的
Date实例。
2) public Date(int year, int month, int date)
这个构造方法将根据所给定的year、month、date参
数创建一个Date 实例。
6.3.2 日期时间类
3) public Date(int year, int month, int date, int
hours, int minutes)
这个构造方法类似于第(2)个方法,只不过这里还提
供了具体的小时(hours)和分钟(minutes)参数,因而
时间要更为精确。
4) public Date(int year, int month, int date,int
minutes,int seconds)
这个构造方法类似于第(3)个方法,所不同的是这里
还提供了具体的(seconds)参数,时间又精确了一些。
下面列出了日期时间类的方法:
public int getYear();返回当前日期中的年份。
6.3.2 日期时间类
public int getMonth();返回当前日期中的月份。
public int getDate();返回当前日期中的日期(0-31)
public int getDay();确认某天是星期几。
public int getHours();返回当前日期中的小时数。
public int getMinutes();返回当前日期中的分钟数。
public int getSeconds();返回当前日期中的秒数。
public Boolean before(Date when);对日期实例所代
表的时间和when进行比较。若比when早,返回True; 否则返回
False
public boolean after(Date when);对日期实例所代表
的时间和when进行比较。若比 when晚,返回True;否则返回
False。
public boolean equal(Object obj);比较两个日期对象。
若相等则返回True;否则返回False
6.3.2 日期时间类
public String toString();返回当前日期参数的字符串表示形
式。注意:不同主机系统的public String toString(); 返回当前日
期参数的字符串表示形式。注意:不同主机系统的日期表示形式不尽相
同。
日期时间类的使用非常简单,只需要创建一个类实例不带任何参
数,即可以生成一代表当前日期的类。如下所示:日期时间类的使用
非常简单,只需要创建一个类实例不带任何参数,即可以生成一代表
当前日期的类。如下所示:
Date today = new Date( )
System. Out. println(today,ToString( ));
或者也可用一更为简单的方式:
System. Out. println(today);
后一种方式中,println将自动调用toString方法,因而无需
显式调用。
6.3.3 向量类及其使用





1 向量和数组的异同
2 向量类的构造方法、属性和方法
3 建立向量实例
4 向量维护
5 对象查找
1. 向量和数组的异同
向量和数组存在许多相似之处,它们均可用以保存
列表对象。数组只能保存固定大小的列表,它必须一次申
请所有的存储单元。
尽管向量与数组相比有许多重要的优点,但它也有
一些不足之处,其中之一是它不能直接存储简单数据类型。
1. 向量和数组的异同
下列场合更适合于使用向量:
下面这些场合适合于使用数组
1) 如果你需要频繁进行对象
的插入和删除工作,或者因为
需要处理的对象数目不定;
2) 列表成员全部都是对象,
或者可以用对象方便地表示;
3) 需要很快确定列表内是否
存在某一特定对象,并且希望
很快了解到对象的存放位置。
1) 所需处理的对象数目固定,
或大致可以确定,尽管具体哪
些对象经常发生变化;
2) 所需处理的是简单数据类型。
另一方面,由于向量只能存储对象,如果需要把简单数据类型保存到向
量,必须使用Java的数据类型类,因此,有些场合下使用数组反而比使
用向量要方便一些。
2. 向量类的构造方法属性和方法
要使用向量,首先必须创建一个Vector类实例,这通过向量的构造方
法创建。
向量类共有3种形式的构造方法,如下所示:
Vector(int capacity, int capacityIncrement);
用指定的向量容量及其增量参数,创建一个空向量。
Vector(int capacity);
用给定的向量容量参数,创建一空向量。
Vector( );
创建一空向量。
向量容量通常是一个大于向量实际元素个数的整数;容量增量则规
定了每当向量元素个数达到极限时,需一次性扩充的向量容量大小。
2 . 向量类的构造方法属性和方法
除构造方法外,向量类还提供了3个属性变量,如下所示
protected int capacityIncrement;
当向量大小不足时,所用的增量大小
protected int elementCount;
向量的元素个数
protected Object elementData[ ];
向量类提供了极为丰富的方法,下面是其中一些主要的方法:
public final synchronized void copyInto(Object
anArray[];把向量元素拷贝到指定数组
public final synchronized void trimToSize();把向量容量
调整到正好等于向量元素个数以压缩向量的存储空间。
public
final synchronized void setSize(int newSize);设置向量大小。
public final int capacity();返回向量容量。
public final int size();返回向量的元素个数,注意和
capacity( )之间的区别。
2. 向量类的构造方法属性和方法
public final Boolean isEmpty();若向量不包括任何元素,返
回true;否则返回false。
public final synchronized Enumeration elements();返回
向量元素所对应的枚举值,以便随后用Enumeration( )方法获取该向
量元素。
public final Boolean contains (Object elem);若向量中包
括了对象elem,返回true ;否则返回false。
public final int indexOf (Object elem);返回向量下标;
若对象不存在,返回-1。
public final synchronized int indexOf (Object elem,int
index);从指定位置(index)开始搜索向量,返回对象所对应的向量
下标值。若未找到对象,返回-1。
public final int lastIndexOf (Object elem);从向量末尾
向前搜索向量,返回对象的下标值。
public final synchronized int lastIndexOf (Object
elem,int index);从指定位置开始向前搜索向量,返回给定对象的下
标值。
2. 向量类的构造方法属性和方法
public final synchronized Object elementAt (int index);
返回指定下标处的对象。
public final synchronized Object fistElement ();返回
向量的第一个元素。
public final synchronized Object lastElement ();返回
向量的最后一个元素。
public final synchronized void setElementAt (Object obj,
int index);把给定对象存放到给定下标处,该下标处的原有对象丢
失。若下标值非法,抛出异常情况ArrayIndexOutOfBoundsException。
public final synchronized void removeElementAt (int
index);删除给定下标处的向量元 素,后面元素前移一个位置。若
下标值非法,抛出异常情况ArrayIndexOutOfBoundsException。
2. 向量类的构造方法属性和方法
public final synchronized void insertElementAt
(Object obj,int index);把给定对象插入到指定下标处。该下标之
后的元素后移一个位置。若下标值非法,抛出异常情况
ArrayIndexOutOfBoundsException。
public final synchronized void addElement (Object
obj);把给定对象增加到向量末尾。
public final synchronized boolean removeElement
(Object obj);从向量中删除指定对象。 若给定对象在向量中保存多
次,则只删除其第一个实例。若向量中没有这个对象,返回false。
public final synchronized void removeAllElements ( );
删除向量中的所有对象。这时向 量将变成空向量。
public final synchronized String toString ( );把向量
转换成字符串。请注意:这个方法
实际上覆盖了Object类中的
toString()方法。
3 . 建立向量实例
向量的内存空间
和数组一样,向量的内存空间通过new操作符实现的。
一个向量在被创建后,将自行在系统内部维护一个专用数
组,并在必要时对数组大小进行动态调整。比方说,如果
插入一个向量元素时,向量的空间需求超过了这个内部数
组的大小,向量将向系统申请一些新的内存空间。
Vector theVector = new Vector
4. 向量维护
创建向量实例之后,就可以把对象插入到向量。这时将用addElement( )
方法。 addElement( )方法把对象插入到向量末尾。
如下面的程序:
Vector theVector = new Vector( )
For( int i = 0; i<10; i++ )
{
Integer newInteger = new Integer(i);
theVector. addElement( newInteger );
}
向量可用以保存任何类型的对象,同一个向量实例甚至还可以保存多种不
同类型的对象。下面我们给出另外一个例子,这个例子中,给向量交错插
入一些浮点数和字符串。
Vector theVector = new Vector( );
4 . 向量维护
new testString;
testString = ″pai″;
theVector.addElement( testString );
Integer testFloat = new Float( 3.14 );
theVector.addElement( testFloat );
testString = ″Faill″;
theVector.addElement(testString);
Integer testFloat = new Float( 59 );
theVector.addElement( testFloat );
执 行 这 段 代 码 , 向 量 将 包 括 如 下 内 容 : {″pai″, 3.14,
″Faill″, 59} 。
增加向量元素的另一方式是通过insertElementAt( )方法。该方法
把对象插入到向量的给定位置。
4. 向量维护
另一经常用到的维护操作是从向量中删除一个对象。
这时有3个方法可用:
removeFlement() 删除指定对象。
removeElementAt() 删 除 给 定 位 置 处 的 对 象 。
removeAllElement()则删除所有向量成员。
对于前两个方法需要注意的是删除之后整个向量的变
化:删除一个对象后,后面的对象将前移一个位置。
对于前面例子中介绍的向量,下面语句将删除其中的
3.14和6059,而不是3.14和″pass″”pai”。
Integer deleteFloat = new Float(3.14);
theVector.removeElement(deleteFloat);
theVector.removeElementAt(2);
5. 对象查找
与数组相比,向量的优势之一在于它提供了丰富的
方法,使我们能够方便快捷地从向量中查找对象。对象查找
中最常遇见的可能是给定一个下标值,希望由此确定该下标
处的对象。这时可使用elementAt( )方法。
有时我们可能需要确定向量中是否包括了某一确定的
对象,这如果使用数组是不太容易实现的。使用向量就方便
得多,因为它有一个contains( )方法,可直接实现此项功
能。
我们再来看用contains()方法:
public final Boolean contains(Object elem);
这个方法返回一布尔值。若向量包含了对象elem,
返回true ,否则返回false。
5. 对象查找
利用下标进行访问
我们经常用下标来访问数组元素,这是使用数组的最有
效方法。向量成员也可用下标进行访问, 方法有:
elementAt() 它可以返回给定下标处的对象。
indexOf()
lastIndexOf()
5. 对象查找
下面我们再来看一下indexOf ()和lastIndexOf()方法:
public final synchronized int indexOf(Object
elem, int start_index )
Public
final
synchronized
int
lastIndexOf(Object elem, int start_index )
这两个方法均用以从给定下标开始搜索向量,确定
向量中是否包含了指定的对象。如果是,则返回该对象的
下标值,否则返回-1。
差别:indexOf()是从前往后搜索,而lastIndexOf()是从
后向前搜索。
6.3.4 哈希表类及其应用
Java的哈希表用于存储对象。

1 哈希表类的构造方法、属性变量和成员方法

2 哈希表的使用
1. 哈希表的构造方法,属性变量和成员方法法
1构造方法
哈希表类(Hashtable)共有三种形式的构造方法:
public Hashtable(int initialCapacity,float
loadFactor);/*参数initialCapacity给定哈希表创建初始
可容纳的元素数,loadFactor为装载因子,取值0.0到1.0之
间.*/
public Hashtable(int initialCapacity); 参数
initialCapacity给定哈希表创建初始可容纳的元素数,
loadFactor为缺省值。
public Hashtable();参数initialCapacity和
loadFactor)为均为缺省值。
2 哈希表类没有定义属性变量
1. 哈希表的构造方法,属性变量和成员方法
3 成员方法
哈希表类定义的主要方法如下:
public int size();返回哈希表大小(即表中元素个数)。
public boolean isEmpty();确认哈希表是否为空。
public synchronized Enumeration keys();返回哈希表示内关
键字枚举值。
public synchronized Enumeration elememnts();返回有关内
元素的一个枚举值对象,随后Emumeration类方法顺序获取对象。
public synchronized booleancontains(Objectvalue);确认哈
希表内是否包括了给定的对象,与containKey()类似。
public synchronized boolean contains(Object key);确认哈
希表内是否包括了给定的关键字。
public synchronized Object get(Object key);获取对应关键
字的对象,如不存在返回null。
1. 哈希表的构造方法,属性变量和成员
方法
protected void rehash();再哈希,扩充哈希表使之可以保存更多
的元素。当哈希表达到饱和时,系统将自动调用此方法。
public synchronized Object put(Object key,Object value);
用给定的关键字把对象保存到哈希表中。随后该对象即可通过同样的关
键字由get方法获取。这里的关键字和元素均不可为空。
public synchronized Object remove(Object key); 从哈希表
中删除与给定关键字相对应的对象,如该对象不存在返回null。
public synchronized void clear();从哈希表中删除所有对象,
使其为空。
public synchronized String toString();把哈希表内容转换为
一字符串,此方法复盖了Object类的toString()方法。
2.哈希表的使用
哈希表创建与Java中其它对象一样需使用new操作,
如下例所示:
HashTable hash_1=new HashTable();
为了把对象保存到哈希表,必须为每一个对象分配一关键
字。关键字可以为任意对象,但必须实现了hashCode()和
equals()方法。Java提供的类几乎都提供了这两个方法。
建好了哈希表后,现在利用上述put方法把数据存放到哈希
表中去。
hash_1.put(“one”,new Integer(1));
hash_1.put(“two”,new Integer(2));
hash_1.put(“three”,new Integer(3));
2.哈希表的使用
为了把对象从哈希表中删除,须使用remove()方法。
hash_1.remove(“two”);
这时对象Integer(2)将从哈希表删除,同时返回这
个被删除的对象。如指定的关键字不存在,remove()返回
null。
使用哈希表类的get()和containKey()访问方法可以查找关键
字。
例如:
Integer n=(Integer) hash_1.get(“three”);
If (n!=null {
System.out.println(‘three=”+n);
}
如果查找的关键字不存在,get返回null。
6.3.5 栈类
Java 语言规范中,栈类是向量类的子类,它满足FIFO(先进
后出)的要求。
栈类定义的主要方法如下:
public Stack(); //栈类构造方法。
public Object push (Object item);//把对象压入
栈。
public Object pop ();//从栈顶弹出一个对象。
public Object peek(); //读栈顶一个对象,但不
弹出。
public boolean empty();//测试栈是否为空,如是,
返回True,否则,返回False。
6.4






6.4.1
6.4.2
流
6.4.3
6.4.4
6.4.5
6.4.6
输入输出包java.io
流(stream)
基本输入输出
File类
字符流文件
字节流文件
过滤流
6.4.7
BufferedInputStream
6.4.8
BufferedOutputStream
6.4.9
DataInputStream和
DataOutputStream
6.4.10
ObjectInputStream和
ObjectOutputStream
6.4.11
RandomAccessFile类
6.4.12
读写文件实例
6.4.1 流(stream)
数据源
数据宿
大多数程序所处理的数据都要从外部输入,即这些数据要从数据
源(source)获得,数据源指提供数据的地方;而程序的运行结果又
是要送到数据宿(destination),数据宿指接收数据的地方。
数据源和数据宿的这种多样性,常常造成程序处理输入/输出的
复杂性。另一方面,就程序而言,他们希望所有的输入/输出操作都能
够有一个相对统一的、简单的操作方式,而不管输入/输出所涉及的数
据源和数据宿是怎样的不同和多样。
流及有关的流类
Jave引入的“流”以及有关的“流类”就是用于解决由于数
据源和数据宿多样性而带来的输入/输出操作的复杂性与程序员所希
望的输入/输出操作的相对统一简单之间的矛盾的有效办法。
6.4.1 流(stream)
“流”可以被理解为一条“管导”。这条“管导”有两个
端口:一端与数据源(当输入数据时)或数据宿(当输出数据时)相连,
另一端与程序相连。在与数据源或数据宿相连的端口,“管导”在读写
数据时能够应付数据源和数据宿的多样性,消化掉因数据源和数据宿的
多样性带来的数据读/写的复杂性;而在与程序相连的端口,“管导”
提供了输入/输出的统一操作界面。由于在程序和数据源/数据宿之间建
立了“导管”,使得程序输入/输出时原本直接对数据源和数据宿的繁
杂
图6-5
6-6
图
有了流,程序和外界的数据交换,都可通过流实现。当程序要
从数据源获得数据时,必须在程序和数据源之间建立输入流(如图65);当程序要把结果输送到数据宿时,必须在程序和数据宿之间连接
建立输出流(如图6-6)。
6.4.1 流(stream)
根据流中的数据传输的方向,将流分为输入流和输出流。
根据“管导”里流动的数据的类型,将流分为字符流(Character
Streams)和字节流(Byte Streams),字符流以字符为传输单位,而字
节流以字节为传输单位。
根据流的建立方式和工作原理,将流分为节点流(Node Streams)与过
滤流(Filter Streams),节点流是直接建立在输入、输出媒体之上的,
而过滤流必须以某一个节点流作为流的来源,可以在读/写数据的同时对
数据进行处理。
虽然Java语言提供了种类繁多的流,但这些流被很好地组织成
树状结构,并不显得错综复杂。一旦熟练地掌握I/O流,编程效率就会
有显著的提高,图6-7 为java 字节流输入输出接口、类的继承图。
6.4.2 基本输入输出流



1 InputStream类
2 OutputStream类
3 System.in和System.out对象
1. inputstream类
InputStream类表示基本输入流,它定义了下面列出的一套所
有输入流都需要用的方法:
public abstract int read() throws IOException 从流中读取
一个字节数据。
public int read(byte b[]) throws IOException 从流中读取
数据并存放到数组b中,同时返回读取到的字节数。
public int read(byte b[],int off,int len) throws
IOException
从流的指定地方开始读取指定长度的数据到数组b中,
返回读取到的字节数。
public long skip(long n) throws IOException 跳过流中的
指定直接数。
public int available() throws IOException 返回当前流中
可用的字节数。
1. inputstream类
public void close() throws IOException 关闭当前流对象。
public synchronized void mark(int readlimit) 在流中标
记一个位置。
public synchronized void reset() throws IOExeeption //
返回流中标记过的位置。
public boolean markSupported() 返回一个流是否支持标记
和复位操作的布尔值。
InputStream 类中的read()方法被重载,它共提供了三种从流
读数据的方法,定义如下所示:
int read() 从流中读取一个字节并将该字节作为整数返回,
若没有数据则返回-1。
1. inputstream类
int read (byte b[]) 从流中读取多个字节到字节数
组b[]中,同时返回实际读取到的字节的数量。
int read (byte b[], int off, int len) 与上一
种方式相同,它同样读取数据到字节数据b[]中,同时可以
在数组中指定一个单元开始存储字符,同时标识所读字符
的最大数量。
当程序中调用InputStream进行请求时,所调用的方
法就处于等待状态,如下例所示:
try
{
int byte=System.in.read();
}
catch(IOException e)
{
1. inputstream类
System.out.println(e.toString());
}
当程序运行到System.in.read()的时候就等待用户
输入且直到用户输入一个回车键为止。
2. outputstream类
与InputStream类相对应的输出流是OutputStream,它
具有所有输出流类的基本功能,同InputStream类相似,它
具有以下相应的方法:
public abstract void write(int b) throws IOException
向流中写一个字节。
public void write(byte b[]) throws IOException 向流中
写入一个字节数组。
public void write(byte b[],int off,int len) throws
IOException 写入len长度从数组b的第off个位置开始的数据。
public void flush() throws IOException 清空流并强制将
缓冲区中的所有数据写入到流中。
void write (int b) 将一个字节写入到流中。
void write (byte b[]) 将字节数组b[]中的所有字节写入到
流中。
void write (byte b[], int off,int len) 将字节数组b[]
指定的开始单元和指定的字节数写入到流中。
3. system.in和system.out对象
为了支持标准的输入输出设备,Java定义了两个流的对象,
静态成员。System.in可以从键盘中读入数据,System.out可
以将数据输出到显示屏。
例6-1 用System.out和System.in对象处理输入和输出
//源程序清单
Class IOTest {
Public static void main(String[]args) {
Byte buffer[]=new byte[255];
System.out.println(″请在下面输入一行字符:n\″);
try {
System.in.read(buffer,0,255);
//System.in.read抛出异常
}
3. system.in和system.out对象
catch(Exception e) { //捕获异常并处理
System.out.println
(“读取输入字符出错,错误信息为:+e.tostring()+\n);
}
System.out.println(“您刚才输入的一个字符为:\n”);
String inputStr=new String(buffer,0);
System.out.println(inputstr);}
}
在该例中,首先利用System.out类在显示器上显示一行字符
“请在下面输入一行字符”,然后利用System.int等待用户输入字符,
最后将用户所输入的字符重新在显示器中输出。在接收用户输入数据
时可能出错,在本例中还对System.in捕获异常,若出现异常则在显示
器中将异常信息显示出来。
6.4.3 file类
文件
目录 路径
文件(file)是存储在辅助存储器中的一组相关信息的集合,它
可以存放程序、文档、图片、声音或视频信息等。为了便于对文件管
理,系统允许用户给文件设置或取消有关的文件属性,如只读属性、
隐藏属性、存档属性、系统属性。
目录(directory)是一种特殊的文件,用以存放普通文件或其
他的目录。。路径(path)是从盘符经过各级子目录到文件的目录序
列。由于文件可以在不同的磁盘、不同的目录中,所以在存取文件时,
必须指定文件的存放位置。
6.4.3 file类
缺省目录 相对路径 绝对路径
文件的存放位置通过路径来描述,路径的表示格式为:盘符\目
录名\…\文件名,比如在C盘根目录下的子目录WINDOW下子目录JAVA中
的文件myfile.java,应表示为
C:\WINDOW\JAVA\myfile.java
其中,C:为驱动器名,表示C盘,而\WINDOW\JAVA\表示
myfile.java所存储的位置,其中第一个“\”表示根目录,其余的“\”
为各子目录之间、最后一级子目录与文件名之间的分隔符。
6.4.3 file类
例如,假使上述文件myfile.java完整的文件标识是
C:
\WINDOW\JAVA\myfile.java,如果当前磁盘
是C盘,可写成\WINDOW\JAVA\myfile.java;如果当前目录
是C:\WINDOW,可写成JAVA\myfile.java;如果当前目录
是C:\WINDOW\JAVA,可改写成myfile.java。
在Java中,目录也被当作文件,可以用list方法列出目录
中的文件名。
创建文件对象有三种构造方法,我们引用三个例子
分别创建了三个文件对象:file1、file2、file3,它们都
指向文件“c:\cgi-bin\demo.java”。
6.4.3 file类
File类具有下面三种构造函数:
File (String path)
File (String path,String name)
File (String dir,String name)
其中参数如下所示:
String path——指定的路径;
String name——执行的文件名;
String dir——执行的目录;
6.4.3 file类
下面列出了File类的有关方法:
public String getName() 得到文件名。
public String getPath()
返回文件路径。
public String getAbsolutePath() 返回文件绝对路径。
public String getCanonicalPath() throws IOException
的规范路径。
public String getParent() 返回文件的父目录。
public boolean exists()
判断文件是否存在。
public boolean canWrite() 判断文件是否可写。
public boolean canRead() 判断文件是否可读。
public boolean isFile()
判断对象是否是文件。
public boolean isDirectory() 判断对象是否目录。
返回文件
6.4.3 file类
public native boolean isAbsolute()
如果文件名为绝对名则返
回真。
public
public
public
public
public
public
public
指定文件。
public
public
public
public
long lastModified() 返回文件最后修改日期。
long length() 返回文件长度。
boolean mkdir() 创建目录。
boolean rename To(File dest) 重命名文件。
boolean mkdirs() 创建目录及子目录。
String[] list()
列出目录下的所有文件和目录。
String[] list(FilenameFilter filter) 列出目录下的
boolean delete() 删除文件对象。
int hashCode()
为文件创建散列代码。
boolean equals(Object obj) 判断是否同对象obj相等。
String toString() 返回文件对象的字符串描述。
6.4.3 file类
从上面方法中可以看出,File类不仅仅是对现有目
录路径、文件和文件组的一个表示,它还可以利用File对
象直接新建一个目录,甚至创建一个完整的目录路径(如
果它还不存在的话);
如果File对象是一个目录,可以调用其list方法列
出File对象中包含的完整列表(包含File对象的文件夹和
文件),若要对这个文件列表作出某种选择,就需要使用
一个“目录过滤器”(也是一种“文件过滤器”),该类
的作用就是按一定条件选择File对象。
6.4.4 字符流文件
上节介绍了如何创建一个文件,但是我们更需要了解
往文件里写数据或读出文件中的内容,这些是通过文件输
入流、文件输出流来实现的。按数据交换的单位不同,文
件流可分为字符流、字节流两种,这一节中,我们以字符
为单位进行文件访问,以字节为单位的方式在下一节介绍。


1 文件输入流(FileReader)
2 文件输出流(FileWriter)
1 文件输入流(FileReader)
因为大多数程序会涉及文件读/写,所以FileReader类
是一个经常用到的类,FileReader类可以在一指定义文件
上实例化一个文件输入流,利用流提供的方法从文件中读取
一个字符或者一组数据。下面的两种构造方法使用同一个磁
盘文件来创建两个文件输入流。这两个构造方法都有可能出
现FileNotFoundExcption异常。
(1)构造方法一:
FileReader f0=new FileReader(″c:\cgibin\demo.java″);
(2)构造方法二
File f=new File(″c:\cgi-big\demo.java″);
FileReader f1=new FileReader(f);
相对来说,第一种方法使用更方便一些,构造一个
输入
1 文件输入流(FileReader)
流,并以文件c:\cgi-bin\demo.java为输入源。第二种方
法构造一个输入流,并使File的对象f和输入流相连接。这
种情况下,我们还可以通过对象f对该文件作进一步的分析,
比如,显示文件的属性、大小等。
FileReader类的最重要的方法是read,FileReader有三种read
方法。
(1)read( ),作用是返回下一个输入的字符的整型表示。
(2)read(char b[ ]),作用是读入字符放到字符数组b
中并返回实际读入的字符数。如果所定义的字符数组容量小于
获得的字符数,则运行时将产生一个IOException例外情况。
(3)read(char b[ ],int off,int len),其作用为读
入len个字符并从下标off开始放到数组b中,并返回实际读入
的字符数。
2 文件输出流(Filewriter)
由FileWrinter类可以实例化一个文件输出流,并提
供向文件中写入一个字符或者一组数据的方法。
FileWriter也有两个和FileReader类似的构造方法。如果
用FileWrite来打开一个只读文件会产生IOException异常。
6.4.5 字节流文件
平常所使用的文件中,有很多是二进制文件,它们以
字节作为数据处理单位。对这些文件就要使用字节流来读
写了,其实字符文件也可以用字节流来进行读写。
FileInputStream和FileOutputStream分别完成字节流文件
的读写。


1 FileInputStream
2 FileOutputStream
1 . fileinputstream
FilelnputStream类方法总结如下:
int available() 返回可读入的字节数;
void close() 关闭输入流,并释放任何与该流有关的资源;
protected void finalize () 当读到无用信息时,关闭该流;
File Descriptor get FD () 返回与该流有关的文件描述符 (即文件
的完整路径);
int read() 从输入流中读取一个字节的数据;
int read(byte[ ]b) 将数据读入到一个字节数组中;
int read(byte[ ]b,int off,int len) 将数据读入到一个字节型数组中;
long skip(long n) 跳过输入流上的n个字节;
2. fileoutputstream
FileOutputStream类方法总结如下:
void close() 关闭输出流,并释放任何该流有关的资源;
protected void finalize() 当写到无用信息时,关闭该流;
File Descriptor get FD () 返回与该流有关的文件描述
符(即文件的完整路径);
void write(in b) 将字节数据b写以入输出流中;
void write(byte[ ]b) 将一个字节数组中的数据写到输出
流中;
long skip(long n) 跳过输出流上的n个字节。
6.4.6 过滤流
过滤流必须建立在节点流之上,对节点流中的数据
进行某些加工、处理,并提供一些友好的方法供用户进行
输入、输出操作以及流控制。在程序设计中,连接是通过在
过滤流的构造方法中指定入口参数——节点流来实现的。
如:
FileInputStream in=new
FileInputStream(“text”);
BufferedInputStream bufin=new
BufferedInputStream(in);
实现了过滤流bufin和文件输入流in连接。
6.4.7
BufferedIputStream
对I/O进行缓冲是一种常见的性能优化方法。Jave的
BufferedInputStream类可以对任何的InputStream流进行
带缓冲的封装以达到性能的改善。该类在已定义输入流上
再定一个具有缓冲的输入流,可以从此流中成批地读取字
符而不会每次都引起直接对数据源的读操作。数据输入时,
首先被放入缓冲区,随后的读操作就是对缓冲区中的内容
进行访问。
该类有两个构造方法:
1)public BufferedInputStream(InputStream in)
2)public BufferedInputStream(InputStream
in,int size)
6.4.7 BufferedIputStream
两种构造方法都有是为某种输入流in 创建一个缓冲流
方法一
创建的缓冲大小为
缺省值(32bytes)
方法二
用户指定缓冲区大小
对方法二,在性能优化时,通常都把size的值设定为内存页
大小或I/O块大小的整数倍。在I/O量不大时,该类所起作
用不是很明显,但当程序I/O量很大,且对程序效率要求很
高时,使用该类就能大大提高程序的效率。
对输入流进行缓冲可以实现部分字符重复使用。除了
InputStream中常用的read和skip方法, BufferedInputStream
还支持mark和reset方法。
注意mark只能严格限制在建立的缓冲区内。
6.4.8 BufferedOutputStream
BufferedOutputStream类在已定义节点输出流上再定
义一具有缓冲功能的输出流。用户可以向流中写字符而不
会每次都引起直接对数据宿的写操作,只有在缓冲区已满
或清空流(flush)时,数据才输出到数据宿上。在Java中
使用输出缓冲是为了提高性能。
该类有两个构造方法:
1)public BufferedOutputStream(OutputStream
out)
2)public
BufferedInOutputStream(OutputStream out,int size)
6.4.9 DataInputStream和
DataOutputStream
DataInputStream用来从一种已定义的节点输入流中
读取Jave基本数据类型的数据,如布尔型数、整型数、浮
点数等,然后再生成一个数据输入流。
DataOutputStream用来将Jave基本数据类型数据写
到一个数据输出流中。
这两个类都是在某节点流上再定义一个数据输入
(输出)流,通过它们,用户可以很方便地按照Jave原始
数据类型来读(写)数据。
6.4.9 DataInputStream和
DataOutputStream
这两个类的构造方法如下:
1)pubilc DataInputStream(InputStream in),其
作用是创建一个新的DataInputStream,该流从输入流in
读取数据。
2)pubilc DataOutputStream(OutputStream
out),其作用是在输出流out上创建一个新的
DataOutputStream,使DataOutputStream的输出数据能够
输出到输出流out中。
6.4.10 ObjectInputStream和
ObjectOutputStream
ObjectInputStream和ObjectOutputStream 是针对
对象的输入输出。它的使用通过下面的例子来介绍。
例6-4 把Hashtable存放的信息输出到磁盘文件中,并再从中读进
Hashtable.
packege ObjectIO
//
import java.io.*;
import java.util.Hashtable;
public class ObjectIOClass extends implements Serializable {
public static void main(String args[]) throws Exception {
Hashtable ht=new Hashtable();
//建立Hashtable 的对象实例
6.4.10 ObjectInputStream和
ObjectOutputStream
DataInputStream dis=new DataInputStream(System.in);
//建立DataInputStream对象,并且与标准输入对象连接。
String st_no=dis.readLine();
//读st_no
String st_rec=dis.readLine();
//读st_rec
ht.put(st_no,st_rec);
//s1作关键字,s2为Hashtable保存的对象,存入ht
System.out.println(ht);
String st_no1=dis.readLine();//读st_no1作查找关键字
if (ht.containsKey(st_no1))
//如Hashtable对象中有s3,打印对应的对象
System.out.println(ht.get(s3));
else System.out.println("error");
6.4.10 ObjectInputStream和
ObjectOutputStream
ObjectReadWrite my_object=new ObjectReadWrite ();
//建立文件对象
my_object.write(ht);
//如ht对象输出到输出源
my_object.read();
//从输入源读入对象
}
}
public class ObjectReadWrite {
static File object_file=new File("d:\","data");
public static void write(Hashtable ht ) {
try{
//建立FileOutputStream对象,并且与文件对象
object_file 连接
FileOutputStream fos=new FileOutputStream(object_file);
6.4.10 ObjectInputStream和
ObjectOutputStream
//建立ObjectOutputStream对象,并且与文件对象fos连接
ObjectOutputStream oos=new ObjectOutputStream ( fos);
oos.writeObject(ht);
//Hashtable对象内容写入输出文件中
//o1.flush();
oos.close();
}
catch(Exception e){};
}
public static void read()
{
//从对象输入文件中读对象
Try
{
6.4.10 ObjectInputStream和
ObjectOutputStream
//建立ObjectOutputStream对象,并且与文件对象object_file 连接。
ObjectInputStream ois=new ObjectInputStream( new
FileInputStream(object_file ));
Hashtable
ht1=(Hashtable)ois.readObject();
//从输入源读入对象
System.out.println(ht1);
//标准输出ht1
}
catch(Exception e){
e.printStackTrace();
}
}
}
6.4.11 RandomAccessFile类
通过构造RandomAccessFile类,它有以下的构造函数。
RandomAccessFile(String filename,String
mode)
RandomAccessFile(File file,String mode)
其中参数如下所示:
String filename——指定的文件名。
String mode——指定的操模式。
File file——指定的文件对象。
其中参数mode用来说明该文件是“只读”还是“读写”
的,即它是“r”或“rw”,如果不是这两种情况,将产生
一外IllegalArgumentException异常。
6.4.11 RandomAccessFile类
RandomAccessFile类的方法非常丰富,下面列出的就是它的方法。
public final File Descriptot get(FD()throws IOException
返回文件的FileDescriptor
public native int read() throws IOException
public int read(byte b[],int off,int len)throws
IOException
public int read(byteb[])throws IOException 读取字节数据
public final void readFully(byte b[])throws IOException
public final void readFully(byte b[],int off,int,len)
throws IOException
public int skipBytes(int n) throws IOException 跳过指
定的数据
6.4.12 读写文件实例
下面将通过一个文件或文件夹拷贝的实例来说明文件
读写的过程。例6-5 用户在主窗口的源文件栏中输入或点
击“浏览”按钮,在弹出的对话框中选择需要拷贝的源文
件或文件夹,再在目标文件栏中选择目标文件或文件夹
(允许不存在,系统将自动创建)。点击“复制”按钮进
行拷贝,同时在右面的列表框中列出当前正在拷贝的文件
名。应用程序执行界面如图6-7所示。下面将具体分步实现
这个过程。



1 创建项目并设计主界面
2 获取、浏览文件
3 文件拷贝
1. 创建项目并设计主界面
打开Jbuilder,选择菜单“File→New…”,在新建向导中分别创
建工程coptyFile.jpr和主窗体类MainFrame.java,并进入该窗口的
design视图,加入如图6-5所示的各个组件,在design视图中双击四
个按钮产生它们的点击事件。。
//源程序清单6-5-1
package CopyFile;
import java.awt.*;
import java.awt.event.*;
import borland.jbcl.layout.*;
import borland.jblcl.control.*;
public class MainFrame extends Frame {
TextField textFieldl= new TextField();
Button button1 =new Button();
Label labe11 =new Label();
1. 创建项目并设计主界面
Label labe12 =new Label();
TextField textField2 =new TextField();
Button button2= new Button();
Button button3= new Button();
Button button4= new Button();
Label labe13 =new Label();
Panel Pane11 =new Panel();
PaneLayout paneLayout1=new PaneLayout();
PaneLayout paneLayout2=new PaneLayout();
GroupBox groupBox1=new PaneLayout();
List list1 =new List();
PaneLayout paneLayout3=new PaneLayout();
public MainFrame() {
try{
1. 创建项目并设计主界面
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setSize(new Dimension(454,154));
this.setTitle(″拷贝文件″);
this.setBackground(new Color(192,192,192);
button.setLabel(″浏览″…);
button1.addActionListener(new
MainFrane_button1_actionAdapter(this));
1. 创建项目并设计主界面
//按钮的监听器接口类MainFrane_button1_actionAdapter由
Jbuild生成
labell.setAlignment(2);
labell.setText(″源文件:″);
labell.setAlignment(2);
labell2.setText(″目的文件″);
button2.setLabel(″复制″);
button2.addActionListener(new
MainFrame_button2_actionAdapter(this));
//按钮的监听器接口类MainFrane_button2_actionAdapter由
Jbuild生成
button3.setLabel(″退出″);
button4.addAactionListener(new
MainFrame_button4_actionAdapter(this));
1. 创建项目并设计主界面
//按钮的监听器接口类MainFrane_button4_actionAdapter由Jbuild生
成
button3.addAactionListener(new
MainFrame_button3_actionAdapter(this));
//按钮的监听器接口类MainFrane_button3_actionAdapter由Jbuild生
成
this.setLayout(paneLayout1);
this.add(panel.new PaneConstraints
(″panell″, ″panell″,PaneConstraints.ROOT,0,5f);
panel1.add(groupBox1,new PanConstraints
(″groupBox1″,″groupBox1″,PaneConstraints.ROOT,1.of));
groupBox1.add(list1. new PaneConstraints
(″listl″, ″listl″,PaneConstraints.ROOT, I.of));
1. 创建项目并设计主界面
this.add(labell,new PanConstraints
(“lable3”, “panell”,PaneConstraints.LEET,0.6300448f);
this.add(labell. new PaneConstraints(“labell,“labell3”,
PaneConstraints,BOTTOM,0.68589747f)
this.add(label2,new PaneConstraints(“label2”,
“labell”,PaneConstraints,BOTTOM,0.6730769f));
this.add(textField,newPaneConstraints(“textField”,“lab
ell”,PaneConstraints,RIGHT,0.76868325f));
this.add(button1,newPaneConstraints(“textField”,“label
l”,PaneConstraints,RIGHT,0.76868325f));
this.add(button4,newPaneConstraints(“textField2”,“labe
l2”,PaneConstraints.RIGHT,0.7686325f));
this.add(button4,new PaneConstraints
1. 创建项目并设计主界面
(“button4”,“textField2”,PaneConstraints.RIGHT,0.34426
23f));
this.add(button3.new PaneConstrains
(“button3”,
“button2”,PaneConstraints.RIGHT,0.4798851f));
}
public static void main(String[] args) {
Main Frame frame=new MainFrame();
frame.setLocation(200,200);
frame.setSize(450,150);
frame.show();
}
void buttonl_actionPewrformed(ActionEvent e) {
}
1. 创建项目并设计主界面
void button2_actionPerformed(ActionEvent e)
{
}
void button3_actionPerformed(ActionEvent e)
{
}
void button4_actionPerformed(ActionEvent e)
{
}
上面程序所生成的四个按钮的监听器接口类由Jbuilder将其
添加到MainFrame.java类的后面。
//源程序清单6-5-1第2部分
class MainFrame_buttonl_actionAdapter implements
java.awt.event.ActionListener
1. 创建项目并设计主界面
{
MainFrame adapter;
MainFrame_buttonl_actionAdapter(MainFrame adaptee)
class MainFrame_button2_actionAdapter implements
java.awt.event.ActionListener
{
MainFrame adaptee;
MainFrame_butto2_actionAdapter(MainFrame adaptee)
{
this.adaptee=adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button2_actionPerformed(e);
1. 创建项目并设计主界面
}
}
class MainFrame_button3_actionAdapter implements
java.awt.event.AactionListener
{
MainFrame adaptee;
MainFrame_button3_actionAdapter(MainFrame adaptee) {
this.adaptee=adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button3_actionPerformed(e);
}
1. 创建项目并设计主界面
}
class MainFrame_button4_actionAdapter implements
java.awt.event.ActionListener
{
MainFrame adaptee;
MainFrame_button4_actionAdapter(MainFrame adaptee) {
this.adaptee =adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button4_actionPerformed(e);
}
}
2. 获取浏览文件
我们可以调用系统的Filer组件来实现对系统文件夹的选取。
Filer组件只作为一个对话框来显示打开或保存一个文件,用户可以通
过对话框选择一个文件来打开或者是保存,而该组件用来打开或保存
一个文件可以通过设置其mode属性来决定,
其构造函数有四种方式:
public Filer():直接实例化一个对象,其它的属性以后再进行设置;
public Filer(java.awt.Frame frame):创建一个以frame为父窗
体的对象;
public Filer(java.awt.Frame frame,String title):创建一个以
frame为父窗口,以title为标题的对象;
public Filer(java.awt.Frame.String title,int mode):创建一个
以frame为父窗口,以title为标题,类型为mode的对象,mode有两
种取值,即:
Filer.load——打开一个文件
Filer.save——保存一个文件
2. 获取浏览文件
实例化Filer对象后,可以通过调用其适当的方法来得到其
需要打开或保存的文件目录.
Filer具有很多方法可以实现对Filer组件的控制:
public String getDirectory() 返回选择后的文
件目录:
public void setDirectory(java.lang.String
dir) 设置文件目录;
public String getFile() 返回选定后的文件名;
public void setFile(java.lang.String file)
设置文件名;
public Frame getFrame() 得到父窗体对象;
public void setFrame(java.awt.Frame frame)
设置父窗体对象;
2. 获取浏览文件
public int getMode(int m) 设置打开保存方式;
public String getTITLE() 得到标题;
public void setTitle(java.lang.String title) 设置标题;
public boolean isVisible() 返回是否可见;
public void setVisible(boolean visible)设置是否可见;
public void show() 显示组件。
在MainFram类中添加两个Filer组件,并实现两个浏览按钮,将用户
选择的打开(源)文件和保存(目的)文件路径显示在文本框中,实
例化Filer组件,具体代码如下所示:
Filer filer1=new Filer(this,″this,选择源文件″,Filer.load);
Filer filer2=new Filer(this,″this,选择目的文件″,Filer.save);
2. 获取浏览文件
同时添加两个按钮的点击事件,最后得到MainFrame类。(粗体为所添
加的代码)
/源代码清单6-5-2
import java.awt.*;
import java.awt.event.*;
import borland.jbcl.layout.*;
import borland.jbcl.control.*;
import java.io.*;
import java.util.*;
public class MainFrame extends Frame
{
TextField textField1 =new TextField();
Button button1=new Button();
Label labe11=new Label();
2. 获取浏览文件
Label labe12=new Label();
TextField textField2=new TextField();
Button button2 =new Button();
Button button3 =new Button();
Filer filer1 =new Filer(this,″this,选择源文件″,Filer.LOAD);
Filer filer2 =new Filer(this,″this,选择目的文件″,Filer.SAVE);
Button button4= new Button();
Label labe13 =new Label();
Panel pane11 =new Panel();
PaneLayout paneLayout1=new PaneLayout();
PaneLayout paneLayout2=new PaneLayout();
GroupBox groupBox1=new GroupBox();
2. 获取浏览文件
List list1=new List();
PaneLayout paneLayout3=new PaneLayout();
Public MainFrame() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
this.setSize(new Dimension(454,154));
this.setBackground(new Color(192,192,192));
button1.setLabel(″浏览″...″);
2. 获取浏览文件
button1.addActionListener(new
MainFrame_button1_actionAdapter(this));
button1.addActionListener(new
MainFrame_button1_actionAdapter(this));
labell.setAlignmetnt(2);
labell.setText(″源文件:″);
labe12.setAlignement(2);
label2.setText(″目的文件″);
button2.setLabel(″复制″);
button3.addActionListener(new
MainFrame_button2_actionAdapter(this));
button3.setLabel(″退出″);
button3.addAactionListener(new
MainFrame_button3_actionAdapter(this));
2. 获取浏览文件
button3.setLabel(″浏览″...);
button4.addActionListerer(new
MainFrame_button3_actionAdapter(this));
label3.setText(“请输入或选择源文件(夹)和目标文件(夹):”);
groupBox1.setLayout(panLayout3);
groupBox1.setLablel(″下载队列″);
this.setLayout(paneLayoutl);
this.add(pane11,new PaneConstraints
(″panell″, ″panell″,PaneConstraints.ROOT,0.51));
pane11.add(groupBox1.new PaneConstraints
(“groupBox1”,“groupBox1”,PaneConstraints.ROOT,1.0f));
groupBox1.add(list1,new PaneConstraints
(″list1″, ″list1″,PaneConstraints.ROOT, 1.0f));
this.add(labe13,new PaneConstraints
2. 获取浏览文件
(“label3”, “panell” ,PaneConstraints.LEFT, 0.6300448f);
this.add(labe11,newPaneConstraints(“labe11”,“labe13”,Pane
Constraints.BOTTOM, 0.68589747f);
this.add(label2.newPaneConstraints(“labe12”,“label11”,Pane
Constraints.BOTTOM, 0.6730769f);
this.add(textField1,newPaneConstraints(“textField1”,PaneCo
nstrains.RIGHT, 0.76868325f);
this.add(button1.newPaneConstraints(“button1”,“textField1”
,PaneConstraints,RIGHT,0.3442623f);
this.add(textField2,newPaneConstraints(“textField2”,“label2,
PaneConstraints.RIGHT,0.76868325f);
this.add(button4,newPaneConstraints(“button4”,“textField2”
,PaneConstraints.RIGHT,0.3442623f);
this.add(button3,new PaneConstraints
2. 获取浏览文件
(“button3”,“buttonon2”,PaneConstraints.RIGHT,0.4798851f);
}
public static void main(String[]args) {
MainFrame frame=new MainFrame();
Frame.setLocation(200,200);
Frame.setSize(450,150);
Frame.show();
}
void button1_actionPerformed(ActionEvent e) {
filer.show(); //粗体为所添加的代码
String dir=filer1.getDirectory();
String file=filer1.getFile();
if(dir.equals(″null″)&&file.equals(″null″))return;
2. 获取浏览文件
textField1.setText(dir+file);
}
void button2_actionPerformed(ActionEvent e) {
String sourceName=textField1.getText();
String desName=textField2.getText();
if(sourceName.equals(“”‖desName.equals(“”))retrun;
pCopyFile=new
PrepareCopyFile(this.sourceName,desName);
pCopyFile.start();
}
void button3_actionPerformed(ActionEvent e)
{
messageDialog mdlg=new MessageDialog
(this, ″警告″,″确定要退出吗?″,6);
2. 获取浏览文件
mdlg.setSize(150.80);
mdlg.show();
if(mdlg.gteResult()==MessageDialog.NO)
retrun;
System.exit(0);
}
void button4_actionPerformed(ActionEvent e) {
filer2.show();
String dir=filer2.getDirectory();
String file=filer2.getFile();
if(dir.eqals(″null″)&&file.equals(″null″))reurn;
textField2.setText(dir+file);
}
}
3. 文件拷贝
在拷贝工作开始之前,还需要进行一些准备工作,
首先就是实现向列表框中添加新拷贝的文件和删除已经拷
贝完成的文件。
//源代码清单6-5-3
Void addCopyingFile(String sname) {
File file=new File(sname);
String sfilename=file.getName();
List,addItem(sfilename);
}
viod removeCopyingFile(String sname) {
File file=new File(sname);
String sfilename=file.getName();
List,remove(sfilename);
}
3. 文件拷贝
然后完成拷贝按钮和退出按钮的事件。拷贝按钮事件
的具体代码如下所示:
String sourceName=textFieldl.getText();
String.desName=textField2.getText();
if(sourceName.equals(“”)‖desName.equals(“”))retrur
n;
pCopyFile=new
PrepareCopuFile(this.sourceName,desName);
pCopyFile.start();
应用程序首先从文本框中得到用户选择或输入的源文
件或目标文件,如果源文件和目标文件都有不为空,就实例
化拷贝线程PrepareCopyFile(该线程目前还不存在),当
然首先还必须在类中进行对象定义,方法如下:
PrepareCopyFile pCopyFile=null;
3. 文件拷贝
退出按钮事件的具体代码如下所示:
MessageDialog mdlg=new Message Dialog
(this, ″警告″,″确定要退出吗?″,6);
mdlg.setSize(150.80);
mdlg.show();
if(mdlg.getResult()==MessageDialog.NO)
retrun;
System.exit(0);
MainFrame类全部源程序清单如下:
//源程序清单6-5
package CopyFile;
import java.awt.*;
import java.awt.event.*;
import borland.jbcl.layout.*;
3. 文件拷贝
import borland.jbcl.control.*;
import.java.io.*;
import.java.util.*;
public class MainFrame extends Frame {
TextField textFedle1=new textField();
Bubtton button1=new Button();
Label labell=new Label();
Label labe12=new Label();
TextField textField2=new TextField();
Button button2=new Button();
Button button3=new Button();
Filer filer1=new Filer(″选择源文件″,Filer.LOAD);
Filer filer2=new Filer(this,″ 选 择 目 的 文 件 ″ ,
Filer.SAVE);
3. 文件拷贝
Button button4=new Button();
PrepareCopyFile pCopyFile=null;
Labe label3=new Label();
Panel panel=new Pane();
Panel panel1=new Panel();
PaneLayout paneLayout1=new PaneLayout();
PaneLayout paneLayout2=new PaneLayout();
GroupBox groupBox1=new GroupBox();
List list1=new List();
PaneLayout paneLayout3=new PaneLayout();
Public MainFrame()
{
try{
jbInit();
3. 文件拷贝
}
catch (Exception e) {
e.printStackTrace();
}
}
private void jbInit() thows Excption {
this.setSize(new Dimension(454.154));
this.setBackgrond(new Color(192,192,192));
button1.setLabl(″浏览...″)
btton.addActionListener(new
MainFrame_button1_actionAdapter(this));
Btton.addActionListener(new
MainFrame_button1_actionAdapter(this));
labell.setAlignment(2);
3. 文件拷贝
labell.setText(″源文件″);
labell.setAlignment(2);
label2.setText(“目的文件″);
button2.setLabel(″复制″);
button2.addActionListener(new
MainFrame_button2_actionAdapter(this));
button3.setLabel(″退出″);
button3.addActionListener(new
MainFrame_button3_actionAdapter(this));
button4.setLabel(″浏览...″);
button4.addActionListener(new
MainFrame_button4_actionAdqapter(this));
label3.setText(“ 请 输 入 选 择 源 文 件 ( 夹 ) 和 目 标 文 件
(夹):”);
3. 文件拷贝
groupBox1.setLabele(″下载队列″);
panel1.setLayout(paneLayout2);
this.setLayout(paneLayout1);
this.add(pane11,new PaneConstraint
(″panell″, ″panell″,PaneConstraints.ROOT,0.5f));
panel.add(groupBox1. , new PaneConstraints
(“groutpBox1”,“groupBox1”,PaneConstraints.ROOT,1.0f));
GroupBo1.add(list1, new PaneConstraints
(“list”,“listl”,PaneConstraint.ROOT,1.0f));
this.add(labe13,
new
PaneConstraint
(“label3
”
,
“panell”,PaneConstraints.LEET,0.6300448f));
this.add(labell. ,new PaneConstraint
(“labell”,“label3”,PaneConstraints.BOTTOM,0.6858974
7f))
3. 文件拷贝
this.add(textFieldl,new PaneConstraint
(“label2”,“labell”,PaneConstraints.BOTTOM,0.6730769f))
this.add(textField,new PaneConstrains
(“textField1”,“labell”,PaneConstraints.RIGHT,0.7686832
5f))
this.add(buttonl,new PaneConstraints
(“buttonl”,“textFieldl”,PaneConstraints.RIGHT,0.344262
3f));
this.add(button2,new PaneConstraints
(“button2”,“label2”,PaneConstraints,BOTTOM,0.57142854f
));
this.add(textField2,new PaneConstraints
(“textField2”,“Label2”,PaneConstraints.RIGHT,0.7686832
5f));
3. 文件拷贝
this.add(button4,new PaneConstraints
(“button4”,“textFileld2”,PaneConstraints.RIGHT,0.34
42623f));
this.add(button3,new PaneConstraints
(“button3”,“button2”,PaneConstraints.RIGHT,0.479885
1f));
}
public static void main(String[] args) {
MainFrame frame=new MainFrame();
frame.setLocation(200.200);
frame.setSize(450,150)l
frame.show();
}
void buttonl_actionPerformed(ActionEvent e) {
3. 文件拷贝
filerl.show();
String dir=feler1.getDirectory();
String file=feler1.getFile();
if(dir.equals(″null″)&&file.equals(″null″)retur
n;
textField1.setText(dir+file);
}
void button2_actionPerformed(ActionEvent e) {
String sourceName=textFileld.getText();
String desName=textField2.getText();
if(sourceName.equals(′′′′)‖desName.equals(′′
′′)return;
pCopyFile=new
PrepareCopyFile(this,sourceName,desName);
3. 文件拷贝
pCopyFile.stary();
}
void addCopyingFile(String sname) {
File file=new File(same);
String sfilename=file.getName();
list1.addItem(sfilename);
}
void removeCopyingFile(String sname) {
File file=new File(sname);
String sfilename=file.getName();
}
void button3_actionPerformed(ActionEvent e) {
MessageDialog mdlg=new MessageDialog(this,“ 警 告 ” ,
确定要退出吗?”,6);
3. 文件拷贝
mdlg.setSize(150,80);
mdlg.show();
if(mdlg.getResult()= =MessageDialog.No)
return;
System.exit(0);
}
void button4_actionPerformed(ActionEvent e) {
filer2.show();
String dir=filer2.getDirectory();
String file=filer2.getFile();
if(dir.equals(″null″)&&file.equals(″null″))retu
rn;
textField2.setText(dir+file);
}
小结
本章介绍了java的主要可重用类包的结构。具体讲述
了java.lang、java.util和jave.io。
三种java基本的可重用包中常用的类。java.lang提
供了String类和StringBuffer类。String类实现一种不能
改变的静态字符串,StringBuffer类实现一种动态可变的
字符串。
数组是相同类型的数据元素按顺序组成的一种结构
数据类型,元素在数组中的相对位置由下标来指明。数组
中的每个元素通过数组名加下标进行引用。
java.util包提供了许多实用的类,如:日期、向量、
哈希表、锁和堆栈等。
小结
本章还重点介绍了输入输出流操作的基本知识。描述
了java输入输出包中字节输入输出流接口、类的层次继承
关系。具体介绍了基本输入输出流中的InputStream类、
OutputStream 类 、 PrintStream 类 以 及 System.in 和
System.out对象的应用,介绍了主要的文件操作方法。例
如 File 类 、 FileInputStream 类 、 FileOutputStream 类 、
DataInputStream 类 、 DataOutputStream 类 、
ObjectInputStream类和ObjectOutputStream类。最后列举
了文件(文件夹)拷贝的实例,说明输入输出流类的应用。
第
7
章
Java小应用程序Applet
第7章









第一节
第二节
第三节
第四节
第五节
第六节
第七节
小结
习题
Java小应用程序Applet
Applet程序开发步骤
Applet的安全基础
Applet类
Applet运行状态控制基本方法
Applet的应用
Applet 的事件及其处理
利用浏览器浏览Applet
Applet概述
Applet是一种Java程序,它通过使用该Applet的
HTML文件,由支持Java的网页浏览器下载运行。也可以通
过java开发工具的appletviewer来运行。Applet 程序离不
开使用它的HTML文件。这个HTML文件中关于Applet的信息
至少应包含以下三点:
1)字节码文件名(编译后的Java文件,以.class为后
缀)
2)字节码文件的地址
3)在网页上显示Applet的方式。
一个HTML文件增加Applet有关的内容只是使网页更加富有
生气,如添加声音、动画等这些吸引人的特征,它并不会改
变HTML文件中与Applet无关的元素。
7.1 Applet程序开发步骤
Applet程序开发主要步骤如下:
1)利用EDIT或Windows Notepad作为编辑器建立
Java Applet源程序。利用Jbuild开发Applet是非常方
便的,详情见第12章例。Java源程序文件的扩展名为
java,例如JavaWorld.java。
2)把Applet的源程序转换为字节码文件。
3 ) 编 制 使 用 JavaWorld.class 的 HTML 文 件 。 在
HTML文件内放入必要的<APPLET>语句。
7.1.1 编辑Applet 的java源文件
例 7-1 创 建 文 件 夹 C : \JAVA_World , 在 该 文 件 夹 下 建 立
JavaWorld.java
该文件的内容如下:
import java.awt.*;
import java.applet.*;
public class JavaWorld extends Applet
//继承Appelet类,这是Appelet Java程序的特点。
{
public void paint(Graphics g )
{
g.drawString(″Java World!″,5,25);
}
}
保存上述程序在C:\JAVA_World\JavaWorld.java文件里。
7.1.2编译Applet
编译JavaWorld.java源文件可使用如下JDK命令:
C:\JAVA_World\>javac JavaWorld.java<Enter>
7.1.3创建HTML文件
在 运 行 创 建 的 JavaWorld.class 之 前 , 还 需 创 建 一 个
HTML文件,appletviewer或浏览器将通过该文件访问创建的
Applet。为运行JavaWorld.class, 需要创建包含如下HTML
语句的名为JavaWorld.HTML的文件。
7.1.4.使用浏览器观JavaWorld..HTML运行
如果用浏览器运行JavaWorld Applet,需在
浏览器的地址栏中输入HTML文件URL地址。
7.1.5.修改已创建的Applet
若想修改Applet,则必须重新编辑并编译该
Applet的源文件。
7.2 Applet的安全基础
由于Applet从远程服务器上下载而在本地机上运行的特殊
性,安全问题显得十分重要,为此,Applet在运行时要受
到更多的限制,诸如:
1) Applet永远无法运行本地机上的可执行程序;
2) Applet除了与所在的服务器联系以外,无法再同任何其他
的 服务器取得联系;
3) Applet无法对本地机上的文件系统进行读写操作。(目前
Netscape是这样的,以后也许会有其他的浏览器减少这方面的限
制,因为Sun公司并没有把这一点作为Java规范的一部分。);
4) Applet无法获得除了本地机使用的Java版本号、操作系统
名称及版本号、文件名分隔符(‘/’或‘\’)和路径以外有关本
地机的其他信息。Applet也无法获得使用者的名字和E-mail地址
等。
7.3 Applet类
Applet类是所有Applet应用的基类,所有的Java小应用程
序都必须继承该类 。如下例所示。
import java. applet.*;
public class TestApplet extends Applet
{…..
}
Applet类的构造函数只有一种,也就是:
public Applet()
Applet实现了很多基本的方法,下面列出了Applet类中常用
方法和用途。
public final void setStub(AppletStub stub)
7.3 Applet类
设置Applet的stub.stub是Java和C之间转换参数并返回值
的代码位,它是由系统自动设定的 。
public boolean isActive();
//判断一个Applet是否处于活动状态。
public URL getDocumentBase();
// 检索表示该Applet运行的文件目录的对象。
public URL getCodeBase();
//获取该Applet 代码的URL地址。
public String getParameter(String name);
//获取该Applet 由name指定参数的值。
public AppletContext getAppletContext();
//返回浏览器或小应用程序观察器。
7.3 Applet类
public void resize(int width,int height);// 调整Applet
运行的窗口尺寸。
public void resize(Dimension d);
//调整Applet
运行的窗口尺寸。
public void showStatus(String msg); //在浏览器的状态条
中显示指定的信息。
public Image getImage(URL url);
//按url指定的地址
装入图象。
public Image getImage(URL url,String name) ; // 按 url 指
定的地址和文件名加载图像。
public AudioClip getAudioClip(URL url);
// 按
url指定的地址获取声音文件。
public AudioClip getAudioClip(URL url, String name);//
按url指定的地址和文件名获取声音。
7.3 Applet类
public String getAppletInfo();
// 返回Applet应用有
关的作者、版本和版权方面的信息;
public String[][] getParameterInfo();
// 返回描述Applet参数的字符串数组,该数组通常包含三
个字符串:参数名、该参数所需值的类型和该参数的说
明。
public void play(URL url);
// 加载并播放一个url
指定的音频剪辑。
public void destroy();
// 撤消Applet及其所占
用的资源。若该Applet是活动的,则先终止该Applet的运
行。
7.4 Applet运行状态控制基本方法
Applet类中的四种基本方法用来控制其运行状
态:
init()、start()、stop()、destroy()
1 init()方法
2 start()方法
3 stop()方法
4 destroy()方法
7.5 Applet的应用
 7.5.1利用Applet来接收从HTML中传递过来的参
数
7.5.2 利用Applet来显示图像
7.5.3 利用Applet播放声音
7.5.4 Applet之间进行通信
7.5.1.1 CODE标志
在HTML文件里设置参数可给编程带来很大的便利。
例7-2 CODE标志说明Applet应用的class文件名
< HTML>
<TITLE>Applet〈/TITLE〉
<APPLET
CODE=″TestApplet.class″
WIDTH=300
HEIGHT=200>
</APPLET>
</HTML>
CODE标志指定Applet的类名;WIDTH和HEIGHT标志指定Applet窗
口的像素尺寸。在APPLET语句里还可使用其他一些标志。下面将
详细介绍这些标志。
7.5.1.2
CODEBASE 标志
CODEBASE标志指定Applet的URL地址。
如下的语句把www.sun.com/Applet指定为Applet的CODEBASE:
例7-3 CODEBASE 标志<HTML>
<TITLE>Applet</TITLE>
<APPLET
CODE=″TestApplet.class″
CODEBASE=″www.sun.com/Applet″
WIDTH=″300″
HEIGHT=”200” >
<APPLET>
<HTML>
7.5.1.3 ALT标志
虽然Java在WWW上很受欢迎,但并非所有浏览器都对其提
供支持。如果某浏览器无法运行Java Applet,那么它在遇到
APPLET语句时将显示ALT标志指定的文本信息。
如 下 的 语 句 用 ALT 标 志 指 定 的 显 示 信 息 为 “ Java Not
Supported”:
例7-4 不支持Applet的浏览器显示ALT标志指明的信息。
<HTML>
<TITLE>Applet</TITLE>
<APPLET
CODE=″TestApplet.class″
ALT=″Java Not Supported″
WIDTH=300
HEIGHT=200>
</APPLET>
</HTML>
7.5.1.4 ALIGN标志
ALIGN标志可用来控制把Applet窗口显示在HTML文档窗口
的什么位置。如下的语句是让浏览器把Applet窗口显示在当
前文档窗口的中部;
例7-5 ALIGN标志控制Applet窗口显示位置
<HTML>
<TITLE>TestApplet</TITLE>
<APPLET
CODE=″TestApplet.class″
ALIGN=MIDDLE
WIDTH=300
HEIGHT=200>
</APPLET>
</HTML>
7.5.1.5 VSPACE与HSPACE标志
VSPACE和HSPACE标志指定浏览器显示在Applet窗口周围的
水平和竖直空白条的尺寸,单位为像素。如下例使用该标志在
Applet窗口之上和之下各留出50像素的空白,在其左和其右各留
出25像素的空白:
例7-6 VSPACE与HSPACE标志
<HTML>
<TITLE〉TestApplet</TITLE>
<APPLET
CODE=″TestApplet.class″
VSPACE=25
HSPACE=50
WIDTH=300
HEIGHT=200>
</APPLET>
</HTML>
7.5.1.6 NAME标志
NAME标志把指定的名字赋予Applet的当前实例。
如下的语句利用NAME标志把本Applet实例命名为Client:
例7-7 NAME标志的作用
<HTML>
<TITLE〉TestApplet</TITLE>
<APPLET
CODE=″ArrayFunction.class″
NAME=″Client″
WIDTH=300
HEIGHT=200>
</APPLET>
</HTML>
7.5.1.7 PARAM标志
通用性是程序设计所追求的目标之一。使
用户或者程序员能很方便地使用同一个
Applet完成不同的任务是通用性的具体表现。
从HTML文件获取信息是提高Applet通用性
的一条有效途径。
7.5.2 利用Applet来显示图像
Java Applet常用来显示存储在GIF文件中的图像。Java
Applet装载GIF图像非常简单,
在Applet内使用图像文件时需定义Image对象。多数Java
Applet使用的是GIF或JPEG格式的图像文件。Applet使用
getImage方法把图像文件和Image对象联系起来。
Graphics类的drawImage方法用来显示Image对象。为了提
高图像的显示效果,许多Applet都采用双缓冲技术:首先
把图像装入内存,然后再显示在屏幕上。
Applet可通过imageUpdate方法测定一幅图像已经装了多
少在内存中。
7.5.2.1 装载一幅图像
例7-10显示图像
//源程序清单
import java.awt.*;
import java.applet.*;
public class ShowImage extends Applet
Image picure;
//定义类型为Image的
成员变量
public void init()
{
picture=getImage(getCodeBase(),″Image.gif″); //装载图像
}
public void paint(Graphics g)
{
g.drawImage(picture,0,0,this); //显示图像
}
}
7.5.2.1 装载一幅图像
为此,HTML文件中有关Applet的语句如下:
<HTML>
<TITLE〉Show Image Applet</TITLE>
<APPLET
CODE=″ShowImage.class″
//class文件名为ShowImage.class
WIDTH=600
HEIGHT=400>
</APPLET>
</HTML>
7.5.2.2. 双缓冲图像
为了提高图像的显示效果应采用双缓冲技术。首先把
图像装入内存,然后再显示在Applet窗口中。
例7-11 使用双缓冲图像技术。
//源程序清单
import java.awt.*;
import java. applet.*;
public class BackgroundImage extends Applet //继承Applet
{
Image picture;
Boolean ImageLoaded=false;
public void init()
{
picture=getImage(getCodeBase(),″Image.gif″); //装载图像
Image offScreenImage=createImage(size().width,size().height);
7.5.2.2. 双缓冲图像
//用方法createImage创建Image对象
Graphics offScreenGC=offScreenImage.getGraphics();
//获取Graphics对象
offScreenGC.drawImage(picture,0,0,this);
//显示非屏幕图像
}
public void paint(Graphics g)
{
if(ImageLoaded)
{
g.drawImage(picture,0,0,null);
//显示图像,第四参数为null,不是this
showStatus(″Done″);
}
else
7.5.2.2. 双缓冲图像
showStatus(″Loading image″);
}
public boolean imageUpdate(Image img,int
infoflags,int x,int y,int w,int h)
{ if(infoflags= =ALLBITS)
{ imageLoaded=true;
repaint();
return false;
}
else
reture true;
}
}
7.5.3
利用Applet播放声音
程序员利用Java能够把文本、图像、动画和声音
集成在一起,编制出多媒体Applet。这部分将讲述
如何在Applet里加入声音。使用Applet播放声音时
需首先定义AudioClip对象。 GetAudioClip方法能
把声音赋予AudioClip对象。如果仅想把声音播放
一遍,应调用AudioClip类的play方法。如果想循
环把声音剪辑,应选用AudioClip类的loop方法。
7.5.3.1 播放声音文件
图像格式各种各样,如BMP、GIF和JPEG等。
声音文件也一样,WAV和AU是最常用的两种声音文
件。目前Java仅支持AU文件,但Windows环境下常
用的却是WAV文件,所以最好能有一个可把WAV文件
转换为AU文件的工具。
7.5.3.2 播放声音的AudioClip类
AudioClip 类 用 来 在 Java Applet 内 播 放 声 音 , 该 类 在
java.Applet包中有定义。
例7-12演示了如何利用AudioClip类播放声音。
例7-12 装入一个名为Sample.Au的声音文件并播放
//源程序清单
import java.awt.*;
import java.applet.*
public class SoundDemo extends Applet
{
public void paint(Graphics g)
{
AudioClip audioClip=getAudioClip(getCodeBase(),”Sample.AU”);
//创建AudioClip对象并用getAudioClip方法将其初始化。
7.5.3.2 播放声音的AudioClip类
g.drawstring(″Sound Demo! ″,5,15);
audioClip.loop();//使用AudioClip类的loop方法循环播放
}
}
需把如下的HTML语句放入SoundDemo.HTML文件,
为运行该Applet做准备。
<HTML>
<TITLE>SoundDemo Applet</TITLE>
<APPLET
CODE=″SoundDemo.class″
WIDTH=300
HEIGHT=200>
</APPLET>
</HTML>
编译并运行该Applet,屏幕上将显示出一个Applet窗口并伴以音乐。关
闭Applet时音乐终止。
7.5.3.3 协调使用声音和图像
在有些情况下,可能需要在发生某事件时伴之以声音,
尢其是在Applet 中装载图像的同时播放声音,这样将大大
地丰富Applet的内容。协调使用图像的声音是十分重要的。
例7-13声音和图像的协调。
//源程序清单
import java.awt.*;
import java.applet.*;
import java.util.*;
public class Appletl extends Applet implements Runnable
{
AudioClip audioClip;
Thread ShapeThread=null;
Random RandomNumber=new Random( );
7.5.3.3 协调使用声音和图像
Color ImageColor;
public void init( )
{ audioClip=getAudioClip(getCodeBase(),
″Sample.AU″);
// 创建一个AudioClip对象
}
public void start( )
{
if (ShapeThread= =null)
{
ShapeThread=new Thread(this);
ShapeThread.start( );
}
}
7.5.3.3 协调使用声音和图像
public void run()
{ while (true)
{ switch (RandomNumber.nextlnt(5)) {
//把随机数转换为0~4之间的值
case 0: ImageColor=Color.black;
break;
case 1: ImageColor=Color.blue;
break;
case 2: ImageColor=Color.cyan;
break;
case3: ImageColor=Color.magenta;
break;
case4: ImageColor=Color.orange;
break;
7.5.3.3 协调使用声音和图像
default: ImageColor=Color.red;
}
try
{ ShapeThread.sleep(300); //线程睡眠
}
catch(InterruptedException e)
{
//忽略异常
repaint();
}
}
public void paint(Graphics g)
{
g.setColor(ImageColor);
7.5.3.3 协调使用声音和图像
audioClip.play();
//播放声音
switch(RandomNumber.nextlnt(2))
//获取随机数与2整除的余
数
{
case0:g.fillRect(25,25,200,200);
//添充一个矩形
break;
default:g.fillOval(25,25,200,200); //添充一个椭圆
break;
}
}
}
7.5.4 Applet之间进行通信
同一个页面不同Applet之间可以互相进行通信,不同的
Applet 利 用 其 名 ( name ) 来 区 分 。 在 HTML 页 面 中 每 个
Applet时必须说明各自的name,如下所示:
<APPLET
CODE=”Applet.class”
NAME=”Applet1”
//Applet名为Applet1
WIDTH=300
HEIGHT=300>
</APPLET>
AppletContext ac=this.getAppletContext();
Applet applet=ac.getApplet(″Applet1″);
这样就得到了另一个Applet对象。
7.6
Applet 的事件及其处理
7.6.1 Applet中的鼠标操作
在图形环境中,多数交互操作都是通
过鼠标进行的。用鼠标点击和拖动屏幕对象
是Applet中重要的一种操作。这部分将围绕
该论题展开讨论。
7.6.1.1.鼠标事件
在Java Applet内,鼠标操作将产生鼠标事件,而
鼠标事件将导致Java调用Applet定义的相应方法:
当用户拖动鼠标时Java将调用mouseDrag方法,当
用户按下鼠标键时Java将调用mouseDown方法,用
户释放鼠标键时Java将调用mouseUp方法。调用这
些方法时Java会把事件发生位置的x坐标和y坐标传
递给它们。把鼠标事件坐标和屏幕对象的坐标相比
较,Applet即可确定鼠标事件是否和屏幕对象相关。
7.6.1.1.鼠标事件
鼠标操作当做Applet或者Java本身必须响应的事件。如果
Applet响应了鼠标事件,则处理该事件的方法应返回true。所
以,Applet定义的鼠标方法的返回值应是布尔(boolean)型
(true或false)。
例7-15 写mouseUp、mouseDown和mouseDrag方法。这方法显示
鼠标事件坐标。
//源程序清单
import java.awt.*;
import java.applet.*;
public class MouseOperations extends Applet
{
7.6.1.1.鼠标事件
String MouseDownEvent=null;
String MouseUpEvent=null;
String MouseDragEvent=null;
public boolean mouseUp(Event event,int x,int y)
{
MouseUpEvent="mouseUp:"+x+","+y;
repaint( );
return true;
}
public boolean mouseDown(Event event,int x,int y)
{
MouseDownEvent="mouseDown:"+x+","+y;
repaint( );
return true;
}
7.6.1.1.鼠标事件
public boolean mouseDrag(Event event,int x,int y)
{
MouseDownEvent="mouseDrag: "+x+","+y;
repaint( );
return true;
}
public void paint(Graphics g)
{
if(MouseDragEvent!=null)
g.drawString(MouseDragEvent,5,15);
//显示MouseDragEvent
7.6.1.1.鼠标事件
if(MouseUpEvent!=null)
g.drawString(MouseUpEvent,5,45);
//显示MouseUpEvent
if(MouseDownEvent!=null)
g.drawString(MouseDownEvent,5,75);
//显示MouseDownEvent
}
}
该Applet把mouseUp、MouseDown和MouseDrag方法定义
为boolean型。这些函数首先把鼠标事件坐标赋予一个String对
象,然后调用repain方法重画Applet窗口。
7.6.1.2.用鼠标点击屏幕对象
很多情况下都要求Applet能够判别出用户是否用鼠标点击
了屏幕对象。下面介绍鼠标点击了屏幕对象的一个例子。
例7-16 在Applet窗口显示了一个Exit按钮。当用户在Exit按钮
上点击鼠标时,Applet将显示一条描述该事件的信息。
//源程序清单
import java.awt.*;
import java.applet.*;
public class MouseExit extends Applet
{
String MouseDownEvent=null;
String MouseUpEvent=null;
String MouseDragEvent=null;
int Button_x;
int Button_y;
7.6.1.2.用鼠标点击屏幕对象
int ButtonHeight;
int ButtonWidth;
public void init()
{
Button_x=5;
Button_y=100;
ButtonHeight=50;
ButtonWidth=100;
}
public boolean mouseUp(Event event,int x ,int y)
{
MouseUpEvent="mouseUp: "+x+", "+y;
repaint();
if((x>=Button_x)&&(x<=Button_x+ButtonWidth))
if((y>=Button_y)&&(y<=Button_y+ButtonHeight))
{
7.6.1.2.用鼠标点击屏幕对象
MouseUpEvent="Exit Selected";
repaint();
}
return true;
}
public boolean mouseDown(Event event,int x ,int y)
{
MouseDownEvent="mouseDown:"+x+","+y;
repaint();
if((x>=Button_x)&&(x<=Button_x+ButtonWidth))
if((y>=Button_y)&&(y<=Button_y+ButtonHeight))
{
MouseDownEvent="Exit Selected";
repaint( );
}
7.6.1.2.用鼠标点击屏幕对象
return true;
}
public boolean mouseDrag(Event event,int x ,int y)
{
MouseDragEvent="mouseDrag:"+x+","+y;
repaint();
if((x>=Button_x)&&(x<=Button_x+ButtonWidth))
if((y>=Button_y)&&(y<=Button_y+ButtonHeight))
{
MouseDragEvent="Exit Selected";
repaint();
}
return true;
}
7.6.1.2.用鼠标点击屏幕对象
public void paint(Graphics g)
{
if(MouseDragEvent!=null)
g.drawString(MouseDragEvent,5,15);
if(MouseUpEvent!=null)
g.drawString(MouseUpEvent,5,45);
if(MouseDownEvent!=null)
g.drawString(MouseDownEvent,5,75);
g.drawRect(Button_x,Button_y,ButtonWidth,ButtonHeight);
g.drawString("EXIT",Button_x+35,Button_y+30);
}
}
7.6.1.2.用鼠标点击屏幕对象
该Applet用drawRect函数画出了一个按钮。Applet在鼠
标函数之内把鼠标事件的坐标与Exit按钮的坐标相比较。
if((x>=Button_x)&&(x<=Button_x+Button Width))
if((y>=Button_y)&&(y<=Button_y+Button Heigh))
{
MouseDragEvent=″Exit Selected″;
repaint( );
}
7.6.2 Applet中的键盘操作
Applet运行状态也可以对键盘事件作出处理。Java
目 前 支 持 两 种 键 盘 事 件 : keyUp 和 keyDown 。 keyUp 和
keyDown 是 处 理 键 盘 事 件 的 函 数 。 当 Java 调 用 keyUp 和
keyDown方法时将给它们传递两个参数。第一个参数定义键
盘事件(其中包括键盘状态以及是否功能键被按下等信
息 ) , 第 二 个 参 数 标 识 事 件 所 涉 及 的 键 。 如 果 keyUp 或
keyDown方法响应了键盘事件则需向Java返回true,指明它
们已处理了该事件。
7.6.2.1键盘事件
Java把键盘操作当做Applet或Java本身必须响应的
事件。每当发生键盘事件时,Java将调用Applet的键盘
方法处理该事件。
例7-17 演示了keyUp和keyDown方法
//源程序清单
import java.awt.*;
import java.applet.*;
public class KeyboardEvents extends Applet
{
String KeyDownEvent=null;
String KeyUpEvent=null;
7.6.2.1键盘事件
public boolean keyUp(Event event,int letter)
{
if(letter= =27) //Esc键
KeyUpEvent=″Esc key released″;
else
Key UpEvent=″KeyUp:″+(char)letter;
repaint( )
return(true);
}
public boolean keyDown(Event event,int letter)
{
if(letter= =27) //Esc 键
KeyDownEvent=″Esc key pressed″;
7.6.2.1键盘事件
else
KeyDownEvent=″KeyDown:″+(char)letter;
repaint( );
return(true);
}
public void paint(Graphics g)
{
if(KeyUpEvent!=null)
g.drawString(KeyUpEvent,5,45);
if(KeyDownEvent!=null)
g.drawString(KeyDownEvent,5,75);
}
}
7.6.2.1键盘事件
该 Applet 定 义 了 keyUp 和 keyDown 方 法 。
Java调用这些方法时将把一个事件对象和一个整数
传递给它们,整数对应于某个键的ASCII码。键值
前的(char)是一个型(cast),编译器根据该型
把键值从int转换为char。如果去掉该型,Applet显
示的将是键的ASCII码值,例如字符“A”对应的码
值为65,而不是该键所对应的字符。请注意,这两
个方法还测试了ESC键(ASCⅡ27),这样实现对
特定键的测试。
7.6.2.2测试功能键
有些情形下需要区别功能键和常规键。Applet可
通过测试Event类id成员来确定键的类型。如果id成
员等于KEY_PRESS,则表明是常规键;如果id成员
等于KEY_ACTION,则表明是功能键
if(event.id= =Event.Key_PRESS)
//常规键
else if(event.id= =Event.Key_ACTION)
//功能键
7.6.2.2测试功能键
例7-17 功能键测试
//源程序清单
import java.awt.*;
import java.applet.*;
public class FunctionKeys extends Applet
{
String Key DownEvent=null;
String Key Event=null;
public boolean keyUp(Event event,int letter)
{
if(event.id= =Event.KEY_ACTION)
KeyUpEvent=″Function key released″;
else if(letter= =27)
//Esc键
KeyUpEvent=″Esc key released″;
7.6.2.2测试功能键
else
KeyUpEvent=″KeyUp: ″+(char)letter;
repaint();
return(true);
}
public boolean keyDown(Event event,int letter)
{
if(event.id)= =Event.KEY_ACTION)
KeyDownEvent=″Function key pressed″;
Else if(letter= =27)
//ESC键
KeyDownEvent=″KeyDown:″+(char)letter;
repaint( );
return(true);
}
7.6.2.2测试功能键
public void paint(Graphics g)
{
if(KeyUpEvent!=null)
g.drawString(KeyUpEvent,5,45);
if(KeyDownEvent!=null)
g.drawString(KeyDownEvent,5,75);
}
}
7.6.2.3测试键盘状态
与区分功能健和常规键一样,有时确定键盘Alt、Shift
和Ctrl的状态也很有必要。Applet可通过测试Event类
的modifiers成员来确定这些键的状态。
例7-19 利用Event类的modifiers成员显示键盘状态。
//源程序清单
import java.awt.*;
import java.applet.*;
public class ShowState extends Applet
{
7.6.2.3测试键盘状态
String KeyboardState=″″;
public boolean KeyDown(Event event,int letter)
{
KeyboardState=″″;
if((event.modifiers & Event.SHIFT_MASK)!=0)
KeyboardState+=″Shift″;
if((event.modifiers & Event.CTRL_MASK)!=0)
KeyboardState+=″Ctrl″;
if((event.modifiers & Event.META_MASK)!=0)
KeyboardState+=″Alt″;
repaint( );
return(true);
}
7.6.2.3测试键盘状态
public void paint(Graphics g)
{
if(KeyboardState!=″″)
g.drawString(KeyboardState,5,75);
else
g.drawString(″No shift state″,5,75);
}
}
把如下的HTML语句放入ShowState.HTML文件,
为运行该Applet做准备。
7.6.2.3测试键盘状态
<HTML>
<TITLE>ShowState Applet</TITLE>
<APPLET
CODE=″ShowState.class″
WIDTH=300
HEIGHT=200>
</APPLET>
</HTML>
7.7 利用浏览器浏览Applet
上面详细说明如何编写Java小应用程
序Applet, Applet最大的应用就是可以通过
浏览器进行浏览。
7.7.1 远程浏览Applet
服务器必须安装了个人Web服务器(windows97为PWS,
widows NT为ⅡS)。
假设用户为Windows操作系统,IP地址为202.96.45.57,
且安装好了PWS,则在用户硬盘中应该具有一个 Inetpub
(就是pws的安装目录),在该目录下面有一个wwwroot目
录,该目录就是该服务器的网络根目录。将用户编译好的
源程序清单得到的字节码文件和相应的HTML文件拷贝到
inetput\wwwroot目录下面,然后打开浏览器,输入地址.
7.7.2 查看Java错误
对于初学者来说,在利用浏览器浏览Applet过程中,
总避免 不了 出现各 种各 样的问 题 ,然而, 浏览 器不同 于
appletviewer 具 有 控 制 台 可 以 查 看 记 录 , 或 者 可 以 通 过
Jbuilder等工具来查看所出现的错误。那么,使用Internet
Explore 5.0按下列设置可以帮助查错:
进入Internet Explore, 选择菜单“工具→Internet选项…”,
在弹出的对话框中选择“高级”选项卡,然后复选中“启
用Java控制台(需要重启动)”,确定即可。如图7-3所示。
关闭浏览器后再重新打开浏览器,我们就可以看到菜单
“查看”下面增加了一个子菜单“Java控制台”。这样,
当利用Internet Explore浏览Applet出现问题时,可以进入:
查看→Java主控台,查看所出现的问题。如图7-4所示。
图7-3
图7-4
小结
Java小应用程序Applet的开发分为四步:编写Applet
Java源程序、编译Applet Java源程序生成其字节码文件、
编写HTML文件、调试、运行Applet 的HTML文件 。运行
Applet的浏览器为Applet提供了安全设施。Applet可以用来
传送参数、实现网页动画、声音播放、对鼠标、键盘的事
件作出处理。Applet与其他网络技术配合可以构成更高级
的应用。
习题
1.
序
2.
3.
4.
5.
以在屏幕上显示“您好!”为例,简述Java 小应用程
Applet 的开发的步骤。
浏览器采取哪些办法,保证Appelt安全运行。
实现一个Applet, 使其可以从服务器下载音乐播放。
实现一个Applet, 使其可以不停地运行一个动画。
实现一个Applet, 使其可以用用三个按钮控制:
(1)从服务器下载音乐播放;
(2)不停地运行一个动画;
(3)使上述动画和音乐协调播放。
第八章 图形用户界面(GUl)










8.1 抽象窗口工具箱——AWT组件
8.2 基本组件
8.3 容器组件
8.4 莱单(Menu)
8.5 AWT中的其他类
8.6事件处理
8.6用组件构造用户接口
8.7组件在容器中的布局
小结
习题
8.1抽象窗口工具箱--AWT组件
8.1.1 AWT的基本组件与容器组件
8.1.1.1 AWT的基本组件
在AWT(Abstract Window Toolkit)的概念
中,窗口系统中所显示的各种对象都统称为
“组件”(Component)。组件有基本组件和
容器组件之分。顾名思义,基本组件是不可再
分割的组件,基本组件各自都有它们特定的功
能。基本组件是构成图形用户界面的基本元素。
表8-1列出了AWT中的基本组件
表8-1
基本组件
中文名称
功能
Button
按钮
完成一个命令
CheckBox
复选框
可以同时进行多个选择
CheckBoxGroup
单选框
只能在一组中选择一项
Choice
下拉式列表
创建一个弹出式的选择菜单
List
列表
创建一个选择列表
Menu
菜单
创建菜单系统
TextField
文本框
输入单行文字
Label
标签
在标签内绘制字符
Canvas
画布
进行绘画
TextArea
多行文本框
输入多行文字
ScrollBar
滚动条
在指定的范围内选择一个值
8.1.1.2 AWT中的容器组件
容器组件是用来包含其他组件的,故称之为容器
(container)。用户可以把各种组件放入到容器中,也可以
把容器放到另一个容器中(因为容器本身也是组件,它们都
是Component类的子类),从而形成具有层次的组件结构。
AWT用Container类来定义最基本的容器,所有可以作为
容器的窗口对象都是Container类的实例或子类所生成的对象。
AWT提供了四个容器类。它们是Window类及其两个子
类、Frame类、Dia1og类和Panel类。除了AWT提供的容器外,
Applet类也是一个容器,它是Panel类的一个子类。
8.2 基本组件







8.2.1 按钮
8.2.2 标签
8.2.3 文本框和多行文本框
8.2.4 .单选框
8.2.5 下拉式列表(Choice)
8.2.6 列表(List)
8.2.7 滚动条(Scrollbar)
AWT中基本组件有:按钮(Button)、标签(Label)、
检 查 框 ( Checkbox ) 、 选 择 框 ( Choice ) 、 列 表 框
(List)、菜单(Menu)和单行文本框(TextField)、。
8.2.1 按钮
按钮(Button)是相对简单的一种组件,它在屏幕
上通常表现为一块有边界的矩形区域,上面有文字标记
来说明该按钮的功能。
8.2.2 标签
标签(Label)是一种用来显示单行文本的组件,标签
中的文本具有三种对齐方式:左对齐、居中和右对齐,用
LABEL.LEET、LABE1.CENTER、LAEL.RIGHT三个静态
常量表示。
8.2.3 文本框和多行文本框
在AWT中,提供了两种显示和编辑文件的组件——
文本框(TextField)和多行文本框(TextArea),它们都
是TextComponent类的子类。在TextComponent类中定义了
一系列方法,可以设定文本域是否可以编辑、选择文本、
返回文本,设定光标以及事件处理等。
8.2.4 单选框
有时在程序与用户的交互中也可能会有这样的情
况:一条信息可以有多项可能的取值,但是用户只能
从中选择一项。单选框是由Checkbox类来创建,不同
的是单选框所对应的Checkbox类对象属于某个选择框
组。
8.2.5 下拉式列表(Choice)
下拉式列表的作用与单选框一样,但是二者形式上有
所不同。单选框将所有的可选项都列在用户界面上供用户
选择,而下拉式列表则以弹出式菜单的形式出现,即在用
户界面上表现为一个带箭头的文本框,当用户用鼠标点击
它时,就会弹出一个列表,表中列出所有的选择项,如果
项数太多,还会出现滚动条,因此,下拉式列表适合于可
选择较多的情况。表中的每一项称为一个列表项,它们在
下拉式列表中有统一的编号,编号从0开始。用户在表中某
一选项上点击后,列表自动消失,该选项出现在文本框中。
8.2.6 列表(List)
同下拉式列表一样,列表也是用于多个列表项中进
行选择,它的列表项编号也有从0开始。但是列表与下拉
式列表有三个不同:首先列表不仅允许多选一,而且允
许进行多重选择;其次列表中的所有选项都显示在屏幕
上,如果显示区域不够,将出现滚动条,而不像下拉式
列表那样,只有当用户点击文本框后才能显示;最后,
列表中用户选中的选项用高亮度表示出,而不是列在某
个文本框中。
8.2.7 滚动条(Scrollbar)
在本节之前,我们已经接触到滚动条这种组件,例如
在列表和多行文本框中,如果显示内容超出了显示区域的
范围,在这些组件旁边将出现滚动条,用户可以通过拖动
滚动条来看到所有要显示的内容。滚动条还有另外一种用
途,即它可以作为一个滑块,用来选择指定的最大值和最
小值之间的一个数值。
也就是说,可以将滚动条看作一个取值范围,滑块左
边界(或上边界)所在的位置给出滚动条的当前值。改变
这个值的方法有三个:直接拖动滑块到所需位置;单击滚
动条两边的箭头使滑块沿箭头所指的方向移动一个单位
(一行);用鼠标单击滑槽内的任意区域,可以使滑块沿
相应方向移动一页。
8.3 容器组件
8.3.1 面板(Panel)
8.3.2 框架(Frame)
8.3.3 对话框(Dialog Boxes)
8.4 菜单(Menu)
8.3.1 面板(Panel)
面板是能在屏幕上实际显示的组件。面板继承了
Container类,它提供容纳其他组件的功能。Applet类是
Panel类的一个子类。要在一个Applet中嵌套其他面板,
用户只能创建一个新的Applet,并把它加到Applet中,
就像用户加入其他GUI组件一样:
8.3.1 面板(Panel)
例:
import java.awt.*;
import java.applet.*;
public class ContainerApplet extends Applet
{
pblic void init()
{ setLayout(new GridLayout(1,2,10,10));
8.3.1 面板(Panel)
Panel Panel1=new Panel();
Panel Panel2=new Panel();
add(Panel1);
add(Panel2);
Panel1.setLayout(FlowLayout());
Panel1.add(new Button("Up"));
Panel1.add(new Button("Down"));
}
8.3.2 框架(Frame)
AWT的Window类使用户可以创建独立于包含App1et的
浏览器窗口的窗口。
Window类提供了用于窗口操作的基本功能。通常,用
户使用Window类的子类Frame类和Dialog类。Frame类使用
户可以创建带有菜单条的全功能窗口;Dialog类用于创建
对话框。
用户可以用下列构造方法创建框架:
1)new Frame()方法 该方法创建一个不带标题的框架
2)new Frame(String)方法 该方法创建一个带有指定标题
的框架
8.3.2 框架(Frame)
框架是容器,就像面板一样,用户可以用add()方法
将其他组件加到该框架容器中。该容器的缺省布局是
BorderLayout:
Window win;
win=new Frame("My cool wlndow");
win.setLayout(new BorderLayout(10,20));
win.add("North",new Button("start"));
win.add("Center",new Button("Move"));
8.3.2 框架(Frame)
要设置新窗口的尺寸,用resize()方法;要设置窗口的
显示位置,用move()方法;要知道App1et窗口在屏幕上的
位置,用location()方法。
win.resize(100,200);
Dimension d=location();
win.move(d.width+50,d.height+50);
当用户创建一个窗口时,窗口是不可见的,用户需要
用show()方法将窗口在屏幕上显示出来(用户还可以用
hide()方法将其隐藏),如下所示:
win.show();
8.3.3 对话框(Dialog Boxes)
对话框功能上与框架(Frame)很相似,它们都在屏
幕上弹出新窗口。但对话框用于短暂的窗口,例如:提
示警告信息的窗口、向用户询问特定信息的窗口等。通
常对话框没有标题条或窗口所具有的许多特点。用户可
以决定对话框的尺寸是否能被改变或使它们模式化。
AWT提供了两种对话框:Dialog类可以生成普通对
话框;FileDialog类可以生成依赖于平台的,用于选择
存储或打开文件的对话框。
8.3.3 对话框(Dialog Boxes)
一、 Dialog类
用户可以用下列构造方法创建普通对话框:
Dialog(Frame) ; // 该方法创建依赖于Frame的无模式对话框
Dialog(Frame, boolean); //该方法创建依赖于Frame的对话
框,布尔值决定此对话框是否为有模式对话框(真值为有模
式,假值为无模式)
Dialog(Frame, String); //该方法创建依赖于Frame的无模式
对话框,并给定对话框的标题
Dialog(Frame, String, Boolean); //该方法创建依赖于Frame
的对话框,指出是否模式对话框,并给定对话框的标题
8.3.3 对话框(Dialog Boxes)
二、FileDialog类
FileDialog(Frame,String); //该方法创建一个打开文件
的对话框,此文件对话框附属于给定的框架,具有给定的
标题。
FileDialog(Frame,String,int); //该方法同上面的方法功
能基本一样。整型参数用来决定此文件//对话框是用来打
开文件还是保存文件,可选择的类型参数为
FileDialog.LOAD和FileDialog.SAVE。
在创建了文件对话框实例之后,用户必须用show()方
法将其显示出来:
FileDialog fd=new FileDialog(this, "FileDialog");
fd.show();
8.4 菜单(Menu)
用户创建的每个新窗口都能有它们自己的菜
单条,每个菜单条可以有一定数量的菜单,菜单
又可以有自己的菜单项。AWT提供了一些类用于
这些组件,它们分别是MenuBar类、Menu类和
MenuItem类。
8.4.1菜单和菜单条
要为指定窗口创建一个菜单条,首先用户应创建
MenuBar类的一个实例:
MenuBar mb=new MenuBar();
然后用户可以用setMenuBar()方法将这个菜单条设置成
窗口的缺省菜单:
Window.setMenuBar(mb);
最后,用户可以创建菜单并把它们加到菜单条上:
Menu m=new Menu("My");
mb.add(m);
8.4.2菜单项
有四种类型菜单项用户可以将其添加到菜单中去:
1)MenuItem类的实例,用于常规的菜单项。
2)CheckBoxMenuItem类的实例,用于触发式菜单项。
3)带有菜单项的其他菜单。
4)分割线,用于将菜单中的菜单项分成组。
常规菜单项可以用add()方法加入:
Menu m=new Menu("Tools");
m.add(new MenuItem("Info"));
m.add(new MenuItem("Colors"));
8.4.2菜单项
子菜单可以通过创建Menu类的一个新的实例创建,然
后将它们加到其他菜单中。如下所示:
Menu sb=new Menu("sizes");
m.add(sb);
sb.add(new MenuItem("Small"));
sb.add(new MenuItem("Medium"));
sb.add(new MenuItem("Large"));
8.5 AWT中的其他类
java.awt软件包不仅仅包含组件,还包含与绘图和事件
处理有关的类。正如我们前面所讲到的,组件是集中放置
在容器中的。但我们没有提到每个容器如何使用布局管理
器(Layout manager)来控制它所包含的组件尺寸和排放位
置。java.awt软件提供了几个布局管理器类(BorderLayout
类、CardLaout类等)。用户可根据需要选择适当的布局管
理器对组件进行布局。
java.awt软件包还提供了几个类来表示尺寸和形状。例
如:Dimension类,它指定了一个矩形区域的尺寸;Insets
类,它通常用来指定容器的周边与容器的显示区域之间的
空白大小;Shape类则包含了点、矩形和多边形。
8.5 AWT中的其他类
AWT中的Color类在表示和调整颜色方面是十分有用
的。它定义了一些常量来表示常用的颜色,比如,
Color.black 表 示 黑 色 , 虽 然 它 使 用 的 颜 色 是 RGB(redgreen-b1ue)格式,但它也可以理解为HSB(hue-saturationbrightness)格式。
AWT中的Image类提供了图像数据的操作途径。
Applet可以通过Applet的getImage()方法获得
GIF(GraphicInformation format)和JPEG(Joint photographic
Expert Group)图像的对象,非Applet类则可以使用Toolkit
类获得。Toolkit类针对AWT的平台独立性,提供了一个
非独立于平台的接口。但是,大部分程序除了获得图像
外,不能直接使用Toolkit对象。
8.6 事件处理
用户接口的作用是使程序的使用者与程序之间进行
交互,如果使用户接口具有交互能力,就必须涉及到事
件处理。所谓“事件”,就是指在系统中有某些我们所
关心的事情(如:鼠标移动,用户按下了某个键等)发
生了,然后系统便通知我们去处理这些事情。
AWT的事件处理系统会自动将这个Event对象沿组件
的继承层次结构向上传递,每一个组件在windows系统完
全处理这个Event对象以前,有机会对此事件作出反应。
每个组件的事件处理程序既可以忽略此事件,也可以用
下面的方法之一对此事件作出反应:
8.6 事件处理
1)在Event对象沿组件的继承层次结构向上传递以前修
改它。 例 如:对 于 一个只 能 以大写 方 式显示 字 符的
TextField类的子类,我们通过修改事件,使其对小写字
母的按键也能作出反应。
2)以其他方式对事件作出反应。例如,一个TextField
类的子类可以通过调用处理文本框内容的方法来对按下
回车键作出反应。
3)终止进一步处理事件。例如:如果一个无效字符被
输入到文本框中,事件处理程序可以终止此事件,使其
不能被进一步处理。从组件的角度来看,AWT的事件
处理系统更像一个事件过滤系统。
8.6.1 Event对象
每个事件发生后,都会创建一个Event对象。一个
Event对象包含了以下信息:
1) id---事件的类型。或是按键,或是单击鼠标
2) target----发生事件的对象
3) when----时间印迹。指出事件发生的时间
4) x, y----发生事件的坐标
5) key----键盘事件中被按下的键
6) arg----一个与事件相关的任意参数。例如:在组件上显
示的字符串
7) modifier----修饰键的状态(即Alt,Ctrl键的状态)
8.6.2如何实现事件处理程序
组件可以通过执行handleEvent()方法或通过执行针对某
种事件的处理方法来对事件作出反应。这些方法包括:
mouseEnter(),mouseExit(),mouseDown(),mouseDrag(),
keyDown()和action()。 mouseMove(),mouseUP(),
8.6.3 典型事件处理





1 、鼠标单击事件
2、鼠标移动事件
3、键盘事件
4、检测修饰键
5 、AWT的事件处理程序
1 、鼠标单击事件
当用户在其Applet中单击鼠标时,就产生事件。用
户可以利用鼠标单击事件来做一些简单的事情。比如,
触发声音开关、清除屏幕等。用户也可以将鼠标单击和
鼠标移动两种事件合起来使用,这样可以完成一些复杂
的功能。
当用户单击鼠标一次,AWT会生成两个事件:
mouseDown事件和MouseUp事件。当按下鼠标键时,
AWT生成mouseDown事件;当松开鼠标键时,AWT生
成mouseUP事件。
1 、鼠标单击事件
在Applet中处理鼠标事件是非常容易的。用户所要做
的只是在自己的Applet中重载正确的方法。当特定的事
件发生时,相应的方法会被自动调用。下面是处理
mouseDown事件的方法:
public boolean mouseDown(Event evt, int x, int y)
{
... ...
}
1 、鼠标单击事件
mouseDown事件(以及mouseUp事件)带有三个参数:
事件本身和事件发生所处的坐标值(x和y)。
事件参数(上例中为evt)是Event类的一个实例。所有
系统生成的事件都是Event类的一个实例,它包含了事件在
何处、何时发生以及事件类型的信息。它还包含了用户想
知道的关于事件的一些其他信息(前面我们已提到)。
事件发生所在的坐标值可以通过参数x, y获得。利用它
们,用户可以精确地知道在什么位置上发生了鼠标单击事
件。例如,下面的例子在mouseDown事件发生时,将显示
mouseDown事件发生所处的x, y坐标值。
1 、鼠标单击事件
如果用户在Applet中加入此方法,每当用户在Applet
中单击鼠标时,都会显示一条信息。上述方法的返回值
是布尔类型,这一点对于用户创建用户接口和管理接口
输入都是非常重要的。因为事件处理程序返回真值还是
假值将决定一个指定的GUI组件是否能够截获此事件,
或此GUI组件是否要将此事件传递给包含它的组件。通
常的规则是:如果用户的事件处理程序处理了该事件,
它就应该返回真值。
2、鼠标移动事件
每当鼠标向任何方向移动时,就会生成一个鼠标移动
事件。 Java中有两种鼠标移动事件:mouseDrag事件和
mouseMove事件。当按下鼠标键后,再移动鼠标时,就会
产生mouseDrag事件。如果没有按下鼠标键而单独移动鼠
标则会产生mouseMove事件。为了管理鼠标移动事件,用
户可以使用mouseDrag()和mouseMove()方法。
1) mouseDrag和mouseMove
当用户将mouseDrag()和mouseMove()方法包含在自己的
Applet代码中时,他就可以截获并处理鼠标移动事件。
MouseMove()方法用于鼠标键没有被按下而移动鼠标的情
况
3、键盘事件
无论何时,当用户按下键盘上的某一个键时,都会产
生键盘事件。利用键盘事件,用户可以获得他所按下键的
值或者字符。
1) keyDown()和keyUP()事件
使用keyDown()方法,用户可以获取按下键盘事件:
public boolean keyDown(Event evt, int key)
{
... ...
}
3、键盘事件
2)
缺省键
Event类提供了一个类变量集,这些类变量与几种标准的
非字母数字键有关。如果用户在接口中使用了这些键,他
们可以在keyDown()方法中检测这些按键的名字而不用检测
它们的数值,从而使代码的可读性提高。例如判断键是否
被按下,用户可以使用下面的代码:
if (key==Event.UP)
{
... ...
}
3、键盘事件
Class variable(类变量)
Represented key(实际键)
Event.HOME
The Home key(home键)
Event.END
The End key(End键)
Event.PGUP
The PageUp key(pageUp键)
Event.PGDN
The PageDown key(pageDown键)
Event.UP
the up arrow(向上箭头键)
Event.DOWN
the down arrow(向下箭头键)
Event.LEFT
the left arrow(向左箭键)
Event.RIGHT
the right arrow(向右箭头键)
表8.2
由Event类定义的标准键
4、检测修饰键
Shift键、Control键和Alt键是修饰键,它们自己不会生成
按键事件。但是,当用户获得一个普通的鼠标或键盘事件时,
用户可以检测在事件发生时,这些修饰键是否被按下。很明
显,Shift键和字母数字键一起被按下,与单独按下字母数字
键是不同的,这会生成另一个不同的事件。对于其他事件,
尤其是鼠标事件,用户也可以处理修饰键同时被按下的事件,
与常规不同。
Event类提供了三个方法用于测试修饰键是否被按下:
shiftDown()方法,altDown()方法和ControlDown()方法,它
们都返回布尔值。返回的是真值还是假值反映了修饰键是否
被真正按下。
5、 AWT的事件处理程序
为了知道事件,用户应检查Event对象的实例变量ID,事
件的ID是一个整数类型。 用户可以在handleEvent()方法中检
测并处理以下键盘事件:
1 ) 当 按 下 键 盘 键 时 , 生 成 Event.KEY_PRESS 事 件 ( 同
mouseDown()方法一样)。
2)当松开按下的键时,生成Event.RELEASE事件。
3)当键被按下后又被松开时,会生成Event.KEY_ACTION
和Event.KEY_ACTION_RELEA
SE事件。
5、 AWT的事件处理程序
用户还可以在handleEvent()方法中检测并处理以下鼠标事件:
1)当按下鼠标键时,生成Event.MOUSE_DOWN事件(同
mouseDown()方法)。
2 ) 当 松 开 鼠 标 键 时 , 生 成 Event.MOUSE_UP 事 件 ( 同
mouseUp()方法)。
3 ) 当 移 动 鼠 标 时 , 生 成 Event.MOUSE_MOVE 事 件 ( 同
mouseMove()方法)。
4)当按下鼠标键,同时移动鼠标时,会产生
Event.MOUSE.DRAG事件(同mouseDrag()方法)。
5、 AWT的事件处理程序
5)当鼠标进入Applet或Applet中的一个组件时,会产生
Event.Mouse_ENTER事件。同样,用mouseEnter()方法,
用户也可以检测并处理此事件。
6)当鼠标指针离开Applet,会生成Event.MOUSE_EXIT事
件。同样,用mouseExit()方法,用户也可以检测并处理此
事件。
8.7用组件构造用户接口
8.7.1 使用组件的一般规则
8.5.1.1 将组件加到容器中
任何组件(除了某种类型的窗口外)要在屏幕上显
示出来,必须先将它加到容器中。当然,容器本身也是
组 件 , 它 们 也 可 以 加 到 另 一 个 容 器 中 。 而 像 Frame 和
Dialog这样的窗口,它们是最高级的容器,不能将它们
加到其他容器中。
Container类中定义了两种类型的add()方法用来加入组
件:单参数的add()方法和双参数的add()方法。使用哪一
种方法要依赖于容器所使用的布局管理器。关于布局管
理器,我们将在以后的章节中介绍。
8.7.1 使用组件的一般规则
8.7.1.2 Component类提供的功能
除了菜单以外的所有组件都是作为Component类的子类
来实现的。从Component类中,它们继承了大量的功能,
Component类为实现所有的绘画功能及事件处理提供了基础。
1) 支持基本的绘画功能
Component类提供了paint()、update()和repaint()方法,
这些方法使组件能够在屏幕上将自己画出来。
2) 事件处理
Component类定义了handleEvent()等一组方法,例如:
action()方法,它用来处理指定类型的事件;Component类也
提供了设置并获得键盘光标,使键盘能够对组件进行控制
等一些方法。
8.7.1 使用组件的一般规则
3) 对显示字体的控制
Component类提供了用于获得和设置当前字体的方法,
以及获得当前字体信息的方法。
4) 对显示颜色的控制
Component类提供了用于获得和设置前景颜色和背景颜
色的方法。
5) 图像处理
Component类提供了对ImageObserver接口的执行,并定
义了帮助组件显示图像的方法。要注意的是,大部分组件
不能显示图像,因为它们外观的显示是以执行特定平台的
代码获得的。而组件Canvas和容器可以显示图像。
8.7.1 使用组件的一般规则
6) 组件在屏幕上的显示尺寸和位置的控制
所有组件的显示尺寸和位置都是由布局管理器控制的。
组件本身也提供了一些方法改变组件的尺寸,将组件放到
适当的位置以及报告组件的合适和最小尺寸。组件还提供
了一些方法用于返回关于组件当前尺寸和位置的信息。
7) 改变组件的外观和行为
大多数组件的外观取决于它们运行的平台。除了由组件
的类和其超类所提供的影响组件外观的方法和变量以外,
用户很难改变组件的外观,尤其是用户不能靠创建某个组
件类的子类来改变组件的外观。要改变组件的外观,用户
必须继承Canvas类,然后让它具有自己所要的外观,具有
用户所期望的行为。
8.8 组件在容器中的布局
8.8.1 使用布局管理器
8.8.2示例程序
8.8.1 使用布局管理器
在缺省情况下,每个容器中都有布局管理器。如果
容器的缺省布局管理器不能满足用户的需要,用户可以
使用别的布局管理器来代替缺省的。AWT提供了五种布
局 管 理 器 : FlowLayout 、 GridLayout 、 BorderLayout 、
CardLayout和GridBagLayout。下面我们介绍布局管理器
的基本规则以及各种布局管理器的特点和使用方法。


1 、使用布局管理器的基本规则
2、布局管理器的创建
1 、使用布局管理器的基本规则
如果用户不指定容器所使用的布局管理器,那么容器
将使用自己缺省的布局管理器。每当容器需要改变外观时,
这些缺省的布局管理器会发挥作用。大多数布局管理器的
方法都不需要程序直接调用。AWT提供的布局管理器各
有各的长处和缺点。
1 、使用布局管理器的基本规则
用户根据不同要求,选用不同的AWT布局管理器。
1) 用户要求尽量使用所有的空间来显示组件,可以考虑使用
BorderLayout和GridBagLayout。如果使用BorderLayout,用
户应该将占用空间最大的组件放在中心部位。如果使用
GridLayout,用户需要为组件设置限制条件。2)用户需要
在紧凑的一行中以组件的自然尺寸显示较少组件时,用户可
以考虑用面板容纳组件,并使用面板的缺省布局管理器
FlowLayout。
3)用户需要在多行或多列中显示一些同样尺寸的组件,
GridLayout最适合此情况。如果有必要的话,可以使用面板
来容纳组件。
2、示例程序
每个容器都有与其相关的缺省的布局管理器。所有面
板(包括Applets)的缺省布局管理器是FlowLayout。所
有窗口(除了一些有特殊目的的窗口,像FileDialog)的
缺省布局管理器是BorderLayout。
如果用户想使用容器的缺省布局管理器,那么用户
不需要做任何事情。因为容器的构造方法创建了布局管
理器的对象,容器初始化为使用此对象。
如果用户不想使用容器缺省的布局管理器,那么用
户需要创建所需的布局管理器的对象,并使容器初始化
为此对象。
2、示例程序
1) BorderLayout
图8-1显示了使用BorderLayout
安排组件的布局情况。
正如图8-1所示,BorderLayout
有五个区域:北、南、东、西和中
心。如果用户扩大窗口,用户将会
看到中心区域会尽可能的扩大,而
其他区域只扩大到容纳组件所需空
间的大小。
图8-1 BorderLayout的布局管理
2、示例程序
当用户向使用BorderLayout的容器中加入组件时,用户
必须使用两个参数的add()方法,而且第一个参数必须为
“North”、“South”、“East”、“West”或“Center”。如果
用户使用一个参数的add()方法或指定的第一个参数无效,
那么该组件将不能显示出来。
在缺省情况下, BorderLayout将使它所管理的组件之间
没有空隙,用户可以用下面的构造方法指定间隙(以像素
为单位):
public BorderLayout(int horizontalGap, int verticalGap)
2、示例程序
2) CardLayout
CardLayout类可使用户管理共享同一显示空间的两个
或更多个组件。从概念上讲CardLayout所管理的每个组件
就像在纸盒里放的纸牌,在某一时刻只有最上面的一张可
见。用户可以通过指定组件的名字或指定最后或第一个组
件(组件的顺序按照它们被加入到容器中的顺序)来选择
要显示的组件。
8.8.1 使用布局管理器
下面列出的是CardLayout所提供允许用户选择组件
的方法。对于每个方法,第一个参数是由CardLayout控
制的容器。
public void first(Container parent)
public void next(Container parent)
public void previous(Container parent)
public void last(Container parent)
public void show(Container parent, String name)
8.8.1 使用布局管理器
3) FlowLayout
FlowLayout将组件以它们合适的尺寸放在一行中。
如果容器中的水平空间对于将所有组件放在一行中太小,
那么FlowLayout将用多行显示。每一行组件将根据
FlowLayout创建时alignment参数的指定要求放在屏幕的
中心位置(缺省)、左侧或右侧。下面的代码创建了
FlowLayout 及它所管理的组件:
8.8.1 使用布局管理器
4) GridLayout
GridLayout将组件放置在格栅的单元空间中。每个
组件占据其单元空间的所有空间,而且每个单元空间具
有相同的尺寸。如果用户改变GridLayout所有管理的窗
口的尺寸,用户将会看到GridLayout改变了单元空间的
尺寸,使它们在容器的可用空间中尽可能的大。下面的
代码创建了GridLayout和它所管理的组件:
8.8.1 使用布局管理器
5) GridBagLayout
GridBagLayout是AWT提供的最灵活、最复杂的布局管
理程序。如图7-1所示,GridBagLayout将组件以多行多列
放置,允许指定的组件跨多行或多列。如图如果用户增
大Applet的窗口,用户会发现最后一行会占有所有新的
垂直方向的空间,所有新的水平方向的空间被所有的列
分割。这个改变尺寸的动作是以Applet分配给
GridBagLayout中单个组件的权为依据的。用户还会看到
每个组件会占据尽可能多的空间,这个动作也是由
App1et指定的。
8.8.2示例程序
利用AWT组件.实现一个
简单的留言板界面, 其界
面如图8-2所示。
操作过程如下:
图8-2
1)在Jbuilder6中单击“File”菜单中的“New”菜单项,
打开“New…”对话框如图8-3所示。在该对话框中选中
“Frame”,单击“OK”,出现“Frame Wizard”对话框,
如图8-4所示。
图8-3 “New…”对话框
图8-4 设置前的“Frame Wizard”对话框
8.8.2示例程序
2)在“Frame Wizard”
对话框中设置“Name of
new Frame:”和“Base
class ”的值如图8-5所示,
单击“OK”。
图8-5 设置后的“Frame Wizard”对话
框
8.8.2示例程序
3)进入“Design”界面,单击“容器”,在属性窗口中
设置:“layout”的值为“XYLayout”,“background”的值为
“lightGray”,“title”的值为“留言板”。
4)首先在“组件选项板”中选择“AWT”选项,然后
在Frame容器中添加“留言板”界面(图8- 2)上的各个组
件(其中有:标签、单行文本框、多行文本框和按钮)。
5)在“Source”界面的代码中添加如下代码,其目的是
运行过程中能看到所设计的界面。
6)单击“run”命令,界面如图8-2所示。
8.8.2示例程序
7)整个代码如下所示:
package Untitled10;
import java.awt.*;
import com.borland.jbcl.layout.*;
import java.awt.event.*;
public class Frametest extends Frame {
XYLayout xYLayout1 = new XYLayout();
Label label1 = new Label();
TextField textField1 = new TextField();
8.8.2示例程序
Label label2 = new Label();
TextArea textArea1 = new TextArea();
Button button1 = new Button();
Button button2 = new Button();
public Frametest() {
try {
jbInit();
8.8.2示例程序
}
catch(Exception e) {
e.printStackTrace();
}
}
private void jbInit() throws Exception {
label1.setText("您的姓名:");
this.setBackground(Color.lightGray);
8.8.2示例程序
this.setBackground(Color.lightGray);
this.setTitle("留言板");
this.setLayout(xYLayout1);
label2.setText("您的建议:");
textArea1.setText("我认为:");
button1.setLabel("复位");
button2.setLabel("提交");
xYLayout1.setHeight(227);
8.8.2示例程序
xYLayout1.setWidth(293);
this.add(label1, new XYConstraints(40, 0, 60, 28));
this.add(textField1, new XYConstraints(101, 5, 96, 20));
this.add(label2, new XYConstraints(2, 30, 72, 17));
this.add(textArea1, new XYConstraints(1, 49, 270, 112));
this.add(button2, new XYConstraints(56, 169, 59, 19));
this.add(button1, new XYConstraints(139, 168, 59, 19));
}
public static void main(String[] args)
8.8.2示例程序
{ Frametest frm=new Frametest();
frm.setSize (280,220);
frm.setVisible(true);
}
小 结
ava提供了多种图形用户接口包。本章介绍
了 抽 象 窗 口 工 具 箱 AWT 中 一 些 常 用基 本 组件
(标签、按钮、检查框、选择框、列表框、菜
单、单行文本框、滑动器、滚动条和多行文本
框)、AWT容器(框架类、对话框类、面板类
和Apllet)。最后讲述了如何使用它们创建基于
图形用户界面的应用。
习题
1.什么是AWT,什么是Swing?
2.什么是基本组件,什么是容器组件?
3.使用组件,制作一元二次方程求根的系统。
要求:
(1) 一元二次方程三个系数a,b,c输入界面
(2) 显示检查b*b-4*a*c 的值
(3) 显示求出的根
4.开发加、减、乘、除四则运算器。
第九章 java的多线程机制
第一节
第二节
第三节
第四节
第五节
第六节
第七节
小结
习题
什么是多线程机制
多线程实现机制
Thread类
Thread类主要方法的使用
通信线程
线程死锁
线程控制综合实例
9.1
什么是多线程机制
图9-1是Windous98两程序同时运行的示例
程序的运行叫进程
计算机上同时运行多个程序叫进程并发。
点击资源管理器,使其运行,如图9-1(a)所示。然
后选择一批文件拷贝到A盘,这时可以发现,文件在拷
贝,资源管理器仍响应键盘事件。两段程序同时运行。
9.1
什么是多线程机制
地址空间(存放text,data,stack)
进程运行期间的主要资源: 打开文件表
资源控制信息
核心栈
除了text可被其他进程共享外,上述资源都属进程私
有。因此CPU从运行一个进程转换到运行另一个进程时,
为进程保存、恢复的开销很大,并发效率低。如果把进
程所占私有资源与进程中的运行代码相分离,在一个地
址空间中便可运行多个代码段,由此产生线程概念。
9.1
什么是多线程机制
图9-2和图9-3分别示意了把一个任务按两个并发进程和两个并发线程
分解后的情况。比较这两张图中进程与进程之间、线程与线程之间的关系。
进程的关系比较疏远各个进
程是在自己独立的址空间内
执行,不但寄存器堆栈是独
有的,动态数据堆静态数据
区和程序代码也相互独立。
线程间的关系则要紧密得多,虽然各线
程为保持自己的控制流而独有寄存器和
堆栈,但由于两线程从属于同一进程它
们共享同一地址空间,所以动态堆、静
态数据区及程序代码为各线程共享。进
程作为独立的实体,为线程提供运行的
资源并构成静态环境。线程和进程一样,
是处理机调度的基本单位。
正是由于线程与进程之间的这些差别,决定了多线程技术的优越性:快速
的切换、系统额外开销减少、 线程之间通信容易实现。
9.1
什么是多线程机制
图9-4 多线程的概念
多线程程序设计
多进程程序设计
多线程程序设计是指单个程序包含并发执行的多个
线程。当多线程程序执行时,该程序对应的进程中就有
多个控制流在同时运行,即具有并发执行的多个线程。
多线程技术用途很多。典型例子有:动画设计中,多个
画面在同时运动可用多线程编程;多个线程共享资源,
表现为它们程序中有共享的变量,为了保证它们对共享
的变量正确的操作,线程需要同步。
图9-1文件向A拷贝的同时资源管
理器在运行
(a)资源管理器运行框
(b)文件向A拷贝
图9-2 进程之间的关系
进程1
寄存器
存储器
进程2
寄存器
存储器
堆栈
堆栈
动态堆
动态堆
静态数据
静态数据
程序代码
程序代码
图9-3进程与线程之间的关系
进程
线程1
线程2
寄存器
寄存器
堆栈
堆栈
存储器
动态堆
静态数据
程序代码
图9-4 多线程的概念
两个线程
程序
9.2
多线程实现机制
Java 有两种方式可以使应用程序实现多线程机制
9.2.1 继承Thread类
9.2.2 实现Runnable接口
9.2.1 继承Thread类
Thread类是java语言包中的一个可重用类。Thread
对象代表Java程序中单个的运行线程。Thread实现了
Runnable接口,Runnable接口规范了run()方法,
Thread的该方法中包含了运行时执行的代码。一个
类通过继承Thread类,同时重写其run()方法,该
类就可以以线程的方式运行,并继承Thread类的所
有方法。Thread类提供了丰富的线程控制方法。
9.2.2 实现Runnable接口
多线程机制的另一种方式是实现Runnable接口。
由于Java不支持多继承性,如果用户需要类以线程方式
运行且继承其他所需要的类,就必须实现Runnable接口。
事实上,Thread类本身就实现了Runnable接口。Runnable
接口只有一个方法run( )。一个类实现Runnable接口时都
需要在类中实现run()方法,该run( )方法完成由特定线程
所完成的功能。
9.3
Thread类
9.3.1 Thread类成员变量及方法
9.3.2 Thread类有关重要概念
9.3.1 Thread类成员变量及方法
Thread类的构造函数有下面几种形式。
public Thread()
public Thread(Runnable target)
public Thread(ThreadGroup group,String name)
public Thread(String name)
public Thread(Runnable target)
public Thread(ThreadGroup group,Runnable target)
public Thread(ThreadGroup group,Runnable targer,String name)
上述Thread构造方法的形式参数说明如下:
Runnable target——当线程启动时,它激发target中的run()方法;
ThreadGroup group——当创建该线程时,可以将该线程加入到不同
的线程组group中;
String name——线程名,可以使用它使线程分离,也可以用null作为
线程名。
9.3.1 Thread类成员变量及方法
Thread类具有下面的一系列方法来控制线程:
public static native Thread currentThread() 返回当前活动线
程的引用;
public static native void yield() 使当前执行线程暂时停止
执行而让其他线程执行;
public static native void sleep(long millis) throws
InterruptedException 使当前活动线程睡眠指定的时间
millisme;
public static void sleep(long millis,int nanos)throws
InterruptedException 使当前活动线程睡眠指定的时间
millisme加上十万分之nanos秒;
9.3.1 Thread类成员变量及方法
public native synchronized void start() 开始运行当前线程;
public void run() 该方法用来定义线程体。
public final void stop() 强制当前线程停止运行,并抛出ThreadDead错误;
public static boolean interupted() 测试当前线程是否被中断;
public boolean isInterrupted() 测试当前线程是否被中断;
public void destroy() 撤消当前线程;
public final native boolean isAlive() 测试当前线程是否在活动;
public final void suspend() 临时挂起当前线程;
public final void resume() 恢复运行挂起的线程;
public final void setPriority(int newPriouity) 设置线程的优先级,
public final int getPriority() 获得当前线程的优先级;
public final void setName(String name) 设置线程名;
public final String getName() 得到当前线程名;
public final ThreadGroup getThreadGroup( ) 返回当前的线程组;
9.3.1 Thread类成员变量及方法
public static int activeCount() 返回当前线程组中有多少个活动的线程;
public static int enumerate(Thread tarray[]) 将当前线程组和子线程组中的活
动线程拷贝到指定的线程数组中;
public native int countStackFrames() 计算线程中的栈帧数量;
public final synchronized void join(long millis)throws InterruptedException
等待当前线程终止运行,等待时间为millisms;
public final synchronized void join(long millis,int nanos) throws
InterruptedException等待当前线程终止运行,等待时间为millisms加上十
万分之nanos秒;
9.3.1 Thread类成员变量及方法
public final void join() throws InterruptedException 无条件等待当前线程
终止运行;
public stativ void dumpStack() 清除当前线程的栈;
public final void setDaemon() 设置当前线程为守护线程;
public final boolean isDaemon() 返回当前线程是否为守护线程;
public void checkAccess() 返回当前活动线程是否可以访问该线程;
public Sting toString() 返回线程的字符串表示,
9.3.2
Thread类有关重要概念
1.守护线程
2.Thread优先权有关的成员变量
3.Thread Group类
4.线程的状态
5.线程的同步
1.守护线程
利用setDaemon(true)方法设置该线程为守护线程。
该设置告诉Java虚拟机,一个Java程序在它的非守护
线程撤消之前处于运行状态。非守护线程称为用户线
程(user thread)。守护线程是后台运行的进行清理
或维护工作的线程,一般Java程序运行到程序中所有
的线程撤消而终止
2.Thread优先权有关的成员变量
MAX_PRIORITY:一个Thread可能有的最大优先
权,值为10;
MIN_PRIORITY:一个Thread可能有的最小优先
权,值为1;
NORM_PRIORITY:一个Thread默认优先权,值
为5;
3.Thread Group类
ThreadGroup提供了一个只允许同组线程互相影响的安全
制。它具有两种实例化方法。
public ThreadGroup(String name);
public ThreadGroup(ThreadGroup parent,String name);
其中参数:
String name——线程组名;
ThreadGroup parent——线程组的父线程组,即该线程组是父
线程组parent的子组。
线程组中的操作与线程Thread中的操作基本相同,唯一
不同的
就是线程组的操作都是针对该线程组中的所有线程对象,
3.Thread Group类
下面列出了线程组中具有的所有方法。
public final String getName() 返回线程组名;
public final ThreadGroup getParent() 返回当前线程组的父线程组;
public final int getMaxPriroity() 得到当前线程组中最大的线程优先
级;
public final boolean isDaemon() 测试当前线程是否是守护线程组;
public synchronized boolean isDestroyed() 测试当前线程组是否消
灭;
public final void setDaemon(boolean daemon) 改变线程的守护状态;
public final void setMaxPriority(int pri) 设置线程组最大优先级;
3.Thread Group类
public final void checkAccess() 返回当前活动线程是否可以
改变该线程组;
public int activeCount() 返回当前线程组中活动线程的个数;
public int enumerate(Thread list[])
public int enumerate(Thread list[],boolean recurse)
public int enumerate(ThreadGroup list[])
public int enumerate(ThreadGroup list[],boolean recurse) 将当
前线程组中的活动线程拷贝到指定的线程数组或线程组数
组中;
public int activeGroupCount() 返回当前活动线程个数;
public final void stop() 停止所有该线程组内的线程运行;
public final void suspend() 挂起该线程组内的所有线程;
3.Thread Group类
public final void resurme() 继续执行该线程组内的所有线程;
public final void destroy() 停止执行该线程组内的所有线程;
public void list() 打印该线程组中的线程和线程组,这个方
法只用在调试过程中;
public void uncaughtException(Thread t,Throwable e) 处理
线程组中由于未捕获的异常引起的线程退出;
public boolean allowThreadSuspension(boolean b) 返回线程
是否容许挂起;
public String toString() 返回此线程组的字符串表示。
4.线程的状态
线程的四种状态
创建(new)
可运行(runnable)
终止(dead)
封锁(blocked)
线程既然是程序中单一的顺序控制流,它就有生
命周期,即由创建而产生,由撤消而消亡。在线程的
生命期中,它总是从一种状态变迁到另一种状态,状
态表示线程正在进行的活动以及在这段时间内线程能
完成的任务。
1).创建状态
线程对象刚刚建立处于创建状态。例如通过如下语句
创建一个名为test的线程对象:。
Thread thread=new Thread(“test″);
此时该线程处于创建状态。创建状态的线程还没有运行。
2).运行状态
此时的线程已经启动(start()),线程运行于
run( )方法之中。但这时可能CPU正在执行它,也可能
不在执行它。只要CPU有空闲,该线程对象就会执行。
调用线程的start()、run( )方法都可以使线程处于“运
行”状态,线程通过start()方法被启动后,就开始执
行run( )方法。
Thread thread=new Thread(“test″);
thread.run( );
线程thread处于可运行状态。
3).终止状态
线程的run( )运行结束,或运行线程调用stop( )方
法都使其从“可运行”状态进入“终止”状态。此
时若再调用stop( )方法将产生一个异常。调用线程
stop( )方法可使运行线程进入“终止”状态。
Thread.stop( );
4).封锁状态
suspend()、wait()、sleep()
当前线程可以运行,但暂时有某些原因阻碍了它
目前的运行。线程堵塞有多种情况,对于不同原因
的封锁可以用不同的方法使其重新“运行”。利用
suspend()、wait()、sleep()方法可以使线程封锁。
notify()、resume()可以使封锁的线程重新“运行”。
线程输入输出等待是线程被封锁的一种原因。当线
程对数据流进行输入输出时,线程便会自动进入
“封锁”状态,例如当利用标准输入设备从键盘中
读取字符而当前没有字符可读时,程序会自封锁一
直到有数据可读为止。
5.线程的同步
当两个或多个线程要同时访问共享数据时,要十分小心地
加控制以便一次只能有一个线程访问共享数据。支持这种互斥
的机制称为监视器(monitor)。在一段时间内只有一个线程拥
有监视器,所有其他的线程在试图访问被所定的资源时,被挂
起的线程等待监视器解锁。
那么Java是如何表示这些监视器对象呢?事实上,所有Java对象
都有与它们相关的隐含监视器。进入对象监视器的办法是调用
对象的synchronized方法,只要一个线程进入synchronized方法,
同一对象的其他线程就不能进入synchronized方法而必须等待。
Java的同步是这样实现的:用关键字synchronized对需要的方法
或代码进行标记。例如将某方法设计成同步,如下所示:
public synchronized void myMethod()
{
……
}
5.线程的同步
带有关键字synchronized的代码段的运行机制同普通的运
行方式有所不同。例如,将访问对象myObject方法的代码段
说明为同步的过程如下:
synchronized(myObject) // myobject是任意的对象引用
{
……包含了对象的方法调用
}
下面是run方法的同步写法:
public void run() {
synchroniaed(target) {
target.call(msg);
}
}
9.4
Thread类主要方法的使用
9.4.1
9.4.2
9.4.3
9.4.4
9.4.5
Sleep方法
Suspend方法
Wait()方法
Yield方法
改变线程状态优先级的方法
9.4.1
Sleep方法
线程调用sleep()方法可以使线程处于"睡眠"状
态,其中具有一个参数,即"睡眠"的时间,在该
时间范围之内,该线程处于"封锁"状态,这个线
程是不会运行的。这种封锁方式将在指定的"睡眠
"时间后自动转换为"可运行"状态.
9.4.2
Suspend方法
调用线程的suspend()方法可以暂停该线程的执行,
此时只有通过重新调用线程的resume( )方法才能返回
“可运行状态”。在下面的程序中,线程ThreadTest3
将实例化一个ThreadTest2对象,其中TheadTest2线程
定时3000ms打印一个数字,而ThreadTest3的run( )方
法中每2000ms打印一个数字,ThreadTest3打印第二个
数字后将被暂停,到ThreadTest3打印完第4个数字后,
调用ThreadTest2的resume( )方法使用其继续执行。
9.4.3
Wait()方法
无论是sleep()方法还是suspend()方法都不可能在调用的
时候解除线程的封锁状态,然而调用线程的wait()期间,线
程封锁,但有关的线程调用的同时会解除该线程的封锁。
wait()方法具有两种形式:
(1)采用一个以ms为单位的参数形式,它具有与sleep()方法相同的含义:
线程暂停一段时间。然而,它们之间的区别是在于wait()方法中对象通过
notify()方法可强制使时间流逝。
(2)是在wait()方法中不采用任何参数,这意味着wait()方法持续执行,一
直到有notify介入为止,当线程中采用了wait()方法置入“睡眠”状态后,
只有一个notify()或notifyAll方法发生的时候,线程才会唤醒,同时检查
当前的条件是否发生变化。
9.4.4
Yield方法
若有一个线程,它进行了大量的计算而占用了CPU,而此时有可
能需要让该线程暂停,让出CPU,从而使其他线程能够执行一段时间。
这种情况下,可以使用yield方法。
例10-7 线程主动放弃CPU。
int sum=0;
for(int m=0;m<10000;m++)
{
for(int n=0;n<10000;n++)
{
sum+=(m*n);
}
Thread.yield(); //使该线程将CPU的使用权暂时让给其他的线程
执行
}
9.4.5
改变线程状态优先级的方法
利用线程的setPriority()和getPriority()方法可以设置
和获得线程的优先级,优先级高的线程将得到更多的执
行机会,例如当很多线程同时处于封锁状态时,若CPU
得到暂时的空闲,则优先级高的线程将优先执行。然而
并不是说,优先级低的线程不会被执行,而仅仅是执行
的机会没有优先级高的线程多而已。
9.5
通信线程
9.5.1
9.5.2
主存读/写通信
管道流通信
9.5.1
主存读/写通信
线程通信: (线程之间交换信息称为线程通信)
wait()和notify()方法只彼此转送一个信号
多个线程借助Java提供的主存读/写机制实现相互间的通信。
Java.io中定义了
类ByteArrayInputStream(字节数组输入流)、
类ByteArrayOutputStream(字节数组输出流)
类StringBufferInputStream(字符串缓冲输入流)。
通过ByteArrayInputStream可以从字节数组中读取数据。字节数组
输入流的初始化是在给定字节数组上完成的,并且,除了重写父类
InputStream的方法read()以外,类ByteArrayInputStream还重写了方法
available()、reset()和skip()。其中,方法reset()重置输入流的读取位置的
起始位置,即字节数组的起始位置。
9.5.1
主存读/写通信
ByteArrayOutputStream
StringBufferInputStreamt
通过ByteArrayOutputStream可以向内存字节数组写入数据。类中提供
了缓冲区以存放数据,并且该缓冲区的大小可随着数据写入而自动动增加。
在构造方法中。可以指定初始缓冲区的大小,也可以使用缺省的32B。在
ByteArrayOutputStream实现的方法中,使用size()可以得到输出流中的有效
字节数,write()用于向输出流的字节数组缓冲区写入数据,通过
toByteArray()可以将字节数组输出流缓冲的内容写入另一个指定的输出流
中。
类StringBufferInputStreamt和ByteArrayInputStream基本类似,不同点只
在于它是从字符串缓冲区StringBuffer中读取16位的Unicode数据,而不是8
位的字节数据。
9.5.2
管道流通信
管道用来把一个程序、线程或代码块的输出连接到另一程序、线
程或代码块的输入。java.io中提供了类PipedInputStream和
PipedOutputStream作为管道的输入/输出部件,在使用管道前,
管道输入/输出流必须要进行连接。
类PipedInputStream和类
PipedOuputstream接口分别为:
void
connect(PipedOutput.Stream
src);
void
connect(PipedInputStream snk);
同时,为了完成输入/输出操作,
PipedInputStream重写了方法
read(),PipedOutputStream重写
了方法write()。
PipedOutputStream中
提供了下面的一些连接方法:
(1)构造方法中,对于管道
输入/输出流来说,线出对应管
道输出/输入流作为参数以进行
连接,其接口分别为:
(2)管道输入/输出流还提供了
方法connect()以进行相应的连接。
9.6
线程死锁
什么是同步?
两个线程甲、乙,如果共享一个表,它们都有权力对表读、修改,
不言而喻,两个线程同时修改表,会引起表的错误。为此,修改表时只
容许一个线程进入修改方法。甲线程已经进入该修改方法而尚未退出,
乙线程也要进入该修改方法,就必须等待。当进入该修改方法的甲线程
退出时,等待的乙线程再进入。线程的这种关系叫同步。
为了说明一个方法同时只能有一个线程进入,在方法说明前加上修饰符
synchronized。Java运行环境保证只能有一个线程进入有修饰符
synchronized的方法。
9.6
线程死锁
假设线程甲、乙共享表1和表2,如下程序所示,线程甲修改表1涉
及修改表2,线程乙修改表2涉及修改表1。
线程甲
synchronized 修改表1()
{……A1
修改表2() //调用修改表2()方
法
……
}
线程乙
synchronized 修改表2()
{ ……A2
修改表1() //调用修改表2()
方法
……
}
如果甲线程进入修改表1,但是尚未调用修改表2(),这时乙线程进
入修改表1,接着乙线程如果试图调用修改表1(),也会被阻隔,甲线程
如果还试图进入调用修改表2就会被阻隔,这样甲、乙线程都处于僵持挂
起状态。这叫线程死锁
小 结
本章主要介绍了多线程的基本概念,
讲述了Java语言用以实现多线程的两种
机制:继承Thread类和实现Runable接
口;较详细地介绍了Thread和
ThreadGroup类,并着重说明了线程的
4种状态:创建、可运行、封锁和终止。
最后介绍线程的通讯、同步与死锁。
习题
1. 什么是线程?它产生的背景是什么?
2. * 从概念、动态性、同步、互斥、通信等多角度比
较线程与进程的异同点。
3. 利用GUI的Text Field (或TextArea)、Button、Label
等控件实现例7-10中Sender和Receiver线程通讯的控制。
4. 从程序上讲述例7-11中线程死锁的原因。
5.* 以client/server应用模式进行client侧和server侧的
程 设计。要求:3个clients,分别有2个、3个、4个请
求服务的项目,server满足3个client的请求外。用Java
写出多线程程序。(大型设计作业)
第10章 Java网络编程基础
10.1 Java网络编程基本概念
Java的特性之一提供灵活方便的网络支持,它用
专门的类来处理较低层次的TCP/IP网络连接。程序
员即使从来没有编过网络程序,也能在很短的时间
内利用Java方便地访问Internet上的资源。在介绍Java
的网络类之前,我们先简单介绍一下有关TCP/IP协
议的基本知识,
10.1.1 TCP/IP协议组






一般TCP/IP协议是指一组通讯协议的总称,她包
括TCP IP(Internet协议)、(传输控制协议)、UDP
( 用 户 数 据 报 协 议 ) 、 HTTP( 超 文 本 传 输 协 议 ) 、
FTP(文件传输协议)、SMTP(简单邮件传输协议)等一系
列协议组成。
1)物理层 (以太网、令牌环、X.25等)
2)网络层 (IP协议)
3)传输层 (TCP协议、UDP协议)
4)应用层 (HTTP协议、FTP协议、SMTP协议
等)
10.1.2 套接口(Socket)

套接口是进程之间的通信的抽象连接点。进程之间想
通信首先要建立套各自的套接口。客户程序可以向套接口里
写入请求,然后服务器会处理这个请求,并把处理结果通过
套接口送回。具体来说,一个服务器应用程序一般侦听一个
特定端口以等待客户的连接请求,当一个连接请求到达时,
客户和服务器建立一个通信连接,在连接过程中,客户被分
配 一 个 本 地 端 口 号 并 且 与 一 个 Socket 连 接 , 客 户 通 过 写
Socket来通知服务器,以读Socket来获取信息。类似地,服
务企也获取一个本地端口号,它需要一个新的端口号来侦听
原始端口上的其他连接请求。服务器也给它的本地端口连接
一个Socket并读写它来与客户通信。
10.1.2 端口

端口是一个逻辑概念。主机每一服务者运行在该主
机的一个对外开放的端口上。一个主机上可以由多种服务
者,也就是由多个端口。程序员可以在创建自己的服务程
序时使用其它端口(即除了系统默认的端口)。端口常以
数字编号,作为客户可指定一个端口号,以这个端口号码
连接服务代理以接收服务。例如 telnet 2010.104.0.241
5555,通过这条命令,即可远程登录到华中理工大学的一
台服务代理,通过5555端口参与一个网络mud游戏。
10.2 java网络包(java.net.)
10.2.1 套接口类(Socket)
套接口类的构造方法有四种:
1 )
Socket(String host, int
UnknownHostException,IOException
参数:host 主机名
port 端口号
port)
throws
2 ) Socket(String host,int port,boolean stream)
参数:host
主机名 port 端口号
stream
用于决定生成的套接口是流套接口还是数
据报套接口(datagram
socket)。
10.2.1 套接口类(Socket)
3
) Socket(InetAddress address,int port)
参数:address
特定的地址
port
端口
4 ) Socket(InetAddress address,int port
boolean stream)
参数:host
主机名
port
端口号
stream
用于决定生成的套接口是流套接
口还是数据报套接口(datagram
socket)。
10.2.1 套接口类(Socket)
套接口类提供的主要方法有:
◆(1) InetAddress getInetAddress()
返回该套接口(socket)所连接的地址。
◆(2) int getPort()
返回该套接口(socket)所连接的远程端口。
◆(3) snchronized void close() throws IOException
关闭套接口
◆(4) InputStream getInputStream() throws IOExcepiton
获得从套接口读入数据的输入流。
注:DataInputStream为InputStream 的子类。
◆(5)InputStream getOutputStream() throws IOException
获得向套接口进行写操作的输出流。
10.2.3 服务者套接口类(ServerSocket)
服务者套接口类有两种构造方法
1)ServerSocket(int port) throws IOException
参数:port
端口号
2)ServerSocket(int port,int count)
参数:port:
端口号
count
对该ServerSocket实体对象与端口间的连接进行监
听的次数。
服务者套接口类提供的主要方法:
1)Socket accept() throws IOException
等待一个连接,该方法将阻塞当前线程,直到连接成功。该方法返回
一个套接口类(Socket)对象,通过该对象,程序与连接的客户进行通
信。
2)void close() throws IOException
关闭套接口。
服务器
时
Socket()
间
Bind()
客 户
Listen()
Socket()
Accept()
Connect()
阻塞、等客户连接请求
Read()
处理服务器请求
Write()
服务请求
Write()
Read()
服务响应
图10.1 基于连接的服务者、客户程序流程图
10.3.1.1 TCP协议通讯的服务方实现
1) 通过输入流从客户获得命令;
2) 以某种方式获取该信息;
3) 通过输出流将信息送给客户。
package untitled4;
import java.io.*;
import java.net.*;
public class Tcpserver
{ static public void main(String args[]) throws IOException
{ ServerSocket svrsoc=null;
Socket soc=null;
BufferedReader in=null;
PrintWriter out=null;
InetAddress clientIP=null;
String str=null;
try
{ svrsoc=new
10.3.1.1 TCP协议通讯的服务方实现
{ svrsoc=new ServerSocket(8000);
// 构造ServerSocket对象,端口为8000
System.out.println("Wait for.......");
soc=svrsoc.accept();
//服务端等待一个连接,返回新套接口soc
in=new BufferedReader(
new InputStreamReader(
soc.getInputStream()));
//在新套接口soc上构造BufferedReader对象
out=new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
10.3.1.1 TCP协议通讯的服务方实现
soc.getOutputStream())),true);
//新套接口soc上构造PrintWriter
对象
clientIP=soc.getInetAddress();
//得到顾客方IP地址
System.out.println("Client's IP address:"+clientIP);
out.println("Welcome!...");
str=in.readLine();
//在in上读一行
while(!str.equals("quit"))
//如读出的不是"quit",继续读
{
System.out.println("Client said:"+str);
str=in.readLine(); //out.println(str);
}
System.out.println("Client want to leave.");
}
10.3.1.1 TCP协议通讯的服务方实现
catch(Exception e)
{
System.out.println("Error:"+e);
}
System.out.println(“Client want to leave.”);
finally {
in.close();
out.close();
soc.close();
svrsoc.close();
}
}
10.3.1.2 TCP协议通讯的客户方实现
源程序如下:
package untitled4;
import java.net.*;
import java.io.*;
public class tcpclient
{
static public void main(String args[])
throws IOException
{
Socket soc=null;
BufferedReader in=null;
PrintWriter out=null;
String strin=null;
String strout=null;
try
{
10.3.1.2 TCP协议通讯的客户方实现
soc=new Socket("localhost",8000);
System.out.println("Connecting to the Server...");
in=new BufferedReader(
new InputStreamReader(
soc.getInputStream()));
out=new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
soc.getOutputStream())),true);
strin=in.readLine();
System.out.println("Server said:"+strin);
byte bmsg[] = new byte[20];
System.in.read(bmsg);
String msg=new String(bmsg,0);
msg=msg.trim();
while(!msg.equals("quit"))
10.3.1.2 TCP协议通讯的客户方实现
{ out.println(msg);
System.in.read(bmsg);
msg=new String(bmsg,0);
msg=msg.trim();
out.println(msg);
}
out.println(strout);
}
catch (Exception e)
{ System.out.println("Error: "+e);
}
finally
{
in.close();
out.close();
soc.close();
System.exit(0);
}
}
}
10.3.1.3 TCP协议通讯的服务方实现源程序











import java.io.*;
import java.net.*;
public class Tcpserver
{
static public void main(String args[]) throws IOException
{
ServerSocket svrsoc=null;
Socket soc=null;
DataInputStream in=null;
PrintStream out=null;
InetAddress clientIP=null;















String str=null;
try
{
svrsoc=new ServerSocket(8000);
// 构造rverSockets对象,端口为8000
soc=svrsoc.accept();
//服务者等待一个连接,返回新套接口soc
in=new DataInputStream(soc.getInputStream());
//在新套接口soc上构造//DataInputStream对象in
out=new PrintStream(soc.getOutputStream());
//在新套接口soc上构造 // PrintStream对象out
clientIP=soc.getInetAddress();
//得到顾客方IP地址
System.out.println("Client's IP address:"+clientIP);
out.println("Welcome!...");

str=in.readLine();
while(!str.equals("quit"))

//在in上读一行
//如读出的不是“quit”,继
续读










{
System.out.println("Client said:"+str);
str=in.readLine();
}
System.out.println("Client want to leave.");
}
catch(Exception e)
{
System.out.println("Error:"+e);
}
10.3.1.4 TCP协议通讯的客户方实现源程序





import java.net.*;
import java.io.*;
public
class
Tcpclient
{ static public void
main(String args[])
throws
IOException
{
Socket soc=null;
DataInputStream in=null;
PrintStream out=null;
DataInputStream sysin=null;
String strin=null;
String strout=null;
try
{












soc=new Socket(args[0],8000); // 本机为“localhost”
System.out.println("Connecting to the Server...");
in=new DataInputStream(soc.getInputStream());
out=new PrintStream(soc.getOutputStream());
strin=in.readLine();
System.out.println("Server said:"+strin);
sysin=new DataInputStream(System.in);
strout=sysin.readLine();
while(!strout.equals("quit"))
{ out.println(strout);
strout=sysin.readLine();
}














out.println(strout);
}
catch (Exception e)
{ System.out.println(“Error: “+e);
}
finally
{ in.close();
out.close();
sysin.close();
soc.close();
System.exit(0);
}
}
}
10.3.2 一对多通讯的服务方实现



如果聊天室有n个网友在聊天,每个网友聊
天时其他网友都“看到“聊的内容。聊天服 务
器Chat Server管理为每一个进入聊天室的网友建
立一个线程,它是一个典型的多线程程序。Chat
Server的流程图如图图10.2所示:
每一个人连入的Chat Applet创建一个对应
的线程,该线程监听对应的Applet是否有信息传
来。如果有则向所有的Chat Applet广播该消息,
也就是说,这时Chat Server应该创建有n个线程。
监听是否有新的Chat Applet()连接到server
设置监听端口号
创建一个新的服务线程
设置线程ID号并添加到Vector中
启动服务线程
通知所有ChatApplet
有新线程加入
图10.2
监听对应的Applet是否发来消息
向所有的Applet广播该消息
ChatServer 的流程图
10.3.2服务者源程序Chat Server如下:
import
import
import
public
java.net.*;
java.io.*;
java.util.*;
class ChatServer {
public static void main(String args[])
{
ServerSocket socket=null;
Vector m_threads=new Vector();
System.out.println("listen...");
try
{
10.3.2服务者源程序Chat
Server如下:
socket=new ServerSocket(5555);
}
catch(Exception e)
{
System.out.println("new ServerSocket()failed!");
return;
}
try
{
int nid=0;
while(true)
{
//监听是否有新Chat Applet连接到Server,
//程序会陷入到该语句,直到有新的连接产生。
10.3.2服务者源程序Chat Server如下:
Socket s=socket.accept();
System.out.println("accepted");
//创建一个新的ServerThread.
ServerThread st=new ServerThread(s,m_threads);
//为该线程设置一个ID号。
st.setID(nid++);
//将该线程加入到m_threads Vector中。
m_threads.addElement(st);
//启动服务线程。
new Thread(st). start();
//通知所有Chat Applet有一个新的网友加入。
for(int i=0;i<m_threads.size();i++)
{
ServerThread
st1=(ServerThread)m_threads.elementAt(i);
10.3.2服务者源程序Chat Server如下:
st1.write("<#>welcome "+st.getID()+" to enter chatroom!");
}
System.out.println("Listen again...");
}
}
catch(Exception e)
{
System.out.println("Server is down...");
}
}
}
class ServerThread implements Runnable
{
Vector m_threads;
Socket m_socket=null;
10.3.2服务者源程序Chat Server如下:
DataInputStream m_in=null;
DataOutputStream m_out=null;
int m_nid;
//初始化该线程。
public ServerThread(Socket s,Vector threads)
{m_socket=s;
m_threads=threads;
try
{
m_in=new
DataInputStream(m_socket.getInputStream());
m_out=new
DataOutputStream(m_socket.getOutputStream());
}
10.3.2服务者源程序Chat Server如下:
catch(Exception e)
{
}
}
public void run()
//线程的执行体。
{ System.out.println("thread is running");
try
{ while(true)
{
//监听对应的Applet是否传来消息
//程序陷入到m_in.readUTF()中,直到有信息传来才返
String s=m_in.readUTF();
If (s==null) break;
//如果Chat Applet传来的信息为"leave",
//则通知所有其他的的Chat Applet自己退出了。
10.3.2服务者源程序Chat Server如下:
if (s.trim().equals ("leave"))
for (int i=0;i<m_threads.size();i++)
{
ServerThread st=(ServerThread)m_threads.elementAt(i);
st.write("*** "+getID()+" leave..."+"***");
}
else
//向所有Chat Applet广播该信息。
for(int i=0;i<m_threads.size();i++)
{
ServerThread st=(ServerThread)m_threads.elementAt(i);
st.write("<"+getID()+">"+s);
}
}
}
10.3.2服务者源程序Chat Server如下:
catch(Exception e)
{ e.printStackTrace();
}
//从m_threads Vector中删除该线程,表示该线程已经离开chat room.
m_threads.removeElement(this);
try
{ m_socket.close();
}
catch (Exception e){}
}
//将msg送回对应的Applet
public void write (String msg)
{
synchronized(m_out)
10.3.2服务者源程序Chat Server如下:
{
try
{ m_out.writeUTF(msg);
}
catch(IOException e){}
}
}
public int getID()
//获得该线程的
ID.
{ return m_nid;
}
public void setID(int nid)
{
m_nid=nid;
}
}
//设置线程的ID.
10.4 URL类
10.4.1 URL类简介
URL类封装了使用统一资源定位器(Uniform Resource Locator)访
问一个WWW上的资源的方法 .
URL的基本表示方法是:
protocol://hostname:/resourcename#anchor
下面是几个合法的URL例子:
http://www.ncsa.uiuc.edu/demoweb/url-primer.htm
http://www.ncsa.uiuc.edu:8080/demoweb/url-primer.htm
http://local/demo/information#myinfo
ftp://local/demo/readme.txt
10.4.2 用URL获取文本和图像
利用URL可以方便的获取文本和图像。文本数据源可以是网上
或者本机上的任何文本文件,只要该文本文件的地址表示符合URL的标
准位置表示法。下面是一个利用URL来获取文本文件(.txt)和图像文
件(.jpeg,.gif)的例子:
import java.net.*;
import java.awt.*;
import java.io.*;
//这个类继承了Frame类,生成一个代标题的窗口
public class getDataByURL extends Frame{
MenuBar menuBar;
boolean drawImage=false;
DataInputStream dataInputStream;
int i=0;
String line_str;
boolean first=true;
Font font;
public getDataByURL () {
//生成一个菜单条
menuBar=new MenuBar();
setMenuBar(menuBar);
//为菜单条第一个菜单取名"display"
Menu display=new Menu("display");
menuBar.add(display);
//生成"display"菜单下的两个菜单项
MenuItem beauty_display=new MenuItem("displaybeauty");
MenuItem text_display=new MenuItem("display text");
display.add(beauty_display);
display.add(text_display);
//设置背景颜色和文本的字体
setBackground(Color.white);
font=new Font("System",Font.BOLD,20);
//设置带有菜单的窗口的标题
setTitle("sample:use URL get data");
resize(400,300);
//显示窗口
show();
}
//处理窗口中的菜单事件
public boolean action(Event evt,Object what) {
if(evt.target instanceof MenuItem) {
String message=(String)what;
if(message=="display beauty") {
drawImage=true;
doDrawImage();
}
else {
drawImage=false;
first=true;
if(message=="display text")
doWrite("file:///d://plbackup/tt.txt");
}
}
return true;
}
//处理窗体事件
public boolean handleEvent(Event evt) {
switch(evt.id) {
case Event.WINDOW_DESTROY: dispose();
System.exit(0)
default:
return super.handleEvent(evt);
}
}
static public void main(String args[]) {
new getDataByURL();
}
public void paint(Graphics g) {
if(drawImage)
{
try {
//生成一个URL对象,它指向本机上
//的一个类型为.jpeg的图形文件
URL image_URL=new URL("file:///D:/plbackup/zy4.jpeg");
Toolkit object_Toolkit=Toolkit.getDefaultToolkit();
Image object_Image=object_Toolkit.getImage(image_URL);
g.setColor(Color.white);
g.fillRect(0,0,300,400);
g.drawImage(object_Image,40,80,160,200,this);
}
catch(MalformedURLException e){}
}
else {
if(first) {
first=false;
g.setColor(Color.white);
g.fillRect(0,0,400,300);
g.setFont(font);
}
if(line_str!=null)
g.drawString(line_str,10,i*20);
i++;
}
}
//画图像函数
private void doDrawImage() {
drawImage=true;
repaint();
}
//写文本函数,它的参数是一个指向绝对URL的字
符串
private void doWrite(String url_str) {
try {
//用参数url_str生成一个绝对的URL,它指
//向本机上的一个文本文件
URL url=new URL(url_str);
dataInputStream=new DataInputStream(url.openStream());
try {
i=1;
line_str=dataInputStream.readLine();
while(line_str!=null) {
paint(getGraphics());
line_str=dataInputStream.readLine();
}
}
catch(IOException e){}
dataInputStream.close();
}
catch(MalformedURLException el){}
catch(IOException e2){}
}
}
10.4.3 用URL获取网上HTML文件
这个例子是利用URL获取网络上资源的html文件:
import java.net.*;
import java.io.*;
public class urlhtml{
static public void main(String args[]) {
try {
//根据参数args[0]生成一个绝对的URL对象
URL url=new URL(args[0]);
//用URL对象打开一个输入流
DataInputStream d=new DataInputStream(url.openStream
());
String inputLine;
//从输入流读入数据
while((inputLine=d.readLine())!=null) {
//将数据信息显示到系统标准输出上
10.4.3 用URL获取网上HTML文件
System.out.println(inputLine);
}
//关闭输入流
d.close();
}
catch(MalformedURLException me){}
catch(IOException ioe){}
}
}
这个例子通过生成一个指向网络上一个特定资源的URL,用openStream
打开其输入流,从而读取指定资源的html文件。
用dos命令运行:
urlhtml http://www.cumt.edu.cn
就可以看到中国矿业大学主页的html文件。
10.4.4 用URL获取WWW资源
在上节的例子中,虽然我们可以非常方便地抓取网上资源的html文件,
但是我们看到的仅仅是html文件本身的内容。实际上在许多应用中,我们还
需要获得Web主页 ,或者FTP到网络上的一个FPT服务器上下载文件,下面
这个例子就可以实现这些功能:
import java.io.*;
import java.net.*;
public class URL_FTP {
static public void main(String args[]) {
byte data=0;
URL obj1;
File obj2;
DataInputStream inf=null;
FileOutputStream outf=null;
if(args.length!=2) {
System.out.println("Download file!");
System.out.println("Usage:java URL_FTP file1
10.4.4 用URL获取WWW资源
//根据参数args[0]构造一个绝对的URL对象
obj1=new URL(args[0]);
}
catch(MalformedURLException e)
{
System.out.println("Open URL "+args[0]+" Error");
return;
}
//根据参数args[1]构造一个File实体对象(文件)
obj2=new File(args[1]);
//系统输出输入文件的有关描述
System.out.println("Input File Description:");
System.out.println("\tProtocol:"+obj1.getProtocol());
System.out.println("\tHost :"+obj1.getHost());
System.out.println("\tPort :"+obj1.getPort());
System.out.println("\tFile :"+obj1.getFile());
System.out.println("\tExternal:"+obj1.toExternalForm());
System.out.println("\ttoString:"+obj1.toString());
//得到输入文件的文件名称
String s=obj2.getName();
System.out.println(s);
try {
//用一个URL的对象obj1打开一个输入流inf
inf=new DataInputStream(obj1.openStream());
}
catch(FileNotFoundException e) {
System.out.println("file1 not found!");
}
catch(IOException e){}
try {
outf=new FileOutputStream(obj2);
}
catch(FileNotFoundException e) {
System.out.println("file2 not found!");
}
catch(IOException e) {
System.out.println("Open Data Stream Error!");
return;
}
try {
do {
//根据输入流inf生成一个用于输出的字节数组
data=(byte)inf.readByte();
//输出字节数组
outf.write(data);
//循环直至将输入数据全部输完为止
}while(true);
}
小结
通讯协议是通讯双方的约定。通讯协议是分层次的。
TCP/IP 协议是internet传送层、网络层的一系列通讯协议
的习惯称呼。
套接口是进程之间的通信的抽象连接点。套接口按使用
的协议不同分为面向连接的Socket和不连接的Socket。使用
套接口通讯的主动方叫顾客,被动方叫服务者。双方首先
要建立各自的套接口。客户程序可以向套接口里写入请求,
然后服务器会处理这个请求,并把处理结果通过套接口送
回。
URL类遵循HTTP应用层协议。利用URL可以通过Java
编程访问WWW网络资源(Web页、文本文件、图形文件、
声频片段等等)。
习题
1. 套接口作用是什么?端口有什么意义?
2.顾客和服务者通过套接口通讯,描述基于有连接和无连接
通讯时的流程图。
3. 利用Java网络包java.io中套接口类Socket和服务者套接口
类(ServerSocket)分别实现有连接和无连接的通讯。
4. 聊天室有n个网友在聊天,聊天服务器就应该为每一个
人连入的聊天 Applet创建一个对应的线程,该线程监听对应
的Applet是否有信息传来。如果有则向所有的聊天 Applet广
播该消息。实现网络聊天系统。监视网友聊天内容,如出现
“love“字句,给予警告(仅作网络搜索练习)。
5.编程读出中国矿业大学主页的html文件,判断主页的
html文件有无关于“教学改革”的内容。
第
11
章
Java与数据库的连结---JDBC技术
第11章 Java与数据库的连接—JDBC技术









第一节
第二节
第三节
第四节
第五节
第六节
第七节
小结
习题
概述
JDBC的基本功能与特点
JDBC产品
JDBC API
JDBC应用
应用JAVA JDBC开发2层C/S数据库应用
综合应用
11.1 概述
JDBC本身是一个产品的商标名,但
它也可被看作为"Java Database
Connectivity(Java 数据库连接)"。
JDBC由一组用Java语言编写的类组成,
它已成为一种供数据库开发者使用的标
准API,用户可以用纯Java API来编写
数据库应用。
11.2 JDBC的基本功能与特点
I.
II.
III.
IV.
V.
JDBC的基本功能
JDBC API特点
JDBC是低级的API与高级API的基
础
JDBC与ODBC和其他API的比较
在数据库存取的二层与三层模型上
的应用
11.2.1
JDBC的基本功能
简单的说,JDBC可以做三件事:
(1) 建立与数据库的连接。
(2) 发送SQL语句。
(3) 处理数据库操作结果。
11.2.1 JDBC的基本功能
例11-1
实现上述的三种JDBC基本功能的程序示例。
connection con=DriveManager.GetConnection(“jdbc:odbc:
people,”examle”,“password”):
//建立与数据库
的接
Statement stmt=con.createstatement(); //建立语句对象
ResultSet rs=stmt.executeQuery(“SELECT a,b,c FROM
Table1”):
//运行SQL语句,返回数据//库操作
结果
while (rs.next()){
int x=getInt(“a”);
//获得数据库表记录a项
的值
string s=getstring(“b”);
//获得数据库表记录b项的值
float f=getFloat(“c”);
//获得数据库表记录c项的值
}
11.2.2 JDBC API特点
11.2.2.1 在SQL水平上的API
JDBC是为Java语言定义的一个SQL调用级界面,也就是
说其关键在于执行基本的SQL说明和取回结果。在此基础上
可以形成更高层次的API,其中的接口包括直接将基本表与
Java中的类相对应,提供更多的通用查询的语义树表示等。
11.2.2.2 与SQL的一致性
一般数据库系统在很大范围内支持SQL的语法语义,
但他们所支持的一般只能是SQL语法全集中的一个子集,并
且通常他们有许多更强的功能,例如,它的外部连接及过
程存储等方面彼此不能一致,而现在的标准SQL扩展包含更
多的功能。那么JDBC是怎么保证与SQL一致性的呢?
(1) JDBC允许使用从属于DBMS的系统的任何查询语句,因此
一个应用程序,可以使用尽可能的功能,尽管在一些
DBMS中可能会出现错误。
(2) 一般认为ANSI SQL 112 Entry Lever标准功能比较完备,
并且,是被广泛支持
11.2.2.3 可在现有数据库接口之上实现
JDBC SQL API保证能在普通的SQL API上实现,特别是
ODBC。这种要求使JDBC的功能变得更加丰富,尤其是在处
理SQL 说明中的OUT参数及有关大的数据块上。
11.2.2.4提供与其他Java 系统一致的Java界
面 JDBC提供与Java系统其他部分一致的Java界面,这对Java
语言来说有着非常重要的意义。在很大程度上,这意味着Java
语言与标准运行系统被认为是一致的,简单化,并且是功能强
大的。
11.2.2.5 JDBC的基本API在最大可能
上简单化
这也是体现在大多数情况下采用简单的
结构来实现特定的任务,而不是提供复杂的
结构,或者说对某个特定的任务,即提供一
种方案,而不是多种复杂的方案。JDBC的
API以后还将不断的扩展以实现更完善的功
能。
11.2.2.6 使用静态的通用数据类型
11.2.2.7 使一般情形简单化
11.2.2.8 多种方法、多种功能
ODBC中定义的界面过程少,利用过程的标志参
数,使它们选择不同的操作。而Java核心的类定义
的界面方法多,方法不带有标志项参数,使用基本
接口时不必被与复杂功能相关的参数所困扰。
11.2.3 JDBC是低级的API与高级API的基础
JDBC是一种“低级”的接口,因为它直接调用SQL命
令,但它又可作为构造高级接口与工具的基础。高级接口是
“用户友善的”、更易理解和更为方便的API,由后台将它
翻译成如JDBC这样的低级接口。有两种基于JDBC的高级正处
在开发之中:
一种是嵌入SQL的Java。
另一种是把关系数据库表直接映射Java类。
11.2.4 JDBC与ODBC和其他API的比较
目前,微软的ODBC API 是访问关系型数据库中运用最广
的编程接口,它几乎能将所有平台的所有数据库连接起来。
现在问题是,可否通过Java来使用ODBC呢?回答是肯定,
但最好的做法是在JDBC的帮助下采用JDBC-ODBC桥接方
式实现。需要JDBC理由如下:
1.Java不能直接使用ODBC。
2不希望把ODBC CAPI逐字翻译成Java API。
3 ODBC难于学习。
4 JDBC的JDBC API提供“纯Java”的解决办法。
11.2.5 在数据库存取的二层与三层模型上的应
用
两层模型中Applet或Application直接和数据库进行对话,
这需要一个能够和特定数据库管理系统通信的JDBC驱动程序,
数据库可以是本地的或网络上另一台机器的,这属于两层的
客户/服务方式(如图11.1)
在三层模型中,命令被发送到服务器端的一个“中间层”,
然后发送到数据库,数据库处理的结果返回到中间层再到用
户(如图11.2所示),三层结构的灵活性、易于维护,而且
可以为用户提供一个更容易使用的高级用户接口,由中间层
负责将其翻译成底层调用。
图11.1
图11.1 两层的客户/服务方式
图11.2 三层结构的客户/服务方式
11.3 JDBC产品
11.3.1 JavaSoft框架
JavaSoft提供了三种JDBC产品组件,并作为
JDK的组成部分:
(1).JDBC驱动器管理程序。
(2).JDBC驱动器测试集。
(3).JDBC—ODBC桥接。
Java Application
JDBC API
JDBC manger
JDBC Driver API
JDBC_Net
Driver
JDBC-ODBC
Bridge Driver
ODBC and
DB Driver
Published
protocol
Proprietary
database
Driver Driver
A
B
…
JDBC
Implementation
alternatives
Access protocols
图11.3JDBC总体结构
11.3.2 JDBC驱动器类型
驱动程序的4种类型:SUN公司将JDBC驱动
程序为以下4种类型,分别适用于不同的场合,
如图11.4所示:
1) JDBC-ODBC桥驱 动 程 序
2) 本机应用编程接口部分Java 驱动程序
3) 数据库中间件的纯Java 驱动程序
4) 直接连接数据库的纯Java 驱动程序
图11.4
11.4 JDBC API
11.4.1 使用方法


JDBC通常有两种常用的方法:java应用
程序接口和小应用程序接口。
11.4.1.1 Applets
11.4.1.2. Applications
11.4.1.1 Applets
目前Java使用的最多的从网络中下载的applet,它
们作为web文件的一个部分。其中有数据库存取applet
和能够使用JDBC来接触数据库的applet。最一般的情
况里面,对applet的使用是通过不可靠的边界的。典
型的applet在几个方面与传统的数据库应用程序有所
不同:
(1) 不可靠的applet被严格地限制在他们被允许执行的
的操作上。
(2) 就标识和连接网上数据库来说,Internet环境里面
的applet面临新的问题。
3) 已验证的applet(Trusted applets)是指那些已经被
Java虚拟机器认定是可以信赖的applet。
11.4.1.2. Applications
Java语言也可以用来构造常规性应用程序。
随着JAVA 语言开发工具完善,以及人们逐渐认
识到发展中的程序语言的创造性和JAVA应用程
序开发的优势所在,JAVA的这种应用将越来越
普遍。
在应用APPLICATIONS的情况下,JAVA的代
码是可信的,并且同其它常用应用程序代码一
样,能读写文件,进行网络连接等。如图11.5
所示
(可允许的下载)
Java应用程序 数据库链路
JDBC驱动器
本地硬盘资源
或其他资源
客户端
LAN或
Intrant
数据库服务器
图11.5 Application 访问网络数据
库
11.4.2 安全性问题
作为网络应用就要考虑相应的安全问题,
这在JDBC的两种方法中有以下体现:
1
Java application使用方法下,Java
的代码是本地的,因此是可信任的。同样
出于安全考虑,可信任的applet也可归于此
类。
2
相反,如一个Java applet是不可
信任的,就不能允许存取本地机上的文件
或是其他网络数据。
11.4.2 安全性问题
11.4.2.2 JDBC和Java的应用程序
对于一个普通的Java应用程序(包括除不可信任的applet来说的所
有的Java代码),JDBC将很迅速地从本地路径上装载驱动程序,并允
许自由地存取文件及远程服务器上的资源等。
而 对 于 applet 来 说 , 若 从 远 程 服 务 器 上 下 载 一 个 不 可 信 任 的
sun.sql.Drive类,那么这个Drive只能与从同一源服务器上下的代码一
起使用。
11.4.2.3 驱动程序在安全方面的职责
(1) 共享TCP连接
(2) 检查所有的本地文件通路作最坏的准备
11.4.3 JDBC接口概貌
接口分为两个层次,一个是面向程
序开发人员的JDBC API。另外一个是
底层的JDBC Driver API。如图11.6所
示
1 JDBC接口定义
2 数据库连接
3 数据传递和结果接收
DriverManager
Connection
Statement
Connection
Statement Statement
ResultSet ResultSet
图11.6
Connection
Statement
ResultSet
JDBC API的类
11.5 JDBC应用
11.5.1
11.5.2
11.5.3
11.5.4
11.5.5
11.5.6
11.5.7
数据库建立连接
执行查询语句
检索结果集
更新数据库操作
参数的输入和输出
动态数据库访
JDBC中的 异常处理
11.5.1 数据库建立连接
所有JDBC的程序的第一步都是与数据库建立连接。用户
得到一个java.sql.Condition类的对象,对这个数据库的
所有操作都是基于这个对象。
1) 加载驱动程序
2) 建立连接
3) 检查警告信息
4) 获得关于数据库源的信息
5) 关闭连接
6) 捕获例外
7) 获取和设置连接选项
11.5.2 执行查询语句
JDBC中查询语句执行方法可分为三类:
Statement,PreparedStatement和CallableStatement对象。
1) Statement类
1. 创建Statement 对象
2. 执行查询语句
3. 获取和设置选项
4. 关闭Statement
2) PreparedStatement
1. 创建PreparedStatement对象
2. 执行查询语句
11.5.2 执行查询语
句
3.获取和设置选项
4. 关闭对象
3)Callablestatetement
1. 创建CallableStatement对象
2. 执行存储过程
3 . 获取和设置选项
4 . 关闭CallableStatement
11.5.3 检索结果集
1) ResultSet类的基本处理方法
以下是一个简单的检索结果的一段程序:
Statement stmt=con.creatStatement();
ResultSet=stmt.executeQuery(“SELECT
a,
c,from table”);
while(r.next()){
int i=r.getInt(1);
String s=r.getString(“namer”);
byte b[]=r.getBytes(3);
System.out.println(“i+”
“+s+” “+B[0]” )
2) 处理多个结果集
b,
11.5.4 更新数据库操作
11.5.4.1 对表中记录的操作
例如,下面的语句将Customeer表中FirsrName为”Li"
的记录的Address项改为Beijing:
stmt.executeUpdate("UPDATE
Customer
SET
Address='Beijing' WHERE
FristName="Li");
而下面的两个语句在Customer表中增添和删除记录:
stmt.executeUpdate("DELECT
FROM
Customer
WHERE Address='Beijing'");
stmt.executeUpdate( "INSERT
INTO
Customer
(Customer(CustometID, First Name)
VALUES(11,'Liu')");
11.5.4 更新数据库操作
11.5.4.2 创建和删除表
下 面 语 句 创 建 一 个 表 another , 它 有 两 列 : 列 ID 为 整 形 值 , 列
Name为字符串:
stmt.executeUpdate("CREATE TABLE another(ID INTEGER,
Name VARCHAR(20)");
11.5.4.3 增加和删除表中的列
下面语句在another表中增加一列Address,数据类型为字符串:
stmt.executeUpdate("ALTER TABLE another ADD COLUMN
Address VARCHAR(50)");
删除表中的一列语句如下:
stmt.executeUpdate("ALTER TABLE another DROP COLUMN
Address");
11.5.4 更新数据库操作
11.5.4.4 使用PreparedStatement对象
同 SQL 查 询 语 句 一 样 , 数 据 更 新 语 句 也 可 以
PreparedStatement 对 象 上 执 行 。 使 用 PreparedStatement
对象,只需要传递一次SQL语句,可以多次执行它。并且可
以利用数据库的预编技术,提高报告效率。
使用PreparedStatement对象的另一个好处是可以接
受参数,这将在下一节中详细讲述。
11.5.5 参数的输入和输出
JDBC 允许在要执行的SQL 语句中设置参数这样
就给数据库操作带来很大的方便。要想使用SQL 语句的输
入参数或输出参数,必须在PREPAREDSTATEMENT对象上进行
操作。由于CALLABLESTATEMENT 类是PREPAR-EDSTATEMENT
类的子类,所以在CALLABLE STATEM -ENT 对象上的操作也
可以使用输入参数和输出参数。在生成PREPAREDSTATEMENT
对象或CALLABLESTAT EMENT对象时,SQL语句中指定输入或
输出参数。在执行这个SQL 语句之前,要对输入参数进行
赋值。
11.5.5 参数的输入和输出
11.5.5.1 使用PreparedStatement对象
下面的一段程序在一个 SQLUPDATE语句中设置了俩个
参数,类型分别为STEING和INT。在循环中,反复执行这
个SQLUPDATE 语句,每次执行时,赋予第二个参数不同
的整数值:
PREPAREDSTATEMENT
PSTMT=CON.PREPARESTATEMENT("UPDATE table SET
m=?WHETE x=?");
c.setString(1,"text");
for (int I=0;I<10;I++);{
Pstmt.setInt(2,I);
Pstmt.executeUpdate();
}
11.5.5 参数的输入和输出
11.5.5.2
11.5.5.3
11.5.5.4
11.5.5.5
使用CallableStatement对象
输入参数的数据转换
接收输出参数
数据截断
11.5.6 动态数据库访问
一般情况下,程序员在JDBC编写程序时,已经了解要
访问的数据库的情况,如各个表名,表中的各个列名以
及了解要访问的数据库的特定情况,选取相应的方法来访
问数据库并检索结果.然而在在某些情况下,JDBC程序需
要动态的获得要访问的数据库的情况,然后再采取相应的
方法去访问数据库和检索结果。JDBC支持这种动态的数
据库访问,以提高程序的灵活性。
11.5.6 动态数据库访问
11.5.6.1 数据库元信息的获取
在对数据库进行连接以后,得到一个Connection 类的对
象,可以从这个对象获得有关数据源的各种信息,包括关于
数据库中的各种表,表中的各个列,数据类型和存储过程等
各方面的信息.根据这些信息,JDBC程序可以访问一个事先
并不了解的数据库。
11.5.6.2 结果集的动态访问
一般情况下程序员对结果集的结构是了解的,可以
采用相应的方法来检索结果集。但是,有时对结果集中的结
果并不了解,这是可以先通过ResultSetMetaData对象来获
取结果集的有关信息。
11.5.6 动态数据库访问
11.5.6.3动态数据访问
以前已经讲过SQL数据类型和Java数据类型之间的映射
关系,例如:一个SQL INTEGER类型的数据一般映射为Java
的int类型,可以使用一种简单的Java数据类型来读写SQL
数据。
11.5.7 JDBC中的异常处理
JDBC 程 序 在 运 行 时 , 除 了 产 生 一 些
Java例外,进行数据库访问时,还可能产生
SQLException类型的异常。此外,JDBC中
还 定 义 了 SQLException 的 子 类 :
SQLWarning和Datatruncation.
11.5.7 JDBC中的 异常处理
11.5.7.1. SQLException
SQLException是java.lang. Exception的子类,它提供
了关于数据库范访问错误的信息。
例11-2 用于捕获SQLException例外,并显示错误信息的
程序段
import java.sql.*;
public class Application1{
public static void main (String args[]) throws
ClassNotFoundException{
String url
= "jdbc:odbc:YZL";
11.5.7 JDBC中的 异常处理
String query = "SELECT * FROM yzl1"; //数据表名yzl
try {
// 调用jdbc-odbc桥driver
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
// 与url 数据库连接,yzl1、yzl1分别为用户名、用
户口令。
Connection
con=DriverManager.getConnection("jdbc:odbc:YZL","G1","G1");
Statement stmt = con.createStatement ();
ResultSet rs = stmt.executeQuery (quer
//显示查询结果
rs.close();
stmt.close();
con.close();
}
11.5.7 JDBC中的异常处理
catch (SQLException ex) {
// 产生 SQLException,捕获例外并显示错误
信息
System.out.println("SQLstate:"+ex.getSQLState());
System.out.println("Message:"+ex.getMessage());
System.out.println("ErrorCode:"+ex.getErrorCode());
System.out.println("");
}}
11.5.7 JDBC中的异常处理
11.5.7.2 SQLWarning
SQLWarning 是SQLException的子类,它提供类数据库
访问的警告信息。
例11-3 用checkForWarning方法检查SQLWarning的一段程
序:
private static boolean checkForWarning(SQLWarning warm)
throws SQLException
{
boolean rc=false;
if (warn!=null){
11.5.7 JDBC中的异常处理
System.out.println(“\n***Warning***\n);
Rc=true
While(warn!=null){
System.out.println(“SQLstate:”+warn.getSQLstate());
System.out.println(“Message:”+ warn. getMessage());
System.out.println(“Vender:”+warn. getErrorCode());
System.out.println(“”);
Ex=warn. getNextException();
}
}
return rc;
11.5.7 JDBC中的异常处理
11.5.7.3 DataTruncation
DataTruncation 是SQLException的子类。当
JDBC例外的截断一个数据时,它产生一个DataTruncation
警告(读操作时)或一个DataTruncation 例外(写操作时)。
DataTruncation 的SQLstate被设置为”01004”。
11.6 应用JAVA JDBC开发2层C/S数据库应
用
前面已经论述的1、2、4型JDBC驱动程序都属于以客户
端为中心的JDBC驱动程序,可以建立2层C/S数据库访问模
式。
11.6.1 JDBC-ODBC桥驱动程序开发数据库应用
ODBC ( 开 放 式 数 据 库 互 连 : Open Database
Connectivity)是用C语言写的,在多种不同的DBMS(数据
库管理系统)中存取数据的标准应用程序接口;目前应用最
广的是微软的ODBC,它几乎可将所有平台的所有数据库连
接起来。ODBC在应用程序与特定数据库之间插入一个驱动
程序管理器,每种数据库引擎都需要向驱动程序管理器注册
它自己的ODBC驱动程序,驱动程序管理器将与ODBC兼容
的SQL请求从应用程序传给ODBC驱动程序,并由ODBC驱
动程序把SQL请求翻译为对数据库的固有调用,从而达到应
用程序访问操作 数据库的目的。
JDBC 采用JDBC-ODBC 桥接方式使Java 应用程序使用
ODBC。如图11.7所示
图11.7
11.6.2 运用纯JAVA JDBC驱动
程序开发数据库应用
利用4型纯JDBC驱动程序可以实现两层
C/S模式数据库访问,由于是纯Java语言编
写的驱动程序,所以可以被动态下载,客户
端可以实现零配置。如图11.8所示
图11.8
利用4型JDBC驱动程序可以实现两层C/S模式数据库访问
11.7 综合应用
例11-5 下面的例子中我们将利用Java来实现对数据库
表中的纪录进行添加、删除和修改操作。这表的名people,
其结构如表11-3所示。该表可以是Access、VFP或其他数据
库表。本例为Access数据库people.mdb,用户名yzl1,口令
yzl1。下面详细说明数据库开发该例的过程。
表11-3
people表结构定义
备注
ID
Name
age
sex
phone
1
朱明
27
女
8657458 浙江
2
赵红
25
女
5611852 北京
4
11.7.1 设计主窗口
在Jbuilder2中利用向导创建一个新的工程database.jpr
和一个主窗体类MainFrame.java,并在MainFrame.java类
的design视图中加入GridControl和四个按钮“添加”、
“删除”、“修改”和“退出”,并进行适当的布局,激
活按钮的响应事件和GridControl的subfocusChanged事件,
同时修改GridControl控件的标题保持同数据库中一致
(“姓名”、“年龄”、“性别”、“电话”、“备
注”)。最后得到MainFrame.java如源程序清单。主窗口
见图11-5。
图11-5。
//源程序清单11-5
package database;
import java.awt.*;
import java.awt.event.*;
import com.borland.jbcl.layout.*;
import com.borland.jbcl.control.*;
import java.sql.*;
import java.util.*;
import com.borland.jbcl.model.*;
public class MainFrame extends DecoratedFrame
//源程序清单11-5
{
Connection connection=null;
PreparedStatement statement=null;
int row=0;
int column=0;
int i;
Vector date=new Vector();
XYLayout xYLayout1 = new XYLayout();
Button button1 = new Button();
Button button2 = new Button();
Button button3 = new Button();
Button button4 = new Button();
GridControl gridControl1 = new GridControl();
public MainFrame()
//源程序清单11-5
{
try {
jbInit();
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connection=DriverManager.getConnection("jdbc:odbc:peop
le","yzl","yzl");
SetGridControl();
}
catch(Exception e)
{
e.printStackTrace();
}
}
//源程序清单11-5
private void jbInit() throws Exception
{
this.setTitle("数据库实例");
this.setLayout(xYLayout1);
gridControl1.setColumnCaptions(new
String[]{"name","sex", "age", "phone", "other"});
button1.setLabel("增加");
button1.addActionListener(new
java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button1_actionPerformed(e);
}
}) ;
button2.setLabel("修改");
//源程序清单11-5
button2.addActionListener(new
java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button2_actionPerformed(e);
}
});
button3.setLabel("删除");
button3.addActionListener(new
java.awt.event.ActionListener() {
//源程序清单11-5
public void actionPerformed(ActionEvent e) {
button3_actionPerformed(e);
}
});
button4.setLabel("退出");
button4.addActionListener(new
java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
button4_actionPerformed(e);
//源程序清单11-5
}
});
xYLayout1.setHeight(328);
xYLayout1.setWidth(471);
this.add(gridControl1, new XYConstraints(2, 7, 4511, 147));
this.add(button1, new XYConstraints(46, 186, 113, 211));
this.add(button3, new XYConstraints(46, 236, 115, 31));
this.add(button2, new XYConstraints(263, 185, 117, 34));
this.add(button4, new XYConstraints(266, 236, 118, 30));
}
public static void main(String[] args)
{
//源程序清单11-5
MainFrame frame=new MainFrame();
frame.setLocation(150,150);
frame.show();
frame.setSize(550,400);
}
void SetGridControl()
{
try
{date.removeAllElements();
String sq1="select * from yzl1";
statement=connection.prepareStatement(sq1);
ResultSet result=statement.executeQuery();
while(result.next())
//源程序清单11-5
{
String name=result.getString("name");
String sex=result.getString("sex");
int age=result.getInt("age");
String phone=result.getString("phone");
String other=result.getString("other");
date.addElement(name);
date.addElement(sex);
date.addElement(Integer.toString(age));
date.addElement(phone);
date.addElement(other);
}
result.close();
statement.close();
int len=date.size()/5;
String[][] items=new String[len][5] ;
for(int i=0;i<len;i++)
{
items[i][0]=(String)date.elementAt(i*5);
items[i][1]=(String)date.elementAt(i*5+1);
items[i][2]=(String)date.elementAt(i*5+2);
items[i][3]=(String)date.elementAt(i*5+3);
items[i][4]=(String)date.elementAt(i*5+4);
}
gridControl1.setReadOnly(false);
gridControl1.setItems(items);
//源程序清单11-5
gridControl1.setReadOnly(true);
}catch(Exception e)
{
System.out.println(e.toString());
}
}
void button1_actionPerformed(ActionEvent e)
{
i+=1;
InputDialog idlg=new InputDialog(this," 添 加 新 记 录
",true);
idlg.setLocation(200,200);
idlg.setSize(300,200);
idlg.show();
}
//源程序清单11-5
void button2_actionPerformed(ActionEvent e)
{
i=i-1;
InputDialog idlg=new InputDialog(this,"修改记录",true);
idlg.groupBox1.setLabel("请输入修改用户信息:");
idlg.setLocation(200,200);
idlg.setSize(300,200);
idlg.show();
}
void button3_actionPerformed(ActionEvent e)
{
//源程序清单11-5
try
{
MessageDialog mdlg=new MessageDialog(this," 删 除 确 认 ","
你确定要删除该记录吗?",6);
mdlg.show();
if(mdlg.getResult()==MessageDialog.NO)
return;
String name=(String)date.elementAt(row*5);
String sq1="delete from yzl1 where name='"+name+"'";
statement=connection.prepareStatement(sq1);
statement.executeUpdate();
statement.close();
SetGridControl();
}catch(Exception ee)
{
//源程序清单11-5
System.out.println(ee.toString());
}
}
void button4_actionPerformed(ActionEvent e)
{ System.exit(0);
}
void gridContro11_subfocusChaned(MatrixSubfocusEvent e)
{try
{
MatrixLocation location=e.getLocation();
if(location==null)return;
row=location.row;
column=location.column;
}catch(Exception ee) {}
}
}
11.7.2
连接数据库
在连接数据库之前,首先应该设置系统的ODBC数据源,设置过程
如图11-10到图11-13所示。
步骤如下:
1}开始-> 设置->控制面板->ODBCSource,进入图11-10 ODBC 数据源
管理框,点击add按钮进入图11-11建立新数据源类型。
2)选择Microsoft Access Driver, 按完成按钮,进入图11-12 建立
数据源框。
3)点击Selected按钮,进入图11-13选择数据源路径框。
4) 选择的:d:\yin\zm2\第8章\people.mdb. 确认后,一步步退出。
图 11-10
图 11-11
图 11-12
图 11-13
11.7.3 显示数据库数据
利用JDBC API 函数可以实现对数据库的访问,
在上一节中我们已经建立和指定数据库的连接,接
下来我们可以通过语句来实现读取数据库功能。
为了在稍后的操作中更加方便而不至于每次对数据
的操作都访问一次数据库,需要定义一个内存矢量
来存放数据库中的数据,该内存矢量中的数据始终
和数据库中保持 一致,同时在进行增加、删除和修
改操作时,一边修改数据库中的数据,一边修改内
存矢量表中的数据,
11.7.4 实现增加和修改对话框
对数据库中记录的增加和修改对话框事实上相同的,区别
仅仅在于修改对话框中具有初始化的该记录数据而增加对话框的
初始化数据为空。利用新建向导来创建一个新的对话框命名为
InputDialog.java,并在对话框的design视图中放置相应的控件,
同时激活两2个按钮的响应事件。
对 四 个 文 本 框 的 命 名 分 别 为 textField1 、 textField2 、
textField3和textField4,若该对话框为增加对话框则在“确定”
按钮事件中需要调用PreparedStatement对象的executeQuery()
方法,若为修改对话框则需要调用 PreparedStatement 对象的
executeUpdate()方法,同时对话框都需要修改调用主窗体中的
显示数据函数来重新将得到的数据显示在主窗口中,同时在该函
数中还可以重新将放置在内存变量中的数据刷新。
11.7.5 生成主窗口事件
1.添加事件
在“添加”按钮事件中只需要直接显示增加对话框即可。
2.修改事件
“修改”按钮事件同“添加”按钮事件相类似,但前者
必须对话框中textField1 的编辑属性设为false (即不可
编辑状态),同时将需要修改的选定记录的有关数据显示在
修改对话框中,并修改提示内容。
11.7.5 生成主窗口事件
3.删除事件
在“删除”按钮事件中首先应该提示用户是否确定删
除,得到用户肯定后获取用户选定记录并调用
PreparedStatement对象中的executeUpdate()方法删除,
同时调用显示数据方法setGridControl()显示删除后数据
库中的数据。
4.数据控件焦点改变事件
在焦点改变事件中,用户把当前点中的记录的行和列信
息保存在全局变量中,从而在“修改”和“删除”事件中
可以得到当前用户所选中的记录信息。
11.7.6 运行结果和源程序
最后得到该程序的所有源程序如源程序清
单11-4和11-5所示
11.7.6 运行结果和源程序
//源程序清单11-4
package database;
import java.awt.*;
import java.awt.event.*;
import borland.jbcl.layout.*;
import borland.jbcl.control.*;
import java.sq1.*;
import java.util.*;
import Borland.jbcl.model.*;
public class MainFrame extends DecoratedFrame
{
//源程序清单11-4
GridControl gridContro11=new GridControl();
Button button1=new Button();
Button button2=new Button();
Button button3=new Button();
Button button4=new Button();
PaneLayout paneLayout1=new PaneLayout();
Connection connection=null;
PreparedStatement statement=null;
int row=0;
int column=0;
Vector date=new Vector();
public MainFrame()
{
//源程序清单11-4
try {
jbInit();
Class.forName(″sun.jdbc.odbc.JdbcOdbcDriver″);
Connection=DriverManager.getConnection(″jdbc:odbc:temp″);
setGridControl();
}
catch(Exception e)
{
e.printStackTrace();
}
}
private void jbInit() throws Exception
{
//源程序清单11-4
this.setTitle(″数据库实例″);
gridContro11.setColumnCaptions(new String[]{″姓名″,″性别″,
″年龄″,″电话″″备注″});
button1.setLabel(″增加″);
button.addActionListener(new MainFrame_butto1_actionAdapter(this));
button2.setLabel(″修改″);
button.addActionListener(new MainFrame_butto2_actionAdapter(this));
button3.setLabel(″删除″);
button.addActionListener(new MainFrame_butto3_actionAdapter(this));
button3.setLabel(″退出″);
button.addActionListener(new MainFrame_butto4_actionAdapter(this));
this.setLayout(paneLayout1);
//源程序清单11-4
this.add(gridControll , new
PaneConstraints(gridContro11″
″gridContro11″,PaneConstraints.ROOT,0.5f));
this.add(button1,new PaneConstraints(″button1″,″gridContro11″,
PaneConstraints.BOTTOM,0.11522633f));
this.add(button2 , new
PaneConstraints(″button2″button1″
PaneConstraints. RIGHT,0.7428571f));
this.add(button3 , new
PaneConstraints(″button3″button2″
PaneConstraints. RIGHT,0.6818182f));
this.add(button4 , new
PaneConstraints(″button4″button3″
PaneConstraints. RIGHT,0.55811744f));
,
,
,
,
//源程序清单11-4
gridControll.setReadOnly(true);
gridContro11.addSubfocusListener(new
MainFrame_gridContro11_subfocusAdapter(this));
}
public static void main(String[] args)
{
MainFrame frame=new MainFrame();
frame.setLocation(150,150);
frame.setSize(550,400);
frame.show();
}
void setGridControl()
{
try
{
//源程序清单11-4
data.removeAllElements();
String sq1=″select* from people″;
statement=connection.prepareStatement(sq1);
ResultSet result=statement.executeQuery();
while(result.next())
{
String name=result.getString(″name″);
String sex=result.getString(″sex″);
int age=result.getInt(″age″)
String phone=result.getString(″phone″);
String other=result.getString(″other″);
//源程序清单11-4
data.addElement(name);
data.addElement(sex);
data.addElement(Integer.toString(age));
data.addElement(phone);
data.addElement(other);
}
result.close();
statement.close();
int len=data.size()/5;
String[][] items=new String[len][5]
for(int i=0;i<len;i++)
{
//源程序清单11-4
items[i][0]=(String)data.e.ementAt(i*);
items[i][1]=(String)data.e.ementAt(i*5+1);
items[i][2]=(String)data.e.ementAt(i*5+2);
items[i][3]=(String)data.e.ementAt(i*5+3);
items[i][4]=(String)data.e.ementAt(i*5+4);
}
gridContro11.setReadOnly(false);
gridContro11.setItems(items);
gridContro11.setReadOnly(true);
}catch(Exception e)
{System.out.println(e.toString());
}
}
//源程序清单11-4
void button1_actionPerformed(ActionEvent e)
{
InputDialog idlg=new InputDialog(this,″添加新记录″,
true);
Idlg.setLocation(200,200);
Idlg.setSize(300,200);
Idlg.show();
}
void button2_actionPerformed(ActionEvent e)
{
InputDialog idlg=new InputDialog(this,″添加新记录″,
true);
//源程序清单11-4
idlg.textField1.setEditable(false);
idlg.groupBox1.setLabel(″请输入修改用户信息:″);
idlg.textField1.setText((String)data.elementAt(row*5));
idlg.textField2.setText((String)data.elementAt(row*5+1));
idlg.textField3.setText((String)data.elementAt(row*5+2));
idlg.textField4.setText((String)data.elementAt(row*5+3));
idlg.textField5.setText((String)data.elementAt(row*5+4));
idlg.setLocation(200,200);
idlg.setSize(300,200);
idlg.show();
}
void button3_actionPerformed(ActionEvent e)
//源程序清单11-4
{
try
{
MessageDialog mdlg=new MessageDialog(this,″删除确认″,
″你确定要删除该记录吗?″,6);
mdlg.show();
if(mdlg.getResult()= =MessageDialog.NO)
return;
String name=(String)data.elementAt(row*5);
String sq1=″delete from people where name=′″+name+″′″;
statement=connection.prepareStatement(sq1);
statement.executeUpdate();
statement.close();
setGridControl();
}catch(Exception ee)
//源程序清单11-4
{System.out.println(ee.toString());
}
}
void button4_actionPerformed(ActionEvent e)
{System.exit(0);
}
void gridContro11_subfocusChaned(MatrixSubfocusEvent e)
{try
{MatrixLocation location=e.getLocation();
if(location= =null)return;
row=location.row;
column=location.column;
}catch(Exception ee)
//源程序清单11-4
{}
}
}
class
MainFrame_button1_actionAdapted
java.awt.event.ActionListener
{
MainFrame adaptee;
MainFrame_button1_actionAdapter(MainFrame adaptee)
{
this.adptee=adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button1_actionPerformed(e);
}
}
implements
//源程序清单11-4
class
MainFrame_button2_actionAdapter
implements
java.awt.event.ActionListener
{MainFrame adaptee;
MainFrame_button2_actionAdapter(MainFrame adaptee)
{this.adaptee=adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button2_actionPerformed(e);
}
}
class
MainFrame_button3_actionAdapter
implements
java.awt.event.ActionListener
{
//源程序清单11-4
MainFrame adaptee;
MainFrame_buton3_actionAdapter(MainFrame adaptee)
{
this.adaptee=adaptee;
}
public void action Performed(ActionEvent e)
{
adaptee.button3_actionPerformed(e);
}
}
class
MainFrame_button4_actionAdapter
implements
java.awt.event.ActionListener
{
MainFrame adaptee;
MainFrame_button4_actionAdapter(MainFrame adaptee)
{
//源程序清单11-4
this.adaptee=adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.button4_actionPerformed(e);
}
}
class MainFrame_gridContro11_subfocusAdapter extends
borland.jbcl.mode1.MatrixSubfocusAdapter
{
MainFrame adaptee;
MainFrame_gridContro11_subfocusAdapter(MainFrame adaptee)
{
this.adaptee=adaptee;
}
public void subfocusChanged(MatrixSubfocusEvent e)
{
adaptee.gridContro11_subfocusChanged(e);
}
//源程序清单11-5
package database;
import java.awt.*;
import java.awt.event.*;
import borladnd.jbcl.layout.*;
import Borland.jbc.control.*;
public class InputDialog extends Dialog
{
Pane1 pane11=new Pane1();
GroupBox groupBox1=new GroupBox();
Button button1=new Button();
Button button2=new Button();
PaneLayout paneLayout1=new PaneLayout();
//源程序清单11-5
Label labe11=new Label();
Label labe12=new Label();
Label labe13=new Label();
Label labe14=new Label();
Label labe15=new Label();
TextField textField1=new TextField();
TextField textField2=new TextField();
TextField textField3=new TextField();
TextField textField4=new TextField();
TextField textField5=new TextField();
PaneLayout paneLyout2=new PaneLayout();
//源程序清单11-5
MainFrame frame=null;
public InputDialog(MainFrame frame,String tile,boolean modal)
{
super(frame,title,modal);
try
{
this.frame=frame;
jbInit();
add(pane11);
pack();
}
catch (Exception e)
{
e.printStackTrace();
}
}
//源程序清单11-5
public InputDialog(MainFrame frame)
{
this(frame,″″,modal);
}
public InputDialog(MainFrame frame,String title)
{
this(frame,title,false);
}
private void jbInit() throws Exception
{
groupBox1.setLayout(paneLayout2);
pane11.setSize(new Dimension(327,154));
groupBox1.setLabel(″请输入添加用户信息:″);
button1.setLabel(″确定″);
//源程序清单11-5
button1.addActionListener(new
InputDialog_button1_actionAdapter(this));
button2.setLabel(″取消″);
button2.addActionListener(new
InputDialog_button2_actionAdapter(this));
lab11.setAlignment(2);
labe11.setText(″姓名:″);
lab12.setAlignment(2);
labe12.setText(″性别:″);
lab13.setAlignment(2);
labe13.setText(″年龄:″);
lab14.setAlignment(2);
labe14.setText(″电话:″);
lab15.setAlignment(2);
labe15.setText(″备注:″);
panel1.setLayout1);
//源程序清单11-5
panel1.add(groupBox1, new PaneConstraints(“groupBox1”, “groupBox1”
PaneConstraints. ROOT, 0.5f));
groupBox1.add(textField3,
new
PaneConstraints(“textField3”,
“textField3”,
Paneconstraints. ROOT, 0.5f));
groupBox1.add(textField5,
new
PaneConstraints(“textField5”,
“textField5”,
Paneconstraints. BOTTOM, 0.37623763f));
groupBox1.add(textField4,
new
PaneConstraints(“textField4”,
“textField5”,
Paneconstraints. TOP, 0.53125f));
groupBox1.add(label4, new PaneConstraints(“label4”, “textField4”,
Paneconstraints. LEFT, 0.2525252f));
groupBox1.add(label5, new PaneConstraints(“label5”, “textField5”,
Paneconstraints. LEFT, 0.2525252f));
//源程序清单11-5
groupBox1.add(label3, new PaneConstraints(“label3”, “textField3”,
Paneconstraints. LEFT, 0.25063288f));
groupBox1.add(label2, new PaneConstraints(“label2”, “label3”,
Paneconstraints. TOP, 0.634112066f));
groupBox1.add(label1, new PaneConstraints(“label1”, “label2”,
Paneconstraints. TOP, 0.5263158f));
groupBox1.add(textField1,
new
PaneConstraints(“textField1”,
“textField3”,
Paneconstraints. TOP, 0.65071137f));
groupBox1.add(textField2,
new
PaneConstraints(“textField2”,
“textField1”,
Paneconstraints. BOTTOM, 0.4634146f));
panel1.add(botton1, new PaneConstraints(“botton1”, “groupBox1”,
Paneconstraints. BOTTOM, 0.14814818f));
panel1.add(botton2, new PaneConstraints(“botton2”, “botton1”,
Paneconstraints. RIGHT, 0.54128444f));
}
void botton1_actionPerformed(ActionEvent e)
{
boolean flag=textField1.isEditable();
if(flag)
addDatabase();
else
changDatabase();
}
void button2_actionperformed(ActionEvent e)
{
this.dispose();
}
//修改纪录
void changeDatabase()
{
try
{
//源程序清单11-5
String sq1=″update people set sex=′″;
sq1+=textField2.getText()+″′,age=″;
sq1+=textField3.getText()+″,phone=′″;
Sq1+=textField4.getText()+″′,other=′″;
sq1+=textField5.getText()+″′where name=′″;
sq1+=textField1.getText()+″′″;
frame.statement=frame.connection.prepareStatement(s
q1);
frame.statement.executeUpdate();
frame.statement.close();
//调用主窗体显示数据函数
frame.setGridControl();
this.dispose();
}catch(Exception e)
//源程序清单11-5
System.out.println(e.toString());
}
}
//增加记录
void addDatabase()
{
try
{
String
sq1=″insert
into
people(name,sex,age,phone,other)
values(′″;
sq1+=textField1.getText()+″′+″′,′″;
sq1+=textField2.getText()+″′,″;
Sq1+=textField3.getText()+″,′″;
sq1+=textField4.getText()+″′,′″;
sq1+=textField5.getText();
//源程序清单11-5
sq1+=″′) ″
frame.statement=frame.connection.prepareStatement(sq1);
frame.statement.executeUpdate();
frame.statement.close();
//调用主窗体显示数据函数
frame.setGridControl();
this.dispose();
}catch(Exception e)
{
System.out.println(e.toString());
}
}
}
class
InputDialog_button1_actionAdapter
implements
java.awt.event.ActionListener
{
InputDialog adaptee;
//源程序清单11-5
InputDialog_button1_actionAdapter(InputDialog adptee)
{this.adaptee=adaptee;
}
public void actionPerformed(ActionEvent e)
{ adaptee.button1_actionPerformed(e);
}
}
class
InputDialog_button2_actionAdapter
implements
java.awt.event.ActionListener
{
InputDialog adaptee;
InputDialog_button2_actionAdapter(InputDialog adptee)
{
this.adaptee=adaptee;
//源程序清单11-5
}
public void actionPerformed(ActionEvent e)
{
adaptee.button2_actionPerformed(e);
}
}
小结
在Java语言中,利用JDBC可以便地建立同数据库的连接
以及对数据库进行各种所需要的操作。JDBC同ODBC基本
相同,作为连接数据库的开发数据连接使得Java程序可以
支持多种不同的数据库。本章中主要讲述了如何利用JDBC
类来实现对数据库的访问,建立同指定数据库的连接,同
时利用JDBC语句来执行相应的SQL语句,从而实现对数据
库记录的的操作。程序在最后给出了一个对数据库操作的
实例来帮助读者更好地理解数据库程序的设计。
习题
1.
如何建立数据库的连接、发
送访问、操作数据库的SQL语句?
2.
如何处理对数据库访问操
作的结果。
3.
什么是动态数据库访问