java基础教程

第一章Java语言的产生及其特色

1.1Java产生的历史与现状

1.1.1Java产生的历史

Java来自于Sun公司的一个叫Green的项目,其原先的目的是为家用消费电子产品开发一个分布式代码系统,这样咱们能够把E-mail发给电冰箱、电视机等家用电器,对它们进行控制,和它们进行信息交流。开始,准备采用C++,但C++太复杂,安全性差,最后基于C++开发一种新的语言Oak(Java的前身),Oak是一种用于网络的精巧而安全的语言,Sun公司曾依此投标一个交互式电视项目,但结果是被SGI战胜。可怜的Oak几乎无家可归,恰巧这时MarkArdreesen开发的Mosaic和Netscape启发了Oak项目组成员,他们用Java编制了HotJava浏览器,获得了Sun公司首席执行官ScottMcNealy的支持,触发了Java进军Internet。Java的取名也有一个趣闻,有一天,几位Java成员组的会员正在讨论给这个新的语言取什么名字,当时他们正在咖啡馆喝着Java(爪哇)咖啡,有一我的灵机一动说就叫Java怎样,获得了其余人的赞扬,因而,Java这个名字就这样传开了。 java

1.1.2Java的现状

Java是Sun公司推出的新的一代面向对象程序设计语言,特别适合于Internet应用程序开发,它的平台无关性直接威胁到Wintel的垄断地位。一时间,"连Internet,用Java编程",成为技术人员的一种时尚。虽然新闻界的报导有些言过其实,但Java做为软件开发的一种革命性的技术,其地位已被确立,这表如今如下几个方面: ios

1.计算机产业的许多大公司购买了Java的许可证,包括IBM,Apple,DEC,Adobe,SiliconGraphics,HP,Oracel,Toshiba,以及最不情愿的Microsoft。这一点说明,Java已获得了工业界的承认。 程序员

2.众多的软件开发商开始支持Java的软件产品。例如:Borland公司正在开发的基于Java的快速应用程序开发环境Latte,预计产品会在1996年中期发布。Borland公司的这一举措,推进了Java进入PC机软件市场。Sun公司本身的Java开发环境JavaWorkshop已经发布。数据库厂商如:Illustra,Sysbase,Versant,Oracle都在开发CGI接口,支持HTML和Java。今天是以网络为中心的计算时代,不支持HTML和Java,应用程序的应用范围只能限于同质的环境(相同的硬件平台)。 数据库

3.Intranet正在成为企业信息系统最佳的解决方案,而其中Java将发挥不可替代的做用。Intranet的目的是把Internet用于企业内部的信息系统,它的优势表如今:便宜,易于使用和管理。用户无论使用何种类型的机器和操做系统,界面是统一的Intrnet浏览器,而数据库、Web页面、应用程序(用Java编的Applet)则存在WWW服务器上,不管是开发人员,仍是管理人员,抑或是用户均可以受益于该解决方案。Java语言正在不断发展和完善,Sun公司是主要的发展推进者,较通用的编译环境有JDK(JavaDevelopKit)与JWS(JavaWorkshop)。还有不少其余公司正在开发Java语言的编译器与集成环境,预计不久Java语言的正确性与效率都将会提升,用户用Java编程和如今用C++编程同样方便。 express

1.2Java语言的特色

Java究竟是一种什么样的语言呢?Java是一种简单的面象对象的分布式的解释的健壮的安全的结构中立的可移植的性能很优异的多线程的动态的语言。 编程

1.2.1简单

Java最初是为对家用电器进行集成控制而设计的一种语言,所以它必须简单明了。Java语言的简单性主要体如今如下三个方面: canvas

1.Java的风格相似于C++,于是C++程序员是很是熟悉的。从某种意义上讲,Java语言是C及C++语言的一个变种,所以,C++程序员能够很快就掌握Java编程技术。 小程序

2.Java摒弃了C++中容易引起程序错误的地方,如指针和内存管理。 数组

3.Java提供了丰富的类库。 浏览器

1.2.2面向对象

面向对象能够说是Java最重要的特性。Java语言的设计彻底是面向对象的,它不支持相似C语言那样的面向过程的程序设计技术。Java支持静态和动态风格的代码继承及重用。单从面向对象的特性来看,Java相似于SmallTalk,但其它特性、尤为是适用于分布式计算环境的特性远远超越了SmallTalk。

1.2.3分布式

Java包括一个支持HTTP和FTP等基于TCP/IP协议的子库。所以,Java应用程序可凭借URL打开并访问网络上的对象,其访问方式与访问本地文件系统几乎彻底相同。为分布环境尤为是Internet提供动态内容无疑是一项很是宏伟的任务,但Java的语法特性却使咱们很容易地实现这项目标。

1.2.4健壮

Java致力于检查程序在编译和运行时的错误。类型检查帮助检查出许多开发早期出现的错误。Java自已操纵内存减小了内存出错的可能性。Java还实现了真数组,避免了覆盖数据的可能。这种功能特征大大缩短了开发Java应用程序的周期。Java提供:Null指针检测数组边界检测异常出口字节代码校验

1.2.5结构中立

另外,为了创建Java做为网络的一个总体,Java将它的程序编译成一种结构中立的中间文件格式。只要有Java运行系统的机器都能执行这种中间代码。如今,Java运行系统有Solaris2.4(SPARC),Win32系统(Windows95和WindowsNT)等.Java源程序被编译成一种高层次的与机器无关的byte-code格式语言,这种语言被设计在虚拟机上运行,由机器相关的运行调试器实现执行。

1.2.6安全

Java的安全性可从两个方面获得保证。一方面,在Java语言里,象指针和释放内存等C++功能被删除,避免了非法内存操做。另外一方面,当Java用来建立浏览器时,语言功能和一?copy;浏览器自己提?copy;的功能结合起来,使它更安全。Java语言在你的机器上执行前,要通过不少次的测试。它通过代码校验,检查代码段的格式,检测指针操做,对象操做是否过度以及试图改变一个对象的类型。

1.2.6.1Byte-code校验

若是byte-code经过代码校验,没有返回错误,咱们可知道:代码没有堆栈上溢出和下溢出全部操做代码参数类型都是正确的没有发生非法数据转换,如将整数转换成指针。访问对象操做是合法的

1.2.6.2类装载

ClassLoader经过将本机类与网络资源类的名称分开,来保持安全性。由于调入类时总要通过检查,这样避免了特洛伊木马现象的出现。从网络上下载的类被调进一个与源相关的私有的名字域。当一个私有类访问另外一个类时,build-in(本机类)首先被检查,而后检查相关的类。这样就避免了破坏本机类状况的出现。

1.2.7可移植的

同体系结构无关的特性使得Java应用程序能够在配备了Java解释器和运行环境的任何计算机系统上运行,这成为Java应用软件便于移植的良好基础。但仅仅如此还不够。若是基本数据类型设计依赖于具体实现,也将为程序的移植带来很大不便。例如在Windows3.1中整数(Integer)为16bits,在Windows95中整数为32bits,在DECAlpha中整数为64bits,在Intel486中为32bits。经过定义独立于平台的基本数据类型及其运算,Java数据得以在任何硬件平台上保持一致。Java语言的基本数据类型及其表示方式以下:byte8-bit二进制补码short16-bit二进制补码int32-bit二进制补码long64-bit二进制补码float32-bitIEEE754浮点数double32-bitIEEE754浮点数char16-bitUnicode字符

在任何Java解释器中,数据类型都是依据以上标准具体实现的。由于几乎目前使用的全部CPU都能支持以上数据类型、8~64位整数格式的补码运算和单/双精度浮点运算。Java编译器自己就是用Java语言编写的。Java运算系统的编制依据POSIX方便移植的限制,用ANSIC语言写成。Java语言规范中也没有任何"同具体实现相关"的内容。

1.2.8解释的

Java解释器(运行系统)能直接运行目标代码指令。连接程序一般比编译程序所需资源少,因此程序员能够在建立源程序上花上更多的时间。

1.2.9高性能

若是解释器速度不慢,Java能够在运行时直接将目标代码翻译成机器指令。Sun用直接解释器一秒钟内可调用300,000个过程。翻译目标代码的速度与C/C++的性能没什么区别。

1.2.10多线程

Java提?copy;的多线程功能使得在一个程序里可同时执行多个小任务。线程--有时也称小进程--是一个大进程里分出来的小的独立的进程。由于Java实现的多线程技术,因此比C和C++更键壮。多线程带来的更大的好处是更好的交互性能和实时控制性能。固然实时控制性能还取决于系统自己(UNIX,Windows,Macintosh等),在开发难易程度和性能上都比单线程要好。任何用过当前浏览器的人,都感受为调一副图片而等待是一件很烦恼的事情。在Java里,你可用一个单线程来调一副图片,而你能够访问HTML里的其它信息而没必要等它。

1.2.11动态

Java的动态特性是其面向对象设计方法的扩展。它容许程序动态地装入运行过程当中所须要的类,这是C++语言进行面向对象程序设计所没法实现的。在C++程序设计过程当中,每当在类中增长一个实例变量或一种成员函数后,引用该类的全部子类都必须从新编译,不然将致使程序崩溃。Java从以下几方面采起措施来解决这个问题。Java编译器不是将对实例变量和成员函数的引用编译为数值引用,而是将符号引用信息在字节码中保存下传递给解释器,再由解释器在完成动态链接类后,将符号引用信息转换为数值偏移量。这样,一个在存储器生成的对象不在编译过程当中决定,而是延迟到运行时由解释器肯定的。这样,对类中的变量和方法进行更新时就不至于影响现存的代码。解释执行字节码时,这种符号信息的查找和转换过程仅在一个新的名字出现时才进行一次,随后代码即可以全速执行。在运行时肯定引用的好处是能够使用已被更新的类,而没必要担忧会影响原有的代码。若是程序链接了网络中另外一系统中的某一类,该类的全部者也能够自由地对该类进行更新,而不会使任何引用该类的程序崩溃。Java还简化了使用一个升级的或全新的协议的方法。若是你的系统运行Java程序时遇到了不知怎样处理的程序,不要紧,Java能自动下载你所须要的功能程序。

1.3与C和C++语言的异同

Java提供了一个功能强大语言的全部功能,但几乎没有一点含混特征。C++安全性很差,但C和C++仍是被你们所接受,因此Java设计成C++形式,让你们很容易学习。Java去掉了C++语言的许多功能,让Java的语言功能很精炼,并增长了一些颇有用的功能,Java去掉了如下几个C和C++功能和特征:指针运算结构typedefs#define须要释放内存全局变量的定义这种功能都是很容易引发错误的地方。

1.4Java的应用简介

1.4.1Web浏览

Web浏览是如今国际网甚至局域网的主要使用方式。文档能很容易地显示文本和各类图片,他还能提供超文本连接。这些浏览器调用HTML语言写的文档,HTML/WWW浏览器技术只限于文本和图象。若是你想播放一种声音或运行一个演示程序,你不得不下载那个文件并用你本机上的能理解和运行那个文件格式的程序来播放它。Java程序和它的浏览器HotJava,提供了可以让你的浏览器运行程序的方法。你能从你的浏览器里直接播放声音。你还能播放页面里的动画。Java还能告诉你的浏览器怎样处理新的类型文件。当咱们能在2400baud线上传输视频图象时,HotJava将能显示这些视频。

1.4.2网络应用系统

Java是一种与平台无关的语言,所以用Java开发的网络应用系统能够在各类平台上运行,大大增长了开发效率,减小重复劳动。并且,Java集成的网络功能充分有利于开发网络应用系统。

本章小结:

1.Java的产生与流行是当今internet发展的客观要求2.java是一门各方面性能都很好的编程语言,它的基本特色是简单、面象对象、分布式、解释的、健壮的、安全的、结构中立的、可移植的、性能很优异的、多线程的、动态的。?reg;分适合在internet环境上开发应用系统。3.java能够制做大部分网络应用程序系统,并且与当今流行的WWW浏览器结合得很好。

第二章Java程序开发与运行环境

2.1JDK环境

Java不只提供了一个丰富的语言和运行环境,并且还提供了一个免费的Java开发工具集(JavaDevelopersKits,简称JDK)。编程人员和最终用户能够利用这个工具来开发java程序或调用Java内容。JDK包括如下工具:javacJava语言编译器,输出结果为Java字节码java,Java字节码解释器javapDisassembeler:Java字节码分解程序,本程序返回Java程序的成员变量及方法等信息。javaprof资源分析工具,用于分析Java程序在运行过程当中调用了哪些资源,包括类和方法的调用次数和时间,以及各数据类型的内存使用状况等。javahC代码处理工具,用于从Java类调用C++代码javaAppletViewer小应用程序浏览工具,用于测试并运行Java小应用程序javaDebuggerAPIJava调试工具APIPrototypeDebuggerJava调试工具原型

Java开发环境还包括Java类库(包括I/O类库、用户界面类库、网络类库等)和HotJavaWWW浏览器。其中,HotJava浏览器提供了在WWW环境下运行Java代码的一个运行系统,并且还为WWW开发人员提供了一个Java开发框架。Java解释器是面向Java程序的一个独立运行系统,它能够一种稳定、高性能方式运行那些独立于平台的Java字节码,Java编译器则用于生成这种字节码。

2.1.1Java程序的编译

Java程序的编译程序是javac.exe。javac命令将Java程序编译成字节码,而后你可用java解释器java命令来解释执行这种Java字节码。Java程序源码必须存放在后缀为.java的文件里。Java程序里的每个类,javac都将生成与类相同名称但后缀为.class文件。编译器把.class文件放在.java文件的同一个目录里,除非你用了-d选项。当你引用到某些本身定义的类时,必须指明它们的存放目录,这就须要利用环境变量参数CLASSPATH。环境变量CLASSPATH是由一些被分号隔开的路径名组成。若是传递给javac编译器的源文件里引用到的类定义在本文件和传递的其它文件中找不到,则编译器会按CLASSPATH定义的路径来搜索。例如:

CLASSPATH=.;C:\java\classes则编译器先搜索当前目录,若是没搜索到,则继续搜索C:\java\classes目录。注意,系统老是将系统类的目录缺省地加在CLASSPATH后面,除非你用-classpath选项来编译。javac_g是一个用于调试的未优化的编译器,功能与用法和javac同样。javac的用法以下:

javac[-g][-O][-debug][-depend][-nowarn][-verbose][-classpathpath][-nowrite][-ddir]file.java...

如下是每一个选项的解释。

选项解释:

-classpathpath定义javac搜索类的路径。它将覆盖缺省的CLASSPATH环境变量的设置。路径是由一?copy;由逗号隔开的路径名组成,通常格式以下:.;<your_path>例如:.;C:\java\doc\classes;C:\tools\java\classes表示编译器遇到一个新类,它先在本文件中查找它的定义,若是没有,则在本文件所处目录下其它文件中查找它的定义,若是尚未,则继续搜索C:\java\doc\classes目录中的全部文件,以此类推。

-ddirectory指明类层次的根目录,格式以下:

javac-d<my_dir>MyProgram.java

这样将MyProgram.java程序里的生产的.class文件存放在my_dir目录里.

-g带调试信息编译,调试信息包括行号与使用java调试工具时用到的局部变量信息。若是编译没有加上-O优化选项,只包含行号信息。

-nowarn关闭警告信息,编译器将不显示任何警告信息。

-O优化编译static,final,private函数,注意你的类文件可能更大。

-verbose

让编译器与解释器显示被编译的源文件名和被加载的类名。

环境变量

CLASSPATH用来提?copy;给系统搜索用户定义的类的缺省路径。各路径由分号隔开,例如:

.;C:\java\doc\classes;C:\tools\java\classes表示编译器遇到一个新类,它先在本文件中查找它的定义,若是没有,则在本文件所处目录下其它文件中查找它的定义,若是尚未,则继续搜索C:\java\doc\classes目录中的全部文件,以此类推。

2.1.2Java程序的调试使用java调试器

jdb导游

在早期前Betal版的Java调试器jdb是命令行形式的,如用Sun公司的dbx调试器。用jdb来调试Java应用程序,在调试?reg;前,要确证你的应用程序是带标志-g编译的。例如:javac-gHelloWorld.java

help命令将显示jdb里的可用命令列表。

>help<命令列表>threads[threadgroup]--列出线程thread<threadid>--设置缺省线程Suspend[threadsid(s)]--将线程挂起resume[threadid(s)]--从新启动线程where[id]|a1|--打印线程的堆栈threadgroups--列出线程组号threadgroup<name>--设置当前线程组print<id>[id(s)]--打印对象或域dump<id>[id(s)]--打印全部对象信息locals--打印当前堆栈全部局部变量classes--列出当前所知的类methods<classid>--列出一个类的成员函数stopin<classid>.<method>--在一个成员函数里设置断点stopat<class.id>:<line>--在一行里设置断点up[nframes]--在线程堆栈里往上移down[nframes]--在线程堆栈里往下移clear<classid>:<line>--清除一个断点step--执行当前行cont--从断点处继续执行catch<class.id>--为指定的状况中断ignor<class.id>--为指定的状况忽略list[linenumber]--打印源程序use[Sourcefilepath]--显示或改变源路径memeory--报告内存使用状况loadclassname--加载Java类以便调试run<args>--开始执行加载的类!!--重复以上的命令help(?)--列出全部的命令exit(orquit)--离开调试器

2.1.3Java程序的执行

java-java语言解释器java命令解释java字节码

语法:java[options]classname<args>java_g[options]classname<args>

描述:java命令由java编译器javac输出的Java字节码。

classname参数是要执行的类名称。注意任意在类名称后的参数都将传递给要执行类的main函数。

java执行完main函数后推出,除非main函数建立了一个或多个线程。若是main函数建立了其它线程,java老是等到最后一个线程推出才推出。

选项:

-cs,-checksource当一个编译过的类调入时,这个选项将比较字节码更改时间与源文件更改时间,若是源文件更改时间靠后,则从新编译此类并调入此新类。

-classpathpath定义javac搜索类的路径。它将覆盖缺省的CLASSPATH环境变量的设置。路径是由一?copy;由逗号隔开的路径名组成,通常格式以下:.;<your_path>例如:.;C:\java\doc\classes;C:\tools\java\classes表示解释器遇到一个新类,它先在本文件中查找它的定义,若是没有,则在本文件所处目录下其它文件中查找它的定义,若是尚未,则继续搜索C:\java\doc\classes目录中的全部文件,以此类推。

-mxx设置最大内存分配池,大小为x,x必须大于1000bytes。缺省为16兆。

-msx设置垃圾回收堆的大小为x,x必须大于1000bytes。缺省为1兆。

-noasyncgc关闭异步垃圾回收功能。此选项打开后,除非显式调用或程序内存溢出,垃圾内存都不回收。本选项不打开时,垃圾回收线程与其它线程异步同时执行。

-ssx每一个Java线程有两个堆栈,一个是java代码堆栈,一个是C代码堆栈。-ss选项将线程理C代码用的堆栈设置成最大为x。

-ossx每一个Java线程有两个堆栈,一个是java代码堆栈,一个是C代码堆栈。-oss选项将线程理java代码用的堆栈设置成最大为x。

-v,-verbose让java解释器在每个类被调入时,在标准输出打印相应信息。

环境变量

CLASSPATH用来提?copy;给系统搜索用户定义的类的缺省路径。各路径由分号隔开,例如:

.;C:\java\doc\classes;C:\tools\java\classes表示解释器遇到一个新类,它先在本文件中查找它的定义,若是没有,则在本文件所处目录下其它文件中查找它的定义,若是尚未,则继续搜索C:\java\doc\classes目录中的全部文件,以此类推。

2.2JWS环境

JavaWorkShop是SUN公司的一个新产品,它是一个集成的java语言开发环境,它包括如下工具:

lPortfolio和Project管理器l源文件编辑器lBuild管理工具l调试器l项目测试l?copy;展在线超文本链接到帮助文件

这?copy;工具在JavaWorkShop的第一页都有相似Web页面的超级链接,如图:

注意,JavaWorkShop采用的是当今浏览器的界面风格,你想做什么,只需找到相应的超级链接就能够了,具体编译及调试功能是用嵌如到HTML文档里的Applet实现的,所以,对习惯于用internet浏览方式的用户来讲,这种界面很容易接受。

2.2.1Portfolio和Project管理器

Protfolios是一?copy;java应用程序或Applet的集中。它让你更好的管理更多的projects。一个project是portfolio里的一个子集,它包含了如下信息:

1.怎样编译本项目2.怎样调试和浏览本项目3.怎样运行本项目4.怎样发布本项目

2.2.2源文件编辑器

源文件编辑器能够从build管理器、调试器和源文件浏览器里的超级链接进入。在这个模块里,你能够输入源文件。

2.2.3Build管理工具

本模块是项目的编译器,你能够点build按钮直接进入本模块,若是某个文件出错,错误信息会提?copy;一个超级链接,直接指到出错的源文件地点。

2.2.4VisulJava(图形界面构造?copy;

顾名思义,本模块能让你可视化建造一?copy;复杂界面,若是你用过VisualBasic,你会发现它们很类似。

2.2.5调试器

调试器能让你很方便地跟踪程序的执行与发现程序的错误。

本章小结:

Java语言有两个开发环境,一个是免费的JDK,是命令行方式的。还有一个JavaWorkShop,是开发java程序的集成环境。本章简要介绍了它们的使用方法。

第三章Java程序设计基础

3.1Java编程概况

如今你能够复习一下Java语言的背景材料,它的基本结构象C/C++,但任何用面向过程语言编写过程序的人均可以了解Java语言的大部分结构.

3.1.1程序结构

Java语言的源程序代码由一个或多个编译单元(compilationunit)组成,每一个编译单元只能包含下列内容(空格和注释除外):*一个程序包语句(packagestatement)*入口语句(importstatements)*类的声明(classdeclarations)*界面声明(interfacedeclarations)每一个Java的编译单元可包含多个类或界面,可是每一个编译单元最多只能有一个类或者界面是公共的。Java的源程序代码被编译以后,便产生了Java字节代码。Java的字节代码由一种不依赖于机器的指令组成,这种指令能被Java的运行系统(runtimesystem)有效地解释。Java的运行系统工做起来如同一台虚拟机。在当前的Java实现中,每一个编译单元就是一个以.java为后缀的文件。每一个编译单元有若干个类,编译后,每一个类生成一个.class文件。.class文件是Java虚拟机可以识别的代码。

3.1.2注释

三种类型以下://注释一行/*一行或多行注释*//**文档注释**/

文档注释通常放在一个变量或函数定义以前,指示在任何自动生成文档系统中调入。这种注释都是声明条目的描述.。

3.1.3标识符

变量,函数,类和对象的名称都是标识符,程序员须要标识和使用的东西都须要标识符。在Java语言里,标识符以字符或_,$开头,后面能够包含数字,标识符是大小写有区别的,没有长度限制。

有效的标识符mynameict_networkHello_sys_path$bill

例子:inta_number;char_onechar;float$bill;

关键词abstractcontinuefornewswitch

booleandefaultgotonullsynchronized

breakdoifpackagethis

bytedoubleimplementsprivatethreadsafe

byvalueelseimportprotectedthrow

caseextendsinstanceofpublictransient

catchfalseintreturntrue

charfinalinterfaceshorttry

classfinallylongstaticvoid

constfloatnativesuperwhile

其它保留词如下单词被保留使用:castfuturegenericinner

operatorouterrestvar

3.1.4数据类型

Java使用五种基本类型:integer(整数),floating(浮点数),point(指针),Boolean(布尔变量),CharacterorString(字符或字符?reg;)。integer整数下边给出的数据表示都是整数的例子:4,15,089,0xAD00

整数长度数据类型表示

8bitsbyte

16bitsshort

32bitsint

64bitslong

floating浮点数下边给出的数据表示都是浮点数的例子:6.37,3.7E15,3e8

浮点数长度数据类型表示

32bitsfloat

64bitsdouble

Boolean布尔变量下边是布尔变量的两种可能取值:truefalse

Character字符下边给出的都是字符的例子:a\t(tab)\u????(unicode)

String字符?reg;下边给出的都是字符?reg;的例子:"Thisisastringliteral""中国科学院计算所"

数组你能够定义任意类型的数组.chars[];这是字符型数组;int[]array;这是整型数组;你还能够定义数组的数组.intblock[][]=newint[2][3];数组边界在运行时被检测,避免堆栈溢出和内存崩溃.

在Java里,数组其实是一个对象,数组有一个成员变量:length。你能够用这个成员函数来查看任意数组的长度.inta[][]=newint[10][3]a.length/*10*/a[0].length/*3*/

建立数组在Java里建立数组,你可以使用两种基本方法?reg;一。建立一个空数组:intlist[]=newint[50];或你能够用初始数值填充数组.Stringnames[]={"Chenji","Yuan","Chun","Yang"};至关于下面功能:Stringnames[];names=newString[4];names[0]=newString("Chenji");names[1]=newString("Yuan");names[2]=newString("Chun");names[3]=newString("Yang");

在编译时你不能象下例那样建立静态数组。intname[50];//将产生一个编译错误

你也不能用new操做去填充一个没定义大小的数组。intname[];for(inti=0;i<9;i++){name[i]=i;}

3.1.5表达式

Java语言的表达式和C语言很是相似。

运算符运算符(operator)优先级从高到底排列以下:.[]()++--!~instanceof*/%+-<<>>>>><><=>\==!=&^&&||?:=op=,

整数运算符在整数运算时,若是操做数是long类型,则运算结果是long类型,不然为int类型,毫不会是byte,short或char型。这样,若是变量i被声明为short或byte,i+1的结果会是int。若是结果超过该类型的取值范围,则按该类型的最大值取模。单目整数运算符是:

运算符操做-单目非~位补码++加1--减1

++运算符用于表示直接加1操做。增量操做也能够用加运算符和赋值操做间接完成。++lvalue(左值?copy;表示lvalue+=1,++lvalue也表示lvalue=lvalue+1(只要lvalue没有反作用)。--运算符用于表示减1操做。++和--运算符既能够做为前缀运算符,也能够作为后缀运算符。双目整数运算符是:

运算符操做**+加-减*乘/除%取模&位与|位或^位异或<<左移>>右移(带符号)>>>添零右移

整数除法按零舍入。除法和取模遵照如下等式:(a/b)*b+(a%b)==a整数算术运算的异常是因为除零或按零取模形成的。它将引起一个算术异常。下溢产生零,上溢致使越界。例如:加1超过整数最大值,取模后,变成最小值。一个op=赋值运算符,和上表中的各双目整数运算符联用,构成一个表达式。整数关系运算符<,>,<=,>=,==和!=产生boolean类型的数据。

布尔运算符布尔(boolean)变量或表达式的组合运算能够产生新的boolean值。单目运算符!是布尔非。双目运算符&,|和^是逻辑AND,OR和XOR运算符,它们强制两个操做数求布尔值。为避免右侧操做数冗余求值,用户能够使用短路求值运算符&&和||。用户能够使用==和!=,赋值运算符也能够用&=、|=、^=。三元条件操做符?:和C语言中的同样。

浮点运算符浮点运算符能够使用常规运算符的组合:如单目运算符++、--,双目运算符+、-、*和/,以及赋值运算符+=,-=,*=,和/=。此外,还有取模运算:%和%=也能够做用于浮点数,例如:a%b和a-((int)(a/b)*b)的语义相同。这表示a%b的结果是除完后剩下的浮点数部分。只有单精度操做数的浮点表达式按照单精度运算求值,产生单精度结果。若是浮点表达式中含有一个或一个以上的双精度操做数,则按双精度运算,结果是双精度浮点数。

数组运算符数组运算符形式以下:<expression>[<expression>]可给出数组中某个元素的值。合法的取值范围是从0到数组的长度减1。取值范围的检查只在运行时刻实?copy;。

?reg;运算符?reg;以String对象实现。运算符"+"完成并?reg;操做,若是必要则自动把操做数转换为String型。若是操做数是一个对象,它可定义一个方法toString()返回该对象的String方式,例如floata=1.0print("Thevalueofais"+a+"\n");+运算符用到?reg;上的例子Strings="a="+a;+=运算符也能够用于String。注意,左边(下例中的s1)仅求值一次。s1+=a;//s1=s1+a//若a非String型,自动转换为String型。

对象运算符双目运算符instanceof测试某个对象是不是指定类或其子类的实例。例如:if(myObjectinstanceofMyClass){MyClassanothermyObject=(MyClass)myObject;…}是断定myObject是不是MyClass的实例或是其子类的实例。

强制和转换Java语言和解释器限制使用强制和转换,以防止出错致使系统崩溃。整数和浮点数?reg;间能够来回强制转换,但整数不能强制转换成数组或对象。对象不能被强制为基本类型。

3.1.6Java流控制

下面几个控制结构是从C语言借鉴的。

分支结构

if/else分支结构

if(Boolean){statemanets;}else{statements;}

switch分支结构

switch(expr1){caseexpr2:statements;break;caseexpr3:statements;break;default:statements;break;}

循环结构for循环结构

for(initexpr1;testexpr2;incrementexpr3){statements;}

While循环结构

While(Boolean){statements;}

Do循环结构

do{statements;}while(Boolean);

通常顺序控制

break[label]continue[label]reutrnexpr;label:statement;

for循环例子下面是一个程序例子,画几条线,分别用红,绿,蓝颜色,这段程序多是Java函数的一部分:

intcount;for(count=1;count<=12;count++){switch(count%3)}case0:setColor(Color.red);break;case1:setColor(Color.blue);break;case2:setColor(Color.green);break;}g.drawLine(10,count*10,80,count*10);}

3.2Java变量和函数的实例

Java的类包含变量和函数。数据变量能够是一?copy;原始的类型,如int,char等。成员函数是一?copy;可执行的过程。例如,下面程序里:publicclassClassOne{inti;publicClassOne(){i=10;}

publicvoidAdd_i(intj){i=i+j;}}

ClassOne包含一个变量i和两个成员函数,ClassOne(intfirst)和Add_i(intj)。

成员函数成员函数是一?copy;可被其它类或本身类调用的处理子程序。一个特殊的成员函数叫构造函数,这个函数名称通常与本类名程相同。它没有返回值。

构造函数和成员函数当你在Java里定义一个类时,你可定义一个或多个可选的构造函数,当建立本类的一个对象时用某一个构造函数来初始化本对象。用前面的程序例子来讲明,当ClassOne类建立一个新实例时,全部成员函数和变量被建立(建立实例)。构造函数被调用。ClassOnemc:mc=newClassOne();

关键词new用来建立一个类的实例,一个类用new初始化?reg;前并不占用内存,它只是一个类型定义,当mc对象初始化后,mc对象里的i变量等于10。你能够经过对象名来引用变量i。(有时称?reg;为实例变量)mc.i++;//mc实例变量加1由于mc有ClassOne类的全部变量和成员函数,咱们能够使用一样的语法来调用成员函数Add_i:Add_i(10);如今mc.i变量等于21.

结束函数Java并不支持析构函数(C++里的定义),由于java自己提?copy;对象无用时自动清除的功能,同时它也提?copy;了一个自动拉圾箱的成员函数,在清除对象时被调用:Protectedvoidfinalize(){close();}

3.3对象有效范围和废物自动回收

对象有必定的生命期并在它的生命期间使用资源,当一个对象再也不被使用时,它应释放内存,避免内存溢出。在Java里,收集和释放内存是一个叫自动废品回收站的线程的责任。这个线程监视对象有效范围并给一个走出有效范围的对象做上标识。

例如:Strings;//没有分配内存s=newString("oldstring");//分配内存s="newstring";//从新分配内存(建立新对象)

咱们将在之后访问String类时将更加明白它的工做过程,但它的快速工做过程是这样的:1.建立一个新的String类对象并填充以"oldstring"2.建立另外一个String对象并填充以"newstring"注意咱们建立了两个对象。Stirng对象"oldstring"Stirng对象"newstring"

在第三条语句里,第一个包括"oldstring"的叫作s的对象已走出了有效范围,没有任何方法能够再访问他,咱们如今有一个新的对象也叫s,包含"newstring"。在下一个废品回收线程,前一个对象将被标识并清除。

3.4子类

子类是利用存在的对象建立一个新对象的机制,好比,若是你有一个Horse类,你能够建立一个Zebra子类,Zebra是Horse的一种。

classZebraextendsHorse{intnumber_OF_stripes:}

关键词extends来定义对象有的子类.Zebra是Horse的子类。Horse类里的全部特征都将拷贝到Zebra类里,而Zebra类里能够定义本身的成员函数和实例变量。Zebra称为Horse的派生类或继承。另外,你也许还想覆盖基类的成员函数。用ClassOne说明,下面是一个派生类覆盖Add_i功能的例子.

importClassOne;publicclassNewClassextendsClassOne{publicvoidAdd_i(intj){i=i+(j/2);}}

当NewClass类的实例建立时,变量i初始化值为10,但调用Add_i产生不一样的结果。NewClassmnc;mnc=newNewClass();mnc.Add_i(10);

访问控制Java里当你建立一个新类时,你能够标明变量和成员函数的访问层次。

publicpublicvoidAnyOneCanAccess(){}public实例变量和成员函数能够任意其它类调用。

protectedprotectedvoidOnlySubClasses(){}protected实例变量和成员函数只能被其子类调用.

privateprivateStringCreditCardNumber;private实例变量和成员函数只能在本类里调用.

friendlyvoidMyPackageMethod(){}缺省的,若是没有定义任何防火控制,实例变量或函数缺省定义成friendly,意味着能够被本包里的任意对象防问,但其它包里的对象不可防问。

静态成员函数和变量有?copy;时候,你建立一个类,但愿这个类的全部实例都公用一个变量。也就是说,全部这个类的对象都只有实例变量的同一个拷贝。这种方法的关键词是static,例如:

classBlock{staticintnumber=50;}

全部从Block类建立的对象的number变量值都是相同的。无任在哪一个对象里改变了number的值,全部对象的number都跟着改变。一样的,你能够定义static成员函数,但这个成员函数不能访问非static函数和变量。

classBlock{staticintnumber=50;intlocalvalue;staticvoidadd_local(){localvalue++;//没有运行}staticvoidadd_static(){number++;//运行}}

3.5this和super

访问一个类的实例变量时,this关键词是指向这个类自己的指针,在前面ClassOne例子中,咱们能够增长构造函数以下:

publicclassClassOne{inti;publicClassOne(){i=10;}

publicClassOne(intvalue)this.i=value;}

publicvoidAdd_i(intj){i=i+j;}}

这里,this指向ClassOne类的指针。若是在一个子类里覆盖了父类的某个成员函数,但又想调用父类的成员函数,你能够用super关键词指向父类的成员函数。

importClassOne;publicclassNewClassextendsClassOne{publicvoidAdd_i(intj){i=i+(j/2);super.Add_i(j);}}

下面程序里,i变量被构造函数设成10,而后15,最后被父类(ClassOne)设成25。

NewClassmnc;mnc=newNewClass();mnc.Add_i(10);

3.6类的类型

至今为止,我用在类前面只用了一个public关键词,其实它有下面4种选择:

abstract一个abstract类必须至少有一个虚拟函数,一个abstract类不能直接建立对象,必须继承子类后才能。

final一个final类声明了子类链的结尾,用final声明的类不能再派生子类。

publicpublic类能被其它的类访问。在其它包里,若是想使用这个类必须先import,不然它只能在它定义的package里使用。

synchronicable这个类标识表示全部?copy;类的成员函数都是同步的。

3.7抽象类

面向对象的一个最大优势就是可以定义怎样使用这个类而没必要真正定义好成员函数。若是程序由不一样的用户实现时是颇有用的,这不需用户使用相同的成员函数名。

在java里Graphics类里一个abstract类的例子以下:publicabstractclassGraphics{publicabstractvoiddrawLine(intx1,inty1,intx2,inty2);publicabstractvoiddrawOval(intx,inty,intwidth,intheight);publicabstractvoiddrawRect(intx,inty,intwidth,intheight);...}

在Graphics类里声明了几个成员函数,但成员函数的实际代码是在另一?copy;地方实现的。

publicclassMyClassextendsGraphics{publicvoiddrawLine(intx1,inty1,intx2,inty2){<画线程序代码>}}

当一个类包含一个abstract成员函数,这个类必须定义为abstract类。然而并非abstract类的全部的成员函数都是abstract的。Abstract类不能有私有成员函数(它们不能被实现),也不能有静态成员函数。

3.8接口

当你肯定多个类的操做方式都很相象时,abstract成员函数是颇有用的。但若是你须要使用这?copy;abstract成员函数,必须建立一个新类,这样有时很繁琐。接口提?copy;了一种抽象成员函数的有利方法。一个接口包含了在另外一个地方实现的成员函数的收集。成员函数在接口里定义为public和abstract。接口里的实例变量是public,static和final。接口和抽象的主要区别是一个接口提?copy;了封装成员函数协议的方法而没必要强迫用户继承类。

例子:publicinterfaceAudiClip{//Startplayingtheclip.voidplay();//Playtheclipinaloop.voidloop();//Stopplayingtheclipvoidstop();}

想使用AudioClip接口的类使用implenents关键词来提?copy;成员函数的程序代码。classMyClassimplementsAudioClip{voidplay(){<实现代码>}voidloop<实现代码>}voidstop<实现代码>}}

优势一个接口类能够被任意多的类实现,每一个类能够共享程序接口而没必要关心其它类是怎样实现的。classMyOtherClassimplementsAudioClip{voidstop(){<实现代码>}...}

内部成员函数Java还提?copy;了调用C和C++函数的方法。用native关键词来定义C和C++的函数。

publicclassDate{intnow;publicDate(){now=time();}privatenativeinttime();

static{System.loadLibrary("time");}}

一?copy;Java代码写好后,就须要如下步骤执行:1.用javah来建立头文件(.h)2.用javah来建立stub文件3.用C和C++写native成员函数的代码4.编译stub文件和.C文件成一个动态可加载库5.用java运行java程序或appletviewer运行applet

注意:Native成员函数超出了类的范围。

3.9包(Packages)

包(Package)由一组类(class)和界面(interface)组成。它是管理大型名字空间,避免名字冲突的工具。每个类和界面的名字都包含在某个包中。按照通常的习惯,它的名字是由"."号分隔的单词构成,第一个单词一般是开发这个包的组织的名称。

定义一个编译单元的包编译单元的包由package语句定义。若是使用package语句,编译单元的第一行必须无空格,也无注释。其格式以下:packagepackageName;若编译单元无package语句,则该单元被置于一个缺省的无名的包中。

使用其它包中的类和界面在Java语言里提?copy;一个包能够使用另外一个包中类和界面的定义和实现的机制。用import关键词来标明来自其它包中的类。一个编译单元能够自动把指定的类和界面输入到它本身的包中。在一个包中的代码能够有两种方式来定义来自其它包中的类和界面:*在每一个引用的类和界面前面给出它们所在的包的名字;//前缀包名法acme.project.FooBarobj=newacme.project.FooBar();*使用import语句,引入一个类或一个界面,或包含它们的包。引入的类和界面的名字在当前的名字空间可用。引入一个包时,则该包全部的公有类和界面都可用。其形式以下://从acme.project引入全部类importacme.project.*;这个语句表示acme.project中全部的公有类被引入当前包。如下语句从acme.project包中进入一个类Employec_List。//从acme.project而引入Employee_Listimportacme.project.Employee_list;Employee_Listobj=newEmployee_List();在使用一个外部类或界面时,必需要声明该类或界面所在的包,不然会产生编译错误。

import(引用)类包(classpackage)用import关键词调入,指定package名字如路径和类名,用*匹配符能够调入多于一个类名。

importjava.Date;importjava.awt.*;

若是java源文件不包含package,它放在缺省的无名package。这与源文件同目录,类能够这样引入:importMyClass。

Java系统包:Java语言提?copy;了一个包含窗口工具箱,实用程序,通常I/O,工具和网络功能的包。

java.applet这个包包含量了一?copy;设计applet的类,用一个类Applet和三个接口.AppletContext;AppletStub;和AudioClip.

java.awt另外一个窗口工具箱包.awt,包含了一?copy;产生装饰物和GUI成员的类。这个package包括:Button,Checkbox,Choice,Component,Graphics,Menu,Pane1,TextArea和TextField。

java.ioI/Opackage包含文件输入/输出类,FileInputStream和FileOutputStream.

java.lang这个包包含Java语言类,包含:对象,线程,异常出口,系统,整数,原点,数学,字符等。

java.net这个类支持TCP/IP网络协议,并包含Socket类,URL和URL相联系的类。

java.util这个类包含一?copy;程序的同步类,它包含Date,Dictionary类等。

3.10异常

当在Java程序中发生一个错误时,例如:一个变元的值非法,代码会发现这个错误,并引起一个异常(exception)。在缺省的状况下,异常会输出一个错误消息,而后停止线程的执行。可是,程序本身能够定义异常处理段(exceptionhandler)来截获(catch)异常,并从错误中恢复。有一?copy;异常是由Java解释器在运行时刻引起的。实际上,任何类均可以定义属于本身的异常,并使用throw语句引起它们。一个throw(引起?copy;语句是由throw关键字和一个对象构成。按常规,该对象应该是Exception类的实例或其子类的实例。throw语句会引发执行转向相应的异常处理段。当一个throw语句执行时,它下面的全部代码再也不执行了,它所在的方法也再也不返回值。下面的例子将演示如何建立一个Exception的子类,而后引起一个异常。classMyExceptionextendsException{}classMyClass{voidoops(){if(/*不出现错误*/){…}else{/*出错*/

}else{/*出错*/thrownewMyException();}}}为了定义一个异常处理段,程序必须用try语句把可能产生异常的代码成组。在try语句后面跟上一个或多个catch(截获?copy;语句,每一个异常对应一个catch语句。每一个catch语句中包含着异常处理段。例如:try{p.a=10;}catch(NullPointerExceptione){println("pwasnull");}catch(Exceptione){println("othererrorsoccured");}catch(Objectobj){println("Whothrewthatobject?");}catch语句和一个方法定义相似,只不过该方法只有一个参数,且无返回类型。参数能够是一个类或一个界面。当一个异常发生时,嵌套的try/catch语句会寻找出与该异常类相匹配的参数。若是一个参数和指定异常匹配则:*该参数和指定的异常是同一个类,或*该参数是指定异常的子类,或*若是参数是一个界面,指定异常类实现了这个界面。第一个参数和异常匹配的try/catch语句,则与其匹配的catch语句执行。在catch语句执行完后,程序的执行被恢复。但已不可能恢复到异常发生处再次执行。例如:print("now");try{print("is");thrownewMyException();print("a");}catch(MyExceptione){print("the");}print("time\n");打印为"nowisthetime"。正如这个例子所示,异常应该主要用于错误处理,若用于其它方面会使代码晦涩难?reg;。异常处理段是能够嵌套的,容许异常处理能够发生在多个地方。嵌套异常处理一般用于当第一个处理程序没法彻底从错误中恢复过来的时候,而不得不执行一?copy;清除代码。为了把异常处理控制传递给更高层的处理段,能够再一次对截获对象实?copy;throw操做。注要再次实?copy;throw异常的方法,throw语句执行完后,会终止执行。try{f.open();}catch(Exceptione){f.close();throwe;}

定局语句finally(定局?copy;语句是用于保证不管在异常是否发生的状况下,某?copy;代码被执行。下例说明finally语句的用法:try{//作某?copy;动做;}finally{//此后清除;}和如下代码相似try{//作某?copy;动做}catch(Objecte){//此后清除;throwe;}

}//此后清除;即便try块中包含return,break,continue,throw语句,finally语句也会被执行。例如:下面的代码"finally"老是被输出,而"aftertry"仅在a!=10时被输出。try{if(a==10){return;}}finally{print("finally\n");}print("aftertry\n");

运行时刻异常本节列出的清单是Java解释器引起的各类异常。当运行时刻发现各类错误,由解释器引起异常。

ArithmeticException若是程序试图除0,或用0取模,会产生ArithmeticException(算术异常?copy;,其它算术操做不会产生异常。有关Java如何处理其它算术错误的信息,见"整数运算符"和"浮点运算符"两节。例如:下面的代码将会引起ArithmeticException异常:classArith{publicstaticvoidmain(Stringargs[]){intj=0;j=j/j;}}

NullPointerException当程序试图访问一个空对象中的变量或方法,或一个空数组中的元素时则引起NullPointerException(空指针异常?copy;。例如,访问长度为0的数组a[0]。有如下类声明,运行时会引起NullPointerException异常:classNull{publicstaticvoidmain(Stringargs[]){Stringo=null;inta[]=null;o.length();a[0]=0;}}有趣的是,若是咱们引起一个空对象,也会产一NullPointerException异常。

IncompatibleClassChangeException当一个类的定义被改变,而引用该类的其它类没有被从新编译时,会产生这一异常。有四种类更改会致使运行时刻引起IncompatibleClassChangException异常。*一个类中的变量声明由static变成非static,而其它访问该类这一变量的类没有被从新编译。*一个类中的变量声明由非static变成static,而其它访问该类这一变量的类没有被从新编译。*类中声明的某个域被删除,而其它访问该域的类没有被从新编译。*类中声明的某个方法被删除,而其它访问该方法的类没有被从新编译。

ClassCastException若是试图把对象o强制成ClassC,而o既不是ClassC的实例,也不是ClassC子类的实例,这时便会产生ClassCastException。classClassCast{publicstaticvoidmain(Stringargs[]){Objecto=newObject();Strings=(string)o;s.length();}}

}

NagativeArraySizeException若是一个数组的长度是负数,则会引起NagativeArraySizeException(数组负下标?copy;异常。例以下面类定义的代码在运行时引起这一异常:classNegArray{publicstaticvoidmain(Stringargs[]){inta[]=newint[-1];a[0]=0;}}

OutOfMemoryException当系统没法再向应用程序提?copy;内存时,会引起OutOfMemoryException(内存溢出?copy;异常。这种异常只能出如今建立新对象的时候,即new被调用的时候。例如,下面一段代码在运行时刻会引起OutOfMemoryException异常:classLink{inta[]=newint[1000000];Linkl;}ClassOutOfMem{publicstaticvoidmain(Stringargs[]){

publicstaticvoidmain(Stringargs[]){Linkroot=newlink();Linkcur=root;while(true){cur.l=newLink();cur=cur.l;}}}

NoClassDefFoundException若是一个类被引用,但在运行时刻,系统没有找到被引用的类,这时会引起NoClassDefFoundException(未找到类定义?copy;异常。例如,NoClass类的声明以下:classNoClass{publicstaticvoidmain(Stringargs[]){Cc=newC();}}当NoClass运行时,若是解释器找不到C类,则会产生NoClassDefFoundException。注意,在NoClass被编译时C类必定要存在。

IncompatibleTypeException若是试图为一界面做实例,则会引起IncompatibleTypeException(类型不兼容?copy;异常。例如,下面的代码会引起一个IncompatibleTypeException。InterfaceI{}classIncompType{publicstaticvoidmain(Stringargs[]){Ir=(I)new("I");}}

ArrayIndexOutOfBoundsException试图访问数组中的一个非法元素时,会引起ArrayIndexOutOfBoundsException(数组索引越界?copy;异常。例如:ClassArrayOut{publicstaticvoidmain(Stringargs[]){inta[]=newint[0];a[0]=0;}}

publicstaticvoidmain(Stringargs[]){inta[]=newint[0];a[0]=0;}}UnsatisfiedLinkException若是一个方法被声明为本机,但该方法在运行时刻却不能链接到一个例程体上去时,会产生UnsatisfiedLinkException(没法链接?copy;异常。例如:ClassNoLink{staticnativevoidfoo();publicstaticvoidmain(Stringargs[]){foo();}}

InternalExceptionInternalException(内部?copy;异常是不能被引起的。只有在运行失败做一致性检查时,才会引起这个异常。

本章小结

1.Java语言的基本结构象C/C++。2.Java语言的源程序代码由一个或多个编译单元(compilationunit)组成。

第四章Java应用程序的基本框架

学习一门新语言最好是先看几个简单的程序例子。下面咱们将看到几个很是基本的程序例子。

4.1Java应用程序的运行环境

Java应用程序是指能够独立运行在Java虚拟机上的程序,它是一种中间代码(byte-code?copy;。好比你的应用程序叫my.java,程序里有一个名称为app1的类,用Javac或其它编译器,编译后将会生成app1.class,则在命令行状态下输入:javaapp1就能够运行此程序。注意,用java命令运行的类必须有main函数,不然不能执行。与普通java应用程序不一样的另外一种另外一种Java程序叫JavaApplet。咱们把它译成Java小程序,这种程序后缀也是.class,但它不能直接在java虚拟机上运行,也就是输入java*.class不能运行,这种程序里能够没有main函数,它必须由某个浏览器来运行,好比Appletviewer或Netscape2.02以上等。这种程序咱们将在后面章节中介绍。

4.2最简单的Java程序解释

让咱们来看一看最简单的Java应用程序例子,来理解一下它的结构:Filename:1.java

classmyfirst{publicstaticvoidmain(Stringargs[]){System.out,println("ThisismyfirstJavaApplication");}}

这就是一个完整的Java应用程序,将它编译:Javac1.java在当前目录下,它将生成myfirst.class文件,Javamyfirst屏幕上将会输出:ThisismyfirstJavaApplication

让咱们来一步一步分析每句话含义(1?copy;classmyfirst这一行用关键词class来定义名为myfirst的新类,myfirst是新类的名称,必须是一个有效的标识符,有效标识符定义请见程序设计基础章节。类的说明包括数听说明和成员函数说明,都放在类后面的大括号里面。通常类定义以下:class类名称{数据定义;函数定义;}

(2)publicstaticvoidmain(Stringargs[])public是一个表示访问权限的关键字,表示此成员函数是公有的,能够被其余类直接调用,包括java解释器。相对应的关键字有private和protected,friend。private表示只能被本类访问,protected表示只能被子类访问,friend是缺省的访问权限,表示能被本包(package)中任意类访问,对其它包中的类是不可访问的。static表示main成员函数在myfirst类的全部对象中是惟一的,所以若是本程序生成另外一个myfirst类对象,调用的main函数将是同一个函数。void表示main函数没有返回值,若是有返回类型值,则可加上interger或boolean诸如此类,对于有返回值的函数,其函数实体的最后应加上return语句。main这个函数是这运行应用程序的入口点,所以编写应用程序是必须有main()函数,且是惟一的。

(3?copy;System.out.println一句这一句是main函数里的功能语句,是调用Java里System包里的out类的println成员函数,是标准输入输出。

4.3Java应用程序参数的传递

Java语言很相似C和C++语言。在C语言里,经过在命令行输入参数,C程序可由main函数读入这?copy;参数,java程序也同样,请看下面程序:Filename:2.java

classMy2{publicstaticvoidmain(Stringargs[]){intarc=args.length;if(arc>0){for(inti=0;i<arc;i++)System.out.println(args[i])}else{System.out.println("Applicationhavenoargs!");}}}

编译:javac2.java将在本目录下生成My2.class文件。

运行:javaMy2输出:Applicationhavenoargs!

运行:javaMy2arg1arg2arg3输出:arg1arg2arg3这说明,java里参数的传递是这样的,命令行里类名后第一个参数放在args[0]里,第二个参数放在args[1]里,以此类推。

4.4创建窗口Java程序基本框架

Frame介绍在Java语言里,Frame类的功能是建立图形用户界面(GUI)的基本窗口。在典型浏览器里,Frame类是显示GUI的父类。

类的层次结构以下:java.lang.Objectjava.awt.Componentjava.awt.Containerjava.awt.Windowjava.awt.Frame

Frame是从没有边框和菜单条的空白窗口?copy;充来的。这种窗口主要用在弹出式窗口方式下。Frame给窗口一个边框,一个布局和一个窗口条。

你能够经过建立本身的多个Frame来建立图形用户界面的应用程序。咱们已知道怎样创建独立的java应用程序,那么怎样将各类迷人的图形结合在一块呢?咱们经过创建一个Frame来实现这个功能。Frame让咱们访问窗口,和applet工做区有许多相同的地方。下面是一个小的应用程序,它弹出一个框架而且在框架里显示信息。(信息能够是命令行参数,也能够是缺省的信息?copy;。若是你在窗口里点一下,程序退出。

基本框架程序importjava.awt.*;

/**Memo.java*Asimplestand-alonegraphicalapplication*/

publicclassMemoextendsFrame{publcStringmotd;

publicMemo(Strings){//setourtitlesuper("MemoFrame");

motd=s;resize(300,300);}

publicMemo(){this("ThisisaMemo");}

publicvoidpaint(Graphicsg){g.drawString(motd,15,15);g.drawString("ClickanywheretoExit",15,25);}

publicvoidstart(){show();}

publicbooleanmouseDown(Evente,intx,inty){//hidethewindowhide();//freeupsystemresourcesdispose();//Quittheapp.System.exit(0);returnfalse;}

publicstaticvoidmain(Stringargs[]){Memom;if(args.length>){m=newMemo(args[0]);}else{m=newMemo();}m.start();}}

将Frame联系起来l熟悉的函数paint()和mouseDown(),看起来有点眼熟。这?copy;函数与applet的函数同样。实际上,一个frame包含各类GUI组件与applet的形式同样。另外一个熟悉的函数是start()。这个函数并没必要要,由于咱们没有覆盖任何已存在的函数。但你想继承applet编程的风格,你仍是能够使用start(),stop(),init()与destroy()等函数。

l新函数咱们熟悉的函数start()调用了show()。show()函数是Window类的继承,它显示Fame及其全部组件。在mouseDown()函数里咱们看到两个函数:hide()和dispose()。hide()只简单地使Frame不可见。你能够在任什么时候候调用它来隐藏窗口。dispose()函数释放由Frame占有的系统资源。只有在你不须要Frame时才调用它。

l构造函数Memo例子还包含了一个新函数Memo()。其实这个类有两个Memo()函数!任何与类名相同的函数都是构造函数。它在建立一个新对象时被调用。你能够认为它是一个对象的init()初始化函数。为何须要两个构造函数呢?有了两个构造函数,咱们能够利用多态性的优势,有两种方法建立一个新Memo对象。咱们能够简单的建立使用缺省信息的Memo对象:m=newMemo();或者,咱们能够本身提?copy;信息:m=newMemo("Ourmessage");

Frame控制本程序的最后功能是在main()里创建并显示frame。它由如下两步实现://Step1m=newMemo();

//Step2m.start();第一步初始化memo对象。咱们象操做其它对象同样操做m。为显示frame,咱们须要调用show()。这在第二步里实现。另外一个要注意的是程序的健壮性:咱们基本的frame里没有包含处理标准WINDOW_DESTROY消息的函数。这样,你将不能在窗口的控制菜单里选中"Quit"或"Exit"。为了增长这个功能,你须要增长如下的处理程序:

publicbooleanhandleEvent(Evente){if(e.id==Event.WINDOW_DESTROY){dispose();System.exit(1);returntrue;}else{//Goaheadanddowhatwenormallywouldhavedonereturnsuper.handleEvent(e);}}

菜单图形界面依靠菜单来指导用户操做。设计独立的Java应用程序时,Java提?copy;建立和使用菜单的直接方法。象其它组件同样,new将建立一个菜单:

MenuoptionMenu;optionsMenu=newMenu("Options");

菜单项一?copy;你建立了一个菜单,你能够使用add()来组建菜单项:

optionsMenu.add(newMenuItem("Option1");optionsMenu.add(newMenuItem("Option2");

菜单事件当你选中某个菜单项时,你建立此菜单项的字符?reg;将在事件中返回。你能够象测试按钮选择同样测试菜单选择:

publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofMenuItem){System.out.println((String)arg);}...}

其它菜单项除了上面描述的简单菜单项外,你还可增长CheckBox菜单项,分割线,以及子菜单。下面是一?copy;例子:Menum,n;m=newMenu("Examples");m.add(newMenuItem("Basic"));m.add(newMenuItem("Simple"));

//addaseparatorm.add(newMenuItem("-"));

//addaCheckboxitemm.add(newCheckboxMenuItem("Check"));

//addasubmenun=newMenu("MoreExamples");n.add(newMenuItem("SubBasic"));n.add(newMenuItem("SubSimple"));m.add(n);

菜单条你建立好菜单后,你应将?reg;放在应用程序的菜单条上:mb=newMenubar();

mb.add(m);mb.add(optionsMenu);

而后你可为applet设置菜单条:setMenuBar(mb);

4.5独立应用程序例子

为了看一个更复杂的独立的图形界面应用程序,下面有一个数字转换的例子:

importjava.awt.*;

publicclassd2xextendsFrame{intdecimalValue=0;StringbaseXValue=newString("0");TextFielddDisplay,xDisplay;

//d2xconstructorpublicd2x(){super("DecimalConverter");//setthetitleoftheframeMenuBarmb=newMenuBar();Buttond2Binary=newButton("Binary");Buttond2Octal=newButton("Octal");Buttond2Hex=newButton("Hex");Buttond2Base36=newButton("Base36");Panelp1=newPanel();Panelp2=newPanel();Panelp3=newPanel();

//addasimplemenuMenum=newMenu("Application");m.add(newCheckboxMenuItem("Base36Active");m.add(newMenuItem("Exit"));

//addmenutomenubarmb.add(m);setMenuBar(mb);//installthismenubarintheframe

//Addbuttonstotheirownpanelp3.setLayout(newFlowLayout());p3.add(d2Binary);p3.add(d2Octal);p3.add(d2Hex);p3.add(d2Base36);

//AddtextfieldsLabeldLabel=newLabel("EnterDeecimal:");LabelxLabel=newLabel("ConvertedValue:");dDisplay=newTextField(integer.toString(decimalValue),7);xDisplay=newTextField(baseXValue,32);xDisplay.setEditable(false);p1.setLayout(newFlowLayout(FlowLayout.LEFT));p2.setLayout(newFlowLayout(FlowLayout.LEFT));p1.add(dLabel);p1.add(dDisplay);p2.add(xLabel);p2.add(xDisplay);

//Addthepanelsadd("North",p1);add("Center",p2);add("South",p3);}//endd2xconstructor

publicvoidstart(){resize(400,150);show();}

publicvoidupdateXDisplay(){xDisplay.setText(baseXValue);}

publicbooleanhandleEvent(Eventevt){if(evt.targtintanceofMenuItem){if("Exit".equals(((MenuItem)evt.target).getLabel())){hide();dispose();System.exit(0);returnfalse;}retruntrue;}elseif(evt.targetinstanceofButton){Stringwhick=((Button)evt.target).getLabel();if(whick.equals("Binary")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,2);}if(whick.equals("Octal")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,8);}if(whick.equals("Hex")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,16);}if(whick.equals("36")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,36);}updateXDisplay();returntrue;}returnfalse;}

publicstaticvoidmain(Stringargs[]){d2xm=newd2x();m.start();}}

输出结构如图:

本章小结:

1.java有两种类型的应用程序,一种是直接运行在java虚拟机上,用java命令执行;另外一种运行在浏览器里,由浏览器调用执行,通常称它为Applet小程序。本书主要讨论第一种应用程序。2.java应用程序是由类组成的,并且用java命令行执行的类必须有main入口函数。3.与C语言类似,java程序也可由命令行传递给main函数参数。4.基本窗口java程序的基本类是Frame。利用它能够很方便地创建图形用户界面程序。

第五章Java的类

类是Java语言面向对象编程的基本元素,它定义了一个对象的结构和行为。在Java程序里,你要表达的概念封装在某个类里。一个类定义了一个对象的结构和它的功能接口,功能接口称为成员函数。当Java程序运行时,系统用类的定义建立类的实例,类的实例是真正的对象。类定义的通常形式以下:

classclassnameextendssuperclassname{typeinstance-variable1;typeinstance-variable2;.................................typeinstance-variableN;typemethodname1(parameter-list){method-body;}typemethodname2(parameter-list){method-body;}....................................................typemethodnameN(parameter-list){method-body;}}

这里,classname和superclassname是合法的标识符。关键词extends用来代表classname是superclassname派生的子类。有一个类叫作Object,它是全部Java类的根。若是你想定义Object的直接子类,你能够省略extends子句,编译器会自动包含它。下面是一个简单的类的定义。

classUniversity{}

5.1对象实例

类名能够做为变量的类型来使用,若是一个变量的类型是某个类,那么它将指向这个类的实例,称为对象实例。全部对象实例和它们的类型(某个类?copy;的子类的实例都是相容的。就象能够把byte型的值赋给int型的变量同样,你能够把Object的子类的任何实例赋给一个Object型的变量。一个实例是类模板的单独的拷贝,带有本身的称为实例变量的数据集。每一个实例也能够做为一个对象。当你定义一个变量的类型是某个类时,它的缺省值是null,null是Object的一个实例。对象null没有值,它和整数0不一样。下面这个例子中,声明变量u的类型是类University。

Universityu;

这里,变量u的值是null。

5.2实例变量

Java经过在类定义的大括号里声明变量来把数据封装在一个类里。这里的变量称为实例变量。下面的例子定义了一个叫作University的类,它有两个实例变量:name和city。

classUniversity{Stringname,city;}

5.3new操做符

操做符new用来生成一个类的实例,下面这个例子生成了类University的一个实例,存放在变量u中。

Universityu=newUniversity();

在此例中,变量u指向这个对象,但并不真正包含这个对象。你能够用多个变量指向同一个对象。下面的例子中,建立了一个University的对象,但建立了两个指向它的变量。

Universityu=newUniversity();Universityu2=u;

对u2所指向的对象的任何改动都会对u所指向的对象起做用,由于它们是同一个对象。对u和u2的赋值只是把它们指向这个对象,既没有分配内存,也没有复制这个对象的任何部分。对u的再赋值只是简单地去掉了u和原来对象的联系,并不影响对象自己,下面的例子说明了这种状况。

Universityu=newUniversity();Universityu2=u;u=null;

尽管u被赋值为null,u2仍指向原来由操做符new建立的对象。在前面的例子里,咱们生成了一个对象而且指向了它两次。这就容许两个变量改变同一个对象。建立一个新的对象时,可直接对它的实例变量赋值。每一个对象都有它所属类的实例变量的拷贝,每一个对象的实例变量都是和其余对象的实例变量分离的,因此改变一个对象的实例变量不会影响其余对象的实例变量。下面的例子建立了两个University的对象,并对它们分别赋值:

classTwoUniversity{publicstaticvoidmain(Stringargs[]){Universityu1=newUniversity();Universityu2=newUniversity();u1.name="北?copy;大学";u1.city="北?copy;";u2.name="清华大学";u2.city="北?copy;";System.out.println("大学:"+u1.name+"城市:"+u1.city);System.out.println("大学:"+u2.name+"城市:"+u2.city);}}

这个例子建立了两个University的对象,而且对它们的name、city分别赋了不一样的值,这说明这两个对象是真正分离的。下面是该程序运行后的输出结果。

C:\>javaTwoUniversity大学:北?copy;大学城市:北?copy;大学:清华大学城市:北?copy;

5.4点(.?copy;操做符

点(.?copy;操做符用来接收一个对象的实例变量和成员函数。下面是用点操做符来接收实例变量的通常形式。

objectreference.variablename

这里objectreference是一个对象实例,variablename是这个对象里你想接收的实例变量。下面的程序段说明了怎样用点操做符来给实例变量赋值。

u.name="北?copy;大学";u.city="北?copy;";

下面说明怎样用点操做符来获得实例变量的值。

System.out.println("大学:"+u.name+"城市:"+u.city);

经过向类University里加入一个成员函数main,咱们建立了一个完整的例子,它使用了new操做符来建立一个University,用点操做符来赋值,而后打印结果。

classUniversity{Stringname,city;publicstaticvoidmain(Stringargs[]){Universityu=newUniversity();u.name="北?copy;大学";u.city="北?copy;";System.out.println("大学:"+u.name+"城市:"+u.city);}}

运行这个程序后,就会获得下面的结果。

C:\>javaUniversity大学:北?copy;大学城市:北?copy;

5.5成员函数定义

成员函数,是类的功能接口,是类定义里的一个子程序,在类的定义里和实例变量处于同一级别。你必须经过一个类的实例来调用成员函数。成员函数能够不用点操做符而直接使用实例变量。成员函数带有输入参数,具备某种类型的返回值。成员函数定义的通常形式以下:

typemethodname(formal-parameter-list){method-body;}

这里type指的是成员函数的返回值的类型,若是没有返回值,就用无值(void?copy;类型。methodname能够是任何合法的标识符,但不能与当前的类名相同。formal-parameter-list是用逗号分隔的类型、标识符对的序列。若是没有参数,括号里就是空的。仍是用咱们的University的例子,下面的成员函数用来初始化两个实例变量。成员函数是在类的大括号?reg;内定义的,和实例变量所处的范围相同。

classUniversity{Stringname,city;voidinit(Stringa,Stringb){name=a;city=b;}}

注意,咱们这里直接给name和city赋值,而没有象之前那样用u1.name。这是由于每一个成员函数都在类的个别实例内执行。咱们建立的类的实例具备它本身的实例变量,因此成员函数可直接使用它们。

5.6成员函数调用

能够用点(.?copy;操做符来调用一个类的实例的成员函数。成员函数调用的通常形式以下:

objectreference.methodname(parameter-list);

这里,objectreference是指向某个对象的变量,methodname是objectreference所属类的一个成员函数,parameter-list是用逗号分隔的变量或表达式的序列,它们要与该成员函数的定义的参数个数及类型匹配。在这个例子里,咱们能够对任何University对象调用成员函数init来给name和city赋值。下面的程序段说明了怎样完成这个工做。

Universityu=newUniversity();u.init("北?copy;大学","北?copy;");

这个例子建立了University的一个实例,存放在u中。经过点操做符来调用这个实例的init成员函数,把"北?copy;大学"和"北?copy;"分别传递给参数a和b。在init成员函数内部,name和city直接指向u所指向的对象的实例变量。把name赋值为"北?copy;大学",city赋值为"北?copy;",而后返回。在这个例子里,init被定义为无值(void?copy;返回类型。在进行这个成员函数调用后,u指向这个name值和city值改变了的University对象。

5.7this

Java有一个特殊的实例值叫this,它用来在一个成员函数内部指向当前的对象。在前面的例子里,咱们调用u.init,一?copy;进入init成员函数内部,this就会指向u所指向的对象。在Java里,在同一个范围定义两个相同名字的局部变量是不能够的。有趣的是,局部变量、成员函数的参数能够和实例变量的名字相同。前面咱们没有用name和city做为成员函数init的参数名字,由于这样它们在成员函数的范围里就把实例变量name和city隐藏了,即name指向参数name,隐藏了实例变量name。this让咱们能够直接指向对象自己。下面是另外一个版本的init,用name和city做为参数名字,用this来接收当前对象的实例变量。

voidinit(Stringname,Stringcity){this.name=name;this.city=city;}

下面是带有新的init初始成员函数的TwoUniversity例子。

classUniversity{Stringname,city;voidinit(Stringname,Stringcity){this.name=name;this.city=city;}}

classTwoUniversityInit{publicstaticvoidmain(Stringargs[]){Universityu1=newUniversity();Universityu2=newUniversity();u1.init("北?copy;大学","北?copy;");u2.init("清华大学","北?copy;");System.out.println("大学:"+u1.name+"城市:"+u1.city);system.out.println("大学:"+u2.name+"城市:"+u2.city);}}

5.8构造函数(Constructor?copy;

每建立一个类的实例都去初始化它的全部变量是乏味的。若是一个对象在被建立时就完成了全部的初始工做,将是简单的和简洁的。所以,Java在类里提?copy;了一个特殊的成员函数,叫作构造函数(Constructor?copy;。一个构造函数是对象被建立时初始对象的成员函数。它具备和它所在的类彻底同样的名字。一?copy;定义好一个构造函数,建立对象时就会自动调用它。构造函数没有返回类型,即便是void类型也没有。这是由于一个类的构造函数的返回值的类型就是这个类自己。构造函数的任务是初始一个对象的内部状态,因此用new操做符建立一个实例后,马上就会获得一个清楚、可用的对象。下面这个例子里,用构造函数取代了成员函数init。

classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}}

classUniversityCreate{publicstaticvoidmain(Stringargs[]){Universityu=newUniversity("北?copy;大学","北?copy;");System.out.println("大学:"+u.name+"城市:"+u.city);}}

new语句中类名后的参数是传给构造函数的。

5.9成员函数重载

对于几个意义相近的成员函数,有时使用相同的名字便于理解。所以,Java语言实现了成员函数重载,便可以建立几个名字相同、参数不一样的成员函数。成员函数重载提?copy;了Java的多态行为。下面的例子用到了重载。

classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}University(){name="北?copy;大学";city="北?copy;";}}

classUniversityCreateAlt{publicstaticvoidmain(Stringargs[]){Universityu=newUniversity();System.out.println("大学:"+u.name+"城市:"+u.city);}}

这个例子建立了一个University对象,调用了第二个构造函数。下面是它的运行结果。

C:\>javaUniversityCreateAlt大学:北?copy;大学城市:北?copy;

一个构造函数能够调用另外一个构造函数来建立实例。例如:

classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}University(){this("北?copy;大学","北?copy;");}}

第二个构造函数调用了第一个构造函数来完成实例的初始化。你也能够用重载来建立通常的成员函数。下面这个例子里有University类的两个版本的samecity成员函数。samecity判断一个大学是否在一个城市里或一个大学和另外一个大学是否在同一个城市里。一个成员函数用city做参数,另外一个用University对象做参数。

classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}booleansamecity(Stringcity){if(city.equals(this.city))returntrue;elsereturnfalse;}booleansamecity(Universityu){returnsamecity(u.city);}}

classUniversityCity{publicstaticvoidmain(Stringargs[]){Stringcity="上海";Universityu1=newUniversity("北?copy;大学","北?copy;");Universityu2=newUniversity("清华大学","北?copy;");System.out.println("u1="+u1.name+","+u1.city);System.out.println("u2="+u2.name+","+u2.city);System.out.println("city="+city);System.out.println("u1.samecity(u2)="+u1.samecity(u2));System.out.println("u1.samecity(city)="+u1.samecity(city));}}

下面是该程序的运行结果。

C:\>javaUniversityCityu1=北?copy;大学,北?copy;u2=清华大学,北?copy;city=上海u1.samecity(u2)=trueu1.samecity(city)=false

5.10继承

第二个基本的面向对象机制是继承。继承是关于有层次关系的类?reg;间的概念。一个类的后代能够继承它的祖先的全部变量和成员函数,就象建立本身的同样。一个类的直接父亲叫作它的超类(superclass?copy;。一?copy;你建立了一个象University这样的类,建立它的子类是很简单的。一个类的子类是它的继承了实例变量和成员函数的特殊的版本。在这个例子里,咱们把University类派生为含有叫作country的第三个元素的子类。

classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname,Stringcity,Stringcountry){this.name=name;this.city=city;this.country=country;}UniversityWorld(){this("北?copy;大学","北?copy;","中国");}}

关键词extends用来表示咱们要建立University的子类。name和city不需再在UniversityWorld中进行声明,由于它们是从University中继承的。Java容许在UniversityWorld中声明变量name和city,但这会隐藏University中的name和city,是与使用子类的目的相矛盾的,应当避免。在UniversityWorld的实例中,name、city和country的地位是同样的。

5.11super在UniversityWorld的例子里,有一段代码和它的超类University的重复,这段代码是初始化name和city的,

this.name=name;this.city=city;

就象在University例子中用this指向第一个构造函数同样,在Java里有另外一个变量叫作super,它直接指向超类的构造函数。下面这个例子用super来初始化变量name和city,而后打印出这个对象的内容。

classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname,Stringcity,Stringcountry){super(name,city);//调用了构造函数University(name,city)this.country=country;}publicstaticvoidmain(Stringargs[]){UniversityWorldu=newUniversityWorld("北?copy;大学","北?copy;","中国");System.out.println("大学:"+u.name+"城市:"+u.city+"国家:"+u.country);}}

下面是运行结果。

C:\>javaUniversityWorld大学:北?copy;大学城市:北?copy;国家:中国

5.12成员函数的覆盖

这个University的新的子类继承了它的超类的成员函数samecity。但这个成员函数samecity判断的是两个城市的名字,这是不够的,由于有可能两个两个名字同样的城市属于不一样的国家,咱们要用同时判断城市和国家的成员函数来覆盖它。下面就是实现覆盖的例子。

classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}booleansamecity(Stringcity){if(city.equals(this.city))returntrue;elsereturnfalse;}booleansamecity(Universityu){returnsamecity(u.city);}}

classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname,Stringcity,Stringcountry){super(name,city);this.country=country;}booleansamecity(Stringcity,Stringcountry){if(city.equals(u.city)&&country.equals(u.country))returntrue;elsereturnfalse;}booleansamecity(UniversityWorldother){returndistance(other.city,other.country);}}

classUniversityWorldCity{publicstaticvoidmain(Stringargs[]){Stringcity="上海";Stringcountry="中国";UniversityWorldu1=newUniversityWorld("北?copy;大学","北?copy;","中国");UniversityWorldu2=newUniversityWorld("清华大学","北?copy;","中国");System.out.println("u1="+u1.name+","+u1.city+","+u1.country);System.out.println("u2="+u2.name+","+u2.city+","+u2.country);System.out.println("city="+city+",country="+country);System.out.println("u1.samecity(u2)="+u1.samecity(u2));System.out.println("u1.samecity(city,country)="+u1.samecity(city,country));}}

下面是输出结果。

C:\>javaUniversityWorldCityu1=北?copy;大学,北?copy;,中国u2=清华大学,北?copy;,中国city=上海,country=中国u1.samecity(u2)=trueu1.samecity(city,country)=false

5.13动态成员函数发送

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

classA{voidcallme(){System.out.println("在A的callme成员函数里");}}

classBextendsA{voidcallme(){System.out.println("在B的callme成员函数里");}}

classDispatch{publicstaticvoidmain(Stringargs[]){Aa=newB();a.callme();}}

有趣的是,在成员函数main里,咱们把变量a声明为类型A,而后把类B的一个实例存放到它上面。咱们在a上调用成员函数callme,Java编译器肯定在类A确实有成员函数callme,可是在运行时,因为a事实上是B的实例,因此调用B的callme,而不调用A的。下面是运行结果:

C:\>javaDispatch在B的callme成员函数里

5.14final

在缺省状况下,全部的成员函数和实例变量均可以被覆盖。若是你但愿你的变量或成员函数再也不被子类覆盖,能够把它们声明为final。这意味着未来的实例都依赖这个定义。例如:

finalintFILE_NEW=1;finalintFILE_OPEN=2;finalintFILE_SAVE=3;fianlintFILE_SAVEAS=4;finalintFILE_QUIT=5;

final变量用大写标识符是一个通常的约定。

5.15静态

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

classStatic{staticinta=3;staticintb;staticvoidmethod(intx){System.out.println("x="+x);System.out.println("a="+a);System.out.println("b="+b);}static{System.out.println("静态初始块");b=a*4;}publicstaticvoidmain(Stringargs[]){method(42);}}

一?copy;这个类被调用,全部的静态变量都被初始化,a被赋为3,而后运行static块,这将打印出一段消息,而且把b赋为a*4,即12。而后解释器调用main成员函数,它调用了成员函数method,参数x为42。这三个println语句打印了两个静态变量a、b和局部变量x。下面是运行结果:

C:\>javaStatic静态初始块x=42a=3b=12

一个静态成员函数能够经过它所属的类名来调用。象调用实例变量同样,你能够用点操做符经过类名来调用静态成员函数和静态变量。Java就是这样实现了全局函数和全局变量。下面的例子里,咱们建立了带有一个静态成员函数和两个静态变量的类。第二个类能够经过名字直接来调用第一个类的静态成员函数和静态变量。

classstaticClass{staticinta=42;staticintb=99;staticvoidcallme(){System.out.println("a="+a);}}

classStaticByName{publicstaticvoidmain(Stringargs[]){StaticClass.callme();System.out.println("b="+staticClass.b);}}

下面是运行结果:

C:\>javastaticByNamea=42b=99

5.16抽象

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

abstractclassA{abstractvoidcallme();voidmetoo(){system.out.println("在A的metoo成员函数里");}}

classBextendsA{voidcallme(){System.out.println("在B的callme成员函数里");}}

classAbstract{publicstaticvoidmain(Stringargs[]){Aa=newB();a.callme();a.metoo();}}

下面是运行结果:

C:\>javaAbstract在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;、文本域(TextField?copy;、文本区(TextArea?copy;。

6.1面板

面板提?copy;了创建应用程序的空间。你能够把图形元件(包括其余面板?copy;放在一个面板上。Applet类提?copy;了一个基本的面板。

6.1.1布局管理

Java提?copy;了几种布局:顺序布局(FlowLayout?copy;、边界布局(BorderLayout?copy;和网格布局(GridLayout?copy;。

6.1.1.1顺序布局

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

importjava.awt.*;importjava.applet.Applet;

publicclassmyButtonsextendsApplet{Buttonbutton1,button2,button3;publicvoidinit(){button1=newButton("肯定");button2=newButton("打开");button3=newButton("关闭");add(button1);add(button2);add(button3);}}

该程序生成的布局以下:

图6.1

6.1.1.2边界布局

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

importjava.awt.*;importjava.applet.Applet;

publicclassbuttonDirextendsApplet{

ButtonbuttonN,buttonS,buttonW,buttonE,buttonC;

publicvoidinit(){setLayout(newBorderLayout());buttonN=newButton("?reg;");buttonS=newButton("火");buttonE=newButton("木");buttonW=newButton("金");buttonC=newButton("土");add("North",buttonN);add("South",buttonS);add("East",buttonE);add("West",buttonW);add("Center",buttonC);}}

下面是该程序运行的结果:

图6.2

6.1.1.3网格布局

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

importjava.awt.*;importjava.applet.Applet;

publicclassbuttonGridextendsApplet{Buttonbutton1,button2,button3,button4,button5,button6,button7,button8;

publicvoidinit(){setLayout(newGridLayout(4,2));button1=newButton("乾");button2=newButton("坤");button3=newButton("艮");button4=newButton("震");button5=newButton("坎");button6=newButton("离");button7=newButton("巽");button8=newButton("兑");add(button1);add(button2);add(button3);add(button4);add(button5);add(button6);add(button7);add(button8);}}

下面是该程序运行的结果:

图6.3

6.2按钮

6.2.1按钮事件

用户点一下按钮,就会有一个按钮事件发生。你能够经过覆盖一个applet的action成员函数来捕捉按钮事件。

publicbooleanaction(Evente,Objecto){if(e.targetinstanceofButton){system.out.println((string)o);}else{System.out.println("Non-buttonevent");}returntrue;}

6.2.2按钮类型

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

6.2.2.1选择式按钮

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

CityChooser=newChoice();

CityChooser.addItem("北?copy;");CityChooser.addItem("上海");CityChooser.addItem("天津");

add(CityChooser);

图6.4

6.2.2.2标记式按钮

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

CheckboxfillStyleButton;fillStyleButton=newCheckbox("Solid");

publicbooleanaction(Evente,Objectarg){if(e.targetinstanceofCheckbox){System.out.println("Checkbox:"+arg);}returntrue;}

图6.5

6.2.2.3按键式按钮

按键式按钮是一组按钮,用户能够选中其中一个,同时这一组中的其余按钮将被关闭。下面是一个按键式按钮的例子:publicclassCheckBoxextendsApplet{CheckboxGroupcbg;

publicvoidinit(){cbg=newCheckboxGroup();add(newCheckbox("one",cbg,true));add(newCheckbox("two",cbg,false));add(newCheckbox("three",cbg,false));}}

图6.6

6.2.3自包含按钮

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

importjava.awt.*;importjava.applet.Applet;

classokButtonextendsButton{

publicokButton(){setLabel("Ok");}

publicbooleanaction(Evente,Objectarg){System.out.println("OKButton");returntrue;}}

publicclassbuttontestextendsApplet{okButtonmyOkButton;

publicvoidinit(){myOkButton=newokButton();add(myOkButton);}}

图6.7

6.3标?copy;

标?copy;是一种放到面板上的静止的正文。下面是一个标?copy;的例子:importjava.awt.*;importjava.applet.Applet;

publicclasslabelextendsApplet{

publicvoidinit(){setLayout(newFlowLayout(FlowLayout.CENTER,10,10));Labellabel1=newLabel("你好!");Labellabel2=newLabel("另外一个标?copy;");add(label1);add(label2);}}

下面是运行结果:

图6.8

6.4列表框

列表框使用户易于操做大量的选项。建立列表框的方法和Choicebutton有?copy;类似。列表框的全部条目都是可见的,若是选项不少,超出了列表框可见区的范围,则列表框的旁边将会有一个滚动条。首先,建立列表框:Listl=newList(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.9

6.4.1在列表框中进行选择

能够用成员函数getSelectedItem()或getSelectedItems()来接收在列表框中被选的选项。在单选列表框里,"双击"一个选项就能够触发一个可被action()成员函数捕捉到的事件。publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofList){System.out.println("Listentry:"+arg);}...}

6.4.2多选列表框

对于多选列表框,要使你的选择产生做用,须要使用其余的外部事件。例如,你能够使用按钮事件:

图6.10

publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofButton){...if("Ok".equals(arg)){string[]selected;selected=l.getSelectedItems();for(intI=0;I<selected.length;I++){System.out.println(selected[i]);}}}}

6.5文本域

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

6.5.1建立文本域

在建立文本域时,有四种类型?copy;你选择:空的、空的而且具备指定长度、带有初始文本内容的和带有初始文本内容并具备指定长度的。下面是生成这四种文本域的代码:

TextFieldtf1,tf2,tf3,tf4;

//空的文本域tf1=newTextField();//长度为20的空的文本域tf2=newTextField(20);//带有初始文本内容的文本域tf3=newTextField("你好");//带有初始文本内容并具备指定长度的文本域tf4=newTextField("你好",30);add(tf1);add(tf2);add(tf3);add(tf4);

图6.11

6.5.2文本域事件

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

publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofTextField){System.out.println("TextField:"+arg);}...}

6.6文本区

文本区能够显示大段的文本。

6.6.1建立文本区

与文本域相似,建立文本区时也有四种类型?copy;选择,但若是指定文本区的大小,必须同时指定行数和列数。

TextAreata1,ta2;//一个空的文本区ta1=newTextArea();

//一个带有初始内容、大小为5x40的文本区ta2=newTextArea("你好!",5,40);

能够用成员函数setEditable()来决定用户是否可对文本区的内容进行编辑。

//使文本区为只读的ta2.setEditable(false)

图6.12

6.6.2接收文本区的内容

能够用成员函数getText()来得到文本区的当前内容。例如:System.out.println(ta1.getText());文本区自己不产生本身的事件。但你能够用外部事件来接收文本区的内容:

publicbooleanaction(Evente,Objecto){if(e.targetinstanceofButton){if("send".equals(o)){StringtextToSend=ta1.getText();System.out.println("sending:"+textTosend);mySendFunction(textToSend);}}else{...}}

6.7画板

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

6.7.1建立画板

importjava.awt.*;importjava.applet.Applet;

publicclasssuperGUIextendsApplet{...myCanvasdoodle;...publicvoidinit(){...//创建咱们的画板doodle=newmyCanvas();doodle.reshape(0,0,100,100);leftPanel.add("Center",doodle);...}}

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

6.7.2画板事件

你能够覆盖通常的事件处理成员函数。下面是一个包含了mouseDown事件处理的例子:

importjava.awt.*;importjava.applet.Applet;

publicclasscanvasextendsApplet{

Buttonb1;

publicvoidinit(){//SetourlayoutasaBorderstylesetLayout(newBorderLayout(15,15));b1=newButton("Test");myCanvasc1=newmyCanvas(100,100);//addthecanvasandthebuttontotheappletadd("Center",c1);add("South",b1);}

publicbooleanaction(Evente,Objectarg){System.out.println("Event:"+arg);returntrue;}

publicbooleanmouseDown(Evente,intx,inty){System.out.println("Mouseworks:("+x+","+y+")");returntrue;}}

classmyCanvasextendsCanvas{privateintwidth;privateintheight;

publicmyCanvas(intw,inth){width=w;height=h;reshape(0,0,w,h);}

publicvoidpaint(Graphicsg){g.setColor(Color.blue);g.fillRect(0,0,width,height);}

publicbooleanmouseDown(Evente,intx,inty){if((x<width)&&(y<height)){System.out.println("Canvasmouseworks:("+x+","+y+")");returntrue;}returnfalse;//NotourmouseDown}}

6.8滚动条

在某?copy;程序中,须要调整线性的值,这时就须要滚动条。滚动条提?copy;了易于操做的值的范围或区的范围。

6.8.1建立滚动条

当建立一个滚动条时,必须指定它的方向、初始值、滑块的大小、最小值和最大值。

publicScrollbar(intorientation,intinitialValue,intsizeOfSlider,intminValue,intmaxValue);

下面是一个例子:

ScrollbarredSlider;publicvoidinit(){redSlider=newScrollbar(Scrollbar.VERTICAL,0,1,0,255);add(redSlider);}

图6.13

6.8.2滚动条事件

和其余接口元件同样,滚动条产生一个你能够控制的事件,但和其余事件不一样,你必须直接使用成员函数handleEvent(),而不能使用成员函数action().

publicbooleanhandleEvent(Evente){if(e.targetinstanceofScrollbar){System.out.println("Scrollbar:"+((Scrollbar)e.target).getValue());returntrue;}returnsuper.handleEvent(e);}

6.8.3滚动条的值的显示

若是你想显示滑块所在位置的值,须要加一个本身的文本域。下面是一个例子。

importjava.awt.*;importjava.applet.Applet;

publicclassredSliderextendsApplet{Scrollbarredslider;TextFieldredvalue;Labelredlabel;

publicvoidinit(){setLayout(newGridLayout(1,3));redslider=newScrollbar(Scrollbar.HORIZONTAL,0,1,0,255);redvalue=newTextField("0",5);redvalue.setEditable(false);redlable=newLabel("Red(0-255)");add(redlabel);add(redslider);add(redvalue);}

publicbooleanhandleEvent(Evente){if(e.targetinstanceofScrollbar){redvalue.setText(Integer.toString(((Scrollbar)e.target).getValue()));returntrue;}returnsuper.handleEvent(e);}

publicbooleanaction(Evente,Objectarg){System.out.println("Event"+arg);returntrue;}}

图6.14

本章小结

1.Java提?copy;了生成一个良好的图形用户接口所须要的一?copy;基本元件:面板(Panel?copy;、按钮(Button?copy;、标?copy;(Label?copy;、画板(Canvases?copy;、滚动条(Scrollbar?copy;、列表框(List?copy;、文本域(TextField?copy;、文本区(TextArea?copy;。2.大部分元件都有本身的事件,你能够捕捉并处理它们。

第七章多线程

7.1多线程的概念

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

7.1.1Java线程的模型

Java的设计思想是创建在当前大多数操做系统都实现了线程调度。Java虚拟机的不少任务都依赖线程调度,并且全部的类库都是为多线程设计的。实时上,Java支持Macintosh和Ms-dos的平台?reg;因此迟迟未出来就是由于这两个平台都不支持多线程。Java利用多线程实现了整个执行环境是异步的。在Java程序里没有主消息循环。若是一个线程等待读取网络数据,它能够运行但不中止系统的其余线程执行。用于处理用户输入的线程大多时间是等待用户敲键盘或击鼠标。你还能够使动画的每一帧?reg;间停顿一秒而并不使系统暂停。一?copy;线程启动后,它能够被挂起,暂时不让它执行。挂起的线程能够从新恢复执行。任什么时候间线程均可以被中止,被中止的线程就不能再从新启动。Java语言里,线程表现为线程类,线程类封装了全部须要的线程操做控制。在你内心,必须很清晰地区分开线程对象和运行线程,你能够将线程对象看做是运行线程的控制面板。在线程对象里有不少函数来控制一个线程是否运行,睡眠,挂起或中止。线程类是控制线程行为的惟一的手段。一?copy;一个Java程序启动后,就已经有一个线程在运行。你可经过调用Thread.currentThread函数来查看当前运行的是哪个线程。一?copy;你获得一个线程的控制柄,你就能够做一?copy;颇有趣的事情,即便单线程也同样。下面这个例子让你知道怎样操纵当前线程。Filename:testthread

classtestthread{publicstaticvoidmain(Stringargs[]){Threadt=Thread.currentThread();t.setName("ThisThreadisrunning");System.out.println("Therunningthread:"+t);try{for(inti=0;i<5;i++){System.out.println("Sleeptime"+i);Thread.sleep(1000);}

}catch(InterruptedExceptione){System.out.println("threadhaswrong");}

}}

执行结果:javatestthreadTherunningthread:Thread[ThisThreadisrunning,5,main]Sleeptime0Sleeptime1Sleeptime2Sleeptime3Sleeptime4

7.1.2启动接口

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

Filename:twothread.java

classtwothreadimplementsRunnable{twothread(){Threadt1=Thread.currentThread();t1.setName("Thefirstmainthread");System.out.println("Therunningthread:"+t1);Threadt2=newThread(this,"thesecondthread");System.out.println("creatanotherthread");t2.start();try{System.out.println("firstthreadwillsleep");Thread.sleep(3000);}catch(InterruptedExceptione){System.out.println("firstthreadhaswrong");}System.out.println("firstthreadexit");}publicvoidrun(){try{for(inti=0;i<5;i++){System.out.println("Sleeptimeforthread2:"+i);Thread.sleep(1000);}

}catch(InterruptedExceptione){System.out.println("threadhaswrong");}

System.out.println("secondthreadexit");}publicstaticvoidmain(Stringargs[]){newtwothread();}}

执行结果:javatwothread

Therunningthread:Thread[Thefirstmainthread,5,main]creatanotherthreadfirstthreadwillsleepSleeptimeforthread2:0Sleeptimeforthread2:1Sleeptimeforthread2:2firstthreadexitSleeptimeforthread2:3Sleeptimeforthread2:4secondthreadexit

main线程用newThread(this,"thesecondthread")建立了一个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;你的程序被分红几个逻辑线程,你必须清晰的知道这?copy;线程?reg;间应怎样相互通信。Java提?copy;了wait和notify等功能来使线程?reg;间相互交谈。一个线程能够进入某一个对象的synchronized函数进入等待状态,直到其余线程显式地将它唤醒。能够有多个线程进入同一个函数并等待同一个唤醒消息。

7.2Java线程例子

7.2.1显式定义线程

在咱们的单线程应用程序里,咱们并无看见线程,由于Java能自动建立和控制你的线程。若是你使用了理解Java语言的浏览器,你就已经看到使用多线程的Java程序了。你也许注意到两个小程序能够同时运行,或在你移动滚动条时小程序继续执行。这并非代表小程序是多线程的,但说明这个浏览器是多线程的。多线程应用程序(或applet)能够使用好几个执行上下文来完成它们的工做。多线程利用了不少任务包含单独的可分离的子任务的特色。每个线程完成一个子任务。

可是,每个线程完成子任务时仍是顺序执行的。一个多线程程序容许各个线程尽快执行完它们。这种特色会有更好的实时输入反应。

7.2.2多线程例子

下面这个例子建立了三个单独的线程,它们分别打印本身的"HelloWorld":

//Defineoursimplethreads.Theywillpauseforashorttime//andthenprintouttheirnamesanddelaytimesclassTestThreadextendsThread{privateStringwhoami;privateintdelay;

//Ourconstructortostorethename(whoami)//andtimetosleep(delay)publicTestThread(Strings,intd){whoami=s;delay=d;}

//Run-thethreadmethodsimilartomain()//Whenrunisfinished,thethreaddies.//Runiscalledfromthestart()methodofThreadpublicvoidrun(){//Trytosleepforthespecifiedtimetry{sleep(delay);}catch(InterruptedExceptione){}//NowprintoutournameSystem.out.println("HelloWorld!"+whoami+""+delay);}}/***Multimtest.Asimplemultithreadthestprogram*/publicclassmultitest{publicstaticvoidmain(Stringargs[]){TestThreadt1,t2,t3;//Createourtestthreadst1=newTestThread("Thread1",(int)(Math.readom()*2000));t2=newTestThread("Thread2",(int)(Math.readom()*2000));t3=newTestThread("Thread3",(int)(Math.readom()*2000));

//Starteachofthethreadst1.start();t2.start();t3.start();}}

7.2.3启动一个线程

程序启动时老是调用main()函数,所以main()是咱们建立和启动线程的地方:

t1=newTestThread("Thread1",(int)(Math.readom()*2000));

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

7.2.4操做线程

若是建立线程正常,t1应包含一个有效的执行线程。咱们在线程的run()函数里控制线程。一?copy;咱们进入run()函数,咱们即可执行里面的任何程序。run()好象main()同样。一?copy;run()执行完,这个线程也就结束了。在这个例子里,咱们试着延迟一个随机的时间(经过参数传递?copy;:sleep(delay);

sleep()函数只是简单地告诉线程休息多少个毫秒时间。若是你想推迟一个线程的执行,你应使用sleep()函数。当线程睡眠是sleep()并不占用系统资源。其它线程可继续工做。一?copy;延迟时间完毕,它将打印"HelloWorld"和线程名称及延迟时间。

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例子:

importjava.awt.*;importjava.awt.image.ImageProducer;importjava.applet.Applet;

publicclassatest3extendsAppletimplementsRunnable{Imageimages[];MediaTrackertracker;intindex=0;Threadanimator;

intmaxWidth,maxHeight;//Ouroff-screencomponentsfordoublebuffering.ImageoffScrImage;GraphicsoffScrGC;

//Canwepaintyes?booleanloaded=false;

//Initializetheapplet.Setoursizeandloadtheimagespublicvoidinit()[//Setupourimagemonitortracker=newMediaTracker(this);

//SetthesizeandwidthofourappletmaxWidth=100;maxHeight=100;

images=newImage[10];//Setupthedouble-bufferandresizeourapplettry{offScrImage=createImage(maxWidth,maxHeight);offScrGC=offScrImage.getGraphics();offScrGC.setColor(Color.lightGray);offScrGC.fillRect(0,0,maxWidth,maxHeight);resize(maxWidth,maxHeight);}catch(Exceptione){e.printStackTrace();}

//loadtheanimationimagesintoanarrayfor(inti=0;i<10;i++){StringimageFile=newString("images/Duke/T"+String.valueOf(i+1)+".gif");images[i]=getImage(getDocumentBase(),imageFile)://Registerthisimagewiththetrackertracker.addImage(images[i],i);}try{//Usetrackertomakesurealltheimagesareloadedtracker.waitForAll();}catch(InterruptedExceptione){}loaded=true;}

//Paintthecurrentframe.publicvoidpaint(Graphicsg){if(loaded){g.drawImage(offScrImage,0,0,this);}}

//Start,setupourfirstimagepublicvoidstart(){if(tracker.checkID(index)){offScrGC.drawImage(images[index],0,0,this);}animator=newThread(this);animator.start();}

//Run,dotheanimationworkhere.//Grabanimage,pause,grabthenext...publicvoidrun(){//GettheidofthecurrentthreadThreadme=Thread.currentThread();

//Ifouranimatorthreadexist,andisthecurrentthread...while((animatr!=null)&&(animator==me)){if(tracker.checkID(index)){//ClearthebackgroundandgetthenextimageoffScrGC.fillRect(0,0,100,100);offScrGCdrawImage(images[index],0,0,this);index++;//Loopbacktothebeginningandkeepgoingif(index>=images.length){index=0;}}//Delayheresoanimationlooksnormaltry{animator.sleep(200);}catch(InterruptedExceptione){}//Drawthenextframerepaint();}}}

7.3多线程?reg;间的通信

7.3.1生产者和消费者

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

buffer

让咱们建立一个简单的"AlphabetSoup"生产者和相应的消费者.

7.3.2生产者

生产者将从thread类里派生:classProducerextendsThread{privateSoupsoup;privateStringalphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ";

publicProducer(Soups){//Keepourowncopyofthesharedobjectsoup=s;}

publicvoidrun(){charc;//Throw10lettersintothesoupfor(inti=0;i<10;i++){c=alphabet.charAt((int)(Math.random()*26));soup.add(c);//printarecordofosradditionSystem.out.println("Added"+c+"tothesoup.");//waitabitbeforeweaddthenextlettertry{sleep((int)(Math.random()*1000));}catch(InterruptedExceptione){}}}}

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

7.3.3消费者

让咱们看看消费者的程序:classConsumerextendsThread{privateSoupsoup;

publicConsumer(Soups){//keepourowncopyofthesharedobjectsoup=s;}

publicvoidrun(){charc;//Eat10lettersfromthealphabetsoupfor(intI=0;i<10;i++){//graboneletterc=soup.eat();//PrintouttheletterthatweretrievedSystem.out.println("Atealetter:"+c);//try{sleep((int)(Math.raddom()*2000));}catch(InterruptedExceptione){}}}}

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

7.3.4监视

Soup类执行监视两个线程?reg;间传输信息的功能。监视是多线程中不可缺乏的一部分,由于它保持了通信的流?copy;。让咱们看看Soup.java文件:classSoup{privatecharbuffer[]=newchar[6];privateintnext=0;//FlagstokeeptrackofourbufferstatusprivatebooleanisFull=false;privatebooleanisEmpty=true;

publicsyschronizedchareat(){//Wecan'teatifthereisn'tanythinginthebufferwhile(isEmpty==true){try{wait();//we'llexitthiswhenisEmptyturnsfalse}catch(InterruptedExceptione){}}//decrementthecount,sincewe'regoingtoeatoneletternext--;//Didweeatthelastletter?if(next==0){isEmpty=true;}//Weknowthebuffercan'tbefull,becausewejustateisFull=false;notify();//returnthelettertothethreadthatiseatingreturn(buffer[next]);}

//methodtoaddletterstothebufferpublicsynchronizedvoidadd(charc){//Waitarounduntilthere'sroomtoaddanotherletterwhile(isFull==true){try{wait();//ThiswillexitwhenisFullturnsfalse}catch(InterruptedExceptione){}}//addthelettertothenextavailablespotbuffer[next]=c;//Changethenextavailablespotnext++;//Arewefull;if(next==6){isFull=true;}isEmpty=false;notify();}}

soup类包含两个重要特征:数据成员buffer[]是私有的,功能成员add()和eat()是公有的。

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

7.3.5联系起来

如今咱们有一个生产者,一个消费者和一个共享对象,怎样实现它们的交互呢?咱们只须要一个简单的控制程序来启动全部的线程并确信每个线程都是访问的同一个共享对象。下面是控制程序的代码,SoupTest.java:classSoupTest{publicstaticvoidmain(Stringargs[]){Soups=newSoup();Producerp1=newProducer(s);Consumerc1=newConsumer(s);

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

7.3.6监视生产者

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

7.4线程API列表

下面是一?copy;经常使用的线程类的方法函数列表:

类函数:如下是Thread的静态函数,便可以直接从Thread类调用。

currentThread返回正在运行的Thread对象yield中止运行当前线程,让系统运行下一个线程sleep(intn)让当前线程睡眠n毫秒

对象函数:如下函数必须用Thread的实例对象来调用。

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

本章小结:

1.多线程是java语言的重要特色,java语言用Thread类封装了线程的全部操做。2.线程的接口名为Runnable3.线程?reg;间同步机制为synchronized关键词4.线程?reg;间通信靠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(ExceptionType1e){//对ExceptionType1的处理}catch(ExceptionType2e){//对ExceptionType2的处理throw(e);//再抛出这个"异常"}finally{}

8.2"异常"的类型

在"异常"类层次的最上层有一个单独的类叫作Throwable。这个类用来表示全部的"异常"状况。每一个"异常"类型都是Throwable的子类。Throwable有两个直接的子类。一类是Exception,是用户程序可以捕捉到的"异常"状况。咱们将经过产生它的子类来建立本身的"异常"。另外一类是Error,它定义了那?copy;一般没法捕捉到的"异常"。要谨慎使用Error子类,由于它们一般会致使灾难性的失败。在Exception中有一个子类RuntimeException,它是程序运行时自动地对某?copy;错误做出反应而产生的。

8.3不捕捉"异常"

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

classExc0{publicstaticvoidmain(Stringargs[]){intd=0;inta=42/d;}}

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

C:\>javaExc0java.lang.arithmeticException:/byzeroatExc0.main(Exc0.java:4)

8.4try与catch

一般咱们但愿本身来处理"异常"并继续运行。能够用try来指定一块预防全部"异常"的的程序。紧跟在try程序后面,应包含一个catch子句来指定你想要捕捉的"异常"的类型。例如,下面的例子是在前面的例子的基础上构造的,但它包含一个try程序块和一个catch子句。

classexc1{publicstaticvoidmain(stringargs[]){try{intd=0;inta=42/d;}catch(arithmeticexceptione){system.out.println("divisionbyzero");}}}

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

8.5多个catch子句

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

classMultiCatch{publicstaticvoidmain(Stringargs[]){try{inta=args.length;System.out.println("a="+a);intb=42/a;intc[]={1};c[42]=99;}catch(ArithmeticExceptione){System.out.println("divby0:"+e);}catch(ArrayIndexOutOfBoundsExceptione){system.out.println("arrayindexoob:"+e);}}}

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

C:\>javaMultiCatcha=0divby0:java.lang.arithmeticexception:/byzeroC:\>javaMutiCatch1a=1arrayindexoob:java.lang.ArrayIndexOutOfBoundsException:42

8.6try语句的嵌套

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

classMultiNest{staticvoidprocedure(){try{intc[]={1}:c[42]=99;}catch(ArrayIndexOutOfBoundsexceptione){System.out.println("arrayindexoob:"+e);}}publicstaticvoidmain(Stringargs[]){try{inta=args.length;system.out.println("a="+a);intb=42/a;procedure();}catch(arithmeticExceptione){System.out.println("divby0:"+e);}}}

成员函数procedure里有本身的try/catch控制,因此main不用去处理ArrayIndexOutOfBoundsException。

8.7throw语句

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

throwThrowableInstance;

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

classThrowDemo{staticvoiddemoproc(){try{thrownewNullPointerException("de3mo");}catch(NullPointerExceptione){System.out.println("caughtinsidedemoproc");throwe;}}publicstaticvoidmain(Stringargs[]){try{demoproc();}catch(NullPointerExceptione){system.out.println("recaught:"+e);}}}

8.8throws语句

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

typemethod-name(arg-list)throwsexception-list{}

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

classThrowsDemo1{staticvoidprocedure()[System.out.println("insideprocedure");thrownewIllegalAccessException("demo");}publicstaticvoidmain(Stringargs[]){procedure();}}

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

classThrowsDemo{staticvoidprocedure()throwsIllegalAccessException{System.out.println("insideprocedure");thrownewIllegalAccessException("demo");}publicstaticvoidmain(Stringargs[]){try{procedure();}catch(IllegalAccessExceptione){System.out.println("caught"+e);}}}

下面是输出结果:

C:\>javaThrowsDemoinsideprocedurecaughtjava.lang.IllegalAccessException:demo

8.9finally

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

classFinallyDemo{staticvoidprocA(){try{System.out.println("insideprocA");thrownewRuntimeException("demo");}finally{System.out.println("procA'sfinally");}}staticvoidprocB(){try{System.out.println("insideprocB");return;}finally{System.out.println("procB'sfinally");}}publicstaticvoidmain(Stringargs[]){try{procA();}catch(Exceptione);procB();}}

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

C:\>javaFinallyDemoinsideprocAprocA'sfinallyinsideprocBprocB'sfinally

本章小结

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

第九章Java输入输出操做

9.1Java输入输出流

全部的程序语言都提?copy;与本机文件系统交互的方式;Java也不例外。咱们将看看Java是怎样处理标准文件输入输出的(包括stdin,stout,stderr)。当你在网络上开发小程序时,你必须注意直接文件输入输出是不安全因素的关键。大多数用户设置他们的浏览器,可以让你自由的访问他们的文件系统,但有?copy;不让你访问。固然,若是你开发你内部的应用程序,你也许须要直接访问文件。

标准输入输出Unix的用户,或其余基于命令行系统的用户(如DOS),都知道标准输入输出的含义。标准输入文件是键盘,标准输出文件是你的终端屏幕。标准错误输出文件也指向屏幕,若是有必要,它也能够指向另外一个文件以便和正常输出区分。

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

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

StderrSystem.err同stdout同样实现stderr。象System.out同样,你能够访问PrintStream成员函数。

9.2标准输入输出例子

这里有一个例子,功能象Unix里的cat或type:

importjava.io.*classmyCat{publicvoidmain(Stringargs[])throwsIOException{intb;intcount=0;while((b=System.in.read())!=-1){count++;System.out.print((char)b);}System.out.println();//blanklineSystem.err.println("counted"+count+"totalbytes.");}}

9.3普通输入输出类

除了基本的键盘输入和屏幕输出外,咱们还须要联系文件的输入输出。咱们将学习下面几个类:lFileInputStreamlDataInputStreamlFileOutputStreamlDataOutputStream

做为参考,再列出一?copy;特定应用的类:lPipedInputStreamlBufferedInputStreamlPushBackInputStreamlStreamTokenizerlPipedOutputStreamlBufferedOutputStreamlRandomAccessFile

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

9.4文件

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

9.4.1建立一个新的文件对象

你可用下面三个方法来建立一个新文件对象:

FilemyFile;myFile=newFile("etc/motd");

myFile=newFile("/etc","motd");//moreusefulifthedirectoryorfilenamearevariables

FilemyDir=newfile("/etc");myFile=newFile(myDir,"motd");

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

9.4.2文件测试和使用

一?copy;你建立了一个文件对象,你即可以使用如下成员函数来得到文件相关信息:

文件名lStringgetName()lStringgetPath()lStringgetAbslutePath()lStringgetParent()lbooleanrenameTo(FilenewName)

文件测试lbooleanexists()lbooleancanWrite()lbooleancanRead()lbooleanisFile()lbooleanisDirectory()lbooleanisAbsolute()

通常文件信息llonglastModified()llonglength()

目录用法lbooleanmkdir()lString[]list()

9.4.3文件信息获取例子程序

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

importjava.io.*;classfileInfo{FilefileToCheck;publicstaticvoidmain(Stringargs[])throwsIOException{if(args.length>0){for(inti=0;i<args.length;i++){fileToCheck=newFile(args[i]);info(fileToCheck);}}else{System.out.println("Nofilegiven.");}}publicvoidinfo(Filef)throwsIOException{System.out.println("Name:"+f.getName());System.out.println("Path:"=f.getPath());if(f.exists()){System.out.println("Fileexists.");System.out.print((f.canRead()?"andisReadable":""));System.out.print((f.cnaWrite()?"andisWriteable":""));System.out.println(".");System.out.println("Fileis"+f.lenght()="bytes.");}else{System.out.println("Filedoesnotexist.");}}}

9.5输入流

InputStreamSequenceInputStreamFileInputStreamPipedInputStreamByteArrayInputStreamFileterInputStreamStringBufferInputStream

DataInputStreamLineNumberInputStreamPushbackInputStreamBufferedInputStream有好几个类是专门用来处理文件输入的。下面是文件输入类的层次结构:

9.5.1FileInputStream对象

FileInputStream典型地表示一种顺序访问的文本文件。经过使用FileInputStream你能够访问文件的一个字节、几个字节或整个文件。

9.5.2打开FileInputStream

为一个文件打开输入流FileInputStream,你必须将文件名或文件对象传送给结构:

FileInputStreammyFileStream;myFileStream=newFileInputStream("/etc/motd");

你还能够象下边这样从FileInputStream里读文件信息:

FilemyFile;FileInputSteammyFileStream;myFile=newFile("/etc/motd");myFileStream=newFileInputStream(myFile);

一?copy;FileInputStream输入流打开,你就能够从里面读取信息了。read()成员函数有如下几种选项:

lintread()//readsonebyte//return-1atendofstreamlintread(byteb[])//fillsentirearray,ifpossible//returnsnumberofbytesread//returns-1ifendofstreamisreached

lintread(byteb[],intoffset,intlen)//readslenbytesintobstartingatb[offset]//Returnsnumberofbytesread,//or-1ifendofstreamisreached.

9.5.3关闭FileInputStream

当你完成一个文件的操做,你可选两种方法关闭它:显式关闭和隐式关闭,隐式关闭是自动垃圾回收时的功能。

显式关闭以下:myFileStream.close();

9.6例程:显示一个文件

若是文件的访问权限足够,你能够在TextArea对象里显示文件内容。

下面是显示文件的程序片段:

FileInputStreamfis;TextAreata;publicvodinit(){byteb[]=newbyte[1024];intI;//makeitbigenoughorwaituntilyou//knowthesizeofthefileStrings;try{fis=newFileInputStream("/etc/motd");}catch(FileNotFoundExceptione){/*dosomethingappropriate*/}try{I=fis.read(b);}catch(IOExceptione){/*dosomethingappropriate*/}s=newString(b,0);ta=newTextArea(s,5,40);add(ta);}

9.7DataInputStreams

DataInputStreams与FileInputStreams差很少。Data流能够直接读任意一种变量类型,如浮点数,整数和字符等。通常来讲,对二进制文件使用DataInputStream流。

9.7.1打开和关闭DataInputStreams

打开和关闭DataInputStreams对象时,其方法与FileInputStreams相同:

DataInputStreamsmyDataStream;FileInputStreamsmyFileStream;

//getafilehandlemyFileStream=newFileInputStream("/usr/db/stock.dbf");//open,or"chain"adatainputfilemyDataStream=newDataOutputStream(myFileStream);

//Nowwecanusebothinputstreamstoaccessourfile//j(Ifwewantto...)myFileStream.read(b);I=myDataStrea.readInt();

//closethedatafrielexplicityly//Alwaysclosethe"topmost"filestreammyDataStream.close();myFileStream.close();

9.7.2读DataInputStreams

当你从DataInputStreams流里访问文件时,你能够使用与FileInputStream流相同的成员函数read()。但你也能够使用其余访问方法来读取不一样种类的数据:

lbytereadByte()lintreadUnsignedByte()lshortreadShort()lintreadUnsighedShort()lcharreadChar()lintreadIntllongreadLong()lfloatreadFloat()ldoublereadDouble()lStringreadLine()

以上每个成员函数都读取相应的数据对象。象StringreadLine()成员函数,你可以使用\n,\r,\r\n,或EOF做为字符?reg;结束符。

读一个长整型,例如:

longserialNo;...serialNo=myDataStream.readLong();

9.8URL输入流

除了基本文件访问外,Java还提?copy;了经过网络使用URL访问对象的功能。在下面这个例子里,咱们用getDocumentBase()成员函数并显式指定URL对象来访问声音和图象。

StringimageFile=newString("images/Duke/T1.gif");images[0]=getImage(getDocumentBase(),imageFile();

若是咱们愿意,能够直接使用URL:URLimageSource;imageSource=newURL("http://555-1212.com/~info");images[0]=getImage(imageSource,"Duke/T1.gif");

咱们能够为相应的URL打开输入流。例如,下面的程序里包括一个数据文件:InputStreamis;bytebuffer[]=newbyte[24];is=newURL(getDocumentBase(),dataname).openStream();

如今咱们能够使用is,就象使用FileInputStream对象同样:is.read(buffer.0,buffer.length);

注意:有?copy;用户设置了他们的浏览器安全属性,能够不让你的程序访问他们的文件。

9.9OutputStreams

上面咱们谈到了读数据,那么如何实现写数据呢?象输入流同样,输出流也有相似的层次结构:

OutputStream

FileOutputStreamPipedOutputStreamByteArrayOutputStreamFilterOutputStream

DataOutputStreamPrintStreamBufferedOutputStream

咱们将分析FileOutputStream和DataOutputStream类来完成咱们碰到的输出流问题。其它的输出流包含了更多的信息和成员函数。象输入流的源文件同样,这?copy;文件在$JAVA_HOME/src/java/io目录下。

9.9.1FileOutputStream类

FileOutputStream对象用于向一个文本文件写数据。象输入文件同样,你得先打开这个文件后才能写这个文件。

9.9.2打开一个FileOutputStream对象

要打开一个FileOutputStream对象,象打开一个输入流同样,你能够将字符?reg;或文件对象做为参数:FileOutputStreammyFileStream;myFileStream=newFileOutputStream("/etc/motd");

象输入流同样,你也可这样使用:FilemyFile;FileOutputStreammyFileStream;myFile=newFile("/etc/motd");myFileStream=newFileOutputStream(myFile);

9.9.3写入一个流

一?copy;文件被打开,你即可以使用write()函数向文件里写一?copy;数据。就象输入流的read()函数同样,你可有三种方法:lvoidwrite(intb);//writesoutonebytelvoidwrite(byteb[]);//writesoutentirearraylvoidwrite(byteb[],intoffset,intlength);//writeoutlengthbytesofb[],startingatb[offset]

9.9.4关闭一个FileOutputStream对象

关闭输出流和关闭输入流方法同样,你能够使用显式方法:myFileStream.close();你也可让系统自动关闭它。

9.10例子:存储信息

下面有一个程序,让用户输入一?copy;姓名和电话号码。每个姓名和号码将加在文件里。用户经过点"Done"按钮来告诉系统整个列表已输入完毕。

一?copy;用户输入完整个列表,程序将建立一个输出文件并显示或打印出来。例如:

555-1212,Tom123-456-7890,PeggyL.234-5678,Marc234-5678,Ron876-4321,Beth&Brian33.1.42.45.70,Jean-Marc

下面是程序的源代码:importjava.io.*;

//Phones.java//Asimpledatabasecreationprogram

classPhones{staticFileOutputStreamfos;publicstaticfinalintlineLength=81;publicstaticvoidmain(Stringargs[])throwsIOExciption{byte[]phone=newbyte[lineLength];byte[]name=newbyte[lineLenght];intI;fos=newFileOutputStream("phone.numbers");while(true){System.err.println("Enteraname(enter'done'toquit)");readLine(name);if("done".equalsIgnoreCase(newString(name,0,0,4))){break;}System.err.println("Enterthephonenumber");readLine(phone);for(i=0;phone[i]!=0;i++){fos.write(phone[i]);}fos.write(',');for(i=0;name[i]!=0;I++){fos.write(name[i]);}fos.write('\n');}fos.close();}

privatestaticvoidreadLine(byteline[])throwsIOException{inti=0,b=0;

while((i<lineLengh-1))&&((b=System.ini.read())!='\n')){line[i++]=(byte)b;}line[i]=(byte)0;}}

9.11BufferedOutput流

若是你处理的数据量不少,或向文件写不少次小数据,你能够使用一个BufferedOutput流。BufferedOutput流提?copy;和FileOutputStream类一样的写操做方法,但全部输出所有存放在一个缓冲区里。当你填满缓冲区,它将一次性写入磁盘。或者你主动将缓冲区写入磁盘。

9.11.1建立BufferedOutput流

若是要建立一个BufferedOutput流,首先须要一个FileOutput流。而后将缓冲区连接到FileOutput流:FileOutputStreammyFileStream;BufferedOutputStreammyBufferStream;//getafilehandlemyFileStream=newFileOutputStream("/usr/db/stock.dbf");//chainabufferedoutputstreammyBufferSSstream=newBufferedOutputStream(myFileStream);

9.11.2更新和关闭BufferedOutput流

和普通FileOutput流同样,向BufferedOutput流里的每一次写操做和写入磁盘操做并非一一对应的。要想在程序结束?reg;前将缓冲区里的数据写入磁盘,除非填满缓冲区,不然只有显式调用flush()函数://forceleft-overdatatodiskmyBufferStream.flush();//closethedatafileexplicitly//Alwaysclosethe"topmost"filestreammyBufferStream.close();myFileStream.close();

9.12DataOutput流

和DataInputStream对应,Java还提?copy;了DataOutput流。使用DataOutput流,咱们能够向文件写入二进制数据。

9.12.1打开和关闭DataOutput流对象

打开和关闭DataOutput流对象与打开、关闭FileOutput流对象方法同样:DataOutputStreammyDataStream;FileOutputStreammyFileStream;BufferedOutputStreammyBufferStream;

//getafilehandlemhyFileStream=newFileOutputStream("/usr/db/stock.dbf");//chainabufferedoutputstream(forefficiency);myBufferStream=newBufferedOutputStream(myFileStream);//chainadataoutputfilemyDataStream=newDataOutputStream(myBufferStream);

//Nowwecanusebothinputstreamstoaccessourfile//(iiIfwewantto...)myBufferStream.write(b);myDataStream.writeInt(i);

//closethedatafileexplicitly//Alwayscolsethe"topmost"filestreammyDataStream.close();myBuffersStream.close();myFileStream.close();

9.12.2向DataOutput流写数据

FileOutput流里的write()函数各类方法都适用于DataOutput流。你还能够看到DataInput流的相似函数方法:lvoidwriteBoolean(booleanv)lvoidwriteByte(intv)lvoidwriteShort(intv)lvoidwriteChar(intv)lvoidwriteInt(intv)lvoidwriteFloat(floatv)lvoidwriteDouble(doublev)lvoidwriteBytes(strings)lvoidwriteChars(strings)

对字符?reg;来讲,有两种选择:byte和char。记住byte是8位数据而char是16位数据。若是你想利用Unicode字符的优势,你应使用writeChars()函数。

9.12.3输出记数

在使用二进制数据输出时经常使用的另一个函数是size()。这个函数返回写入文件数据的总字节数。你也可用size()函数将数据文件分红四字节为单位的块,例如:...intbytesLeft=myDataStream.size()%4;for(intI=0;I<bytesLeft;I++){myDataStrea.write(0);}...

9.13随机访问文件

咱们读文件经常不是从头到尾顺序读的。你也许想将一文本文件看成一个数据库,读完一个记录后,跳到另外一个记录,它们在文件的不一样地方。Java提?copy;了RandomAccessFile类让你操做这种类型的输入输出。

9.13.1建立随机访问文件

打开随机访问文件有两种方法:l用文件名myRAFile=newRandomAccessFile(Stringname,Stringmode);l用文件对象myRAFile=newRandomAccessFile(Filefile,Stringmode);

mode参数决定了访问文件的权限,如只读'r'或读写'wr'等。

例如,咱们打开一个数据库更新数据:RandomAccessFilemyRAFile;myRAFile=newRandomAccessFile("/usr/db/stock.dbf","rw");

9.13.2访问信息

RandomAccessFile对象的读写操做和DataInput/DataOutput对象的操做方式同样。你能够使用在DataInputStream和DataOutputStream里出现的全部read()和write()函数。

还有几个函数帮助你在文件里移动指针:llonggetFilePointer();返回当前指针lvoidseek(longpos);将文件指针定位到一个绝对地址。地址是相对于文件头的偏移量。地址0表示文件的开头。llonglength();返回文件的长度。地址"length()"表示文件的结尾。

9.13.3增长信息

你能够使用随机访问文件来设置成增长信息模式:myRAFile=newRandomAccessFile("/tmp/java.log","rw");myRAFile.seek(myRAFile.length());//Anysubsequentwrite()swillbeappendedtothefile

9.13.4追加信息例子下面是一个在已存在文件后面追加字符?reg;的例子:importjava.io.IOException;importjava.io.RandomAccessFile;

classraTest{publicstaticvoidmain(Stringargs[])throwsIOException{RandomAccessFilemyFAFile;Strings="InformationtoAppend\nHimom!\n";//openourrandomaccessfilemyRAFile=newRandomAccessFile("/tmp/java.log","rw");//movetotheendofthefilemyRAFile.seek(myRAFile.length());//Startappending!myRAFile.writeBytes(s);

myRAFile.close();}}

本章小结

1.Java经过系统类达到访问标准输入输出的功能。2.你能够建立、读、写文件。

相关文章
相关标签/搜索