Java好象随处可见 - 甚至在TV中。尽管如此,说清楚Java是什 么和它能做什么却不那么容易。刚开始接触Java的人通常有三 个问题: 什么是Java? Java能做什么?
Java怎样改变我的生活?
让我们先回答第一个问题:什么是Java? Java既是一种编程语言,又是一个平台。 Java程序语言
Java是具有以下特征的高级程序语言: 简单 面向对象 可分布 可解释 强壮 安全性 结构化 轻便 功能强大 多线程 动态
Java既可以被编译,也可以被解释。通过编译器,可以把Java 程序翻译成一种中间代码 - 称为字节码 - 可以被Java解释器 解释的独立于平台的代码。通过解释器,每条Java字节指令被 分析,然后在计算机上运行。只需编译一次,程序运行时解释 执行。下图说明了它是如何工作的:
可以把Java字节码看作运行在Java虚拟机(Java VM)上的机器 代码指令。每中Java解释器,不管是Java开发工具还是可以运
行Java小应用程序的Web浏览器,都是一种Java VM的实例。Java VM也可以由硬件实现。
Java字节码使“写一次,到处运行”成为可能。可以在任何有 Java编译器的平台上把Java程序编译成字节码。这个字节码可以 运行在任何Java VM上。例如,同一个Java程序可以运行在Windows NT、Solaris和Macintosh上。
Java平台
平台是程序运行的硬件或软件环境。Java平台与大多数其它平台 不同之处在于它是运行于其它基于硬件平台的纯软件平台。大 多数其它平台是硬件和操作系统的结合。 Java平台由两部分组成: Java虚拟机(Java VM) Java应用程序界面(Java API)
我们已经介绍了Java VM,它是Java平台的基础,可以移植到各 种基于硬件的平台上。
Java API是软件组件的集合,它们提供了很多有用的功能,如图 形用户界面(GUI)。Java API被分组为相关组件的库(包)。 下图描述了一个运行在Java平台上的Java程序,如应用程 序(application)或小应用程序(applet)。如图中显示的,Java
API和VM把Java程序从硬件依赖中分离出来。
作为一种独立于平台的环境,Java比本地代码慢一些。然而,聪 明的编译器、很好地调制过的解释器和即时字节码编译器可以 在不牺牲可移植性的条件下使Java的表现接近本地代码。
第二页:Java能做什么?
最有名的Java程序可能是Java小应用程序(applet)。小应用程 序是遵循一定的规则,运行在支持Java的浏览器上的Java程序。 然而,Java不只用来为WWW写聪明的、娱乐性的小应用程序,它 是一个通用的、高级程序语言和强大的软件平台。使用通用的Java API,可以写很多种程序。
最常见的程序类型可能是小应用程序和应用程序,Java应用程序 是直接运行在Java平台上的独立的程序。一种特殊的称为“服 务器”的应用程序可以支持网络上的客户。服务器的包括Web 服务器、代理服务器、邮件服务器、打印服务器和导入服务 器。另一种特殊的程序是servlet,它与小应用程序类似 - 在都 是应用程序的实时扩展方面。不运行在浏览器中,servlet运行 在Java服务器上,配置或裁剪服务器。
Java API是如何支持各种程序的呢?它通过软件包提供各种功 能。核心API(core API)是实现Java平台功能的API。核心API有 以下特征:
要素:对象、字符串、线程、数字、输入和输出、数据 结构、系统属性、日期和时间等。
小应用程序:Java小应用程序使用的指令集。 网络:URL、TCP和UDP插槽、IP地址。
国际化:可以写可本地化的代码。程序可自动适应特殊的 场所和以适当的语言显示。
安全性:在低级和高级两方面,包括电子签名、公/私密 钥管理、访问控制、认证。
软件组件:称为JavaBean,可以插到已有的组件结构中, 如微软的OLE/COM/Active-X结构、OpenDoc、网景的Live Connect。
对象系列化:可以通过RMI(远程方法调用)进行轻量级 的持续和通讯。
JDBC(Java数据库互联):提供对大量关系数据库的统一 访问。
Java不仅有核心API,还有标准扩展。标准扩展定义了3D、服 务器、合作、电话、演讲、动画等的API。
第三页:Java怎样改变我的生活?
我们不能保证你学Java会成名、发财或者找到工作。Java只是使 你的程序更好和比其它语言付出更少的努力。我们相信Java会帮 你做以下事情:
迅速开始:虽然Java是一种功能强大的面向对象的语言, 但是它很容易学,尤其是对那些熟悉C或++的程序员。 写更少的代码:程序韵律学(类、方法等)使用Java写的 程序比用C++写的小四倍。
写更好的代码:Java语言鼓励好的编程实践,它的垃圾收 集功能可以帮你避免使用内存时出问题。Java的面向对 象、JavaBeans组件结构和它的广泛涉及、容易扩展的API 可以使你重用别人的经过测试的代码和引入尽量少的错误。 更快地开发程序:开发时间只相当于写同样C++程序的 一半。为什么?因为用Java写的代码行数更少,Java也比 C++程序简单。
用100%纯Java可以避免对平台的依赖性:通过遵守它的语 言规则和避免使用其它语言库,可以保持程序的可移植性。 写一次,到处运行:因为100%纯Java程序可以编译成独立 于机器的字节码,它们可以运行在任何Java平台上。 更容易的分发软件:可以容易地从一个中心服务器升级小 应用程序。小应用程序可以利用Java的新类即时装载而不 用重新编译整个程序的特性。
\"Hello World\"小应用程序
按照以下步骤,可以创建一个Java小应用程序。 创建Java源文件
建立一个名为HelloWorld.java的文件: import java.applet.Applet; import java.awt.Graphics;
public class HelloWorld extends Applet { public void paint(Graphics g) {
g.drawString(\"Hello world!\} }
编译源文件
用Java编译器编译这个源文件。
如果编译成功,编译器产生一个与Java源文
件(HelloWorldApp.java)在同一个目录(文件夹)的名 为HelloWorldApp.class的文件。这个class文件包含Java字 节码,是可以被Java运行系统解释的独立于平台的代码。 如果编译失败,查看敲入的文件名是否正确,尤其是大小写。 如果还不能发现问题,可以查看编译问题。 建立一个包含小应用程序的HTML文件
用文本编辑器建立一个与HelloWorld.class在同一目录下 的Hello.html文件。这个HTML文件包含以下代码:
Here is the output of my program: 运行小应用程序
要运行小应用程序,需要在可以运行Java小应用程序的应用程序 中调用上面的HTML文件。这个应用程序可以是支持Java的浏览 器,或者是一个Java小应用程序查看程序,如JDK提供的Applet Viewer。例如,可以在浏览器的URL窗口中输入: file:/home/kwalrath/HTML/Hello.html
如果这些步骤成功,可以在浏览器窗口中看到如下的结果:
常见编译和解释错误
如果你在编译Java源代码或运行应用程序时遇到问题,本页的内容 可能会帮助你。如果还不能解决问题,可以参看你使用的编译器或 解释器的文档。 编译问题
Can't Locate the Compiler
在UNIX系统中,如果路径设得不对,可能看到以下的错误信息: javac: Command not found
用setenv或类似的命令修改PATH环境变量,把Java编译器的目录包含 进去。 Syntax Errors
如果程序中的某些部分输入错误,编译器会产生一个语法错误消 息。这个消息通常显示错误的类型、发生错误的行号、错误行的代 码和代码中错误的位置。下面是一个在句末丢失分号的错误: testing.java:14: `;' expected. System.out.println(\"Input has \" + count + \" chars.\") ^ 1 error
有时编译器不能理解你的意图,打印出令人迷惑的错误信息或者很 多行错误信息。例如,下面的代码片段取消了一个分号: while (System.in.read() != -1) count++
System.out.println(\"Input has \" + count + \" chars.\");
当处理这个代码时,编译器发布两条错误信息: testing.java:13: Invalid type expression. count++ ^
testing.java:14: Invalid declaration.
System.out.println(\"Input has \" + count + \" chars.\"); ^ 2 errors
编译器发布两个错误信息,因为在处理count++后,编译器的状 态指出它在表达式的中间。没有分号,编译器无法知道这个句 子结束了。
如果发现编译器错误,那么你的程序就不会被成功编译,也不 产生.clsss文件。仔细检查这个程序,修正错误,然后再试。 Semantic Errors
除了验证程序在语句构成上是正确的,编译器还检查其它基本的 错误。例如,编译器在你使用一个没有被初始化的变量时会警 告你:
testing.java:13: Variable count may not have been initialized. count++ ^
testing.java:14: Variable count may not have been initialized. System.out.println(\"Input has \" + count + \" chars.\"); ^ 2 errors 解释器问题 Can't Find Class
使用UNIX或Windows 95/NT JDK的Java初学者的一个常见错误是 试图编译编译器产生的.class文件。例如,如果你试图解 释HelloWorldApp.class,而不是类HelloWorldApp,解释器显 示如下错误信息:
Can't find class HelloWorldApp.class Java解释器的参数是类名,而不是文件名。 The main Method Is Not Defined
Java解释器要求你执行的类有一个称为main的方法,因为解释 器必须有一个开始执行Java应用程序的地方。
如果试图运行一个没有main方法的类,解释器将显示如下错误 信息:
In class classname: void main(String argv[]) is not defined
这里,classname是你试图运行的类名。 Changes to My Program Didn't Take Effect
有时当你在编辑/调试/运行的循环中,好象你对程序的改变不 起作用- 例如,一个print语句不打印。这在MacOS上使用Java Runner运行Java应用程序时很常见。如果重新编译.class文 件,必须停止Java Runner,然后重新运行,因为Java Runner不 会重新装载类。
面向对象编程
过去几年里你可能听说过很多面向对象编程的事,每个人都在 谈论它。
那么什么是对象和面向对象技术呢?是真事还是骗局呢?事实是 - 两者都有。面向对象技术确实为软件开发者和他们的产品提 供了很多便利。然而,由于历史上很多骗局包围在这种技术 周围,使经理和程序员都很困惑。很多公司认为是这种艰辛的 受害者,然后宣称他们的软件产品的面向对象的,实际上,它 们不是。这些虚假导致困惑的顾客、引起对面向对象技术的广 泛的误导和不信任。
然而,尽管对面向对象技术的过度使用和滥用,计算机产业正 在开始克服这个骗局。人们对它的理解正在逐渐加深。 本课揭穿了骗局,解释了面向对象编程、设计和开发后面的关 键概念。
什么是对象?
如同在“面向对象”这个名称中所隐含的,对象是理解面向对 象技术的关键。你现在可以看看周围的世界,这里有很多现实 世界中对象的例子:你的狗、你的书桌、电视机和自行车。 这些现实世界中的对象有两个相同的特征:它们都有状态和 行为。例如,狗有状态(名字、颜色、种类)和行为(叫、吃 东西)。自行车也有状态(当前档、两个轮子、档的个数)和 行为(刹车、加速、减慢、换档)。
软件对象以现实世界中的对象为模型,它们也有状态和行为。 软件对象在变量中包含它的状态、用方法实现它的行为。
可以用软件对象表示现实世界中的对象,也可以用它表示抽象 概念。例如,GUI窗口系统中的对象中的事件可以表示用户按鼠 标或按键的行为。
在很多语言中,包括Java,一个对象可以选择把它的变量暴露给 其它对象,允许其它对象查看甚至改变变量。同时,对象也可 以把方法隐藏起来,防止其它对象的调用。一个对象对其它对 象能否访问它的变量和方法有绝对的权限。 封装的好处
把相关变量和方法封装在一个软件包中为软件开发者提供了两 个好处:
模块化 - 一个对象的源代码可以独立的编写和维护。 同时,对象也可以容易地在系统中传送。 信息隐藏 - 一个对象有一个公共界面,其它对象可以与 之通讯。但是对象也可以维护私有信息和方法,而不影响 依赖于它的其它对象。
什么是消息?
一个单一的对象本身一般没什么大用,它通常作为包含很多对 象的更大的程序或应用程序的一个组件而存在。通过这些对象 的交互作用,可以得到更高级别的功能和更复杂的行为。 软件对象通过互相发送消息进行交互和通讯。当对象A希望对象 B执行B中的一个方法时,对象A发送一条消息给对象B。
有时,接收信息的对象需要更多的信息,以便确切知道做什
么。这些与消息一起传送的信息叫做参数。 消息由三部分组成: 消息到达的对象。 执行的方法名。 方法需要的参数 消息的好处:
对象的行为通过它的方法表达,因此消息传送支持所有可 能的对象之间的交互。
对象不必在相同的程序或者在相同的机器上,也能互相传 送消息。
什么是类?
在现实世界中,经常有属于同一类的对象。例如,你的自行车 只是世界上很多自行车中的一辆。在面向对象软件中,也有很 多共享相同特征的不同的对象:矩形、雇用记录、视频剪辑 等。可以利用这些对象的相同特征为它们建立一个蓝图。对象 的软件蓝图称为类。
定义:类是定义同一类所有对象的变量和方法的蓝图或原型。例如,可以建立一个定义包含当前档位等实例变量的自行车 类。这个类也定义和提供了实例方法(变档、刹车)的实现。 实例变量的值由类的每个实例提供。因此,当你创建自行车类 以后,必须在使用之前对它进行实例化。当创建类的实例时, 就建立了这种类型的一个对象,然后系统为类定义的实例变量 分配内存。然后可以调用对象的实例方法实现一些功能。相同 类的实例共享相同的实例方法。
除了实例变量和方法,类也可以定义类变量和类方法。可以从 类的实例中或者直接从类中访问类变量和方法。类方法只能操 作类变量 - 不必访问实例变量或实例方法。
系统在第一次在程序中遇到一个类时为这个类建立它的所有类 变量的拷贝 - 这个类的所有实例共享它的类变量。 类和对象
你可能会注意到对象和类的说明很相似。实际上, 类和对象之间的差别经常是一些困惑的起源。在现 实世界中很明显,类不是它描述的对象 - 自行车的 蓝图不是自行车。但是在软件中就有点难区分类和 对象。着部分是由于软件对象只是现实世界的电子
模型或抽象概念。但是也由于很多人用“对象”指 类和它们的实例这两者。 类的好处
对象提供了模型化和信息隐藏的好处。类提供了可 重用性的好处。自行车制造商一遍一遍地重用相同 的蓝图来制造大量的自行车。软件程序员用相同 的类,即相同的代码一遍一遍地建立对象。
什么是继承?
一般来说,对象用类来定义。通过了解类可以了解对象。即使 你不知道penny-farthing是什么,但是如果我告诉你它是一种自 行车,你就会知道它有两个轮子、车把和脚蹬。
面向对象系统比这走得更远,它允许在其它类的基础上定义 类。例如,山地车、跑车是自行车的不同类型。在面向对象的 术语中,山地车和跑车都是自行车类的子类。自行车类是山地 车和跑车的父类。
每个子类从父类继承状态(以变量定义的形式)。山地车和跑 车拥有相同的状态:速度等。每个子类从父类继承方法。山地 车和跑车共享相同的行为:刹车和变档等。
但是,子类不限于他们的父类提供的状态和行为。子类可以在 继承的父类中增加变量和方法。 子类也可以覆盖被继承的方法和提供这些方法的特定实现。可 以不只限于一层继承。继承树,即类的层次,可以尽量深。方 法和变量沿着层次向下传递。一般来说,层次越深,行为越 特定。 继承的好处
子类可以基于父类的一般元素提供特定的行 为。通过使用继承,程序员可以重用父类的代 码很多次。 程序员可以实现定义“一般”行为的父类,称 为抽象类。抽象父类定义和可能部分实现行 为,但是多数类的行为没有被定义和实现。其 它程序员可以在其子类中填充细节。
Java语言
下面例子中的类Count包含一个名为countChars的方法,它从一 个Reader(实现字符输入流的一个对象)读和计算字符然后显示 读出的字符数。即使这样小的方法都使用了Java API中很多传统 语言的特征。 import java.io.*; public class Count {
public static void countChars(Reader in) throws IOException {
int count = 0; while (in.read() != -1) count++;
System.out.println(\"Counted \" + count + \" chars.\"); }
// ... main method omitted ... }
通过对这个简单方法的逐行研究,本课描述了Java语言中的传统 语言特征,如变量和数据类型、操作符、表达式、流程控制等。
变量和数据类型
变量是程序语言中的名词,即它们是执行动作或被执行动作的 实体(值和数据)。countChars方法定义了两个变量 - count 和in。程序每次从变量in中读一个字符,然后count加1。这两 个变量的定义以黑体出现: import java.io.*; public class Count {
public static void countChars(Reader in) throws IOException {
int count = 0; while (in.read() != -1) count++;
System.out.println(\"Counted \" + count + \" chars.\"); }
// ... main method omitted ... }
变量定义包含两部分:变量的类型和变量名。变量定义的位置 决定了它的范围。 数据类型
Java语言中的所有变量都必须有数据类型。变量的数据类型决 定了变量可以包含的值和可以在其上进行的操作。例如,定义int count定义count是一个整数。
Java语言中有两类数据类型:原始数据类型和参考数据类型。 下表按关键字类出了Java支持的所有原始数据类型、它们的大 小和格式和简单描述。 类型 大小/格式 描述 (integers) byte short int long 8-bit two's complement 16-bit two's complement 32-bit two's complement 64-bit two's complement Byte-length integer Short integer Integer Long integer (real numbers) float 32-bit IEEE 754 Single-precision floating point floating point double 64-bit IEEE 754 Double-precision (other types) char 16-bit Unicode character A single character A boolean value (true or false) boolean true or false 原始数据类型的变量包含它的类型的单一值:数字、字符或布 尔值。例如,int的值是整数,char的值是一个16位的Unicode 字符。
数组、类和接口是参考数据类型。参考数据类型的值是对变量 表示的真实值或值的集合的参考。参考数据类型变量不是数组 或对象本身,而是到达它的一种方式。
countChars方法使用参考数据类型in - 一个Reader对象。当用 在语句或表达式中时,in指向对象的参考。因此可以用对象名 访问它的成员变量或它的方法。 变量名
程序通过变量名引用它的值。例如,当countChars方法希望引 用变量count的值时,只需简单地使用名字count。 变量的范围
变量的范围是变量可以被访问和确定变量什么时候被建立和破 坏的代码块。程序中的变量定义的位置建立了它的范围并使之 成为下面四种类型中的一种: 成员变量 局部变量 方法参数 例外处理参数
成员变量是类或对象中的一个成员。它可以在类中任何地方 定义,除了在方法中。它在类中对所有代码可用。Count类没有 定义成员变量。
可以在方法或方法的代码块中定义局部变量。在countChars 中,count是局部变量。count的范围,即可以访问count的代 码,从count的定义开始到countChars方法的结束。一般来说, 局部变量的范围从它的定义到它定义所在的代码块的结束为止。 方法参数变量和异常处理参数将在以后的课程中提到。 变量初始化
局部变量和成员变量可以在它们定义时用赋值语句初始化。赋 值语句两端的数据类型必须匹配。countChars方法在定义count时 初始化它为0: int count = 0;
方法参数变量和异常处理变量不能以这种方法初始化。参数的 值被调用者设置。 Final变量
可以在任意范围内把变量定义为final,包括方法和构造函数的 参数。final变量的值在初始化后不能被改变。 final变量的定义方式如下: final int aFinalVar = 0;
这句话定义了一个final变量并初始化它。以后如果给aFinalVar 赋值,将产生一个编译错误。有必要的话,可以推迟final变量的 初始化。可以定义一个变量,以后再初始化之:
final int blankfinal; . . .
blankfinal = 0;
一个被定义但是还没初始化的final变量称为blank final(空的final)。
操作符
countChars方法使用了几种操作符,包括=、!=、++和+: import java.io.*; public class Count {
public static void countChars(Reader in) throws IOException {
int count = 0; while (in.read() != -1) count++;
System.out.println(\"Counted \" + count + \" chars.\"); }
// ... main method omitted ... }
操作符可以在一个、两个或三个操作数上执行一定的功能。要 求一个操作数的操作符称为一元操作符。例如,++是使操作数加 1的一元操作符。需要两个操作数的操作符称为二元操作符。 如,=是一个从右到左赋值的二元操作符。三元操作符是需要三 个操作数的操作符。Java语言只有一个三元操作符 - ?:,是if-else 的缩写。
Java的一元操作符可以用前缀或后缀。前缀如下所示: operator op 后缀如下所示: op operator
二元操作符在两个操作数中间: op1 operator op2 三元操作符的用法如下: expr ? op1 : op2
除了执行操作,操作符也返回值。返回值和它的类型取决于操 作符和它的操作数的类型。例如,算术操作符返回算术运算的
结果。算术操作符的返回结果数据类型依赖于它的操作数的 类型:如果把两个整数相加,结果是整数。 Java的操作符可以分为以下几类: 算术操作符
Java语言支持所有浮点数和整数的各种算术运算,包括+、-、 *、/,%为取余操作符。下表为一元操作符: 操作符 + - * / % 用法 op1 + op2 op1 - op2 op1 * op2 op1 / op2 op1 % op2 描述 把op1和op2 相加 从op1减去op2 op1乘以op2 op1除以op2 op1被op2除的余 +和-也可以作为一元操作符: 操作符 + - 用法 +op -op 描述 如果是byte、short或char,就提升为int 取负值 ++和--的用法如下: 操作符 ++ ++ -- -- 用法 op++ ++op op-- --op 描述 给op加1,在加1前赋值 给op加1,在加1后赋值 给op减1,在减1前赋值 给op减1,在减1后赋值 关系和条件操作符 关系操作符: 操作符 > >= < 用法 op1 > op2 op1 >= op2 op1 < op2 返回真,如果... op1 大于op2 op1大于或等于op2 op1 小于 op2 <= == != 条件操作符: 操作符 && || ! & | 移位操作符 操作符 >> << >>> & | ^ ~ 赋值操作符 操作符 += -= *= /= %= &= |= op1 <= op2 op1 == op2 op1 != op2 op1 小于或等于 op2 op1 等于op2 op1 不等于op2 用法 op1 && op2 op1 || op2 ! op op1 & op2 op1 | op2 返回真,如果... op1和op2 都是真,条件性的取op2的值 op1或op2为真,条件性的取op2的值 op 为假 op1和op2 都是真,取op1和op2 的值 op1或op2为真,取op1和op2 的值 用法 op1 >> op2 op1 << op2 op1 >>> op2 op1 & op2 op1 | op2 op1 ^ op2 ~op2 操作 右移op1 op2位 左移op1 op2位 右移op1 op2位 (无符号移位) 位与 位或 位异或 位余 用法 op1 += op2 op1 -= op2 op1 *= op2 op1 /= op2 op1 %= op2 op1 &= op2 op1 |= op2 等价于 op1 = op1 + op2 op1 = op1 - op2 op1 = op1 * op2 op1 = op1 / op2 op1 = op1 % op2 op1 = op1 & op2 op1 = op1 | op2 ^= <<= >>= >>>= op1 ^= op2 op1 <<= op2 op1 >>= op2 op1 >>>= op2 op1 = op1 ^ op2 op1 = op1 << op2 op1 = op1 >> op2 op1 = op1 >>> op2
表达式
表达式有双重功能:执行表达式的运算和返回值。 下表说明了Java操作符的优先级: 后缀操作符 一元操作符 新建 乘除 加减 移位 关系 等 位与 位异或 位同或 逻辑与 逻辑或 条件 赋值 [] . (params) expr++ expr-- ++expr --expr +expr -expr ~ ! new (type)expr * / % + - << >> >>> < > <= >= instanceof == != & ^ | && || ? : = += -= *= /= %= &= ^= |= <<= >>= >>>=
流程控制
Java中的流程控制语句包括:
语句 判断 循环 异常处理 其它 if-else语句
关键字 if-else, switch-case for, while, do-while try-catch-finally, throw break, continue, label: , return Java的if-else语句与C的相似,可参看下面的例子程序: int testscore; char grade;
if (testscore >= 90) { grade = 'A';
} else if (testscore >= 80) { grade = 'B';
} else if (testscore >= 70) { grade = 'C';
} else if (testscore >= 60) { grade = 'D'; } else { grade = 'F'; }
swith语句
switch语句基于表达式的值执行不同的语句,下面的例子是根 据month的值执行不同的打印语句的程序段: int month; . . .
switch (month) {
case 1: System.out.println(\"January\"); break; case 2: System.out.println(\"February\"); break; case 3: System.out.println(\"March\"); break; case 4: System.out.println(\"April\"); break; case 5: System.out.println(\"May\"); break; case 6: System.out.println(\"June\"); break; case 7: System.out.println(\"July\"); break; case 8: System.out.println(\"August\"); break; case 9: System.out.println(\"September\"); break; case 10:System.out.println(\"October\"); break; case 11:System.out.println(\"November\"); break;
case 12:System.out.println(\"December\"); break; } 循环语句
有两种循环语句:for循环和do-while循环。
for循环经常用在数组元素或字符串中字符的循环中,如: // a is an array of some kind . . . int i;
int length = a.length;
for (i = 0; i < length; i++) { . . .
// do something to the i th element of a . . . }
for循环的一般形式是: for (初始化; 结束条件; 增量) 语句
do-while是另一种循环方式: do { 语句
} while (布尔表达式);
在满足“布尔表达式”的情况下重复执行“语句”。 异常处理语句: 将在后续课程中讲解。
数组和字符串
Java通过数组对象收集和管理多个值的集合,也可以通过String 对象管理多个字符组成的数据。 数组
在使用数组前要定义之,数组的定义由两部分组成:数组的类 型和数组名。数组中的元素的数据类型必须是相同的。下面是 一个整数数组的定义: int[] arrayOfInts;
下面是一个包含10个整数的数组:
int[] arrayOfInts = new int[10] 一般来说,创建数组的格式如下:
元素类型[] 数组名 = new 元素类型[数组大小] 字符串
一系列的字符数据称为字符串,由Java环境String类来实现。字 符串的定义如下: String[] args
它定义了一个包含字符串对象的名为args的数组。 字符串的连接
可以用“+”很容易地连接字符串。例如: \"Counted \" + count + \" chars.\"
上面表达式中的count在连接时被转换成字符串。
类的简介
下面是一段代表2D空间的点的称为SimplePoint的类的代码: public class SimplePoint { public int x = 0; public int y = 0; }
这个代码段定义了一个类 - SimplePoint。这个类包含两个整数 成员变量,x和y。x和y的定义前的关键字public意味着任何其它 类都可以访问这两个成员。
可以通过初始化一个类来建立对象。当建立一个新的SimplePoint 对象时,就为对象和它的成员x和y分配了空间。同时,对象内的 x和y被初始化为0,因为这两个成员在定义时被赋值为0。
下面是一个类SimpleRectangle,代表一个2D空间
中的矩形:
public class SimpleRectangle { public int width = 0; public int height = 0;
public SimplePoint origin = new SimplePoint(); }
这段代码定义了类SimpleRectangle,包含两个整数成员,width 和height。类SimpleRectangle中也包含了另一个成员,origin,它的 数据类型是SimplePoint。类名SimplePoint在变量定义中作为变量 类型出现。在可以用原始数据类型的地方都可以使用类名。 和width是一个整数,height是一个整数一样,origin是一
个SimplePoint。另一方面,SimpleRectangle对象有一个SimplePoint。 当建立新的SimpleRectangle对象时,为对象和它的成员分配空 间,成员按照它们的定义初始化。有趣的是,origin成员初始化 建立了有代码new SimplePoint()的SimplePoint对象:
了原始数据类型和参考数据类型的区别。width和height
是整数,并且完全包含在SimpleRectangle中。另一方面,origin只 是简单地指向一个SimplePoint对象。
SimplePoint和SimpleRectangle对象是它们的类的实现。这两者都提 供了初始化它们的成员的机制。另外,SimpleRectangle能提供计 算它的面积的方法,而且因为SimpleRectangle创建时建立了一 个SimplePoint,类应该提供当SimpleRectangle清除时SimplePoint也 清除的方式。于是,有一个SimplePoint的新的版本,称为Point, 包含一个可以初始化新的Point的构造函数: public class Point { public int x = 0; public int y = 0; // a constructor!
public Point(int x, int y) { this.x = x; this.y = y; }
上图显示
}
现在,当你建立一个Point时,可以象下面这样初始化: new Point(44, 78)
44和78的值被传递给构造函数,然后象下面这样赋值给新的 Point对象的成员x和y:
下面是SimpleRectangle的一个新版本,称为
Rectangle,包含四个
构造函数,一个用来“移动”矩形;一个计算矩形的面积;一 个finalize方法清除对象: public class Rectangle { public int width = 0; public int height = 0; public Point origin; // four constructors public Rectangle() { origin = new Point(0, 0); }
public Rectangle(Point p) { origin = p; }
public Rectangle(int w, int h) { this(new Point(0, 0), w, h); }
public Rectangle(Point p, int w, int h) { origin = p; width = w; height = h; }
// a method for moving the rectangle public void move(int x, int y) { origin.x = x; origin.y = y; }
// a method for computing the area // of the rectangle public int area() {
return width * height; }
// clean up!
protected void finalize() throws Throwable { origin = null; super.finalize(); } }
这四个构造函数允许不同的类型和初始化。可以建立一个新 的Rectangle然后为所有成员提供缺省值,或者创建对象时初始 化origin、width和height的值。
对象的生命周期
典型地,一个Java程序可以从类中建立很多对象。这些对象通过 互相发送消息相互交互。通过这些对象交互,Java程序可以实 现GUI,运行动画或通过网络接收和发送信息。一旦一个对象完 成了它的工作,就被垃圾收集,它的资源可以被其它对象使 用。下面是对象生命周期的三个阶段: 创建对象
在Java中,通过建立类的实例创建一个对象。一般的语句形式 如下:
Rectangle rect = new Rectangle(); 这个语句执行三个动作:
定义:Rectangle rect是一个变量定义,rect将被指向一 个Rectangle对象。可以注意到类名被用做变量名。 实例化:new是建立新对象的Java操作符。
初始化:Rectangle()是Rectangle的构造函数,用来初始化 对象。 使用对象
对象执行动作有两种方式: 操作或查看它的变量。 调用它的方法。
理想的面向对象编程不鼓励直接操作对象的变量,鼓励通过方 法观察和改变它的状态。这可以防止对象进入不一致的状态。 可以象这样引用对象的变量:
rect.origin = new Point(15, 37); 可以象这样调用对象的方法: rect.move(15, 37); 清除不再使用的对象
Java运行环境在确定对象不再被使用时删除之。这个过程称为垃 圾收集。对象当不被引用时被垃圾收集,也可以显式地通过设 置对象变量为null删除一个对象:
protected void finalize() throws Throwable{ origin = null; super.finalize();
创建类
下面是一个实现后进先出(LIFO)的栈的类的例子:
类定义
(Class Declaration)、类实体(Class Body)、成员变量定义(Variable)、构造函数(Constructor)
和方法(Method)已在 图中清晰地标出。
一个类的实例
下面是一个Java小应用程序(applet)的例子。在这个小应用 程序中,我们显示了如何继承类和实现接口。 当你点击这个applet时,会出现一个斑点:
管理接口
extends定义了一个类是另一个类的子类。一个类只能有一个 父类。那么这些类都是从哪开始的呢?
如下图所示,最高级的类,也就是所有其它类的起源,是 在java.lang中定义的Object类。
Object类定义和实现了Java系统需要的
每个类的行为。它是最一
般的类。它的直接子类实现了一般性的行为;接近层次底部的 类提供了更特殊的行为。
定义:子类是扩展另一个类的类。子类从它的所有祖先继承状 态和行为。父类指一个类的直接子类和所有的继承类。
创建接口
Java语言支持使用定义在类层次中任何地方的任何类中行为的 协议的接口。 什么是接口?
在Java中,接口是不相关的对象相互交互的设备。Java接口与 协议很相似。实际上,其它面向对象语言也有Java的接口的 功能,但是它们称为协议。
Java接口定义了一套方法,但没有实现它们。一个实现接口的 类可以实现定义在这个接口中的所有方法,因此可以实现一定 的行为。
定义:接口是方法定义(而不实现)的被命名的集合。接口也 可以包括常量定义。
最好通过例子来理解接口,所以让我们看看一个接口和两个类 交互的例子。然后我们在抽象概念上讨论接口。 例子:AlarmClock和Sleeper
AlarmClock类是一个服务提供者 - 经过一定的时间后通知对象。 为了得到AlarmClock的“sleepers”清单,一个对象必须做两 件事:
让闹钟把它叫醒。 实现wakeUp方法。
第一步由对象调用AlarmClock的letMeSleepFor方法实现: public synchronized boolean letMeSleepFor(Sleeper theSleeper, long time) { int index = findNextSlot(); if (index == NOROOM) { return false; } else {
sleepers[index] = theSleeper; sleepFor[index] = time;
new AlarmThread(index).start(); return true; } }
如果AlarmClock有空间,它就注册sleepers,为它开始一个新 的AlarmThread,然后返回true。经过一段特定的时 间,AlarmClock调用theSleeper的wakeUp方法。
然后是第二步,一个想使用AlarmClock的对象必须实现wakeUp 方法(于是AlarmClock可以在过了一段时间后调用它来通知 对象)。这通过被注册的对象的数据类型实现。
letMeSleepFor方法的第一个参数是希望被叫醒的对象。这个参 数的数据类型是Sleeper,使用了接口的名字: public interface Sleeper { public void wakeUp();
public long ONE_SECOND = 1000; // in milliseconds public long ONE_MINUTE = 60000; // in milliseconds }
Sleeper接口定义了wakeUp方法但没有实现之。它也定义了两个 有用的常数。实现这个接口的类继承了这两个常数,并且必须 实现wakeUp。
任何是Sleeper的对象都实现了这个接口。这意味着它实现了接 口定义的所有方法。于是一个Sleeper对象实现了wakeUp方法, 满足了第二个需求。
例如,下面的类GUIClock实现了Sleeper接口,显示当前时间, 并使用一个AlarmClock对象每分钟唤醒它一次,达到更新的 目的:
class GUIClock extends Applet implements Sleeper{ . . .
public void wakeUp() { repaint();
clock.letMeSleepFor(this, ONE_MINUTE); } }
这是一个运行的GUIClock的小应用程序:
为什么不只用抽象类?
很多程序员疑惑接口和抽象类有什么区别。接口只是未实现的 因此是抽象方法的清单。下面的Sleeper类与Sleeper接口是一回 事吗?
abstract class Sleeper { public abstract void wakeUp(); }
不,这两者不同。如果Sleeper是一个抽象类,那么所有希望使 用AlarmClock的对象都必须是从Sleeper继承的类的实例。然而,
很多希望使用AlarmClock的对象已经有了一个父类。例
如,GUIClock是一个Applet,它必须是运行在浏览器中的小应用 程序。但是Java不支持多个继承。因此GUIClock不能既是一 个Sleeper,又是一个Applet。所以要用一个接口替代。 这是对问题的实际解释。概念性的解释是这样的:AlarmClock不 能在它的用户上强制类关系。是什么类没有关系,要关心的是 它们实现了一个特殊方法。 那么接口提供了多个继承吗?
接口一般被描述成多个类继承的替代物。虽然接口可以解决类 似的问题,接口和多个类继承还是不同的,尤其是: 一个类只固定地从一个接口继承。 类不能从接口中继承方法的实现。
接口层次独立于类层次。实现相同接口的类不一定通过类层次关联。多个继承不是这样。 然而,Java允许多个接口继承。也就是说,一个接口可以有多个 父接口。
那么,接口能做什么呢? 接口可以用在下列情况下:
不用强制类关系而在不相关的类之间实现相似性。 定义一个或多个类希望实现的方法。 不用类而实现对象编程(称为匿名对象)。
实现嵌套类
在Java中可以把一个类定义为另一个类的成员。这样的类称为嵌 套类,如下所示: class EnclosingClass{ . . .
class ANestedClass { . . . } }
可以用嵌套类反映和强制两个类之间的联系。作为一个类的 成员,嵌套类有自己的特权:可以对嵌套它的类的成员有不受 限制的权限,即使它们被定义为private。
和其它成员一样,嵌套类可以被定义为static,称为静态嵌套 类。非静态嵌套类称为内部类。如下面的例子: class EnclosingClass{ . . .
static class AStaticNestedClass { . . . }
class InnerClass { . . . } }
和静态方法和变量一样,静态嵌套类与嵌套它的类相关联。和 类方法一样,静态嵌套类不能直接指向嵌套它的类中定义的实 例变量或方法 - 只能通过对象参考来访问。
和实例方法和变量一样,内部类与嵌套它的类的实例相关,可 以直接访问对象的实例变量和方法。同时,因为内部类与实例 有关,它不能定义任何静态成员。
Java和Shockwave 作者Eric Eaton
问:我想搞清楚Shockwave和Java各自制作动画的优劣。 为什么更多的人喜欢看Java制作的动画。
答: 我想其中的主要原因应该是:技术人员恨设计人员, 而设计人员害怕技术人员。
技术人员恨设计人员,因为设计人员赚钱多,穿得体面。技 术人员恨Shockwave,因为这种软件使得设计人员有能力制 作出本来只有技术人员才能制作的特技。曾几何时,技术人 员用自己所掌握的计算机技术所制作出的特技迷倒过多少孩 子,而现在Shockwave使没有学过编程的普通人也有可能制 作出这些神奇的学过。
设计人员害怕技术人员因为他们觉得技术人员可以作很复杂 的除法,而设计人员往往在学校上学时数学都不是很好,即 便是算出10除以2等于几也会让他们觉得这已经是一件很不 容易的事。而这些事情对于技术人员来说简直是轻而易举的 事。所以设计容易通常觉得技术人员很伟大。当面对技术人 员时,设计容易往往觉得底气不足。
设计人员讨厌Java因为他们只会使用按钮和菜单,面对代码 他们会觉得象是看天书。他们喜欢只通过按钮和键盘就进行
创作,这样不用考虑复杂的代码。而Shockwave则是他们所 企盼的东西。
现在我正式Java和Shockwave制作动画的区别。Shockwave是 互联网媒体世界中的FIMO。它多姿多彩、丰富有趣、易于使 用。就象是自由市场卖的大批量制作的廉价FIMO首饰。也就 是说,任何人都可以用Shockwave制作出漂亮的玩意儿,但 漂亮不见得品位就高。
Java就象一个神奇的黑匣子,从这个黑匣子中你可以不断发 现神奇和新鲜的
东西,你会爱不释手,但也可能象吸食毒品那样深陷其中, 不能自拔。
不可否认,Shockwave的Director就象是一个一出生就先天 不足的孩子,长得不漂亮,但又不容忽视-它是我必备的工 具之一。我可以写一点lingo, 并且我理解Java的基本原理, 但我不会编程,而且我也不会试图编程。
我很喜欢Director的图形界面。我常把它和Java的功能作类 比。当设计applets时,我常常也将颜色和字体的尺寸考虑在 内, 就象在用Diector直接动画时那样。这是不是有点类似于 告诉你我喜欢什么颜色的内裤?咳,其实谁在乎这些事呢。 不说废话了。总之,Java的功能更强大,但Shockwave更容易 使用。下面的图表列出了两种方式的各自特点: Java 与浏览器集成 是一种语言,不是一种现成的 产品。潜力巨大而且成本低。 面向对象的编程 如果你不是计算机技术人员, 你很难使用它 Macintosh浏览器最近才开始 支持这种语言。 Shockwave 没有被集成到浏览器中,但 Netscape 很快会这样做。 是一种产品,但价格昂贵. 非面向对象的编程 即使设计人员也比较容易掌握 其使用方法。 Director已经具备跨平台应用相 当一段时间。 怎样制作弹出式工具栏的解答
问:怎样创建类似于当你链接到热连线工具栏时跳出的那种时髦的小工具栏? 那种浏览器支持它?
答:我知道你指的是什么。
在非Web应用的世界中,弹出式窗口随处可见。当你打开一文件夹,则出现 一新窗口。当你执行一个命令,则会弹出一个窗口,并且出现几个浮动工具 板。在Photoshop中,就可见5个这种工具板,它使你不用中断思路而快速执 行命令。
但在网上世界中,长久以来我们被定义仅可以以一个窗口工作。于是当网景 的Javascript出现,它在客户机端运行,当脱离服务器时其内容可以是动态的。 它也包含数个组件允许你控制显现环境。用以创建“热连线”网络菜单中的 这种称为“魔术窗口组建”。它创建你自己的工具条,直接放在你的HTML 文件中(一般在
和标签之中)。 (例)这就是你所需要的全部。但这时你尚不能真正开始,你还需要一个事件去触 发它。当一个网页下线完毕后它被激活,或当用户点击一个链接时激活。在 我们的举例中你已看到其效果。其HTML看起来就象如此:
现在你就有了自己的漂亮的小菜单条了。请保证你已彻底清楚其原理了,让 我们一步步来运行: