留侯论知识点总结ppt:java布局管理器

来源:百度文库 编辑:中财网 时间:2024/04/26 18:17:12

java简要提醒   

2008-07-06 13:44:14|  分类: JAVA |  标签: |字号大中小 订阅

组件在容器中的布局
当我们把组件添加到容器中时,希望控制组件在容器中的位置。在默认情况下,每个容器中都有布局管理器。如果容器的默认布局管理器 不能满足用户的需要,用户可以使用别的布局管理器来代替它。Java的java.awt包中定义了5种布局类,每个布局类的实例 对应一种布局策略,这5个布局类分别是:FlowLayout、BorderLayout、CardLayout、GridLa yout和GridBagLayout。其中GridBagLayout是最复杂、功能最强大的一种。
1、FlowLayout布局
FlowLayout是Panel型容器和Applet型容器默认使用的布局。如果不专门为Panel和Applet指定布局, 则它们就使用FlowLayout布局。
FlowLayout对应的布局非常简单,遵循这种布局的容器将其中的组件按照加入的先后顺序从左向右排列,一行排满之后就转到 下一行继续从左至右排列,每一行中的组件都居中排列。在组件不多时,使用这种策略非常方便,但是当容器内的组件元素增多时,就显 得高低不平。以前的例子使用的就是这种布局。
对于使用FlowLayout的容器,加入组件使用简单的add(组件名)命令即可,这些组件将顺序地排列在容器中。有时由于F lowLayout的布局能力有限,程序会采用容器嵌套的方法,这个被嵌套的容器组件可以有自己的组件和自己的布局,使整个容器 的布局达到应用的需求。
对于一个原本不使用FlowLayout布局的容器,若需要将其布局策略改为FlowLayout,可以使用如下的语句:set Layout(new FlowLayout());
setLayout()方法是所有容器的父类Container的方法,用于为容器设定布局。
FlowLayout类有3个构造方法:
public FlowLayout()        
public FlowLayout(int alignment)
public FlowLayout(int alignment,int horizontalGap,int verticalGap)
alignment参数的值必须是FlowLayout.LEFT、FlowLayout.CENTER或FlowLayout .RIGGHT。horizontalGap和verticalGap参数指定了组件间隔距离(以像素为单位)。如果用户没有指 定间隔值,FlowLayout将自动指定其值为5。
2、BorderLayout布局
BorderLayout也是一种简单的布局策略,它把容器内的空间简单地划分为东、西、南、北、中5个区域,每加入一个组件都 应该指明把这个组件加到哪个区域中。
BorderLayout只能指定5个区域位置,如果容器中需要加入超过5个组件,就必须使用容器的嵌套或改为其他的布局策略。 (组件可以不足5个)
当用户向使用BorderLayout布局管理器的容器中加入组件时,用户必须使用两个参数的add()方法,而且第一个参数必 须为“North”、“South”、“East”、“West”或“Center”。如果用户使用一个参数的add()方法或 指定的第一个参数无效,那么该组件将不能显示出来。
在默认的情况下,BorderLayout将使它管理的组件之间没有空隙,用户可以用下面的构造方法指定间隙:public BorderLayout(int horizontalGap,int verticalGap)
举例:E13、Example12_1
3、CardLayout布局
使用CardLayout的容器可以容纳多个组件,但是实际上同一时刻容器只能从这些组件中选出一个来显示,就像一叠纸牌每次只 能显示最上面的一张一样,这个被显示的组件将占据所有的容器空间。使用CardLayout的一般步骤如下(假设容器名为con ):
(1)创建CardLayout对象作为布局。如:Mycard = new CardLayout();
(2)使用容器的setLayout()方法为容器设置布局。如:con.setLayout(Mycard);
(3)调用容器的add()方法将组件加入容器。con.add(组件代号,组件);组件代号是另外给的,和组件的名字没有必然 联系。
(4)创建的布局Mycard用CardLayout类提供的show()方法,更据容器名字con和其中的组件代号显示这一组 件:Mycard.show(con,组件代号);或按组件加入容器的顺序显示组件,如:first(con)方法显示con中 的第一个组件、last(con)方法显示con中的最后一个组件、next(con)显示下一个组件、previous(co n)显示前一个组件等(最先加入con的是“卡片”式的第一张,依次排序)。
举例:E14
4、GridLayout布局
GridLayout是使用较多的布局编辑器,其基本布局策略是把容器划分成若干行乘若干列的网格区域,组件就位于这些划分出来 的小格中。GridLayout比较灵活,划分多少网格由程序自由控制,而且组件定位也比较精确,使用GridLayout布局 编辑器的一般步骤如下:
创建GridLayout对象作为布局,指定划分网格的行数和列数,并使用容器的setLayout()方法为容器设置这个布局 编辑器:setLayout(new GridLayout(行数,列数))。
调用容器的方法add()将组件加入容器,组件从左到右排满第一行之后再排第二行,依次类推。每个网格中都必须填入组件,如果希 望某个网格为空白,可以为它加入一个空的标签:add(new Label())。
举例:E15(Example12_3)
由于GridLayout布局中每个网格都是相同大小并且强制组件与网格的大小相同,容器中的每个组件也都是相同的大小,显得很 不自然。为了克服这个缺点,可以使用容器嵌套。如:一个容器使用GridLayout布局,将容器分为三行一列的网格;然后可以 把另一个容器添加到某个网格中,而添加的这个容器又可以设置为GridLayout布局,把自己分为若干网格。利用这种方法,可 以设计出符合一定需要的布局。
例题:E16(Example12_4)
5、GridBagLayout布局
GridBagLayout是AWT提供的最灵活、最复杂的布局管理器。GridBagLayout将组件以多行多列放置,允许 指定的组件跨多行或多列。如果用户增大Applet的窗口,用户会发现最后一行会占有所有新的垂直方向的空间,所有新的水平方向 的空间将被所有的列分割。这个改变尺寸的动作是以Applet分配给GridBagLayout中单个组件的权为依据的。用户还 会看到每个组件会占据尽可能多的空间,这个动作也是由Applet指定的。暂略讨论。
6、null布局与setBounds方法
我们可以把一个容器的布局设置为null布局(空布局)。
setBounds(int a,int b,int width,int height)方法是所有组件都拥有的一个方法,组件调用该方法可以设置本身的大小和在容器中的位置。
假设p是某个容器:p.setLayout(null);把p的布局设置为空布局。
向空布局的容器p添加一个组件c需要两个步骤,首先使用add(c)方法向容器添加组件,然后组件c再调用setBounds( int a,int b,int width,int height)方法设置该组件在容器中的位置和本身的大小。组件都是一个矩形结构,方法中的参数a、b是组件c的左上角在容器中 的位置坐标;weidth、height是组件c的宽和高。
举例:E17(Example12_6)
使用布局管理器的基本规则:
(1)用户要求尽量使用所有的空间来显示组件,可以考虑使用BorderLayout和GridBagLayout。如果使用B orderLayout,用户应该将占用空间最大的组件放在中心部位。如果使用GridLayout,用户需要为组件设置限制条 件。
(2)用户需要在紧凑的一行中以组件的自然尺寸显示较少组件时,用户可以考虑用面板容纳组件,并使用面板的缺省布局管理器Flo wLayout。
(3)用户需要在多行或多列中显示一些同样尺寸的组件,GridLayout最适合此情况。如果有必要的话,可以使用面板来容纳 组件。
画布
画布是一种通用组件,本身不具有任何功能,也不能处理任何事件。它是一个可以在上面绘画的简单组件,不是容器。Java.awt 包中的类Canvas负责创建画布对象。创建画布对象的常用方法是用Canvas的子类来创建画布对象,并在子类中重写父类的方 法:paint。需要注意的是:一定要在创建画布的类的构造方法中给出画布的尺寸(单位为像素)。
举例:E18(Example11_3)
在上例中,Mycanvas类中的构造方法必须通过getSize()方法得到它的大小,当使用getSize()方法时,该类 中的getPreferredSize()方法会自动被执行,可以在这个方法的方法体中通过使用Dimension类(在jav a.awt包中)给出画布的大小。
使用AWT组件绘图
使用AWT组件中的Graphics类,除了可以显示字符串以外,还可以在应用程序的框架窗口中绘图。在Graphics类中定 义了丰富的方法来绘制各种图形,常用的一些方法有:
1、显示字符串与字符数组
drawString(String s, int x, int y);
在屏幕上从由x和y参数所指定的位置上开始往右显示字符串。
drawChars(char data[ ], int offset, int length, int x, int y);
x和y参数指定显示字符的位置,offset和length参数指定数组中要显示的字符。
举例:E19(Example17_2)
2、绘制直线
drawLine(int x1, int y1, int x2, int y2);
3、绘制长方形
drawRect(int x, int y, int width, int height);
x和y参数指定长方形左上角的位置,width和length参数指定长方形的宽度和高度。
fillRect(int x, int y, int width, int height);
drawRect方法只画出长方形的轮廓,而fillRect方法画出长方形并填充它。
drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight);
画圆角矩形,arcWidth和arcHeight参数指定矩形圆角的尺寸。当width=height=arcWidth=a rcHeight时为圆环。
4、画椭圆
drawOval(int x, int y, int width, int height);
x和y表示椭圆距x轴和y轴的距离,单位为像素;参数width、height表示椭圆的宽和高。当width=height时 为圆环。
fillOval(int x, int y, int width, int height);
fillOval方法使用setColor方法所指定的颜色画着色的椭圆。
5、绘制圆弧
drawArc(int x, int y, int width, int height, int starAngle, int arcAngle); 
starAngle和arcAngle表示圆弧的开始角和圆弧对应的圆心角。
fillArc(int x, int y, int width, int height, int starAngle, int arcAngle);
fillOval方法使用setColor方法所指定的颜色画填色的圆弧。
举例:E20
      E21(Example12_2)
处理选择框事件
当用户把选择框从未选中状态变成选中状态或从选中状态变成未选中状态时就发生了一个选择框事件。
对于选择框这个事件源,它有一种事件,就是选择框的选择情况的变化。这种事件源获得监视器的方法是addItemListene r()。处理该事件的接口是ItemListener,接口中的方法是itemStateChanged(ItemEvent e)。选择框这种事件的类型是ItemEvent类型,即当从未选中到选中或从选中到未选中后,Java包Java.awt.e vent中的类ItemEvent自动创建了一个事件对象。
ItemEvent类中有一个重要的方法:getItemSelectable();它返回引发选中状态变化事件的事件源。也就 是说,对于ItemEvent事件类型的事件,如果想寻找事件源,需使用getItemSelectable()方法。
举例:E22(Example13_4)
      E23(Example13_3)
处理下拉式列表事件
下拉式列表实际上就是一种选择控件。当用户在选择控件中选出某个选项时就发生了一个选择控件上的事件。
对于选择控件这个事件源,它有一种事件,就是选择控件的选择情况的变化。这种事件源获得监视器的方法是addItemListe ner()。处理该事件的接口是ItemListener,接口中的方法是itemStateChanged(ItemEven t e)。选择控件这种事件的类型是ItemEvent类型,即当从选择控件选中选项时,Java包Java.awt.event中 的类ItemEvent自动创建了一个事件对象。
ItemEvent类中有一个重要的方法:getItemSelectable();它返回引发选中状态变化事件的事件源。也就 是说,对于ItemEvent事件类型的事件,如果想寻找事件源,需使用getItemSelectable()方法。
举例:E24(Example14_2)
处理列表事件
滚动列表事件源有两种事件:一种是鼠标双击某个选项,另一种是鼠标单击某个选项。对于第一种事件事件源获得监视器的方法是add ActionListener()。处理该事件的接口是ActionListener,接口中的方法是actionPerfor med(ActionEvent e)。这种事件的类型是ActionEvent,即当从列表中双击某个选项时,Java包Java.awt.event中的类A ctionEvent自动创建了一个事件对象。
ActionEvent类中有一个重要的获得事件源方法:getSource()。
对于第二种事件源获得监视器的方法是:addItemListener()。处理该事件的接口是ItemListener,接口 中的方法是itemStateChanged(ItemEvent e)。选择控件这种事件的类型是ItemEvent类型,即当单击了选项时,Java包Java.awt.event中的类It emEvent自动创建了一个事件对象。
ItemEvent类中有一个重要的获得事件源方法:getItemSelectable()。它返回引发选中状态变化事件的事 件源。也就是说,对于ItemEvent事件类型的事件,如果想寻找事件源,需使用getItemSelectable()方法 。
举例:E25(Example14_4)
窗口
1、建立窗口
举例:E26(Example15_1)
      E27(Example15_3)
2、向窗口增加菜单
举例:E28(Example15_4)
3、处理菜单项上的事件
菜单项事件源有1种事件:用鼠标单击某个选项。
事件源获得监视器的方法是:addActionListener()。处理该事件的接口是ActionListener,接口中 的方法是actionPerformed(ActionEvent e)。这种事件的类型是ActionEvent,即当单击菜单项某项时,java包java.awt.event中的类Acti onEvent自动创建了一个事件对象。
ActionEvent类中有一个重要的获得事件源的方法:getSource()。
举例:E29(Example15_5)
      E30(Example15_6)
4、增加菜单分割线和嵌入子菜单
要增加菜单分割线,只需要使用Menu类中的addSeparator()方法。
菜单项本身还可以是一个菜单,这样的菜单项称为子菜单。
举例:E31(Example15_9)
对话框
1、普通对话框
对话框必须依赖一个窗口,因此要建立对话框,必须首先建立一个创建窗口的窗口类。对话框的默认布局是BorderLayout布 局,在创建对话框时必须有对话框大小的设置,在其构造方法中要通过使用super()方法调用父类的相应的构造方法。
举例:E32(Example16_1)
在上面的例子中,创建的对话框是可见的。但我们希望在它依赖的窗口内设置按钮或菜单,通过菜单项事件或按钮事件随时打开或关闭对 话框。另外,我们还希望对话框能完成某些任务。
举例:E33(Example16_2)
2、文件对话框
FileDialog是Dialog类的子类,它创建的对象称为文件对话框。文件对话框是一个打开文件和保存文件的对话框窗口。 文件对话框也必须依附一个窗口(Frame)对象。
文件对话框仅仅提供了一个文件操作的界面,要真正实现对文件的操作,要学习文件的输入输出流。
举例:E34(Example16_3)
输入输出流
I/O流提供一条通道程序,可以使用这条通道把源中的字节序列送到目的地。Java的I/O流库提供大量的流类(在包java. io中)。其中,所有输入流类都是InputStream(输入流)抽象类的子类,而所有输出流都是OutputStream( 输出流)抽象类的子类。
一、FileInputStream类
如果用户的文件读取需求比较简单,那么用户可以使用FileInputStream类,该类是从InputStream中派生出 来的简单输入类。该类的所有方法都是从InputStream类继承来的。为了创建FileInputStream类的对象,用 户可以调用它的构造器:
FileInputStream(String name);//参数为文件名
FileInputStream(File file);//参数为文件对象
1.使用文件输入流读取文件
文件输入流(输入流的子类)提供对文件的存取。为了读取文件,使用文件输入流构造器来打开一个到达该文件的输入流。例如,为了读 取一个名为myfile.dat的文件,建立一个文件输入流对象如下:FileInputStream istream=new FileInputStream(“myfile.dat”);
或File f=new File(“myfile.dat”);FileInputStream istream=new FileInputStream(f);
 File类有两个常用的构造方法:
 File(String s);//s确定文件的名字,并且该文件和应用程序在同一目录下。
 File(String directory,String s);//directory确定文件的目录,s确定文件的名字。
 2.处理I/O异常
 使用文件输入流构造器建立通往文件的输入流时,可能会出现异常,Java生成一个出错信号,它使用一个IOExce ption(IO异常)对象来表示这个出错信号。程序必须使用一个catch(捕获)块检测并处理这个异常。为了把一个文件输入 流对象与一个文件关联起来,使用类似于下面所示的代码:
    try { FileInputStream ins = new FileInputStream(“myfile.dat”);}
 catch(IOException e){ System.out.println(“File read error:”+e);}
 由于I/O操作对于错误特别敏感,所以许多其他的流类构造器和方法也产生I/O异常。同样必须按上述程序段所示的相 同方式捕获(catch)或处理这些异常。
 3.从输入流中读取字节
 输入流的唯一目的是提供通往数据的通道,程序可以通过这个通道读取数据,read()方法给程序提供一个从输入流中 读取数据的基本方法。
 int read();//read()方法从输入流中读取单个字节的数据。该方法返回字节值(0~255之间的一个整数),如果该方法 到达输入流的末尾,则返回-1。
 read方法还有其他一些形式。这些形式能使程序把多个字节读到一个字节数组中:
 int read(byte b[ ]);//返回被读取的字节个数,如果到达输入流的末尾,则返回-1。
 int read(byte b[ ],int off,int len);//返回被读取的字节个数,如果到达输入流的末尾,则返回-1。其中off参数指定read方法把数据存放在字节数组 b中的什么地方;len参数指定该方法就读取的最大字节数。
 4.关闭流
 虽然Java在程序结束时自动关闭所有打开的流,但是当使用完流后,显示地关闭任何打开的流是一个良好的习惯。一个 被打开的流可能会用尽系统资源,如果没有关闭那些被打开的流,那么在这个或另一个程序试图打开新的流时,可能得不到资源。关闭输 出流的另一个原因是把该流缓冲区的内容冲洗掉(在操作系统把程序所写到输入流上的那些字节保存到磁盘上之前,内容被存放在内存缓 冲区中)。
 通过调用close()方法,可以保证操作系统把流缓冲区的内容写到它的目的地。
 举例:E35(Example20_2)
二、FileOutputStream类
与FileInputStream类相对应的类是FileOutputStream类。FileOutputStream类提供 了基本的文件写入能力。除了从OutputStream类继承来的方法以外,FileOutputStream类还有3个构造器 :
FileOutputStream(String name);//使用给定的文件名name创建一个FileOutputStream对象
FileOutputStream(File file);//使用File对象创建FileOutputStream对象
FileOutputStream(FileDescriptor fdobj);//使用FileDescriptor对象创建FileOutputStream对象
可以使用write()方法把字节发送给输出流:
public void write(byte b[ ]);//写b.length个字节到输出流
public void write(byte b[ ],int off,int len);//从给定字节数组中起始于偏移量off处写len个字节到输出流,参数b是数据;off是数据的起始偏移量;len 是要输出的字节数。
举例:E36(Example20_3)
三、FileReader类和FileWriter类
与FileInputStream类和FileOutputStream类等价的读取器是FileReader类和FileWr iter类,它们分别是InputStream和OutputStream的子类。其构造方法分别是:
FileReader(String filename);
FileWriter(String filename);
使用这两个类时需要处理FileNotFoundException异常。
下例是一段创建FileReader对象的代码,可以使用该对象读取名为Student.txt的文件。
FileReader file = new FileReader(“Student.txt”);
Student.txt文件存放了一个学生名单,每个姓名占一行。如果我们想读取名字,那么每次必须读取一行,但FileRea der类没有提供这种方法,所以我们必须把这个流(对象)再接到另外一个流上,从后一个流中读取名单。Java提供了名为Buf feredReader的类,构造方法是:BufferedReader(Reader in);//能够读取文本行,方法是readLine()。
通过向BufferedReader传递一个Reader对象(如FileReader的实例),来创建一个BufferedR eader对象,代码如下:
BufferedReader in = BufferedReader(new FileReader(“Student.txt”));
然后再从流in中读取学生名单。
举例:E37(Example20_4)
下例是一段创建FileWriter对象的代码,可以使用该对象写名hello.txt的文件。
FileWriter tofile = new FileWriter(“hello.txt”);
我们用Java的BufferedWriter类创建一个流,接到流tofile上,然后使用BufferedWriter类的 方法:write(String s,int off,int len);把字符串s写到hello.txt中。
BufferedWriter out = new BufferedWriter(new FileWriter(“hello.txt”));
out.write(“how are you”,0,s.length());
但out.write(“how are you”,0,s.length())只是将字符串“how are you”写入了缓冲区,如想写入到文件,必须再执行语句:out.flush();将缓冲区的字符串写入文件hello.txt 。
举例:E38(Example20_5)
四、使用文件对话框打开和保存文件
在学习了流之后,我们可以使用文件对话框来打开和保存文件了。
在下例中我们把一个文件的内容显示在一个文本区内,并把文本区的内容保存到硬盘的某个文件夹中去。
举例:E39(Example20_7)
在下例中我们使用文件对话框打开本地机器上一个可执行文件。要执行一个本地机上的可执行文件时,可以使用java.lang包中 的Runtime类。首先使用Runtime类声明一个对象:
Runtime ec;
然后使用该类的静态方法getRuntime()创建这个对象:
ec = Runtime.getRuntime();
ec可以调用exec(String command)方法打开本地机的可执行文件。
举例:E40(Example20_8)
五、数据流
DataInputStream类和DataOutputStream类创建的对象被称为数据输入流和数据输出流。它们允许程序 按与机器无关的风格读取Java原始数据。即当我们读取一个数值时,不必再关心这个数值应当是多少个字节。
DataInputStream(InputStream in):将创建的数据输入流指向一个由参数in指定的输入流,以便从后者读取数据(按与机器无关的风格读取)。
DataOutputStream(OutputStream out):将创建的数据输出流指向一个由参数out指定的输出流,然后通过这个数据输出流把Java数据类型的数据写到输出流o ut。
数据流的部分方法:
close();关闭流
readBoolean();读取一个布尔值
readByte();读取一个字节
readChar();读取一个字符
readDouble();读取一个双精度浮点值
readFloat();读取一个单精度浮点值
readInt();从文件中读取一个int值
readLong();读取一个长型值
readShort();读取一个短型值
readUnsignedByte();读取一个无符号字节
readUnsignedShort();读取一个无符号短型值
readUTF();读取一个UTF字符串
SkipBytes(int n);跳过给定数量的字节
writeBoolean(boolean v);把一个布尔值作为单字节值写入
writeBytes(String s);写入一个字符串
writeChars(String s);写入字符串
writeDouble(double v);写入一个双精度浮点值
writeFloat(float v);写入一个单精度浮点值
writeInt(int v);一个int值
writeLong(long v);一个长型值
writeShort(int v);一个短型值
writeUTF(String s);写入一个UTF字符串
举例:E41(Example20_12)
      E42(Example20_13)
使用StringTokenizer类分析字符串
有时我们需要分析字符串并将字符串分解成可被独立使用的单词,这些单词叫做语言符号。例如,对于字符串“We are Students”,如果我们把空格作为了该字符串的分隔符,那么该字符串有3个单词(语言符号)。而对于字符串“We,are ,Student”,如果我们把中文逗号作为了该字符串的分隔符,那么该字符串有3个单词(语言符号)。
当我们分析一个字符串并将字符串分解成可被独立使用的单词时,可以使用java.util包中的StringTokenizer 类,该类有两个常用的构造方法:
StringTokenizer(String s):为字符串s构造一个分析器。使用默认的分隔符集合,即空格符(若干个空格被看做一个空格)、换行符、回车符、Tab符、进 纸符。
StringTokenizer(String s,String delim):为字符串s构造一个分析器。参数delim中的字符被作为分隔符。例如:
StringTokenizer fenxi = new StringTokenizer(“we are student”);或
StringTokenizer fenxi = new StringTokenizer(“we,are,student”,“,;”);
我们把一个StringTokenizer对象称作一个字符串分析器。一个分析器可以使用nextToken()方法逐个获取字 符串中语言符号(单词),每当调用nextToken()时,都将在字符串中获得下一个语言符号。通常用while循环来逐个获 取语言符号,为了控制循环,我们可以使用StringTokenizer类中的hasMoreTokens()方法,只要字符串 中还有语言符号,该方法就返回true,否则返回false。另外我们还可以调用countTokens()方法得到字符串一共 有多少个语言符号。
举例:E43(Example5_7) 
      E44(Example8_6)
      E45(Example20_6)
