当前位置:首页 > java学习 > 第一章 章节有错,我要报告!
广告图片:言情小说吧

第一章

??????

5.13    动    态    成    员    函    数    发    送    

当    你    用    点    操    作    符    调    用    一    个    对    象    实    例    的    成    员    函    数    时,    对    象    实    例    所    属    的    类    在    编    译    时    要    被    检    查,    以    确    保    调    用    的    成    员    函    数    在    该    类    中    是    存    在    的。    在    运    行    时,    对    象    实    例    可    以    指    向    所    声    明    类    型    的    子    类    的    实    例。    在    这    ?copy;    情    况    下,    如    果    子    类    覆    盖    了    要    调    用    的    成    员    函    数,Java就    用    实    例    来    决    定    调    用    哪    一    个    成    员    函    数。    如    下    面    的    例    子,    两    个    类    是    子    类    和    超    类    的    关    系,    子    类    覆    盖    了    超    类    的    成    员    函    数。    

class    A    {    void    callme(    )    {    System.out.println(\"在A的callme成    员    函    数    里\");    }    }    

class    B    extends    A    {    void    callme(    )    {    System.out.println(\"在B的callme成    员    函    数    里\");    }    }    

class    Dispatch    {    public    static    void    main(String    args[])    {    A    a    =    new    B(    );    a.callme(    );    }    }    

有    趣    的    是,    在    成    员    函    数main里,    我    们    把    变    量a声    明    为    类    型A,    然    后    把    类B的    一    个    实    例    存    放    到    它    上    面。    我    们    在a上    调    用    成    员    函    数callme,Java编    译    器    确    定    在    类A确    实    有    成    员    函    数callme,    但    是    在    运    行    时,    由    于a事    实    上    是B的    实    例,    所    以    调    用B的callme,    而    不    调    用A的。    下    面    是    运    行    结    果:    C:\\>java    Dispatch    在B的callme成    员    函    数    里    

5.14    final    

在    缺    省    情    况    下,    所    有    的    成    员    函    数    和    实    例    变    量    都    可    以    被    覆    盖。    如    果    你    希    望    你    的    变    量    或    成    员    函    数    不    再    被    子    类    覆    盖,    可    以    把    它    们    声    明    为final。    这    意    味    着    将    来    的    实    例    都    依    赖    这    个    定    义。    例    如:    final    int    FILE_NEW    =    1;    final    int    FILE_OPEN    =    2;    final    int    FILE_SAVE    =    3;    fianl    int    FILE_SAVEAS    =    4;    final    int    FILE_QUIT    =    5;    final变    量    用    大    写    标    识    符    是    一    个    一    般    的    约    定。    

5.15    静    态    

如    果    你    想    要    创    建    一    个    可    以    在    实    例    的    外    部    调    用    的    成    员    函    数,    那    么    你    只    需    声    明    它    为    静    态    的    (static?copy;,    它    就    会    正    常    运    行。    静    态    成    员    函    数    只    能    直    接    调    用    其    他    静    态    成    员    函    数,    而    不    能    以    任    何    方    式    使    用this或super。    你    也    可    以    把    变    量    声    明    为    静    态    的。    如    果    你    想    初    始    化    一    个    静    态    变    量,    你    可    以    用    static声    明    一    个    恰    好    在    类    调    用    时    执    行    一    次    的    程    序    块。    下    面    的    例    子    是    一    个    带    有    一    个    静    态    成    员    函    数,    几    个    静    态    变    

量,    和    一    个    静    态    初    始    块    的    类。    

class    Static    {    static    int    a    =    3;    static    int    b;    static    void    method(int    x){    System.out.println(\"x    =    \"    +    x);    System.out.println(\"a    =    \"    +    a);    System.out.println(\"b    =    \"    +    b);    }    static    {    System.out.println(\"静    态    初    始    块\");    b    =    a    *    4;    }    public    static    void    main(String    args[])    {    method(42);    }    }    这    个    类    被    调    用,    所    有    的    静    态    变    量    都    被    初    始    化,a被    赋    为3,    然    后    运    行static块,    这    将    打    印    出    一    段    消    息,    并    且    把b赋    为a*4,    即12。    然    后    解    释    器    调    用main成    员    函    数,    它    调    用    了    成    员    函    数    method,    参    数x为42。    这    三    个println语    句    打    印    了    两    个    静    态    变    量a、b和    局    部    变    量x。    下    面    是    运    行    结    果:    C:\\>java    Static    静    态    初    始    块    x    =    42    a    =    3    b    =    12    一    个    静    态    成    员    函    数    可    以    通    过    它    所    属    的    类    名    来    调    用。    象    调    用    实    例    变    量    一    样,    你    可    以    用    点    操    作    符    通    过    类    名    来    调    用    静    态    成    员    函    数    和    静    态    变    量。Java就    是    这    样    实    现    了    全    局    函    数    和    全    局    变    量。    下    面    的    例    子    里,    我    们    创    建    了    带    有    一    个    静    态    成    员    函    数    和    两    个    静    态    变    量    的    类。    第    二    个    类    可    以    通    过    名    字    直    接    来    调    用    第    一    个    类    的    静    态    成    员    函    数    和    静    态    变    量。    

class    staticClass    {    static    int    a    =    42;    static    int    b    =    99;    static    void    

callme(    )    {    System.out.println(\"a    =    \"    +    a);    }    }    

class    StaticByName    {    public    static    void    main(String    args[])    

{    StaticClass.callme(    );    System.out.println(\"b    =    \"    +    staticClass.b);    }    }    

下    面    是    运    行    结    果:    C:\\>java    staticByName    a    =    42    b    =    99    

5.16    抽    象    

有    时    你    需    要    定    义    一    个    给    出    抽    象    结    构、    但    不    给    出    每    个    成    员    函    数    的    完    整    实    现    的    类。    如    果    某    个    成    员    函    数    没    有    完    整    实    现,    必    须    要    由    子    类    来    覆    盖,    你    可    把    它    声    明    为    抽    象(abstract?copy;    型。    含    有    抽    象    型    成    员    函    数    的    类    必    须    声    明    为    抽    象    的。    为    了    把    一    个    类    声    明    为    抽    象    的,    你    只    需    在    类    定    义    的class关    键    词    前    放    置    关    键    词abstract。    这    ?copy;    类    不    能    直    接    用new操    作    符    生    成    实    例,    因    为    它    们    的    完    整    实    现    还    没    有    定    义。    你    不    能    定    义    抽    象    的    构    造    函    数    或    抽    象    的    静    态    成    员    函    数。    抽    象    类    的    子    类    或    者    实    现    了    它    的    超    类    的    所    有    抽    象    的    成    员    函    数,    或    者    也    被    声    明    为    抽    象    的。    下    面    例    子    是    一    个    带    有    抽    象    成    员    函    数    的    类,    其    后    是    一    个    实    现    了    该    成    员    函    数    的    类。    

abstract    class    A    {    abstract    void    callme(    )    ;    void    metoo(    )    {    system.out.println(\"在A的metoo成    员    函    数    里\");    }    }    class    B    extends    A    {    void    callme(    )    {    System.out.println(\"在B的callme成    员    函    数    里\");    }    }    class    Abstract    {    public    static    void    main(String    args[])    {    A    a    =    new    B(    );    

a.callme(    );    a.metoo(    );    }    }    

下    面    是    运    行    结    果:    C:\\>java    Abstract    在B的callme成    员    函    数    里    在A的metoo成    员    函    数    里    

本    章    小    结    

1.    类    是Java语    言    面    向    对    象    编    程    的    基    本    元    素,    它    定    义    了    一    个    对    象    的    结    构    和    功    能。    2.    Java通    过    在    类    定    义    的    大    括    号    里    声    明    变    量    来    把    数    据    封    装    在    一    个    类    里,    这    里    的    变    量    称    为    实    例    变    量。    3.    成    员    函    数,    是    类    的    功    能    接    口,    是    类    定    义    里    的    一    个    子    程    序,    在    类    的    定    义    里    和    实    例    变    量    处    于    同    一    级    别。

第    六    章    Java图    形    用    户    接    口    

对    一    个    优    秀    的    应    用    程    序    来    说,    良    好    的    图    形    用    户    接    口    是    必    不    可    少    的。    缺    少    良    好    的    图    形    用    户    接    口,    将    会    给    用    户    理    解    和    使    用    应    用    程    序    带    来    很    多    不    便。    很    难    想    象    用    户    为    了    学    会    使    用    一    个    应    用    程    序,    去    记    一    大    堆    命    令。    Java提copy;    了    生    成    一    个    良    好    的    图    形    用    户    接    口    所    需    要    的    一copy;    基    本    元    件:    面    板(Panel    copy;、    按    钮    (Button    copy;、    标copy;(Label    copy;、    画    板(Canvases    copy;、    滚    动    条(Scrollbar    copy;、    列    表    框(List    copy;、    文    本    域(Text    Field    copy;、    文    本    区(Text    Area    copy;。    

6.1    面    板    

面    板    提copy;    了    建    立    应    用    程    序    的    空    间。    你    可    以    把    图    形    元    件(包    括    其    他    面    板    copy;    放    在    一    个    面    板    上。    Applet类    提copy;    了    一    个    基    本    的    面    板。    

6.1.1    布    局    管    理    

Java提copy;    了    几    种    布    局:    顺    序    布    局(Flow    Layout    copy;、    边    界    布    局(Border    Layout    copy;    和    网    格    布    局    (Grid    Layout)

6.1.1.1    顺    序    布    局    

顺    序    布    局(Flow    Layout    copy;    是    最    基    本    的    一    种    布    局,    面    板    的    缺    省    布    局    就    是    顺    序    布    局。    顺    序    布    局    指    的    是    把    图    形    元    件    一    个    接    一    个    地reg;    平    地    放    在    面    板    上。    下    面    是    一    个    顺    序    布    局    的    例    子:    

import    java.awt.*;    import    java.applet.Applet;    

public    class    myButtons    extends    Applet    {    Button    button1,    button2,    button3;    

public    void    init()    {    button1    =    new    Button(\"确    定\");    button2    =    new    Button(\"打    开\");    button3    =    new    Button(\"关    闭\");    add(button1);    add(button2);    add(button3);    }    }    

6.1.1.2    边    界    布    局    

边    界    布    局    包    括    五    个    区:    北    区、    南    区、    东    区、    西    区    和    中    区。    这    几    个    区    在    面    板    上    的    分    布    规    律    是\"    上    北    下    南,    左    西    右    东\"。    下    面    是    一    个    边    界    布    局    的    例    子:    

import    java.awt.*;    import    java.applet.Applet;    

public    class    buttonDir    extends    Applet    {    Button    buttonN,    buttonS,    buttonW,    buttonE,    buttonC;    

public    void    init()    {    setLayout(new    BorderLayout());    buttonN    =    new    Button(\"    reg;\");    buttonS    =    new    Button(\"火\");    buttonE    =    new    Button(\"木\");    buttonW    =    new    Button(\"金\");    buttonC    =    new    Button(\"土\");    add(\"North\",    buttonN);    add(\"South\",    buttonS);    add(\"East\",    buttonE);    add(\"West\",    buttonW);    add(\"Center\",    buttonC);    }    }    

6.1.1.3    网    格    布    局

网    格    布    局    把    面    板    分    成    一    个    个    的    网    格,    你    可    以    给    出    网    格    的    行    数    和    列    数。    下    面    是    一    个    网    格    布    局    的    例    子:    

import    java.awt.*;    import    java.applet.Applet;    

public    class    buttonGrid    extends    Applet    {    Button    button1,    button2,    button3,    button4,    button5,    button6,    button7,    button8;    

public    void    init()    {    setLayout(new    GridLayout(4,2));    button1    =    new    Button(\"乾\");    button2    =    new    Button(\"坤\");    button3    =    new    Button(\"艮\");    

button4    =    new    Button(\"震\");    button5    =    new    Button(\"坎\");    button6    =    new    Button(\"离\");    button7    =    new    Button(\"巽\");    button8    =    new    Button(\"兑\");    

add(button1);    add(button2);    add(button3);    add(button4);    add(button5);    add(button6);    add(button7);    add(button8);    }    }    

6.2    按    钮    

6.2.1    按    钮    事    件

用    户    点    一    下    按    钮,    就    会    有    一    个    按    钮    事    件    发    生。    你    可    以    通    过    覆    盖    一    个applet的action成    员    函    数    来    捕    捉    按    钮    事    件。    public    boolean    action    (Event    e,    Object    o)    {    if    (e.target    instanceof    Button)    {    system.out.println    ((string)    o);    }    else    {    System.out.println    (\"Non-button    event\");    }    return    true;    }    

6.2.2    按    钮    类    型    

Java提copy;    了    标    准    的    按    压    式    按    钮,    同    时    也    提copy;    了    选    择    式    按    钮    和    标    记    式    按    钮。    

6.2.2.1    选    择    式    按    钮    

选    择    式    按    钮    提copy;    了    从    几    个    选    项    中    选    一    个    选    项    的    功    能。    下    面    是    从    几    个    市    中    选    一    个    市    的    例    子,    市    名    放    在    选    择    式    按    钮    中:    

CityChooser    =    new    Choice();    

CityChooser.addItem(\"北copy;\");    CityChooser.addItem(\"上海\");    

CityChooser.addItem(\"天    津\");    

add(CityChooser);    

6.2.2.2    标    记    式    按    钮

标    记    式    按    钮    的    状    态    作    为    标    记    框    事    件    的    对    象    参    数    返    回。    下    面    是    一    个    标    记    式    按    钮    的    例    子:    

Checkbox    fillStyleButton;    fillStyleButton    =    new    Checkbox(\"Solid\");    

public    boolean    action(Event    e,    Object    arg)    {    if    (e.target    instanceof    

Checkbox)    {    System.out.println(\"Checkbox:    \"    +    arg);    }    return    true;    }    

6.2.2.3    按    键    式    按    钮

按    键    式    按    钮    是    一    组    按    钮,    用    户    可    以    选    中    其    中    一    个,    同    时    这    一    组    中    的    其    他    按    钮    将    被    关    闭。    下    面    是    一    个    按    键    式    按    钮    的    例    子:    public    class    CheckBox    extends    Applet    {    CheckboxGroup    cbg;    public    void    init()    {    cbg    =    new    CheckboxGroup();    add    (new    Checkbox(\"one    \",    cbg,    true));    add    (new    Checkbox(\"two    \",    cbg,false));    add    (new    Checkbox(\"three\",    cbg,    false));    }    }    

6.2.3    自    包    含    按    钮    

Java语    言    的    面    向    对    象    特    性    使    我    们    能    够    创    建    完    全    自    包    含    的    按    钮。    在    自    包    含    按    钮    里,    你    可    以    在copy;    展    按    钮    类    里    建    立    事    件    控    制    函    数。    下    面    是    一    个    自    包    含    按    钮    的    例    子:    

import    java.awt.*;    import    java.applet.Applet;    

class    okButton    extends    Button    {    

public    okButton()    {    setLabel(\"Ok\");    }    

public    boolean    action(Event    e,    Object    arg)    

{    System.out.println(\"OKButton\");    return    true;    }    }    

public    class    buttontest    extends    Applet    {    okButton    myOkButton;    

public    void    init()    {    myOkButton    =    new    okButton();    add(myOkButton);    }    }    

6.3    标copy;    

标copy;    是    一    种    放    到    面    板    上    的    静    止    的    正    文。    下    面    是    一    个    标copy;    的    例    子:    import    java.awt.*;    import    java.applet.Applet;    public    class    label    extends    Applet    {    public    void    init()    {    setLayout(new    FlowLayout(FlowLayout.CENTER,    10,    10));    Label    label1    =    new    Label(\"你    好!\");    Label    label2    =    new    Label(\"另    一    个    标copy;\");    add(label1);    add(label2);    }    }    

6.4    列    表    框    

列    表    框    使    用    户    易    于    操    作    大    量    的    选    项。    创    建    列    表    框    的    方    法    和Choice    button有copy;    相    似。    列    表    框    的    所    有    条    目    都    是    可    见    的,    如    果    选    项    很    多,    超    出    了    列    表    框    可    见    区    的    范    围,    则    列    表    框    的    旁    边    将    会    有    一    个    滚    动    条。    首    先,    创    建    列    表    框:    List    l    =    new    List(4,    false);    这    个    成    员    函    数    创    建    了    一    个    显    示4行    的    列    表    框。    第    二    个    参    数\"false\"表    示    这    个    列    表    框    是    单    选    的,    如    果    是\"true    \",    则    表    示    是    多    选    的。    下    面    增    加    列    表    框    的    选    项:    l.addItem(\"北copy;    大    学\");    l.addItem(\"清    华    大    学\");    l.addItem(\"吉林    大    学\");    l.addItem(\"复copy;    大    学\");    l.addItem(\"南    开    大    学\");    l.addItem(\"天    津    大    学\");    l.addItem(\"南copy;    大    学\");    add(l);    

6.4.1    在    列    表    框    中    进    行    选    择    

可    以    用    成    员    函    数getSelectedItem()或getSelectedItems()来    接    收    在    列    表    框    中    被    选    的    选    项。    在    单    选    列    表    框    里,\"    双    击\"    一    个    选    项    就    可    以    触    发    一    个    可    被action()成    员    函    数    捕    捉    到    的    事    件。    public    boolean    action(Event    e,    Object    arg)    {    .    .    .    if    (e.target    instanceof    List)    {    System.out.println(\"List    entry:\"    +    arg);    }    .    .    .    }    

6.4.2    多    选    列    表    框

对    于    多    选    列    表    框,    要    使    你    的    选    择    产    生    作    用,    需    要    使    用    其    他    的    外    部    事    件。    例    如,    你    可    以    使    用    按    钮    事    件:    public    boolean    action(Event    e,    Object    arg)    {    .    .    .    if    (e.target    instanceof    Button)    {    .    .    .    if    (\"Ok\".equals(arg))    {    string[]    selected    ;    selected    =    l.getSelectedItems(    );    for    (int    I    =    0;    I<    selected.length;    I++)    {System.out.println(selected[i]);    }    }    }    }    

6.5    文    本    域    

文    本    域    一    般    用    来    让    用    户    输    入    象    姓    名、    信    用    卡    号    这    样    的    信    息,    它    是    一    个    能    够    接    收    用    户    的    键    盘    输    入    的    小    块    区    域。    

6.5.1    创    建    文    本    域    

在    创    建    文    本    域    时,    有    四    种    类    型copy;    你    选    择:    空    的、    空    的    并    且    具    有    指    定    长    度、    带    有    初    始    文    本    内    容    的    和    带    有    初    始    文    本    内    容    并    具    有    指    定    长    度    的。    下    面    是    生    成    这    四    种    文    本    域    的    代    码:    TextField    tf1,    tf2,    tf3,    tf4;    //    空    的    文    本    域    tf1    =    new    TextField()    ;    //    长    度    为20的    空    的    文    本    域    tf2    =    new    TextField(20)    ;    //    带    有    初    始    文    本    内    容    的    文    本    域    tf3    

=    new    TextField(\"你    好\")    ;    //    带    有    初    始    文    本    内    容    并    具    有    指    定    长    度    的    文    本    域    tf4    =    new    TextField(\"你    好\",    30)    ;    add(tf1)    ;    add(tf2)    ;    add(tf3)    ;    add(tf4)    ;    

6.5.2    文    本    域    事    件    

当    用    户    在    文    本    域    里    敲\"    回    车\"    键    时,    就    产    生    了    一    个    文    本    域    事    件。    象    其    他    事    件    一    样,    你    可    以    以    在    成    员    函    数action()中    捕    捉    到    这    个    事    件。    

public    boolean    action(Event    e,    Object    arg)    {    .    .    .    if    (e.target    instanceof    TextField)    {    System.out.println(\"TextField:    \"+arg);    }    .    .    .    }    

6.6    文    本    区    

文    本    区    可    以    显    示    大    段    的    文    本。    

6.6.1    创    建    文    本    区

与    文    本    域    类    似,    创    建    文    本    区    时    也    有    四    种    类    型copy;    选    择,    但    如    果    指    定    文    本    区    的    大    小,    必    须    同    时    指    定    行    数    和    列    数。    TextArea    ta1,    ta2;    //    一    个    空    的    文    本    区    ta1    =    new    TextArea();    //    一    个    带    有    初    始    内    容、    大    小    为5    x    40    的    文    本    区    ta2    =    new    TextArea(\"你    好!\",    5,    40);    

可    以    用    成    员    函    数setEditable()来    决    定    用    户    是    否    可    对    文    本    区    的    内    容    进    行    编    辑。    //    使    文    本    区    为    只    读    的    ta2.setEditable(false)    

6.6.2    接    收    文    本    区    的    内    容

可    以    用    成    员    函    数getText()来    获    得    文    本    区    的    当    前    内    容。    例    如:    System.out.println(ta1.getText());    文    本    区    本    身    不    产    生    自    己    的    事    件。    但    你    可    以    用    外    部    事    件    来    接    收    文    本    区    的    内    容:    public    boolean    action(Event    e,    Object    o)    {    if    (e.target    instanceof    Button)    {    if    (\"send\".equals(o))    {    String    textToSend    =    ta1.getText    ();    

System.out.println(\"sending:    \"    +    textTosend);    

mySendFunction(textToSend);    }    }    else    {    .    .    .    }    }    

6.7    画    板

画    板    能    够    捕    捉    到copy;    露    事    件、    鼠    标    事    件    和    其    他    类    似    的    事    件。    基    本    的    画    板    类    不    处    理    这copy;    事    件,    但    你    可    以copy;    展    它    来    创    建    有    你    所    需    功    能    的    画    板    类。    

6.7.1    创    建    画    板    

import    java.awt.*;    import    java.applet.Applet;    

public    class    superGUI    extends    Applet    {    .    .    .    myCanvas    doodle;    .    .    .    public    

void    init()    {    .    .    .    //    建    立    我    们    的    画    板    doodle    =    new    myCanvas();    

doodle.reshape(0,    0,    100,    100);    leftPanel.add(\"Center\",doodle);    .    .    .    }    }    

class    myCanvas    extends    Canvas    {    public    void    paint(Graphics    g)    

{    g.drawRect(0,    0,    99,    99);    g.drawString(\"Canvas\",    15,    40);    }    }    

6.7.2    画    板    事    件    

你    可    以    覆    盖    一    般    的    事    件    处    理    成    员    函    数。    下    面    是    一    个    包    含    了mouseDown事    件    处    理    的    例    子:    import    java.awt.*;    import    java.applet.Applet;    

public    class    canvas    extends    Applet    {    

Button    b1;    

public    void    init()    {    //    Set    our    layout    as    a    Border    style    setLayout(new    

BorderLayout(15,    15));    b1    =    new    Button(\"Test\");    myCanvas    c1    =    new    

myCanvas(100,    100);    //    add    the    canvas    and    the    button    to    the    applet    

add(\"Center\",    c1);    add(\"South\",    b1);    }    

public    boolean    action(Event    e,    Object    arg)    {    System.out.println(\"Event:    

\"    +    arg);    return    true;    }    

public    boolean    mouseDown(Event    e,    int    x,    int    y)    

{    System.out.println(\"Mouse    works:    (\"    +    x    +    \",\"    +    y    +    \")\");    return    true;    }    }    

class    myCanvas    extends    Canvas    {    private    int    width;    private    int    height;    

public    myCanvas(int    w,    int    h)    {    width    =    w;    height    =    h;    reshape(0,    0,    w,    

h);    }    

public    void    paint(Graphics    g)    {    g.setColor(Color.blue);    g.fillRect(0,    0,    

width,    height);    }    

public    boolean    mouseDown(Event    e,    int    x,    int    y)    {    if    ((    x    <    width)    &&    (y    

第    七    章    多    线    程

7.1    多    线    程    的    概    念    

多    线    程    编    程    的    含    义    是    你    可    将    程    序    任    务    分    成    几    个    并    行    的    子    任    务。    特    别    是    在    网    络    编    程    中,    你    会    发    现    很    多    功    能    是    可    以    并    发    执    行    的。    比    如    网    络    传    输    速    度    较    慢,    用    户    输    入    速    度    较    慢,    你    可    以    用    两    个    独    立    的    线    程    去    完    成    这    ?copy;    功    能,    而    不    影    响    正    常    的    显    示    或    其    他    功    能。    多    线    程    是    与    单    线    程    比    较    而    言    的,    普    通    的WINDOWS采    用    单    线    程    程    序    结    构,    其    工    作    原    理    是:    主    程    序    有    一    个    消    息    循    环,    不    断    从    消    息    队    列    中    读    入    消    息    来    决    定    下    一    步    所    要    干    的    事    情,    一    般    是    一    个    子    函    数,    只    有    等    这    个    子    函    数    执    行    完    返    回    后,    主    程    序    才    能    接    收    另    外    的    消    息    来    执    行。    比    如    子    函    数    功    能    是    在    读    一    个    网    络    数    据,    或    读    一    个    文    件,    只    有    等    读    完    这    ?copy;    数    据    或    文    件    才    能    接    收    下    一    个    消    息。    在    执    行    这    个    子    函    数    过    程    中    你    什    么    也    不    能    干。    但    往    往    读    网    络    数    据    和    等    待    用    户    输    入    有    很    多    时    间    处    于    等    待    状    态,    多    线    程    利    用    这    个    特    点    将    任    务    分    成    多    个    并    发    任    务    后,    就    可    以    解    决    这    个    问    题。    

7.1.1    Java线    程    的    模    型    

Java的    设    计    思    想    是    建    立    在    当    前    大    多    数    操    作    系    统    都    实    现    了    线    程    调    度。Java虚    拟    机    的    很    多    任    务    都    依    赖    线    程    调    度,    而    且    所    有    的    类    库    都    是    为    多    线    程    设    计    的。    实    时    上,Java支    持Macintosh和Ms-dos    的    平    台    ?reg;    所    以    迟    迟    未    出    来    就    是    因    为    这    两    个    平    台    都    不    支    持    多    线    程。Java利    用    多    线    程    实    现    了    整    个    执    行    环    境    是    异    步    的。    在Java程    序    里    没    有    主    消    息    循    环。    如    果    一    个    线    程    等    待    读    取    网    络    数    据,    它    可    以    运    行    但    不    停    止    系    统    的    其    他    线    程    执    行。    用    于    处    理    用    户    输    入    的    线    程    大    多    时    间    是    等    待    用    户    敲    键    盘    或    击    鼠    标。    你    还    可    以    使    动    画    的    每    一    帧    ?reg;    间    停    顿    一    秒    而    并    不    使    系    统    暂    停。    一    ?copy;    线    程    启    动    后,    它    可    以    被    挂    起,    暂    时    不    让    它    执    行。    挂    起    的    线    程    可    以    重    新    恢    复    执    行。    任    何    时    间    线    程    都    可    以    被    停    止,    被    停    止    的    线    程    就    不    能    再    重    新    启    动。    Java语    言    里,    线    程    表    现    为    线    程    类,    线    程    类    封    装    了    所    有    需    要    的    线    程    操    作    控    制。    在    你    心    里,    必    须    很    清    晰    地    区    分    开    线    程    对    象    和    运    行    线    程,    你    可    以    将    线    程    对    象    看    作    是    运    行    线    程    的    控    制    面    板。    在    线    程    对    象    里    有    很    多    函    数    来    控    制    一    个    线    程    是    否    运    行,    睡    眠,    挂    起    或    停    止。    线    程    类    是    控    制    线    程    行    为    的    唯    一    的    手    段。    一    ?copy;    一    个Java程    序    启    动    后,    就    已    经    有    一    个    线    程    在    运    行。    你    可    通    过    调    用Thread.currentThread    函    数    来    查    看    当    前    运    行    的    是    哪    一    个    线    程。    

你    得    到    一    个    线    程    的    控    制    柄,    你    就    可    以    作    很    有    趣    的    事    情,    即    使    单    线    程    也    一    样。    下    面    这    个    例    子    让    你    知    道    怎    样    操    纵    当    前    线    程。    Filename:testthread    

class    testthread    {    public    static    void    main(String    args[])    {    Thread    t    

=Thread.currentThread();    t.setName(\"This    Thread    is    running\");    

System.out.println(\"The    running    thread:\"    +    t);    try    {    for    (int    i=0;i<5;i++)    

{    System.out.println(\"Sleep    time    \"+i);    Thread.sleep(1000);    }    

}    catch    (InterruptedException    e)    {System.out.println(\"thread    has    wrong\");    }    

}    }    

执    行    结    果:java    testthread    The    running    thread:Thread[This    Thread    is    running,5,main]    Sleep    time    0    Sleep    time    1    Sleep    time    2    Sleep    time    3    Sleep    time    4    

7.1.2    启    动    接    口

一    个    线    程    并    不    激    动    人    心,    多    个    线    程    才    有    实    际    意    义。    我    们    怎    样    创    建    更    多    的    线    程    呢?    我    们    需    要    创    建    线    程    类    的    另    一    个    实    例。    当    我    们    构    造    了    线    程    类    的    一    个    新    的    实    例,    我    们    必    须    告    诉    它    在    新    的    线    程    里    应    执    行    哪    一    段    程    序。    你    可    以    在    任    意    实    现    了    启动    接    口    的    对    象    上    启    动    一    个    线    程。    启    动    接    口    是    一    个    抽    象    接    口,    来    表    示    本    对    象    有    一    ?copy;    函    数    想    异    步    执    行。    要    实    现    启    动    接    口,    一    个    类    只    需    要    有    一    个    叫run的    函    数。    下    面    是    创    建    一    个    新    线    程    的    例    子:    

Filename:twothread.java    

class    twothread    implements    Runnable    {    twothread()    {    Thread    t1    

=Thread.currentThread();    t1.setName(\"The    first    main    thread\");    

System.out.println(\"The    running    thread:\"    +    t1);    Thread    t2    =    new    

Thread(this,\"the    second    thread\");    System.out.println(\"creat    another    

thread\");    t2.start();    try    {    System.out.println(\"first    thread    will    

sleep\");    Thread.sleep(3000);    }catch    (InterruptedException    e)    

{System.out.println(\"first    thread    has    wrong\");    }    

System.out.println(\"first    thread    exit\");    }    public    void    run()    {    try    {    for    

(int    i=0;i<5;i++)    {    System.out.println(\"Sleep    time    for    thread    2:\"+i);    

Thread.sleep(1000);    }    

}    catch    (InterruptedException    e)    {System.out.println(\"thread    has    

wrong\");    }    

System.out.println(\"second    thread    exit\");    }    public    static    void    

main(String    args[])    {    new    twothread();    }    }    

执    行    结    果:java    twothread    

The    running    thread:Thread[The    first    main    thread,5,main]    creat    another    

thread    first    thread    will    sleep    Sleep    time    for    thread    2:0    Sleep    time    for    

thread    2:1    Sleep    time    for    thread    2:2    first    thread    exit    Sleep    time    for    

thread    2:3    Sleep    time    for    thread    2:4    second    thread    exit    

main线    程    用new    Thread(this,    \"the    second    thread\")创    建    了    一    个Thread对    象,    通    过    传    递    第    一    个    参    数    来    标    明    新    线    程    来    调    用this对    象    的run函    数。    然    后    我    们    调    用start函    数,    它    将    使    线    程    从run函    数    开    始    执    行。    

7.1.3    同    步    

因    为    多    线    程    给    你    提    ?copy;    了    程    序    的    异    步    执    行    的    功    能,    所    以    在    必    要    时    必    须    还    提    ?copy;    一    种    同    步    机    制。    例    如,    你    想    两    个    线    程    通    讯    并    共    享    一    个    复    杂    的    数    据    结    构,    你    需    要    一    种    机    制    让    他    们    相    互    牵    制    并    正    确    执    行。    为    这    个    目    的,Java用    一    种    叫    监    视    器(monitor)的    机    制    实    现    了    进    程    间    的    异    步    执    行。    可    以    将    监    视    器    看    作    是    一    个    很    小    的    盒    子,    它    只    能    容    纳    一    个    线    程。    一    ?copy;    一    个    线    程    进    入    一    个    监    视    器,    所    有    其    他    线    程    必    须    等    到    第    一    个    线    程    退    出    监    视    器    后    才    能    进    入。    这    ?copy;    监    视    器    可    以    设    计    成    保    护    共    享    的    数    据    不    被    多    个    线    程    同    时    操    作。    大    多    数    多    线    程    系    统    将    这    ?copy;    监    视    器    设    计    成    对    象,Java提    ?copy;    了    一    种    更    清    晰    的    解    决    方    案。    没    有Monitor类;    每    个    对    象    通    过    将    他    们    的    成    员    函    数    定    义    成synchronized来    定    义    自    己    的    显    式    监    视    器,    一    ?copy;    一    个    线    程    执    行    在    一    个synchronized函    数    里,    其    他    任    何    线    程    都    不    能    调    用    同    一    个    对    象    的    

synchronized函    数。    

7.1.4    消    息    

你    的    程    序    被    分    成    几    个    逻    辑    线    程,    你    必    须    清    晰    的    知    道    这    ?copy;    线    程    ?reg;    间    应    怎    样    相    互    通    讯。Java    提    了wait和notify等    功    能    来    使    线    程    ?reg;    间    相    互    交    谈。    一    个    线    程    可    以    进    入    某    一    个    对    象    的synchronized    函    数    进    入    等    待    状    态,    直    到    其    他    线    程    显    式    地    将    它    唤    醒。    可    以    有    多    个    线    程    进    入    同    一    个    函    数    并    等    待    同    一    个    唤    醒    消    息。    

7.2    Java线    程    例    子    

7.2.1    显    式    定    义    线    程    

在    我    们    的    单    线    程    应    用    程    序    里,    我    们    并    没    有    看    见    线    程,    因    为Java能    自    动    创    建    和    控    制    你    的    线    程。    如    果    你    使    用    了    理    解Java语    言    的    浏    览    器,    你    就    已    经    看    到    使    用    多    线    程    的Java程    序    了。    你    也    许    注    意    到    两    个    小    程    序    可    以    同    时    运    行,    或    在    你    移    动    滚    动    条    时    小    程    序    继    续    执    行。    这    并    不    是    表    明    小    程    序    是    多    线    程    的,    但    说    明    这    个    浏    览    器    是    多    线    程    的。    多    线    程    应    用    程    序(或applet)可    以    使    用    好    几    个    执    行    上    下    文    来    完    成    它    们    的    工    

作。    多    线    程    利    用    了    很    多    任    务    包    含    单    独    的    可    分    离    的    子    任    务    的    特    点。    每    一    个    线    程    完    成    一    个    子    任    务。    但    是,    每    一    个    线    程    完    成    子    任    务    时    还    是    顺    序    执    行    的。    一    个    多    线    程    程    序    允    许    各    个    线    程尽快    执    行    完    它    们。    这    种    特    点    会    有    更    好    的    实    时    输    入    反    应。    

7.2.2    多    线    程    例    子

下    面    这    个    例    子    创    建    了    三    个    单    独    的    线    程,    它    们    分    别    打    印    自    己    的\"Hello    World\":    

//Define    our    simple    threads.They    will    pause    for    a    short    time    //and    then    

print    out    their    names    and    delay    times    class    TestThread    extends    Thread    

{    private    String    whoami;    private    int    delay;    

//Our    constructor    to    store    the    name    (whoami)    //and    time    to    sleep    (delay)    

public    TestThread(String    s,    int    d)    {    whoami    =    s;    delay    =    d;    }    

//Run    -    the    thread    method    similar    to    main()    //When    run    is    finished,    the    

thread    dies.    //Run    is    called    from    the    start()    method    of    Thread    public    void    

run()    {    //Try    to    sleep    for    the    specified    time    try    {    sleep(delay);    }    

catch(InterruptedException    e)    {}    //Now    print    out    our    name    

System.out.println(\"Hello    World!\"+whoami+\"\"+delay);    }    }    /**    *    Multimtest.    

A    simple    multithread    thest    program    */    public    class    multitest    {    public    

static    void    main(String    args[])    {    TestThread    t1,t2,t3;    //Create    our    test    

threads    t1    =    new    TestThread(\"Thread1\",(int)(Math.readom()*2000));    t2    =    

new    TestThread(\"Thread2\",(int)(Math.readom()*2000));    t3    =    new    

TestThread(\"Thread3\",(int)(Math.readom()*2000));    

//Start    each    of    the    threads    t1.start();    t2.start();    t3.start();    }    }    

7.2.3    启    动    一    个    线    程    

程    序    启    动    时    总    是    调    用main()函    数,    因    此main()是    我    们    创    建    和    启    动    线    程    的    地    方:    

t1    =    new    TestThread(\"Thread1\",(int)(Math.readom()*2000));    

这    一    行    创    建    了    一    个    新    的    线    程。    后    面    的    两    个    参    数    传    递    了    线    程    的    名    称    和    线    程    在    打    印    信    息    ?reg;    前    的    延    时    时    间。    因    为    我    们    直    接    控    制    线    程,    我    们    必    须    直    接    启    动    它:    t1.start();    

7.2.4    操    作    线    程    

如    果    创    建    线    程    正    常,t1应    包    含    一    个    有    效    的    执    行    线    程。    我    们    在    线    程    的run()函    数    里    控    制    线    程。    一    ?copy;    我    们    进    入run()函    数,    我    们    便    可    执    行    里    面    的    任    何    程    序。run()好    象main()一    样。    

run()    执    行    完,    这    个    线    程    也    就    结    束    了。    在    这    个    例    子    里,    我    们    试    着    延    迟    一    个    随    机    的    时    间(通    过    参    数    传    递?)    sleep(delay);    

sleep()函    数    只    是    简    单    地    告    诉    线    程    休    息    多    少    个    毫    秒    时    间。    

如    果    你    想    推    迟    一    个    线    程    的    执    行,    你    应    使    用sleep()函    数。    当    线    程    睡    眠    是sleep()并    不    占    用    系    统    资    源。    其    它    线    程    可    继    续    工    作。    一    ?copy;    延    迟    时    间    完    毕,    它    将    打    印\"Hello    World\"和    线    程    名    称    及    延    迟    时    间。    

7.2.5    暂    停    一    个    线    程

我    们    经    常    需    要    挂    起    一    个    线    程    而    不    指    定    多    少    时    间。    例    如,    如    果    你    创    建    了    一    个    含    有    动    画    线    程    的    小    程    序。    也    许    你    让    用    户    暂    停    动    画    至    到    他    们    想    恢    复    为    止。    你    并    不    想    将    动    画    线    程    仍    调,    但    想    让    它    停    止。    象    这    种    类    似    的    线    程    你    可    用suspend()函    数    来    控    制:    t1.suspend();    这    个    函    数    并    不    永    久    地    停    止    了    线    程,    你    还    可    用resume()函    数    重    新    激    活    线    程:    t1.resume();    

7.2.6    停    止    一    个    线    程    

线    程    的    最    后    一    个    控    制    是    停    止    函    数stop()。    我    们    用    它    来    停    止    线    程    的    执    行:    t1.stop();    

注    意:    这    并    没    有    消    灭    这    个    线    程,    但    它    停    止    了    线    程    的    执    行。    并    且    这    个    线    程    不    能    用t1.start()重    新    启    动。    在    我    们    的    例    子    里,    我    们    从    来    不    用    显    式    地    停    止    一    个    线    程。    我    们    只    简    单    地    让    它    执    行    完    而    已。    很    多    复    杂    的    线    程    例    子    将    需    要    我    们    控    制    每    一    个    线    程。    在    这    种    情    况    下    会    使    用    到stop()函    数。    如    果    需    要,    你    可    以    测    试    你    的    线    程    是    否    被    激    活。    一    个    线    程    已    经    启    动    而    且    没    有    停    止    被    认    为    是    激    活    的。    t1.isAlive()    如    果t1是    激    活    的,    这    个    函    数    将    返    回true.    

7.2.7    动    画    例    子    

下    面    是    一    个    包    含    动    画    线    程    的applet例    子:    

import    java.awt.*;    import    java.awt.image.ImageProducer;    import    

java.applet.Applet;    

public    class    atest3    extends    Applet    implements    Runnable    {    Image    images[];    

MediaTracker    tracker;    int    index    =    0;    Thread    animator;    

int    maxWidth,maxHeight;    //Our    off-screen    components    for    double    buffering.    

Image    offScrImage;    Graphics    offScrGC;    

//Can    we    paint    yes?    boolean    loaded    =    false;    

//Initialize    the    applet.    Set    our    size    and    load    the    images    public    void    init()    

[    //Set    up    our    image    monitor    tracker    =    new    MediaTracker(this);    

//Set    the    size    and    width    of    our    applet    maxWidth    =    100;    maxHeight    =100;    

images    =    new    Image[10];    //Set    up    the    double-buffer    and    resize    our    applet    

try    {    offScrImage    =    createImage(maxWidth,maxHeight);    offScrGC    =    

offScrImage.getGraphics();    offScrGC.setColor(Color.lightGray);    

offScrGC.fillRect(0,0,maxWidth,maxHeight);    

resize(maxWidth,maxHeight);    }catch    (Exception    e)    

{    e.printStackTrace();    }    

//load    the    animation    images    into    an    array    for    (int    i=0;i<10;i++)    {    String    

imageFile    =    new    String    (\"images/Duke/T\"    +String.valueOf(i+1)    +\".gif\");    

images[i]    =    getImage(getDocumentBase(),imageFile):    //Register    this    

image    with    the    tracker    tracker.addImage(images[i],i);    }    try    {    //Use    

tracker    to    make    sure    all    the    images    are    loaded    tracker.waitForAll();    }    

catch    (InterruptedException    e)    {}    loaded    =    true;    }    

//Paint    the    current    frame.    public    void    paint    (Graphics    g)    {    if    (loaded)    

{    g.drawImage(offScrImage,0,0,this);    }    }    

//Start    ,setup    our    first    image    public    void    start()    {    if    (tracker.checkID    

(index))    {    offScrGC.drawImage    (images[index],0,0,this);    }    animator    =    new    

Thread(this);    animator.start();    }    

//Run,do    the    animation    work    here.    //Grab    an    image,    pause    ,grab    the    next...    

public    void    run()    {    //Get    the    id    of    the    current    thread    Thread    me    =    

Thread.currentThread();    

//If    our    animator    thread    exist,and    is    the    current    thread...    while    

((animatr!=    null)    &&    (animator==me))    {    if    (    tracker.checkID    (index))    

{    //Clear    the    background    and    get    the    next    image    

offScrGC.fillRect(0,0,100,100);    

offScrGCdrawImage(images[index],0,0,this);    index++;    //Loop    back    to    the    

beginning    and    keep    going    if    (index>=    images.length)    {    index    =    0;    }    }    

//Delay    here    so    animation    looks    normal    try    {    animator.sleep(200);    }catch    

(InterruptedException    e)    {}    //Draw    the    next    frame    repaint();    }    }    }    

7.3    多    线    程    间    的    通    讯

7.3.1    生    产    者    和    消    费    者    

多    线    程    的    一    个    重    要    特    点    是    它    们    ?reg;    间    可    以    互    相    通    讯。    你    可    以    设    计    线    程    使    用    公    用    对    象,    每    个    线    程    都    可    以    独    立    操    作    公    用    对    象。    典    型    的    线    程    间    通    讯    建    立    在    生    产    者    和    消    费    者    模    型    上:    一    个    线    程    产    生    输    出;    另    一    个    线    程    使    用    输    入buffer    

让    我    们    创    建    一    个    简    单    的\"Alphabet    Soup\"生    产    者    和    相    应    的    消    费    者.    

7.3.2    生    产    者    

生    产    者    将    从thread类    里    派    生:    class    Producer    extends    Thread    

{    private    Soup    soup;    private    String    alphabet    =    \"    

ABCDEFGHIJKLMNOPQRSTUVWXYZ\";    

public    Producer(Soup    s)    {    //Keep    our    own    copy    of    the    shared    object    soup    

=    s;    }    

public    void    run()    {    char    c;    //Throw    10    letters    into    the    soup    for    (int    

i=0;i<10;i++)    {    c    =    alphabet.charAt((int)(Math.random()    *26));    

soup.add(c);    //print    a    record    of    osr    addition    

System.out.println(\"Added\"+c    +    \"to    the    soup.\");    //wait    a    bit    before    we    

add    the    next    letter    try    {    sleep((int)(Math.random()    *1000));    }    catch    

(InterruptedException    e)    {}    }    }    }    

注    意    我    们    创    建    了Soup类    的    一    个    实    例。    生    产    者    用soup.add()函    数    来    建    立    字    符    池。    

7.3.3    消    费    者

让    我    们    看    看    消    费    者    的    程    序:    class    Consumer    extends    Thread    {    private    Soup    soup;    

public    Consumer    (Soup    s)    {    //keep    our    own    copy    of    the    shared    object    soup    

=    s;    }    

public    void    run()    {    char    c;    //Eat    10    letters    from    the    alphabet    soup    for    

(int    I=0    ;i<10;i++)    {    //grab    one    letter    c    =    soup.eat();    //Print    out    the    

letter    that    we    retrieved    System.out.println(\"Ate    a    letter:    \"    +c);    //try    

{    sleep((int)(Math.raddom()*2000));    }    catch    (InterruptedException    e)    {}    }    }    }    

同    理,    象    生    产    者    一    样,    我    们    用soup.eat()来    处    理    信    息。    那    么,Soup类    到    底    干    什    么    呢?    

7.3.4    监    视    

Soup类    执    行    监    视    两    个    线    程    ?reg;    间    传    输    信    息    的    功    能。    监    视    是    多    线    程    中    不    可    缺    少    的    一    部    分,    因    为    它    保    持    了    通    讯    的    流    ?copy;。    让    我    们    看    看Soup.java文    件:    class    Soup    {    private    char    buffer[]    =    new    char[6];    private    int    next    =    0;    //Flags    to    keep    track    of    

our    buffer    status    private    boolean    isFull    =    false;    private    boolean    isEmpty    

=    true;    public    syschronized    char    eat()    {    //We    can\'t    eat    if    there    isn\'t    anything    

in    the    buffer    while    (isEmpty    ==    true)    {    try    {    wait()    ;//we\'ll    exit    this    

when    isEmpty    turns    false    }catch    (InterruptedException    e)    {}    }    //decrement    

the    count,since    we\'re    going    to    eat    one    letter    next--;    //Did    we    eat    the    

last    letter?    if    (next==    0)    {    isEmpty    =    true;    }    //We    know    the    buffer    can\'t    

be    full,because    we    just    ate    isFull    =    false;    notify();    //return    the    letter    

to    the    thread    that    is    eating    return    (buffer[next]);    }    

//method    to    add    letters    to    the    buffer    public    synchronized    void    add(char    

c)    {    //Wait    around    until    there\'s    room    to    add    another    letter    while    (isFull    

==    true    )    {    try{    wait();//This    will    exit    when    isFull    turns    false    }catch    

(InterruptedException    e)    {}    }    //add    the    letter    to    the    next    available    spot    

buffer[next]=c;    //Change    the    next    available    spot    next++;    //Are    we    full;    

if    (next    ==6)    {    isFull    =true;    }    isEmpty    =false;    notify();    }    }    soup类    包    含    两    个    重    要    特    征:    数    据    成    员buffer[]是    私    有    的,    功    能    成    员add()和eat()是    公    有    的。    

数    据    私    有    避    免    了    生    产    者    和    消    费    者    直    接    获    得    数    据。    直    接    访    问    数    据    可    能    造    成    错    误。    例    如,    如    果    消    费    者    企    图    从    空    缓    冲    区    里    取    出    数    据,    你    将    得    到    不    必    要    的    异    常,    否    则,    你    只    能    锁    住    进    程。    同    步    访    问    方    法    避    免    了    破    坏    一    个    共    享    对    象。    当    生    产    者    向soup里    加    入    一    个    字    母    时,    消    费    者    不    能    吃    字    符,    诸    如    此    类。    这    种    同    步    是    维    持    共    享    对    象    完    整    性    的    重    要    方    面。notify()函    数    将    唤    醒    每    一    个    等    待    线    程。    等    待    线    程    将    继    续    它    的    访    问。    

7.3.5    联    系    起    来    

现    在    我    们    有    一    个    生    产    者,    一    个    消    费    者    和    一    个    共    享    对    象,    怎    样    实    现    它    们    的    交    互    呢?    我    们    只    需    要    一    个    简    单    的    控    制    程    序    来    启    动    所    有    的    线    程    并    确    信    每    一    个    线    程    都    是    访    问    的    同    一    个    共    享    对    象。    下    面    是    控    制    程    序    的    代    码,SoupTest.java:    

class    SoupTest    {    public    static    void    main(String    args[])    {    Soup    s    =    new    

Soup();    Producer    p1    =    new    Producer(s);    Consumer    c1    =    new    Consumer(s);    

p1.start();    c1.start();    }    }    

7.3.6    监    视    生    产    者    

生    产    者/消    费    者    模    型    程    序    经    常    用    来    实    现    远    程    监    视    功    能,    它    让    消    费    者    看    到    生    产    者    同    用    户    的    交    互    或    同    系    统    其    它    部    分    的    交    互。    例    如,    在    网    络    中,    一    组    生    产    者    线    程    可    以    在    很    多    工    作    站    上    运    行。    生    产    者    可    以    打    印    文    档,    文    档    打    印    后,    一    个    标    志    将    保    存    下    来。    一    个(或    多    个?copy;    消    费    者    将    保    存    标    志    并    在    晚    上    报    告    白    天    打    印    活    动    的    情    况。    另    外,    还    有    例    子    在    一    个    工    作    站    是    分    出    几    个    独    立    的    窗    口。    一    个    窗    口    用    作    用    户    输    入(生    产    者)    另    一    个    窗    口    作    出    对    输    入    的    反    应(消    费    者)。    

7.4    线    程API列    表    

下    面    是    一    个    常    用    的    线    程    类    的    方    法    函    数    列    表:    类    函    数:    以    下    是Thread的    静    态    函    数,    即    可    以    直    接    从Thread类    调    用。    

currentThread    返    回    正    在    运    行    的Thread对    象    yield    停    止    运    行    当    前    线    程,    让    系    统    运    行    下    一    个    线    程    sleep(int    n)    让    当    前    线    程    睡    眠n毫    秒    对    象    函    数:    以    下    函    数    必    须    用Thread的    实    例    对    象    来    调    用。    

start    start函    数    告    诉java运    行    系    统    为    本    线    程    建    立    一    个    执    行    环    境,    然    后    调    用    本    线    程    的run()函    数。    run    是    运    行    本    线    程    的    将    要    执    行    的    代    码,    也    是Runnable接    口    的    唯    一    函    数。    当    一    个    线    程    初    始    化    后,    由start函    数    来    调    用    它,    一    ?copy;run函    数    返    回,    本    线    程    也    就    终    止    了。    stop    让    某    线    程    马    上    终    止,    系    统    将    删    除    本    线    程    的    执    行    环    境    suspend    与stop函    数    不    同,suspend将    线    程    暂    停    执    行,    但    系    统    不    破    坏    线    程    的    执    行    环    境,    你    可    以    用resume来    恢    复    本    线    程    的    执    行    resume    恢    复    被    挂    起    的    线    程    进    入    运    行    状    态    setPriority(int    p)    给    线    程    设    置    优    先    级    getPriority    返    回    线    程    的    优    先    级    setName(String    name)    给    线    程    设    置    名    称    getName    取    线    程    的    名    称    

本    章    小    结:    

1.多    线    程    是java语    言    的    重    要    特    点,java语    言    用Thread类    封    装    了    线    程    的    所    有    操    作。    2.线    程    的    接    口    名    为Runnable    3.线    程    间    同    步    机    制    为synchronized关    键    词    4.线    程    间    通    讯    靠wait与notify消    息    

第    八    章    Java的\"    异    常\"

\"    异    常\"    指    的    是    程    序    运    行    时    出    现    的    非    正    常    情    况。    在    用    传    统    的    语    言    编    程    时,    程    序    员    只    能    通    过    函    数    的    返    回    值    来    发    出    错    误    信    息。    这    易    于    导    致    很    多    错    误,    因    为    在    很    多    情    况    下    需    要    知    道    错    误    产    生    的    内    部    细    节。    通    常,    用    全    局    变    量errno来    存    储\"    异    常\"    的    类    型。    这    容    易    导    致    误    用,    因    为    一    个errno的    值    有    可    能    在    被    处    理    ?reg;    前    被    另    外    的    错    误    覆    盖    掉。    即    使    最    优    美    的C语    言    程    序,    为    了    处    理\"    异    常\"    情    况,    也    常    求    助    于goto语    句。    Java对\"    异    常\"    的    处    理    是    面    向    对    象    的。    一    个Java的Exception是    一    个    描    述\"    异    常\"    情    况    的    对    象。    当    出    现\"    异    常\"    情    况    时,    一    个Exception对    象    就    产    生    了,    并    放    到    产    生    这    个\"    异    常\"    的    成    员    函    数    里。    

8.1    基础    

Java的\"    异    常\"    处    理    是    通    过5个    关    键    词    来    实    现    的:try,    catch,    throw,    throws和finally。    用try    来    执    行    一    段    程    序,    如    果    出    现\"    异    常\",    系    统    抛    出(throws?copy;    一    个\"    异    常\",    你    可    以    通    过    它    的    类    型    来    捕    捉(catch?copy;    它,    或    最    后(finally?copy;    由    缺    省    处    理    器    来    处    理。    下    面    是\"    异    常\"    处    理    程    序    的    基    本    形    式:    try    {    //程    序    块    }    catch    (ExceptionType1    e)    {    //    对ExceptionType1的    处    理    }    catch    (ExceptionType2    e)    {    //    对ExceptionType2的    处    理    throw(e);    //再    抛    出    这    个\"    异    常\"    }    finally    {    }    

8.2    \"异    常\"    的    类    型

在\"    异    常\"    类    层    次    的    最    上    层    有    一    个    单    独    的    类    叫    做Throwable。    这    个    类    用    来    表    示    所    有    的\"    异    常\"    情    况。    每    个\"    异    常\"    类    型    都    是Throwable的    子    类。Throwable有    两    个    直    接    的    子    类。    一    类    是Exception,    是    用    户    程    序    能    够    捕    捉    到    的\"    异    常\"    情    况。    我    们    将    通    过    产    生    它    的    子    类    来    创    建    自    己    的\"    异    常\"。    另    一    类    是Error,    它    定    义    了    那    ?copy;    通    常    无    法    捕    捉    到    的\"    异    常\"。    要    谨    慎    使    用Error子    类,    因    为    它    们    通    常    会    导    致    灾    难    性    的    失    败。    在Exception中    有    一    个    子    类RuntimeException,    它    是    程    序    运    行    时    自    动    地    对    某    ?copy;    错    误    作    出    反    应    而    产    生    的。    

8.3    不    捕    捉\"    异    常\"    

\"    异    常\"    对    象    是Java在    运    行    时    对    某    ?copy;\"    异    常\"    情    况    作    出    反    应    而    产    生    的。    例    如,    下    面    这    个    小    程    序    包    含    一    个    整    数    被0除    的\"    异    常\"。    

class    Exc0    {    public    static    void    main(String    args[])    {    int    d    =    0;    int    a    =    42/d;    }    }    

当Java执    行    这    个    除    法    时,    由    于    分    母    是0,    就    会    构    造    一    个\"    异    常\"    对    象    来    使    程    序    停    下    来    并    处    理    这    个    错    误    情    况,    在    运    行    时\"    抛    出\"(throw?copy;    这    个\"    异    常\"。    说\"    抛    出\"    是    因    为    它    象    一    个    滚    烫    的    马    铃    薯,    你    必    须    把    它    抓    住    并    立    即    处    理。    程    序    流    将    会    在    除    号    操    作    符    处    被    打    断,    然    后    检    查    当    前    的    调    用    堆    栈    来    查    找\"    异    常\"。    一    个\"    异    常\"    处    理    器    是    用    来    立    即    处    理\"    异    常\"    情    况    的。    在    这    个    例    子    里,    我    们    没    有    编    一    个\"    异    常\"    处    理    器,    所    以    缺    省    的    处    理    器    就    发    挥    作    用    了。    缺    省    的    处    理    器    打    印Exception的    字    符    ?reg;    值    和    发    生    \"    异    常\"    的    地    点。    

下    面    是    我    们    的    小    例    子    的    输    出。    

C:\\>java    Exc0    java.lang.arithmeticException:    /    by    zero    at    Exc0.main(Exc0.java:4)    

8.4    try与catch    

通    常    我    们    希    望    自    己    来    处    理\"    异    常\"    并    继    续    运    行。    可    以    用try来    指    定    一    块    预    防    所    有\"    异    常\"    的    的    程    序。    紧    跟    在try程    序    后    面,    应    包    含    一    个catch子    句    来    指    定    你    想    要    捕    捉    的\"    异    常\"    的    类    型。    例    如,    下    面    的    例    子    是    在    前    面    的    例    子    的    基础上    构    造    的,    但    它    包    含    一    个try程    序    块    和    一    个catch子    句。    class    exc1    {    public    static    void    main(string    args[])    {    try    {    int    d    =    0;    int    a    =    42    /    d;    }    catch    (arithmeticexception    e)    {    system.out.println(\"division    by    zero\");    }    }    }    

catch子    句    的    目    标    是    解    决\"    异    常\"    情    况,    把    变    量    设    到    合    理    的    状    态,    并    象    没    有    出    错    一    样    继    续    运    行。    如    果    一    个    子    程    序    不    处    理    某    个\"    异    常\",    则    返    到    上    一    级    处    理,    直    到    最    外    一    级。    

8.5    多    个catch子    句

在    某    情    况    下,    同    一    段    程    序    可    能    产    生    不    止    一    种\"    异    常\"    情    况。    你    可    以    放    置    多    个catch子    句,    其    中    每    一    种\"    异    常\"    类    型    都    将    被    检    查,    第    一    个    与    ?reg;    匹    配    的    就    会    被    执    行。    如    果    一    个    类    和    其    子    类    都    有    的    话,    应    把    子    类    放    在    前    面,    否    则    将    永    远    不    会    到    达    子    类。    下    面    是    一    个    有    两    个catch子    句    的    程    序    的    例    子。    

class    MultiCatch    {    public    static    void    main(String    args[])    {    try    {    int    a    

=    args.length;    System.out.println(\"a    =    \"    +    a);    int    b    =    42/a;    int    c[]    =    

{1};    c[42]    =    99;    }    catch(ArithmeticException    e)    {    System.out.println(\"div    

by    0:    \"    +    e);    }    catch(ArrayIndexOutOfBoundsException    e)    

{    system.out.println(\"array    index    oob:    \"    +    e);    }    }    }    

如    果    在    程    序    运    行    时    不    跟    参    数,    将    会    引    起    一    个0做    除    数    的\"    异    常\",    因    为a的    值    为0。    如    果    我    们    提    ?copy;    一    个    命    令    行    参    数,    将    不    会    产    生    这    个\"    异    常\",    因    为a的    值    大    于0。    但    会    引    起    一    个    ArrayIndexOutOfBoundexception的\"    异    常\",    因    为    整    型    数    组c的    长    度    是1,    却    给c[42]赋    值。    下    面    是    以    上    两    种    情    况    的    运    行    结    果。    

C:\\>java    MultiCatch    a    =    0    div    by    0:    java.lang.arithmeticexception:    /    by    

zero    C:\\>java    MutiCatch    1    a    =    1    array    index    oob:    

java.lang.ArrayIndexOutOfBoundsException:42    

8.6    try语    句    的    嵌    套    

你    可    以    在    一    个    成    员    函    数    调    用    的    外    面    写    一    个try语    句,    在    这    个    成    员    函    数    内    部,    写    另    一    个try语    句    保    护    其    他    代    码。    每    当    遇    到    一    个try语    句,\"    异    常\"    的    框    架    就    放    到    堆    栈    上    面,    直    到    所    有    的try语    句    都    完    成。    如    果    下    一    级    的try语    句    没    有    对    某    种\"    异    常\"    进    行    处    理,    堆    栈    就    会    展    开,    直    到    遇    到    有    处    理    这    种\"    异    常\"    的try语    句。    下    面    是    一    个try语    句    嵌    套    的    例    子。    

class    MultiNest    {    static    void    procedure()    {    try    {    int    c[]    =    {    1    }:    c[42]    

=    99;    }    catch(ArrayIndexOutOfBoundsexception    e)    

{    System.out.println(\"array    index    oob:    \"    +    e);    }    }    public    static    void    

main(String    args[])    {    try    {    int    a    =    args.length;    system.out.println(\"a    

=    \"    +    a);    int    b    =    42/a;    procedure();    }    catch(arithmeticException    e)    

{    System.out.println(\"div    by    0:    \"    +    e);    }    }    }    

成    员    函    数procedure里    有    自    己    的try/catch控    制,    所    以main不    用    去    处    理    ArrayIndexOutOfBoundsException。    

8.7    throw语    句

throw语    句    用    来    明    确    地    抛    出    一    个\"    异    常\"。    首    先,    你    必    须    得    到    一    个Throwable的    实    例    的    控    制    柄,    通    过    参    数    传    到catch子    句,    或    者    用new操    作    符    来    创    建    一    个。    下    面    是throw语    句    的    通    常    形    式。    

throw    ThrowableInstance;    

程    序    会    在throw语    句    后    立    即    终    止,    它    后    面    的    语    句    执    行    不    到,    然    后    在    包    含    它    的    所    有try块    中    从    里    向    外    寻    找    含    有    与    其    匹    配    的catch子    句    的try块。    下    面    是    一    个    含    有throw语    句    的    例    子。    

class    ThrowDemo    {    static    void    demoproc()    {    try    {    throw    new    NullPointerException(\"de3mo\");    }    catch(NullPointerException    e)    {    System.out.println(\"caught    inside    demoproc\");    throw    e;    }    }    public    static    void    main(String    args[])    {    try    {    demoproc();    }    

catch(NullPointerException    e)    {    system.out.println(\"recaught:    \"    +    e);    }    }    }    

8.8    throws语    句    

throws用    来    标    明    一    个    成    员    函    数    可    能    抛    出    的    各    种\"    异    常\"。    对    大    多    数Exception子    类    来    说,Java    编    译    器    会    强    迫    你    声    明    在    一    个    成    员    函    数    中    抛    出    的\"    异    常\"    的    类    型。    如    果\"    异    常\"    的    类    型    是Error或    RuntimeException,    或    它    们    的    子    类,    这    个    规    则    不    起    作    用,    因    为    这    ?copy;    在    程    序    的    正    常    部    分    中    是    不    期    待    出    现    的。    如    果    你    想    明    确    地    抛    出    一    个RuntimeException,    你    必    须    用throws语    句    来    声    明    它    的    类    型。    这    就    重    新    定    义    了    成    员    函    数    

的    定    义    语    法:    type    method-name(arg-list)    throws    exception-list    {    }    

下    面    是    一    段    程    序,    它    抛    出    了    一    个\"    异    常\",    但    既    没    有    捕    捉    它,    也    没    有    用throws来    声    明。    这    在    编    译    时    将    不    会    通    过。    

class    ThrowsDemo1    {    static    void    procedure(    )    [    System.out.println(\"inside    

procedure\");    throw    new    IllegalAccessException(\"demo\");    }    public    static    

void    main(String    args[])    {    procedure(    );    }    }    

为    了    让    这    个    例    子    编    译    过    去,    我    们    需    要    声    明    成    员    函    数procedure抛    出    了IllegalAccessException,    并    且    在    调    用    它    的    成    员    函    数main里    捕    捉    它。    下    面    是    正    确    的    例    子:    

class    ThrowsDemo    {    static    void    procedure(    )    throws    IllegalAccessException    

{    System.out.println(\"inside    procedure\");    throw    new    

IllegalAccessException(\"demo\");    }    public    static    void    main(String    args[])    

{    try    {    procedure(    );    }    catch    (IllegalAccessException    e)    

{    System.out.println(\"caught    \"    +    e);    }    }    }    

下    面    是    输    出    结    果:    

C:\\>java    ThrowsDemo    inside    procedure    caught    

java.lang.IllegalAccessException:    demo    

8.9    finally    

当    一    个\"    异    常\"    被    抛    出    时,    程    序    的    执    行    就    不    再    是    线    性    的,    跳    过    某    ?copy;    行,    甚    至    会    由    于    没    有    与    ?reg;    匹    配    的catch子    句    而    过    早    地    返    回。    有    时    确    保    一    段    代    码    不    管    发    生    什    么\"    异    常\"    都    被    执    行    到    是    必    要    的,    关    键    词finally就    是    用    来    标    识    这    样    一    段    代    码    的。    即    使    你    没    有catch子    句,finally程    序    块    也    会    在    执    行    try程    序    块    后    的    程    序    ?reg;    前    执    行。    每    个try语    句    都    需    要    至    少    一    个    与    ?reg;    相    配    的catch子    句    或finally子    句。    一    个    成    员    函    数    返    回    到    调    用    它    的    成    员    函    数,    或    者    通    过    一    个    没    捕    捉    到    的\"    异    常\",    或    者    通    过    一    个    明    确    的return语    句,finally子    句    总    是    恰    好    在    成    员    函    数    返    回    前    执    行。    下    面    是    一    个    例    子,    它    有    几    个    成    员    函    数,    每    个    成    员    函    数    用    不    同    的    途    径    退    出,    但    执    行    了finally子    句。    

class    FinallyDemo    {    static    void    procA(    )    {    try    

{    System.out.println(\"inside    procA\");    throw    new    

RuntimeException(\"demo\");    }    finally    {    System.out.println(\"procA\'s    

finally\");    }    }    static    void    procB(    )    {    try    {    System.out.println(\"inside    

procB\");    return;    }    finally    {    System.out.println(\"procB\'s    finally\");    }    }    

public    static    void    main(String    args[])    {    try    {    procA(    );    }    catch    (Exception    

e);    procB(    );    }    }    

下    面    是    这    个    例    子    的    运    行    结    果:    

C:\\>java    FinallyDemo    inside    procA    procA\'s    finally    inside    procB    procB\'s    finally    

本    章    小    结    

1.    \"    异    常\"    指    的    是    程    序    运    行    时    出    现    的    非    正    常    情    况。    2.    在\"    异    常\"    类    层    次    的    最    上    层    的    类    叫Throwable,    它    有    两    个    直    接    的    子    类:Exception和Error。    3.    Java的\"    异    常\"    处    理    通    过5个    关    键    词    来    实    现:try,catch,throw,throws和finally。

第    九    章    Java输    入    输    出    操    作

9.1    Java    输    入    输    出    流

所    有    的    程    序    语    言    都    提    ?copy;    与    本    机    文    件    系    统    交    互    的    方    式;Java也    不    例    外。    我    们    将    看    看Java是    怎    样    处    理    标    准    文    件    输    入    输    出    的(包    括stdin,stout,stderr)。    当    你    在    网    络    上    开    发    小    程    序    时,    你    必    须    注    意    直    接    文    件    输    入    输    出    是    不    安    全    因    素    的    关    键。    大    多    数    用    户    设    置    他    们    的    浏    览    器,    可    让    你    自    由    的    访    问    他    们    的    文    件    系    统,    但    有    ?copy;    不    让    你    访    问。    当    然,    如    果    你    开    发    你    内    部    的    应    用    程    序,    你    也    许    需    要    直    接    访    问    文    件。    标    准    输    入    输    出    Unix的    用    户,    或    其    他    基    于    命    令    行    系    统    的    用    户(如DOS),    都    知    道    标    准    输    入    输    出    的    含    义。    标    准    输    入    文    件    是    键    盘,    标    准    输    出    文    件    是    你    的    终    端    屏    幕。    标    准    错    误    输    出    文    件    也    指    向    屏    幕,    如    果    有    必    要,    它    也    可    以    指    向    另    一    个    文    件    以    便    和    正    常    输    出    区    分。    

系    统    类    Java通    过    系    统    类    达    到    访    问    标    准    输    入    输    出    的    功    能。    上    面    提    到    的    三    个    文    件    在    这    个    系    统    类    中    实    现:    Stdin    System.in作    为InputStream类    的    一    个    实    例    来    实    现stdin,    你    可    以    使    用read()和skip(long    n)两    个    成    员    函    数。read()让    你    从    输    入    中    读    一    个    字    节,skip(long    n)让    你    在    输    入    中    跳    过n个    字    节。    

Stout    System.out作    为PrintStream来    实    现stdout,    你    可    以    使    用print()和println()两    个    成    员    函    数。    这    两    个    函    数    支    持Java的    任    意    基    本    类    型    作    为    参    数。    

Stderr    System.err同stdout一    样    实    现stderr。    象System.out一    样,    你    可    以    访    问PrintStream    成    员    函    数。    

9.2    标    准    输    入    输    出    例    子    

这    里    有    一    个    例    子,    功    能    象Unix里    的cat或type:    import    java.io.*    class    myCat{    public    void    main(String    args[])    throws    IOException{    int    b;    int    count    =    0;    while    ((b    =    System.in.read())    !=    

-1){    count++;    System.out.print((char)b);    }    System.out.println();    

//blank    line    System.err.println(\"counted\"+count+\"total    bytes.\");    }    }    

9.3    普    通    输    入    输    出    类    

除    了    基    本    的    键    盘    输    入    和    屏    幕    输    出    外,    我    们    还    需    要    联    系    文    件    的    输    入    输    出。    我    们    将    学    习    下    面    几    个    类:    l    FileInputStream    l    DataInputStream    l    FileOutputStream    l    DataOutputStream    

作    为    参    考,    再    列    出    一    ?copy;    特    定    应    用    的    类:    l    PipedInputStream    l    BufferedInputStream    l    PushBackInputStream    l    StreamTokenizer    l    PipedOutputStream    l    BufferedOutputStream    l    RandomAccessFile    

我    们    不    在    此    讨    论    这    ?copy;    类,    但    你    可    以    在JAVA_HOME/src/java/io目    录    里    查    看    每    个    类    的    成    员    函    数    定    义。    

9.4    文    件    

在    我    们    进    行    文    件    操    作    时,    需    要    知    道    关    于    文    件    的    信    息。File类    提    ?copy;    了    一    ?copy;    成    员    函    数    来    操    纵    文    件    和    获    得    文    件    的    信    息。    

9.4.1    创    建    一    个    新    的    文    件    对    象    

你    可    用    下    面    三    个    方    法    来    创    建    一    个    新    文    件    对    象:    File    myFile;    myFile    =    new    File(\"etc/motd\");    或    myFile    =    new    File(\"/etc\",\"motd\");    //more    useful    if    the    directory    or    filename    are    variables    或    File    myDir    =    new    file(\"/etc\");    myFile    =    new    File(myDir,\"motd\");    

这    三    种    方    法    取    决    于    你    访    问    文    件    的    方    式。    例    如,    如    果    你    在    应    用    程    序    里    只    用    一    个    文    件,    第    一    种    创    建    文    件    的    结    构    是    最    容    易    的。    但    如    果    你    在    同    一    目    录    里    打    开    数    个    文    件,    则    第    二    种    或    第    三    种    结    构    更    好。    

9.4.2    文    件    测    试    和    使    用    

一    但    你    创    建    了    一    个    文    件    对    象,    你    便    可    以    使    用    以    下    成    员    函    数    来    获    得    文    件    相    关    信    息:    文    件    名    l    String    getName()    l    String    getPath()    l    String    getAbslutePath()    l    String    getParent()    l    boolean    renameTo(File    newName)    文    件    测    试    l    boolean    exists()    l    boolean    canWrite()    l    boolean    canRead()    l    boolean    isFile()    l    boolean    isDirectory()    l    boolean    isAbsolute()    一    般    文    件    信    息    l    long    lastModified()    l    long    length()    目    录    用    法    l    boolean    mkdir()    l    String[]    list()    

9.4.3    文    件    信    息    获    取    例    子    程    序

这    里    是    一    个    独    立    的    显    示    文    件    的    基    本    信    息    的    程    序,    文    件    通    过    命    令    行    参    数    传    输:    

import    java.io.*;    class    fileInfo{    File    fileToCheck;    public    static    void    main(String    args[])    throws    IOException{    if    (args.length>0){    for    (int    i=0;i   

新书推荐

近期最受关注书籍

[历史] 明朝上门女婿 [仙侠] 天道计划 [玄幻] 圣灵创造
[仙侠] 洪荒之因果缠身 [仙侠] 道骨 [都市] 弄潮
[都市] 重活之圆梦人生 [历史] 朱门风流 [游戏] 贼胆
[都市] 史上第一妖 [都市] 超级成长 [仙侠] 掌天地
[玄幻] 灭世法神 [都市] 逛荡 [历史] 大宋之风流才子
[都市] 重返都市 [仙侠] 仙吟 [都市] 国医