Java的多线程机制
以往我们开发的程序都是单线程的,即一个程序只有一条从头至尾的执行线索。然而现实世界中的很多过程都是具有多条线索同时动作的 特性。例如:我们可以一边看电视一边活动胳膊。再如一个网络服务器可能需要同时处理多个客户机的请求等。Java语言的一大特性 就是内置对多线程的支持。
一、Java中的线程
1.程序、进程和线程
程序是一段静态的代码,它是应用软件执行的蓝本。
进程是程序的一次动态执行过程,它对应了从代码加载、执行至执行完毕的一个完整过程,这个过程也是进程本身从产生、发展至消亡的 过程。如果把上课比作一个进程,那么上课铃是进程的开始,下课铃是进程的结束。
线程是比进程更小的执行单位。一个进程在其执行过程中,可以产生多个线程,形成多条执行线索,每条线索,即每个线程也有它自身的 产生、存在和消亡的过程,也是一个动态的概念。就如上课开始后,可以有多个不同的“线程”,如老师的讲解、学生的听讲。每个进程 都有一段专用的内存区域,而线程间可以共享相同的内存单元(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必 要的同步操作。
2.线程的状态与生命周期
每个Java程序都有一个缺省的主线程。对于应用程序,主线程是main()方法执行的线索。对于applet,主线程指挥浏览 器加载并执行Java小程序。要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类及其子类的对象 来表示线程。新建的线程在它的一个完整的生命周期中通常要经历如下的5种状态:
(1)新建:当一个Thread类及其子类的对象被声明并创建时,新生的线程对象处于新建状态。此时它已经有了相应的内存空间和 其他资源。
(2)就绪:处于新建状态的线程被启动后,将进入线程队列排队等待CPU服务,此时它已经具备了运行的条件,一旦轮到它来享用C PU资源时,就可以脱离创建它的主线程独立开始自己的生命周期了。
(3)运行:当就绪状态的线程被调度并获得处理器资源时,便进入运行状态。每一个Thread类及其子类的对象都有一个重要的r un()方法,当线程对象被调度执行时,它将自动调用本对象的run()方法,从第一句开始顺序执行。run()方法定义了这个 线程的操作和功能。
(4)阻塞:一个正在执行的线程如果在某些特殊情况下,如被人为挂起或需要执行费时的输入输出操作时,将让出CPU并暂时中止自 己的执行,进入阻塞状态。阻塞时它不能进入排队队列,只有当引起阻塞的原因被消除时,线程才可以转入就绪状态,重新进入到线程队 列中排队等待CPU资源,以便从原来终止处开始继续运行。
(5)死亡:处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有二,一个是正常运行的线程完成了它的全部工作,即执行完 了run()方法的最后一个语句并退出,另一个是线程被提前强制性地终止。
3.线程调度与优先级
处于就绪状态的线程首先进入就绪队列排队等候处理器资源,同一时刻在就绪队列中的线程可能有多个。多线程系统会给每个线程自动分 配一个线程的优先级,任务较紧急的重要线程,其优先级就较高;相反则较低。在线程排队时,优先级高的线程可以排在较前的位置,能 优先享用到处理器资源,而优先级较低的线程则只能等到排在它前面的高优先级线程执行完毕之后才能获得处理器资源。对于优先级相同 的线程,则遵循队列的“先进先出”原则,即先进入就绪状态排队的线程被优先分配到处理器资源,随后才为后进入队列的线程服务。
当一个在就绪队列中排队的线程被分配到处理器资源而进入运行状态之后,这个线程就称为是被“调度”或被线程调度管理器选中了。线 程调度管理器负责管理线程排队和处理器在线程间的分配,一般都配有一个精心设计的线程调度算法。在Java系统中,线程调度依据 优先级基础上的“先到先服务”原则。
二、Java的线程类与Runnable接口
1.Thread类
Thread类综合了Java程序中一个线程需要拥有的属性和方法,主要有:
(1)构造函数
Thread类的构造函数有多个,其对应的操作有如下两种。
public Thread():创建一个线程对象。
public Thread(Runnable target):创建线程对象,参数target称为被创建线程的目标对象。创建目标对象target的类负责实现Runnab le接口,给出该接口中run()方法的方法体,在方法体中给出该线程的操作和功能。
利用构造函数创建新线程对象之后,这个对象中的有关数据被初始化,从而进入线程的生命周期的第一个状态(新建)。
(2)线程优先级
Thread类有3个有关线程优先级的静态常量:MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORI TY。其中MIN_PRIORITY代表最小优先级,通常为1。MAX_PRIORITY代表最高优先级,通常为10。NORM _PRIORITY代表普通优先级,缺省数值为5。
对应一个新建线程,系统会遵循如下的原则为其指定优先级:
新建线程将继承创建它的父线程的优先级。父线程是指执行创建新线程对象语句的线程,它可能是程序的主线程,也可能是某一个用户自 定义的线程。一般情况下,主线程具有普通优先级。另外,用户可以通过调用Thread类的setPriority(int a)方法来修改系统自动设定的线程优先级,使之符合程序的特定需要。a取值是:
Thread. MIN_PRIORITY,Thread. MAX _PRIORITY,Thread. NORM _PRIORITY。
(3)其他主要方法
启动线程的start()方法。start()方法将启动线程对象,使之从新建状态转入就绪状态并进入就绪队列排队。
定义线程操作的run()方法。Thread类的run()方法与Runnable接口中的run()方法的功能和作用相同,都 用来定义线程对象被调度之后所执行的操作,都是系统自动调用而用户程序不得引用的方法。系统的Thread类中,run()方法 没有具体内容,所以用户程序需要创建自己的Thread类的子类,并重写run()方法来覆盖原来的run()方法。
使线程暂时休眠的sleep()方法。线程的调度执行是按照其优先级的高低顺序进行的,当高级线程不完成,即未死亡时,低级线程 没有机会获得处理器。有时,优先级高的线程需要优先级低的线程做一些工作来配合它,或者优先级高的线程需要完成一些费时的操作, 此时优先级高的线程应该让出处理器,使优先级低的线程有机会执行。为达到这个目的,优先级高的线程可以在它的run()方法中调 用sleep()方法来使自己放弃处理器资源,休眠一段时间。休眠时间的长短由sleep()方法的参数决定。
sleep(int millsecond):millsecond是以毫秒为单位的休眠时间。
sleep(int millsecond,int nanosecond):nanosecond是以纳秒为单位的休眠时间。
中止线程的yield()方法。程序中需要强制终止某线程的生命周期时可以使用yield()方法。
判断线程是否未消亡的isAlive()方法。在调用yield()方法终止一个线程之前,最好先用isAlive()方法检查 一下该线程是否仍然存活,杀死不存在的线程可能会造成系统错误。Thread类还有一个静态方法:currentThead() ,判断当前正在占有CPU的那个线程。
2.Runnable接口
Runnable接口只有一个方法run(),所有实现Runnable接口的用户类都必须具体实现这个run()方法,为它提 供方法体并定义具体操作。Runnable接口中的run()方法是一个较特殊的方法,它可以被运行系统自动识别和执行。具体的 说,当线程被调度并转入运行状态时,它所执行的就是run()方法中规定的操作。所以,一个实现了Runnable接口的类实际 上定义了一个主线程之外的新线程的操作,而定义新线程的操作和执行流程,是实现多线程应用的最主要和最基本的工作之一。
三、如何在程序中实现多线程
在程序中实现多线程有两个途径:创建Thread类的子类或实现Runnable接口。无论采用哪种途径,程序员可以控制的关键 性操作有两个:
(1)定义用户线程的操作,即定义用户线程的run()方法。
(2)在适当时候建立用户线程实例。
1.用Thread类的子类创建线程
在这个途径中,用户程序需要创建自己的Thread类的子类(Thread在java.lang包中),并在子类中重新定义自己 的run()方法,该run()方法中包含了线程的操作。这样程序需要建立自己的线程时,只需要创建一个已定义好的Thread 子类的实例就可以了。当你创建的线程调用start()方法开始运行时,run()方法就被自动执行。
举例:E46(Example19_1)
2.实现Runnable接口
举例:E47(Example19_2)
在具体应用中,采用哪种方法来构造线程体要视具体情况而定。通常,当一个线程已继承了另一个类,而想在该线程中再创建一个新的线 程时,就应该用第二种方法来构造,即实现Runnable接口。
小应用程序实际上是浏览器的一个线程,这个线程由浏览器启动执行,浏览器自动调用执行小应用程序中的init()、start( )方法等。因此要在小应用程序中创建一个新的线程,最好把新线程的启动放在小程序的start()方法中。
举例:E48(Example19_4)
四、Thread类的静态方法sleep()
由于sleep()方法可以被类名直接调用,因此当需要推延程序的执行时可以使用如下语句:Thread. sleep(int time);
举例:E49(Example19_8)
五、线程同步
Java使我们可以创建多个线程,在处理多线程问题时,我们必须注意这样一个问题:当两个或多个线程同时访问同一个变量,并且一 个线程需要修改这个变量。我们应对这样的问题作出处理,否则可能发生混乱,比如一个工资管理负责人正在修改雇员的工资表,而一些 雇员也正在领取工资,如果容许这样做必然出现混乱。
在处理线程同步时,要做的第一件事就是要把修改数据的方法用关键字synchronized来修饰。一个方法使用关键字sync hronized修饰后,当一个线程A使用这个方法时,其他线程想使用这个方法时就必须等待,直到线程A使用完该方法。
举例:E50(Example19_9)
六、在同步方法中使用wait()、notify()和notifyAll()方法
当一个线程正在使用一个同步方法时(用synchronized修饰的方法),其他线程就不能使用这个同步方法。对于同步方法, 有时涉及到某些特殊情况,比如当你在一个售票窗口排队购买电影票时,如果你给售票员的钱不是零钱,而售票员又没有零钱找给你,那 么你就必须等待,并允许你后面的人买票,以便售票员获得零钱找给你。
当一个线程使用的同步方法中用到某个变量,而此变量又需要其他线程修改后才能符合本线程的需要,那么可以在同步方法中使用wai t()方法,使本线程等待,并允许其他线程使用这个同步方法。其他线程如果在使用这个同步方法时不需要等待,那么它使用完这个同 步方法的同时,用notifyAll()方法通知所有的由于使用这个同步方法而处于等待的线程结束等待,再次使用这个同步方法。 如果使用notify(),那么只是通知第一个处于等待的线程结束等待。
举例:E51(Example19_10)
Java网络编程
一、使用URL
1.Internet寻址
“IP”代表Internet Protocol(Internet协议)。一个IP地址唯一的标识了Internet上的一台计算机。没有IP(Intern et Protocol)地址就不能区分连在Internet上不同的计算机。
2.使用URL定位资源
IP地址唯一标识了Internet上的计算机,而URL则标识了这些计算机的资源。URL(uniform resource locators:统一资源定位符)充当一个指针 ,指向Web上的Web页、二进制文件以及其他的信息对象。当我们输入例如http://www.dlrin.edu.cn/hotlink.html的网址时,实际上就提供了该站点主页的URL。
因为Web页驻留在连接到Internet的某台计算机上,所以当引用该页面时就需要确定计算机,故IP地址在URL中扮演了重 要的角色。因此,URL自然包含给定资源的计算机的IP地址,包含资源的计算机就是资源的主机。我们分析一下网址
http://www.dlrin.edu.cn/hotlink.html所包含的信息:
http:服务使用的协议(HTTP)。
dlrin.edu.cn:存储资源的计算机的域名地址。
hotlink.html:资源。
3.客户与服务器
Web的客户/服务器体系结构的含义就是,客户需要某些类型的信息,而服务器提供客户所需要的信息。客户需要连接到服务器上,并 向服务器请求信息;而服务器则向客户发送信息。两者协同工作,各得其所。
4.一些基本网络类
下面是一些由Java的网络API(应用程序接口)所提供的基本网络类,它们都包含在java.net包中。其中的URL类提供 了许多构造方法,可以利用它们创建URL。其中最简单的构造方法只要求提供表示URL资源的字符串:public URL(String s);
下面的例子利用这种构造方法创建一个URL对象url:
try{ url = new URL(“http://www.dlrin.edu.cn”);}
catch(MalformedURLException e){ System.out.println(“Bad URL:”+url);}
由于这个URL构造方法在运行出错时会产生异常,所以必须在try-catch结构中创建URL对象。但如上面代码所示,做起来 并不麻烦。
成功创建了URL对象后,就可以练习其他的Java网络功能了。例如,可以在Applet中链向另外的Web页面,下面的代码实 现了这个功能:
getAppletContext().showDocument(url);
在小程序中可以放心使用这个方法,因为getAppletContext()方法是在Applet类中定义的,showDocu ment()实际完成定位到另一个Web页面的工作,程序中只需要提供URL,其他的工作将由Java自动完成。
举例:E52(Example21_1)
      E53(Example21_3)
二、套接字
1.套接字(Socket和ServerSocket)
IP地址标识Internet上的计算机,端口号标识正在计算机上运行的进程(程序)。端口号与IP地址的组合得出一个网络套接 字。端口号被规定为一个16位的整数(0~65535)。其中,0~1023被预先定义的服务通信占用(如http占用端口80 ,ftp占用端口21等)。除非我们需要访问这些特定服务,否则,就应该使用1024~65535这些端口中的某一个进行通信, 以免发生端口冲突。
当两个程序需要通信时,可以通过使用Socket类建立套接字连接。我们可以把套接字连接想像为一个电话呼叫,当呼叫完成后,谈 话的任何一方都可以随时讲话。但是在最初建立呼叫时,必须有一方呼叫,而另一方则监听铃声。这样,呼叫的一方为“客户”,负责监 听的一方为“服务器”。
2.客户建立到服务器的套接字对象(Socket)
客户端的程序使用Socket类建立与服务器的套接字连接。其构造方法是:
Socket(String host,int port);//参数host是服务器的IP地址,port是一个端口号。
建立时可能发生IOException异常,因此建立与服务器的套接字连接应像如下所示:
try { Socket  mysocket = new  Socket(“http://dlrin.edu.cn”,1880); }
catch ( IOException  e ) { }
当套接字连接mysocket建立后,你可以想像一条通信“线路”已经建立起来。Mysocket可以使用getInputSt ream()方法获得一个输入流,然后这个输入流读取服务器放入“线路”的信息(但不能读取自己放入“线路”的信息,就像打电话 时只能听到对方的声音一样)。mysocket还可以使用getOutputStream()方法获得一个输出流,然后用这个输 出流将信息写入“线路”。
在编写程序时,把mysocket使用getInputStream()方法获得的输入流接到另一个数据流上(和在文件输入输出 流所进行的连接,道理是一样的),然后就可以从这个数据流读取来自服务器的信息,之所以这样做是因为DataInputStre am流有更好的从流中读取信息的方法。同样的道理,把mysocket使用getOutputStream()方法得到的输出流 接到另一个DataOutputStream数据流上,然后向这个数据流写入信息,发送给服务器端。
3.建立接受客户套接字的服务器套接字(ServerSocket)
客户负责建立客户到服务器的套接字连接,即客户负责呼叫。因此服务器必须建立一个等待接收客户套接字的服务器套接字。服务器端的 程序使用ServerSocket类建立接收客户的套接字的服务器套接字。其构造方法是:
ServerSocket(int port);//port是一个端口号。port必须和客户呼叫的端口号相同。
建立服务器套接字时可能发生IOException异常,因此应像下面那样建立服务器套接字:
try {  ServerSocket  server_socket = new ServerSocket(1880);}
catch(IOException e){ }
当服务器的套接字连接server_socket建立后,可以使用accept()方法接收客户的套接字mysocket。
server_socket.accept();
接收客户套接字的过程也可能发生IOException异常,因此应像下面那样建立接受客户的套接字:
try {  Socket  sc = server_socket.accept(); }
catch(IOException e){ }
收到这个客户的mysocket后,把它放到一个已声明的Socket对象sc中,那么sc就是mysocket(当serve r_socket再收到另外一个客户的套接字,比如hersocket,可以再把它放到另外一个Socket对象中)。这样服务 器端的sc可以使用方法getOutputStream()获得一个输出流,然后用这个输出流向“线路”写入信息,发送给客户端 。可以使用getInputStream()方法获得一个输入流,然后用这个输入流读取客户放入“线路”中的信息。
双方通信完毕后,应友好地关闭套接字连接:sc.close();
举例:E54(Example21_4)
4.把套接字连接放在一个线程中
套接字连接中涉及到输入流和输出流操作,为了不影响我们做其他的事情,我们应把套接字连接放在一个单独的线程中去进行。另外,服 务器收到一个客户的套接字后,就应该启动一个专门为该客户服务的线程。
举例:E55(Example21_5)
Java与数据库的连接(JDBC技术)
JDBC(Java DataBase Connectivity)是Java数据库连接API。简单地说,JDBC能完成3件事:
(1)与一个数据库建立连接;
(2)向数据库发送SQL语句;
(3)处理数据库返回的结果。
JDBC在设计上和ODBC很相似。JDBC和数据库建立连接的一种方式是首先建立起一个JDBC-ODBC桥接器。由于ODB C驱动程序被广泛的使用,建立这种桥接器后,使得JDBC有能力访问几乎所有类型的数据库。
一、设置数据源
假设有一个用Access设计的数据库:student.mdb,该库中有一个表,表的名字是chengjibiao。为了和这 个数据库建立连接,首先配置一个ODBC数据源。
假设操作系统为WindowsXP,打开WindowsXP中的控制面板,选择“性能和维护”-“管理工具”,双击“数据源(O DBC)”,选择“系统DSN”-“添加”-“Microsoft Access Driver(*.mdb)”,点击“完成”按钮进入“ODBC Microsoft Access安装”窗口,输入数据源名“redsun”,点击“选择”按钮进入“选择数据库”路径窗口,选择完数据库目录和数据 库名student.mdb后,确认并一步步退出。(P285)
点击“高级”按钮可设置“login name”和“password”。
二、JDBC-ODBC桥接器
1.建立JDBC-ODBC桥接器
现在我们有了一个数据源,这个数据源就是一个数据库。
为了连接到这个数据库,首先要加载JDBC-ODBC bridge驱动程序,即建立一个JDBC-ODBC桥接器:Class.forName(“sun . jdbc . odbc . JdbsOdbcDriver”);
Class是包java.sql中的一个类,该类通过调用它的静态方法forName()就可以建立JDBC-ODBC桥接器。
建立桥接器时可能发生异常,因此必须捕获这个异常。所以建立桥接器的标准方法是:
try {  Class.forName(“sun . jdbc . odbc . JdbsOdbcDriver”);}
catch ( ClassNotFoundException e ) { }
2.连接到数据库
首先使用包java.sql中的Connection类声明一个对象,再使用类DriverManager调用它的静态方法ge tConnection()创建这个连接对象:
Connection con = DriverManager.getConnection(“jdbc:odbc:数据源名字”,“数据源的login name”,“数据源的password”);
如果没有为数据源设置login name和password,那么连接形式为:
Connection con = DriverManager.getConnection(“jdbc:odbc:数据源名字”,“”,“”);
所以为了能和数据源redsun交换数据,先建立connection对象如下:
Connection con = DriverManager.getConnection(“jdbc:odbc:redsun”,“snow”,“ok”);
建立连接时应捕获SQLException异常。
try {
Connection con = DriverManager.getConnection(“jdbc:odbc:redsun”,“snow”,“ok”); }
catch(SQLException e){ }
这样就建立了到数据库student.mdb的连接。
3.向数据库发送SQL语句
首先使用Statement声明一个SQL语句对象,然后通过刚才创建的连接数据库的对象con调用createStateme nt()方法创建这个SQL语句对象。
try { Statement sql = con.createStatement();}
catch(SQLException e){ }
4.处理查询结果
有了SQL对象后,就可以调用相应的方法实现对数据库的查询和修改。并将查询结果存放在一个ResultSet类声明的对象中, 也就是说SQL语句对数据库的查询操作将返回一个ResultSet对象:
ResultSet  rs = sql.executeQuery(“SELECT * FROM  成绩表”);
ResultSet对象实际上是一个管式数据集,即它是由统一形式的列组织的数据行组成。ResultSet对象一次只能看到一 个数据行,使用next()方法走到下一数据行。获得一行数据后,ResultSet对象可以使用位置索引(第一列使用1,第二 列使用2等等)或使用列名称,以便使用getxxxx()方法获得字段值。
ResultSet对象的若干方法:
boolean  next();
byte  getByte(int columnIndex);
Date  getDate(int columnIndex);
double  getDouble(int columnIndex);
float  getFloat(int columnIndex);
int  getInt(int columnIndex);
long  getLong(int columnIndex);
String  getString(int columnIndex);
byte  getByte(String columnName);
Date  getDate(String columnName);
double  getDouble(String columnName);
float  getFloat(String columnName);
int  getInt(String columnName);
long  getLong(String columnName);
String  getString(String columnName);
举例:E56(Example23_2)
Java中的鼠标事件和键盘事件
 发生鼠标事件的事件源往往是一个容器,当鼠标进入容器、离开容器,或者在容器中单击鼠标、拖动鼠标时都发生了鼠标事 件。
一、使用MouseListener接口处理鼠标事件
1.事件源发生的鼠标事件有5种:按下鼠标键、释放鼠标键、击鼠标键、鼠标进入和鼠标退出。
鼠标事件的类型是MouseEvent,即当发生鼠标事件时,MouseEvent类自动创建一个事件对象。
MouseEvent类中有下列几个重要的方法:
getX(),getY():获取鼠标的坐标位置。
getModifiers():获取鼠标的左或右键。
getClickCount():获取鼠标被点击的次数。
2.事件源获得监视器的方法是addMouseListener(监视器)。
3.处理事件源发生的事件的接口是MouseListener。接口中有如下方法:
MousePressed(MouseEvent):负责处理鼠标按下事件。即当在事件源按下鼠标时,监视器发现这个事件后将自 动调用接口中的这个方法对事件作出处理。
MouseReleased(MouseEvent):负责处理鼠标释放事件。即当在事件源释放鼠标时,监视器发现这个事件后将 自动调用接口中的这个方法对事件作出处理。
MouseEntered(MouseEvent):负责处理鼠标进入容器事件。即当鼠标进入容器时,监视器发现这个事件后将自 动调用接口中的这个方法对事件作出处理。
MouseExited(MouseEvent):负责处理鼠标离开容器事件。即当鼠标离开容器时,监视器发现这个事件后将自动 调用接口中的这个方法对事件作出处理。
MouseClicked(MouseEvent):负责处理点击鼠标事件。即当鼠标被击时,监视器发现这个事件后将自动调用接 口中的这个方法对事件作出处理。
举例:E57(Example18_1)
二、使用MouseMotionListener接口处理鼠标事件
1.事件源发生的鼠标事件有2种:拖动鼠标和移动鼠标(鼠标键不按下)。
鼠标事件的类型是MouseEvent,即当发生这类事件时,MouseEvent类自动创建一个事件对象。
2.事件源获得监视器的方法是addMouseMotionListener(监视器)。
3.处理事件源发生的事件的接口是MouseMotionListener。接口中有如下方法:
MouseDragged(MouseEvent):负责处理鼠标拖动事件。
MouseMoved(MouseEvent):负责处理鼠标移动事件。
举例:E58(Example18_6)
三、控制鼠标的指针形状
当发生鼠标事件时,可以使用setCursor()方法设置鼠标指针形状,所有的组件都从它们的父类继承下来了这个方法。下面列 出了Windows平台上的常见鼠标指针形状的定义:
HAND_CURSOR     MOVE_CURSOR     WAIT_CURSOR     CROSSHAIR_CURSOR
NE_RESIZE_CURSOR     SE_RESIZE_CURSOR     N_RESIZE_CURSOR     E_RESIZE_CURSOR
举例:E59(Example18_8)
四、键盘事件
当按下、释放或敲击键盘上一个键时就发生了键盘事件。当一个组件处于激活状态时,它就可以成为发生键盘事件的事件源。
事件源使用addKeyListener方法获得监视器。监视器是一个对象,创建该对象的类必须实现接口keyListener 。接口keyListener中有3个方法:
public void keyPressed(KeyEvent e);
public void keyTyped(KeyEvent e);
public void keyReleased(KeyEvent e);
 当按下键盘上某个键时,监视器就会发现,然后keyPressed()方法会自动执行,并且KeyEvent类自动 创建一个对象传递给方法keyPressed中的参数e。方法keyTyped是keyPressed和keyReleased 方法的组合,当键被按下又释放时,keyTyped()方法被调用。
 KeyEvent类提供了一个方法:public int getKeyCode();用来判断哪个键被按下或释放。
 举例:E60(Example18_12)