【IT168 技术文章】
为什么要使用 Java 技术来实现 Web 支持?
Java 平台既是一种应用程序编程语言,又是一个执行环境。它在语法上类似 C++,并具有类似 SmallTalk 的执行环境。Sun Microsystems 在 1994 年首次发布了 Java 语言,Internet 浏览器的广泛应用使得 Java 语言非常流行。现在业界对于 Java 语言的认可已经非常普遍。计算机和软件供应商以及数百家商业公司都已经对 Java 技术非常认可。
Java 语言是一种简单易用的面向对象语言,它是基于上一代面向对象编程语言(SmallTalk 和 C++)最好的一些特性而开发出来的。Java 语言与体系结构无关,具有良好的可移植性、安全性、稳定性,它是一种分布式的、解释性的动态语言,对于目标应用程序来说性能很高。Java 还语言包括一个完整的应用程序执行环境。这些特性综合作用形成以下三个主要的优点:
Java 语言在源代码和二进制文件两个层次上都是与平台无关的;您只需编写一次 Java 代码就可以在任何地方运行这些代码。
Java 语言的面向对象的基本特性可以大力支持重用,并能让您构建现代的应用程序和组件。
Java 语言的标准 API 集可以让您将应用程序部署到由多个不同供应商所提供的执行环境中。
J2EE 的优点
J2EE 是分布式企业级的 Java 应用程序平台。J2EE 的标准环境可以让您分布式地部署应用并重用应用程序组件。J2EE 中基于组件的现代应用程序体系结构可以大力倡导代码的重用,简化应用程序及组件的开发与维护,为部署提供充分的灵活性,允许使用负载均衡和分布组件提高性能,并为应用程序提供更高的可扩展性。
由于 J2EE 的基础是 Java 技术,而 J2EE 本身就是一种标准,因此它具有良好的可移植性。很多 J2EE 的服务器、供应商以及组件都遵循 J2EE 开放标准,这样就为您的开发和部署环境提供了一种选择。Java 和 J2EE 应用程序都是面向网络的,而且是分布式的,这一特性可以允许您编写基于 Web 的瘦客户机应用程序。与原来的 Windows 应用程序或 Windows 客户机-服务器应用程序相比,这种体系结构提供了很多优点,其中包括:
对 Web 浏览器、各种移动计算设备以及其他应用程序提供统一的访问程序的方法
使用完整的 Java 客户机程序访问相同的应用程序可以获得更丰富的交互性和外观
升级应用程序到新版本非常容易,因为终端用户不需要更新或安装任何代码
可以与采用 Java 语言或 J2EE 应用的现代组件的外部应用程序很好地集成,这样可以提高重用性以及应用程序的灵活性
Java 编程基础
在我们潜心研究为应用程序增加 Web 功能之前,首先请您深入了解一下 Java 编程语言。使用 Java 语言,您既可以编写服务器端的应用程序,也可以编写具有 GUI 的客户机应用程序。本文假设您希望使用 Java 语言在服务器端实现一个基于 Web 的界面,因此我们就忽略 GUI 编程环境,并着重介绍 Java 平台非可视化的部分。我们首先简要介绍一下 Java 软件开发包(SDK),然后向您介绍一下如何使用 Java 语言编写一个久负盛名的“Hello World”程序。接下来,我们向您揭示 Visual Basic 6 与 Java 语言之间的区别。
Java SDK
Java SDK 是您在编写和运行 Java 程序时所需要的一组命令行工具和包。Java 程序会首先使用 JIT 编译器编译成一个与平台无关的字节码,然后这个字节码会在运行时再被编译成专用的代码。最重要的工具是 Java 编译器(javac.exe)和 Java 解释器(java.exe),后者用来运行 Java 程序。SDK 还包括一些基本的类(称为 Java 平台),这些类提供了您开始编写应用程序所需要的基本功能和 API。
Sun Microsystems 已经为它的 5 个主要的 Java 平台发行版各自发布发布了一个 SDK。对于本文来说,我们建议您下载最新的 SDK(Java 1.4.2)。Java SDK 是免费的。如果您现在还没有 SDK,可以立即去下载(请参阅 参考资料)。
您可以参考 Java 2 标准版(J2SE)API 的在线文档(请参阅 参考资料)。这些资料是一些 HTML 文档,您可以使用标准的 Web 浏览器进行查看。API 文档是一份必备的参考手册,您可能会频繁地使用这份手册。
Java SDK 基本步骤
使用可以支持长文件名的文本编辑器用普通的 ASCII 码格式编写源代码。
将源代码保存为一个扩展名为 .java 的文件。
所有的 Java 关键字都是小写的。对于变量名和方法名来说,您可以任意使用大小写,但是在一个类中使用时必须保持一致。
在一个命令行提示符中使用 javac 编译器对程序进行编译,javac 是 SDK 中提供的。此时通常应该指定 .java 扩展名。
在命令行提示符中使用随 SDK 一起提供的 Java 解释器运行 Java 程序。此时 不要指定 .class 扩展名。
如果需要,就对代码进行调试。在编译或运行 Java 程序时,可能会出现错误。
安装 SDK
下载 SDK 之后,您需要将其安装到自己的机器上。安装过程非常简单明了。如果在安装过程中提示您选择“典型安装”或“定制安装”,那么您可以选择“典型安装”。(只有当您清楚地了解自己要做什么,并且知道自己不想向机器上安装什么内容时,才应该选择“定制安装”。)安装程序通常会让您选择将标准的 Java 平台类的源代码也安装到机器上。如果您的机器上有足够的磁盘空间,我们建议您接受这个选项。这些文件可以提供一个机会,让您了解构成 Java 语言和标准的 API 的类是如何实现的。这些类的设计和实现通常都非常精巧,您可以从中学到很多知识。
当您安装好 SDK 之后,可能需要对其进行配置,这样才能在您的系统中正确使用。应该如何配置 SDK 取决于您的操作系统和正在使用的 SDK 的版本。SDK 中包括了完整的安装和配置提示。
第一个 Java 程序
现在,您可以编写自己的第一个 Java 程序了—— 也就是闻名于世的“Hello World”。打开您的文本编辑器,然后输入清单1 中的源代码。
清单 1. Hello World 程序
2
3 public static void main(String[] args) {
4
5 System.out.println("Hello World");
6
7 }
8
9 }
10
11
Java 语言是大小写敏感的,而诸如 class 和 public 之类的关键字通常都是小写。对于您自己的变量名和方法名,您可以任意使用大小写,只要在一个类中使用时保持一致即可。当您输入完源代码时,将这些代码保存到一个 HelloWorld.java 文件中。当源代码被保存为这个文件时,我们没有任何选择。Java 源代码使用一个 .java 扩展名,每个 Java 源代码文件的文件名都 必须 与该文件中定义的类同名。我们无论怎么重申这一点都不过分:大小写非常重要,因此诸如 HELLOWORLD.JAVA 和 Helloworld.java 之类的文件名都会导致一个编译错误。您可以将 HelloWorld.java 保存到机器中的任何目录中。但是您在使用命令行工具时,需要进入这个目录,因此应该确保文件保存的位置比较方便。
编译程序
现在您已经准备好编译 HelloWorld 程序了。随 SDK 一起提供的 Java 编译器是一个名为 javac.exe 的命令行程序。要编译一个 Java 源代码文件,您可以简单地向 javac.exe 程序传递一个名为 .java 的文件。要编译 HelloWorld 程序,请打开一个命令行提示符,然后将当前目录切换到您刚才将 HelloWorld.java 保存到的目录中。接下来执行下面的命令:
javac HelloWorld.java
与 Visual Basic 的编译器类似,Java 编译器可能会产生很多错误。通常,在 Java 编译器成功编译 HelloWorld 程序之前,您需要首先改正这些错误。一次成功的编译会生成一个名为 HelloWorld.class 的文件。这个文件代表您要在 Java 解释器中运行的可执行文件。
运行程序
与 SDK 一起提供的 Java 解释器是一个名为 java.exe 的命令行程序。要运行一个 Java 字节码可执行程序,您可以简单地向 Java 解释器传递一个 Java 程序名。当您使用 Java 解释器时,请不要指定 .class 扩展名。解释器只希望接受类文件,因此如果添加上 .class 扩展名会导致产生错误。要运行您的 HelloWorld 程序,请打开一个命令行提示符,然后将当前目录切换到您刚才编译 HelloWorld.java 文件所在的地方。您的字节码可执行文件 HelloWorld.class 应该就在这个目录中。然后,执行下面的命令:
java HelloWorld
Java 解释器试图执行 HelloWorld 程序的 main() 方法。返回类型为 void 的 Java 方法等价于 Visual Basic 中的 Sub 。返回其他类型的 Java 方法等价于 Visual Basic 中的 Function 。
Java 解释器可能会报告一个运行时错误,这通常会中断程序的执行。像在 Visual Basic 中一样,Java 运行时错误比编译时错误更难调试,但是出现的频率也比后者少。当运行时错误发生时,您可以从容地处理这些错误,因为 Java 程序是在一个受控的环境中执行的,这样可以减少那些“肆意运行的代码”导致整台机器崩溃的可能性。
现在,您对 Java 代码的大概样子以及应该如何在您的测试机器上编译并运行 Java 程序有了一个基本的概念,已经准备好开始深入了解 Java 语言的结构和语法了,包括 Java 编程环境和 Java 中标准的数据类型。由于您已经对 Visual Basic 中的编程非常熟悉,因此我们将通过对 Java 和 Visual Basic 进行比较来学习 Java 编程的知识。我们将通过对 Java 平台上的基本组件与 Visual Basic 编程框架中相应组件的对应关系及其区别对 Java 平台上的基本组件进行介绍。如果您是一个 C/C++ 程序员,就可以跳过本节,转而参考免费的 developerWorks中的“ Java programming for C/C++ developers”指南。
Visual Basic 执行环境
Visual Basic 是一种高级编程语言;其目的是简化人类开发计算机程序的过程。计算机并不能理解高级语言;它们只能理解低级的机器语言——即可以直接在计算机的处理器上执行的二进制指令序列。出于这个原因,使用高级语言编写的程序在计算机上运行之前必须被转换成机器语言的程序——即可执行程序。不同的计算机使用的机器语言也不同。在一台计算机上可以运行的可执行程序在其他使用不同机器语言的机器上可能无法运行。
将高级编程语言的可执行程序转换成机器语言的可执行程序有两种方法:编译和解释。编译是将使用高级语言编写的程序全部转换成一个完整的机器语言程序,后者就完全可以执行了。解释是将一个高级程序逐行转换成机器指令;每一行指令都是在下一行指令之前被翻译并执行的。编译和解释在逻辑上是等价的,但是编译后的程序的执行速度比解释后的程序速度更快。Visual Basic 程序使用一个称为编译器的程序编译成机器语言可执行程序。
Java 执行环境
与 Visual Basic 程序一样,Java 程序也会被编译。但是 Java 程序有一点与 Visual Basic 程序不同:Java 程序并不会被编译成与平台相关的机器语言。相反,Java 程序被编译成一种与平台无关的语言,称为 字节码。虽然字节码与机器语言非常类似,但是字节码并不是被设计用来在真正的物理机器上运行的。相反,它被设计用来通过一个名为 Java 虚拟机(JVM)的程序运行,JVM 模拟了一台真正的机器。
简单来说,JVM 是一个解释器,负责将 Java 字节码转换成真正的机器语言指令,后者可以在底层的物理机器上执行。更具体一点说,术语 Java 虚拟机通常就用来表示执行 Java 类文件的任何程序。Java 解释器程序 java.exe 就是 JVM 的一个特定实现。
Java 平台使用虚拟机器层来确保使用 Java 语言编写的程序是与平台无关的。Java 程序一旦被编译成字节码之后,就可以在具有 JVM 的任何系统上运行。这些系统包括 UNIX、Linux、Windows 以及其他很多系统。使用其他语言编写的程序必须对每一种要在其上执行的平台都重新进行编译,而 Java 程序只需要编译一次即可。
数据类型
Java 语言有两种不同类型的数据:类定义(或将其作为 SDK 或第三方类库的一部分使用)和“原始的”(primitive)类型( boolean , char , byte , short , int , long , float 和 double ),Java 运行库可以直接理解后者。大部分 Java 原始类型在 Visual Basic 中都有等价的类型,用户定义的类与 Visual Basic 中的类大部分都非常相似。表 1 列出了 Java 语言中的原始数据类型及其在 Visual Basic 中等价的类型。
表 1. Java 语言的原始数据类型及其在 Visual Basic 中对应的数据类型Java 原始类型 范围 Visual Basic 类型 说明 boolean
true, false Boolean
booleans 类型的合法值只有 true
和 false
。char
0 - 65535 String
(长度为 1)Java 语言使用 Unicode 对字符进行编码。 byte
8位整数(有符号) Byte
? short
16位整数(有符号) Integer
? int
32位整数(有符号) Long
? long
64位整数(有符号) N/A ? float
32位浮点数 Single
? double
64位浮点数 Double
? N/A ? Variant
Java 语言没有 Variant
类型。N/A ? Date
Java 语言没有对应的原始数据类型。您可以使用 Date
类。N/A ? Currency
Java 语言没有对应的原始数据类型。您可以使用 BigDecimal
类。
清单 2 给出了在两种语言中声明原始数据类型的一些例子。
清单 2. 声明原始数据类型
2 Option Explicit // Note that all Java
3 Dim LetterJ As String, I As Integer, x As Byte // variables must be declared
4 Dim Point1 As Single, Point2 As Double // before they can be used
5 Dim IsEmpty As Boolean char letterJ = 'j';
6 LetterJ = "j" int i = 0;
7 I = 0 byte x = 12
8 X = 12 boolean isEmpty = false;
9 IsEmpty = False float point1 = 1.1F;
10 Point1 = 1.1 double point2 = 0.0025;
11 Point2 = 0.0025
12
操作符
在 Visual Basic 和 Java 语言中,操作符具有很多共同点,也有很多关键的区别。Java 语言使用的操作符集与 C 和 C++ 使用的操作符集完全相同。表 2 类出了在这两种语言中最常使用的一些操作符。
表 2. Visual Basic 和 Java 语言中的操作符Java 操作符 说明 用法 Visual basic 操作符 说明 ++
自增 ++num
num++ N/A 这个一元操作符允许对一个非浮点数字执行自增运算。 --
自减 --num
num-- N/A 这个一元操作符允许对一个非浮点数字执行自减运算。 *
/
乘
除num * num
num / num
*
/
? /
整除 num/num
\
Java 语言对于整除和数学除使用同样的操作符。如果操作符是整数,那么就执行整除运算。 %
取模 num % num
Mod
? +
-
加
减num + num
num - num
+
-
? +
字符串拼接 str + str
&
? <
<=
Less小于 than
Less 小于或等于than or equalexpr < expr
expr <= expr
<
<=
? >
>=
大于
大于或等于expr > expr
expr >= expr
>
>=
? !=
不等于 expr != expr
<>
? ==
等于(基本类型) expr == expr
=
? ==
等于(对象) obj == obj
Is
? !
逻辑非 !boolean
Not
Visual Basic 对整数执行的位反和对布尔表达式所执行的逻辑非运算使用相同的操作符,而 Java 则不是。 ~
位非 ~int
Not
? &
位 AND
布尔 ANDint & int
expr & expr
And
? |
位 OR
布尔 ORint | int
expr | expr
Or
? ^
位 XOR
布尔 XORint ^ int
expr ^ expr
Xor
? &&
条件 AND if (expr &&expr)...
And
Java 语言对条件与和逻辑布尔与使用不同的操作符。 ||
条件 OR if (expr || expr) ...
Or
Java 语言对条件或和逻辑布尔或使用不同的操作符。 N/A 模式匹配 str Like pattern
Like
您可以使用 Java 中 String 对象的方法来实现 Visual Basic 中这个操作符的功能。
Visual Basic 的函数和子过程与 Java 方法的比较
在 Visual Basic 中您可以定义函数和子过程。函数与子过程之间的主要区别是子过程没有返回值,而函数有返回值。在 Java 术语中,函数被称为 方法 。Java 语言中没有与 Visual Basic 中的子过程等价的概念。然而,在 Java 语言中,您可以使用关键字 void 定义一个没有返回值的方法,这基本上就等价于一个子过程。您可以将 Java 方法声明为类的成员;但是不能在一个 Java 类之外定义方法。清单 3 给出了一个具有返回值的 Java 方法和一个没有返回值的 Java 方法。
清单 3. Java 方法的返回类型
2
3 // This method doesn't return a value
4
5 public void myMethod1(String arg) {
6
7 ...
8
9 }
10
11 // This method returns an integer
12
13 public int myMethod2(String arg) {
14
15 int i;
16
17 ...
18
19 return i ;
20
21 }
22
23
数组
在 Java 语言中,数组是具有很多属性的对象,其中最重要的是 length 属性,您可以使用这个属性来确定数组的大小。Java 数组通常是从 0 开始索引的,并且数组声明的大小中就包含了第 0 个元素。因此,大小为 100 的数组就意味着其有效的索引值是从 0 到 99。还有,方括号([ ])标记用来说明数组的类型,而不是数组名。Java 语言允许您将数组初始化为预设的一组值。清单 4 给出了一些例子。
清单 4. 数组
2 'An array with 100 integers // An array of 100 integers
3 Dim a(99) As Integer int[] a = new int[100];
4
5 'An array of Strings initialized // An array of Strings initialized
6 b = Array("Tom","Dick", "Harry") String[] b = {"Tom","Dick", "Harry"};
7
8 'Iterating through an array // Iterating through an array of length 100
9 ' of length 100 int [] c = new int [100];
10 Dim c(99) As Integer for (int i = 0; i <.length; i++) {
11 For i=0 To UBound(c) c[i] = i;
12 c(i) = i }
13 Next
14
15
字符串
Visual Basic 有一个 String 数据类型来表示字符串。您可以使用 String 类来表示 Java 中的字符串。Visual Basic 和 Java 中的字符串都可以表示成使用引号括起来的一系列字符。在 Java 语言中,您有两种方法可以创建一个 String 对象:您可以使用一个字符串声明,也可以使用一个 构造函数。 String 对象是不可变的,这就是说一旦您为一个 String 对象赋了一个初值,就不能再修改它了。换而言之,如果您希望修改一个 String 引用对象的值,就必须为这个引用分配一个新的 String 对象。由于 Java 中的字符串就是对象,因此您必须使用 String 类中定义的 接口才能与其进行交互。(在本文后面 面向对象编程简介 一节中您将学习到更多有关构造函数和接口的知识。) String 类具有非常丰富的接口,其中提供了一些非常有用的方法。
清单 5 向我们展示了一些最常用的方法。请尝试编译并运行这个例子。记住将源文件命名为 StringTest.java,不要忘记文件名的大小写是非常重要的。
清单 5. Java 语言中的字符串
2
3 * The StringTest class simply demonstrates
4
5 * how Java Strings are created and how
6
7 * String methods can be used to create
8
9 * new String objects. Notice that when you
10
11 * call a String method like toUpperCase()
12
13 * the original String is not modified. To
14
15 * actually change the value of the original
16
17 * String, you have to assign the new
18
19 * String back to the original reference.
20
21 */
22
23 public class StringTest {
24
25 public static void main(String[] args) {
26
27 String str1 = "Hi there";
28
29 String str2 = new String("Hi there");
30
31 // Display true if str1 and str2 have the value
32
33 System.out.println(str1.equals(str2));
34
35 // A new uppercase version of str1
36
37 System.out.println(str1.toUpperCase());
38
39 // A new lowercase version of str2
40
41 System.out.println(str1.toLowerCase());
42
43 System.out.println(str1.substring(1,4));
44
45 // A new version of str1 w/o any trailing whitespace chars
46
47 System.out.println(str1.trim());
48
49 // Display true if str1 start with "Hi"
50
51 System.out.println(str1.startsWith("Hi"));
52
53 // Display true if str1 ends with "there"
54
55 System.out.println(str1.endsWith("there"));
56
57 // Replace all i's with o's
58
59 System.out.println(str1.replace('i', 'o'));
60
61 }
62
63 }
64
65
main() 方法
要在命令行中作为应用程序运行的 Java 类必须定义一个 main() 方法才能运行。在 Java 代码中, main() 方法遵循一个严格的命名约定。您可以按照下面的方式声明 main() 方法:
2
注意: 您可以保留 public 和 static 修饰符,您也可以将 String 数组命名为任何您喜欢的名字。然而要记住上面的格式是常规的用法。并不是所有的类都需要一个 main() 方法 —— 只有那些从命令行中运行的程序才需要。一个典型的 Java 应用程序有一个具有 main() 方法的类和其他几个没有 main() 方法的支持类。
包
像 Java 这种面向对象的语言有助于促进类的重用。由于大部分程序员在自己的类中都使用非常简单的描述性名字(例如 Invoice 或 User ),因此在重用他人提供的资源时命名发生冲突的可能性就会很高。Java 语言通过让每一个类都属于一个 包 来解决这个问题。您可以同时使用不同包中的同名类。要将一个类与一个包关联在一起,您必须在类的源代码中的第一行就使用 package 语句进行声明。下面是一个例子:
2
按照约定,您的包名的前缀要采用 Internet 域名反转的形式(例如 com.yourco.somepackage )。要使用其他包中的类,您有两种选择。第一种选择是使用该类的全名,其中包括包名。清单 6 给出了一个例子。
清单 6. 使用完整的类名
2
3 public static void main(String[] args) {
4
5 Java.util.Date today = new java.util.Date();
6
7 System.out.println("The date is " + today);
8
9 }
10
11
另外一种方法是在源代码文件中对其他包中的类使用一个 import 语句。这样就不再需要全名了,如清单 7 所示。
清单 7. 使用 import 语句
2
3 public class PackDemo1 {
4
5 public static void main(String[] args) {
6
7 Date today = new Date();
8
9 System.out.println("The date is " + today);
10
11 }
12
13
您可以使用通配符来导入一个包中的所有类。如果您需要使用同一个包中的多个类,这种功能就会非常有用,如清单 8 所示。
清单 8. 使用带有通配符的 import 语句
2
3 public class PackDemo1 {
4
5 public static void main(String[] args) {
6
7 Date now = new Date();
8
9 System.out.println("The date is " + today);
10
11 }
12
13
包的重用
在 Visual Basic 中,您可以先编写一些代码,并将其编译为动态链接库(DLL),所谓动态链接库就是文件系统中一个扩展名为 .dll 的文件。其他程序可以通过引用这些 DLL 来使用其中包含的代码。Java 语言还允许您将多个类文件打包成称为 Java 档案包(JAR)的文件,以便易于重用。具有 .jar 扩展名的文件是一种标准的压缩文件,可以使用 WinZip 或其他压缩工具进行维护。然而,为了方便起见,Java SDK 包含了一个名为 jar.exe 的工具(在 Windows 平台上),您可以使用这个工具将多个类打包到一个 .jar 文件中。
在我们向您介绍一个使用 jar.exe 工具的例子之前,重要的一点是您需要理解包名与 Java 平台用来构建类并在运行时装入类时所使用的目录结构有关。考虑一个名为 Test 的类,其源代码文件名为 Test.java。如果您将 Test.java 定义成包 com.mycompany.test 的一部分,那么编译器就为生成的 .class 模块创建一个目录树。这个目录树是根据包名生成的,在本例中这个目录树是 com\mycompany\test,其中包名中的小数点被转换为一级级的目录。
现在,请打开一个命令提示符,并创建一个目录(例如 c:\javapack)。然后将当前路径切换到该目录中( cd javapack ),并使用您喜欢的文本编辑器将清单 9 中的代码添加到一个名为 Test.java 的新文件中。
清单 9. 使用包的例子
2
3 public class Test
4
5 {
6
7 public static void main(String[] args) {
8
9 System.out.println("In test");
10
11 }
12
13 }
14
15
现在,使用下面的命令编译 Test.java( -d 选项应该指向您刚才为本例创建的目录):
现在在 c:\javapack 目录中应该已经有了一个名为 com 的子目录。实际上,您可以看到 comTest.class 文件的完整名,该文件是在对 Test.class 编译时产生的。注意包名( com.mycompany.test )如何被转换成相应的目录结构(com\mycompany\test),该目录结构相对于您刚才使用 -d 选项指定的目录。
现在,我们将向您展示 Test.class 如何才能更易于其他类重用。在 c:\javapack 目录中运行这个命令:
2
这个命令会创建一个文件,文件名为 Test.jar,其中包含了 com 子目录中的所有类。
运行下面这个命令,使用 Test.jar 文件中的类:
2
注意您只能使用全名才能在命令行中运行这个命令,还要注意使用 -classpath 选项指向 Test.jar 的方法。另外,您可以将 Test.jar 文件加入 CLASSPATH 环境变量中, CLASSPATH 环境变量是一个使用冒号分割的列表,每一项的内容是 JAR 文件或目录,Java 编译器和 JVM 都会使用这个环境变量来查找要装载的类。
其他区别
我们已经了解了 Java 语言 与 Visual Basic 之间主要的一些语法区别,它们之间还有以下一些区别:
全局变量:与 Visual Basic 不同,Java 语言并不提供声明全局变量(或方法)的方法。
GoTo : 虽然 Java 语言保留了 goto 作为一个关键字,但是它并没有类似于一个 Visual Basic 用法的 Goto 语句。
自由放置变量:您根据需要可以在任何地方声明 Java 变量。您并不需要集中声明变量,但是在 Visual Basic 中必须如此。
继承:Visual Basic 并不会让您定义对其他类的功能进行扩展的类。Java 语言可以让您定义一个继承除超类私有成员之外的部分的类。这些新类可以对所继承的类进行扩展,也可以重载所继承的成员的一些行为。(在下一节中您将了解更多有关继承的内容。)
Java 一种面向对象的编程语言。Visual Basic 有很多对象特性,但是它却不是一种严格的面向对象的语言。在本节,我们将向您介绍如何在 Visual Basic 中构建一个类,然后再介绍如何在 Java 语言中构建一个等价的类。
类的使用
您可以认为 类就是您要定义的一种数据类型。一个类的变量实例称为 对象。与其他变量不同,对象具有类型、一组属性以及一组操作。对象的类型可以使用该对象实例化时所使用的类表示。对象的属性表示该对象的值或状态。对象的操作是您为了改变对象状态而调用的所有函数集。
考虑一下 Visual Basic 的基本数据类型 Integer ,它表示一个整数。您可以使用这种类型来创建一些变量,这些变量是一个整数的实例。每个 Integer 变量都有一个属性,这个属性表示该变量所持有的是整数数值。每个 Integer 变量都有相同的操作集,这些操作可以修改变量的状态(或值)。您可以对 Integer 变量执行的操作包括:加(+)、减(-)、乘(*)、除(\)以及取模(Mod)。
定义 Visual Basic 类
现在,让我们假设这样一种情况:您希望开发一个自己的类型——它可以表示一个复杂的对象,而在 Visual Basic 语言的基本类型并不支持这种类型。假设您是一名金融系统软件开发小组的成员,而您的任务是开发一些代码来表示一个典型的银行帐号。虽然一个银行有很多种帐号,但是每个帐号都有一些相同的基本属性和操作。具体来说,每个帐号都有一个余额和一个 ID 号。清单 10 中给出的 Visual Basic 代码就定义了一个 account 类。该类中定义三个操作: Deposit , Withdrawal 和 InitAccount (用来对帐户余额和帐户号码进行初始化)。注意您是如何使用一个私有变量来记录实际的帐号余额的,并且定义了一个名为 Balance 的属性以便让使用该类的用户可以获得帐户余额。
清单 10. 定义 Visual Basic 类
2
3 Private theAccountNumber As Integer
4
5 Public Sub InitAccount (number As Integer, initBal As Currency)
6
7 theAccountNumber = number
8
9 theBalance = initBal
10
11 End Sub
12
13 Public Sub Deposit (amount As Currency)
14
15 theBalance = theBalance + amount
16
17 End Sub
18
19 Public Sub Withdrawal (amount As Currency)
20
21 theBalance = theBalance - amount
22
23 End Sub
24
25 Public Property Get Balance() As Currency
26
27 Balance = theBalance
28
29 End Property
30
31 Public Property Get AccountNumber() As Integer
32
33 AccountNumber = theAccountNumber
34
35 End Property
36
37
定义 Java 类
清单 11 使用 Java 语言实现的了 Account 类。
清单 11. Java 语言 Account 类
2
3 private double balance;
4
5 private int number;
6
7 public Account(int number, double balance) {
8
9 number = argNumber;
10
11 balance = argBalance;
12
13 }
14
15 public void deposit(double amnt) {
16
17 balance += amnt;
18
19 }
20
21 public void withdrawal (double amnt) {
22
23 balance -= amnt;
24
25 }
26
27 public double getBalance() {
28
29 return balance;
30
31 }
32
33 public int getNumber() {
34
35 return number;
36
37 }
38
39 }
40
41
正如您可以看到的一样,定义一个 Java 类与定义一个 Visual Basic 类并不相同。这两种语言各自的 account 类间的主要区别如下:
在 Java 代码中,您并不需要使用一个单独的方法初始化一个 Account 类的实例。您使用的是 构造函数(constructor)。顾名思义,您可以使用构造函数来构造一个类的实例。构造函数名必须与定义它的类名相同,而且构造函数可以接收参数。您可以对一个类创建多个构造函数。如果您没有提供构造函数,那么程序可以自动使用一个没有参数的默认构造函数。您可以使用下面的方法来使用清单 11 中构造函数:
2
与 Visual Basic 不同,Java 语言对属性没有特殊的规定。按照约定,Java 属性都是私有的域,您通常会提供一组称为访问方法(accessor)的方法对包含这些属性的域进行访问。用来返回属性值方法称为 取值方法(getter),用来设置属性值的方法称为 赋值方法(setter)。下面是一个赋值方法的例子:
2
对一个类成员的默认访问修饰符不是 public ,这与 Visual Basic 不同(后文会进一步介绍访问修饰符)。
对象的优点
在诸如 Java 之类的面向对象语言中使用类和对象有三个主要的优点: 封装(encapsulation)、 继承(inheritance)和 多态(polymorphism)。
封装(或信息隐藏)是指将一个对象看作一个“黑盒”;也就是说,您可以不用知道(或关心)一个对象是如何实现的就可以使用这个对象。通过类中定义的方法(操作)所定义的接口来访问对象可以修改类的实现,而不会破会使用该类对象的任何代码。
多态是对相同的名字关联不同特性、而且可以根据上下文选择正确特性的能力。多态最常见的例子是方法的重载,此时您可以定义名字相同的多个方法,前提是这些方法使用不同的参数。
继承 是指通过编写对现有的类进行扩展的新类而对代码进行重用。例如,让我们假设您希望编写一个新类来代表一个支票帐号。由于支票帐号是一种特殊的银行帐号,因此您可以编写一个 CheckingAccount 类(称为子类)对 Account 类进行扩展。这样 CheckingAccount 类就可以自动获得 Account 类中的所有状态和所有操作(函数)。您只需要向 CheckingAccount 类中添加一些特殊的新状态和操作即可。例如,您可以添加一个 cashCheck() 函数来执行为支票帐号存入现金的操作。如果需要,您还可以修改子类继承的状态或行为。例如,一个用户可能会被允许从她的支票帐号中提款,因此您就可能需要重载原来的 withdrawal 函数。
现在您已经理解了类和对象在面向对象编程框架中的基本角色,也已经准备好专心深入了解 Java 平台上类的结构和实现的一些特性了:
类成员:类成员通常或者是一个 域,或者是一个 方法。域代表数据,方法代表操作。类可以定义任意多个成员。
访问修饰符:您可以使用 访问修饰符来声明类成员,这样可以指定类之外的元素对这些成员是否可访问。例如,被声明为私有的成员在类之外根本不能访问,但是公开成员可以自由访问。
对象:类实际上只是定义而已。您在代码中真正使用的是类的实例,称为 对象。后面您将了解到如何从类创建对象。
构造函数: 构造函数是一个用来创建对象的特殊操作。通常来讲,如果您不能对一个类创建对象,那么这个类也就没多少用处了。构造函数非常重要,因为它们提供了创建新类的实例的能力。
this 关键字: 隐式引用 Java 对象本身。您必须理解如何为这种目的而使用 this 关键字,这非常重要。
类成员
Java 类是一个独立的代码模块,其中以 成员的形式定义了很多属性和操作。域和方法就是成员的例子。
域是在类中声明的变量。Java 域有两种变种: 实例变量和 类变量。实例变量与类的每个实例有关,每个实例都有自己的一份实例变量的副本。类变量使用 static 关键字进行声明,与类整体有关,该类会与所有的类实例共享一个类变量。例如, BankAccount 中的 balance 域就是一个实例域,因为每个 BankAccount 实例都有自己的 balance 域,这与其他所有的 Account 对象的 balance 无关。另外一方面,您可以将一个 interest 域声明为一个类域,因为每个 BankAccount 对象都使用相同的利率。
方法是在类中声明的函数。Java 方法有两种变体: 实例方法和 类方法。对于实例方法来说,每个类实例都有自己的实例方法的副本;但是类方法则只有一份副本,所有的类实例都要共享这个副本。您可以使用 static 关键字来声明类方法。您应该对实例变量使用实例方法,对类变量使用类方法。例如, BankAccount 类中的 deposit() 方法是一个实例方法,因为每个 BankAccount 都有自己的 balance 域, deposit() 方法就会修改该域的值。您可以将 setInterest() 方法声明为一个类方法,因为所有的 BankAccount 都共享这个唯一的 interest 域,而 setInterest() 方法会修改该域的值。
清单 12 中给出的 BankAccount 类有 5 个成员。其中两个成员是域: balance 和 interest ,前者是一个实例域,后者是一个类域;三个成员是方法: deposit() 和 withdraw() 是实例方法,而 setInterest() 是类方法。注意您要使用对象名来访问实例成员,使用类名来访问类成员。
清单 12. BankAccount 类
2
3 public class BankAccount {
4
5 private float balance; // an instance field
6
7 private static float interest; // a class, or static, field
8
9 // an instance method
10
11 public void deposit(float amount) {
12
13 balance += amount;
14
15 }
16
17 // an instance method
18
19 public void withdraw(float amount) {
20
21 balance -= amount;
22
23 }
24
25 // a class, or static, method
26
27 public static void setInterest(float interestRate) {
28
29 interest = interestRate;
30
31 }
32
33 public static void main(String[] args) {
34
35 // create a new account object
36
37 BankAccount account = new BankAccount();
38
39 // deposit $250.00 into the account
40
41 account.deposit(250.00F);
42
43 // set interest rate for all BankAccount objects
44
45 BankAccount.setInterest(5.0F);
46
47 }
48
49 }
50
51
访问修饰符
与 Visual Basic 类似,Java 语言允许您对类成员的可见性进行设置。Java 成员使用 public 修饰符来说明一个成员在类内和类外都可以自由访问,使用 private 修饰符来说明一个成员只能在类内使用。私有成员在类外是不能访问的。
现在让我们再次考虑一下 BankAccount 类。假设您希望使用 BankAccount 对象的其他程序员也可以使用 deposit() 和 withdraw() 方法来修改 balance。那么您就需要将这两个方法声明为 public 类型的,这样就可以在 BankAccount 类之外的代码中调用这个类了。然而,您并不希望其他程序员直接修改 balance 域,因此就要将 balance 域声明为 private 类型的。
您可能会正在考虑默认的访问权限到底是哪个级别——也就是说,那些您既没有声明为 public 也没有声明为 private 的类成员的访问级别是什么呢?您可能会猜想默认的访问级别应该是 public ,在 Visual Baisc 中默认的访问级别就是 public 。实际上,在 Java 语言中默认的访问级别称为 包访问,因为只有同一个包中的类才有权访问这些类成员。如果您希望将一个成员声明为包访问的,就不要使用任何访问修饰符关键字。
Java 语言另外定义了一个访问级别,称为 受保护级别。当您希望在子类中可以访问一个超类的成员时,就可以使用 protected 修饰符。
创建对象
如果您浏览一下清单 12 中 BankAccount 类的 main() 方法就会看到创建一个新 BankAccount 对象的代码,如下所示:
2
首先,您声明了一个 BankAccount 类型的对象(也就是一个变量)。正如您可能猜到的一样, new 关键字会申请足够的内存来创建一个新对象。新创建的对象实际上是使用这个语句创建的: BankAccount() 。这条语句很像是一个方法调用。然而,清单 12 并没有声明一个这样的方法,因此您可能会纳闷这条语句到底执行什么操作。
实际上,这条语句是一个构造函数调用。如果没有构造函数,您就不能创建 Java 对象,因此如果您编写了一个没有构造函数的类,那么编译器就会为您创建一个默认的构造函数。这就是为什么即使我们没有在 BankAccount 类中显式地编写一个构造函数,仍然可以可以调用 BankAccount() 。
显式引用
Java 语言使用 this 关键字来引用当前对象。您可以使用 this 关键字显式地引用当前类中的域、方法和构造函数。
this 关键字最常见的用法是用来解决变量作用范围的问题。例如, BankAccount 类有一个 balance 域。让我们假设您希望编写一个名为 setBalance(float balance) 的方法,这个方法可以设置该对象的 balance 域。问题是在 setBalance(float balance) 方法中,当您引用 balance 时,您实际上是在引用 balance 参数,而不是 balance 域。您可以使用 this 关键字显式地引用这个域,方法如清单 13 所示。
清单 13. this 关键字
2
3 public void setBalance(float balance) {
4
5 this.balance = balance;
6
7 }
8
继承是面向对象编程最重要的优点之一。它是如此重要,以至于您为了最高效地利用继承的特点,就必须正确理解继承的概念。继承包括以下一些主要概念:
extends 关键字: 继承是在对类进行声明时定义的。您可以使用 extends 关键字来指定您正在编写的类的超类。
构造函数:在子类中并不能继承构造函数,但是您通常可以在子类的构造函数中调用超类的构造函数。
重载/覆盖: 重载是指编写多个名字相同但是参数不同的方法。 覆盖是指在子类中修改所继承的方法的实现。
Object 类: 所有的 Java 对象最终都是从 Object 类继承来的, Object 类定义了每个 Java 对象具有的所有基本功能。
接口: 接口是对行为的描述,但是并不提供实现。
扩展类
在 Visual Basic 中,一个类不能继承其他类,但是 Java 语言允许单继承。继承是一种代码重用方法。如果类 A 继承了类 B(或者说类 A 对类 B 进行了扩展),那么类 A 就自动继承了类 B 中的所有 public 和 protected 类型的成员。如果类 A 与类 B 在同一个包中,那么类 A 还会继承所有具有默认(或 包)访问权限的成员。但是有一点非常重要,需要提醒大家注意,子类永远不会继承它们所扩展的超类的的私有成员。
当您对一个类进行扩展之后,就可以在新类中添加用来定义与超类中不同的属性和操作的新域和新方法了。而且,您也可以 覆盖子类中那些与超类行为不同的操作。
在定义类时,您可以显式地对一个类进行扩展。要扩展一个类,您只需要子类名后面简单地跟上 extends 关键字及要扩展的类名即可。如果您没有显式地对一个类进行扩展,那么 Java 编译器就会自动对 Object 类进行扩展。这样,所有的 Java 对象最终都是 Object 类的一个子类。
扩展的例子
让我们假设您想创建一个新的 CheckingAccount 类。 CheckingAccount 是一种特殊的 BankAccount 。换而言之, CheckingAccount 与 BankAccount 具有相同的属性和操作。然而, CheckingAccount 多了一个操作——存入现金。因此您可以定义 CheckingAccount 类,使其对 BankAccount 进行扩展,并添加一个 cashCheck() 方法,如清单 14 所示。
清单 14. 扩展类
2
3 public void cashCheck(float amount) {
4
5 withdraw(amount);
6
7 }
8
9 }
10
11
子类的构造函数
构造函数实际上并不是类的成员,构造函数也不会被子类继承。 BankAccount 构造函数创建的是 BankAccount 对象,因此您不能在 CheckingAccount 类中使用它来创建 CheckingAccount 对象。然而,您可以使用超类的构造函数作为子类的一部分使用。换而言之,您通常需要在子类的构造函数中调用超类的构造函数,从而对子类的对象进行部分初始化。您可以使用 super 关键字实现这种功能,后面跟上一串参数,表示您要调用的超类的构造函数的参数。如果您正在一个构造函数中使用 super 关键字来调用超类的构造函数,那么它就必须作为构造函数体的第一条语句出现。
例如,假设您要创建一个 CheckingAccount 构造函数来对 CheckingAccount 对象进行初始化。您希望创建的 CheckingAccount 对象都有 balance 初始值,因此您要传递一个美元数量作为参数。这与 BankAccount 类中的构造函数完全相同,因此您想使用构造函数来为您实现这种功能,如清单 15 所示。
清单 15. 子类的构造函数
2
3 public CheckingAccount(float balance) {
4
5 super(balance);
6
7 }
8
9 public void cashCheck(float amount) {
10
11 withdraw(amount);
12
13 }
14
15 }
16
17
您也可以在子类中使用 super 关键字来显式地引用超类的成员。
重载和覆盖
Java 语言允许您定义多个同名的方法,前提是这些方法采用的参数不同。例如,清单 16 定义了第二个 cashCheck() 方法,这个方法使用要存入的支票数量和使用的费用作为参数。这称为方法 重载(overloading)。
清单 16. 方法重载
2
3 withdraw(amount);
4
5 }
6
7 public void cashCheck(float amount, float fee) {
8
9 withdraw(amount+fee);
10
11 }
12
13
当您创建一个子类时,通常都希望 重载 从超类中继承的方法的行为。例如,让我们假设 CheckingAccount 和 BankAccount 之间的区别是您从 CheckingAccount 帐号中提款时要收取一定的费用。在 CheckingAccount 类中,您需要对 withdraw() 进行重载,以便多扣取 $0.25 的费用。您可以定义 CheckingAccount withdraw() 方法,在这个方法中使用 super 关键字调用 BankingAccount 的 withdraw() 方法,如清单 17 所示。
清单 17. 方法覆盖
2
3 super.withdraw(amount+0.25F);
4
5 }
6
7
Object 类
Object 类是 Java 类层次结构中的一个非常特殊的类。所有的 Java 类最终都是 Object 类的一个子类。换而言之,Java 语言支持一种具有一个中心根类的层次结构,而 Object 类就是这个类层次的根类。在 Visual Basic 中也有类似的概念: Object 变量可以被实例化为任何类的对象。
由于所有的 Java 对象都是从 Object 类继承来的,因此您可以对任何 Java 对象都调用在 Object 类中定义的方法,而且每个对象的行为都类似。例如, Object 类定义了一个 toString() 方法,该方法会返回一个代表该对象的 String 对象。您可以对任何 Java 对象调用 toString() 方法,并期望得到该对象的一个字符串表示。大部分定义都会覆盖 toString() 方法,这样就可以返回一个表示这个特殊类的一个特殊的字符串。
让 Object 位于 Java 类层次的根位置的另一种含义是,所有对象都可以强制类型转换为 Object 对象。在 Java 语言中,您可以定义具有 Object 类对象的数据结构,这些数据结构中可以存放任何 Java 对象。
接口
我们已经提到一个 Java 类只允许单继承,这就是说一个 Java 类只能对一个类进行扩展。Java 语言的设计者认为多继承太过复杂,因此就决定不支持多继承,而是支持 接口。 接口类似于一个不能实例化的类,其中有方法的定义,但是没有真正实现。
您可以像类一样定义接口,不同之处是要使用 interface 关键字,而不是使用 class 关键字。一个接口可以对任意多个超级接口进行扩展。接口中的方法不能包括实现。接口方法只是一些简单的方法定义;不能有任何方法函数体。这与 Visual Basic 中使用方法的概念类似;它们只包括属性和方法定义,但是没有任何代码。
Account 接口
清单 18 中所列出的代码显示了如何编写一个基本的 Account 接口,该接口为银行帐号定义了一组基本的功能。注意您在接口中声明的这些方法没有函数体。
清单 18. Account 接口
2
3 public static final float INTEREST = 0.35F;
4
5 public void withdraw(float amount);
6
7 public void deposit(float amount);
8
9 }
10
11
接口实现
一个 Java 类只能对一个类进行扩展,但是却可以 实现任意多个接口。当一个类实现一个接口时,必须实现该接口中定义的所有方法。
清单 19 定义了一个 SavingsAccount 类,它实现了 Account 接口。由于 Account 接口定义了两个方法: withdraw(float amount) 和 deposit(float amount) ,因此 SavingsAccount 类必须对这两个方法都提供一个实现。 SavingsAccount 类仍然可以扩展其他类,也可以实现其他任何接口,前提是这些接口不能定义与 Account 接口相同的成员。
清单 19. 接口实现
2
3 private float balance;
4
5 public SavingsAccount(float balance) {
6
7 this.balance = balance;
8
9 }
10
11 public void cashCheck(float amount, float fee) {
12
13 withdraw(amount+fee);
14
15 }
16
17 public void withdraw(float amount) {
18
19 balance += balance;
20
21 }
22
23 public void deposit(float amount) {
24
25 balance -= balance;
26
27 }
28
29 }
30
31
回顾
到目前为止,您应该已经掌握了 Java 语言的基本组件,并且能够熟练编写简单的 Java 程序。特别是,您应该能够:
编写一个具有 main() 方法的 Java 类,编译并运行它。
编写一个 Java 接口并编译它。
为您的类编写一个或多个构造函数。
编写一个扩展另一个类的类,并实现一个或多个接口。
创建并应用使用了新关键字和构造函数的调用。
您还应该足够自信地开始探究更高级的 Java 代码。一个很好的起点就是 Java 平台类本身。获得 Java 语言使用经验的非常好的途径是浏览 API 文档,并开始使用那些类来编写程序。此外,也请参阅侧栏 磨练您的技巧以获得补充本文的一些精选资源。
与您阅读本文之初相比,现在您已经知道了更多关于 Java 语言的知识,下面应该返回到支持 Web 的 Windows 客户机-服务器应用程序这个主题了。将 Java 或 J2EE 应用程序构造为一个多层的、模块化的、基于组件的体系结构,这样在 Web 支持、重用、可维护性和灵活性方面带来了许多好处。
典型的 J2EE Web 应用程序体系结构基于模型-视图-控制器(Model-View-Controller、MVC)体系结构设计模式(请参阅 图 1),并且包含以下关键元素:
基于瘦客户机 Web 浏览器/Java applet 模型的客户机,它支持从基于浏览器和基于客户机的纯 Java 程序对基于服务器的 Web 应用程序进行普遍访问,从而按需交付应用程序组件。体系结构合理的 Web 应用程序支持同时用于终端用户和外部应用程序的多种接口类型。J2EE 对 Java servlet 和 JavaServer Pages(JSP)的使用,允许用户从瘦客户机 Web 浏览器和其他设备通过 HTTP 访问应用程序。您可以使用 HTML、XHTML、XML、JavaScript、Java applet 以及其他瘦客户机技术来构建瘦客户机接口。还可以使用 Java 语言和 J2EE 来构建功能丰富的“瘦客户机”应用程序,以访问位于内部网络/防火墙之内的基于服务器的应用程序。
提供服务的网络基础结构比如 TCP/IP、防火墙、路由器、LDAP 目录服务器、Web 服务器和安全 -- 您可以通过开放标准的接口和协议访问访问这些服务的功能。
业务逻辑组件,它通过控制事务的执行来强制业务单元的策略和过程,并请求用户通过用户界面提交请求来访问和更新后端数据源和应用程序。能够实现应用程序业务逻辑的 Java 和 J2EE 应用程序组件包括 servlet、JavaBean 组件和 Enterprise JavaBean(EJB)组件。
应用服务器软件,它为基于 Web 的电子商务应用程序提供平台。它通常包括一个 HTTP 服务器、数据库和事务服务、会话管理、邮件和群件服务,以及消息服务。诸如 Apache JBoss 和 IBM WebSphere 之类的 J2EE 应用服务器能够提供这些服务。您需要一个 J2EE 应用服务器来部署和运行 Web 应用程序的 J2EE 服务器端。
集成中间件和组件,它们允许访问现有的数据、应用程序组件、数据源,以及在有些情况下访问整个应用程序。Java 技术和 J2EE 提供了许多标准组件和技术,用于数据源集成、通过 EJB 组件实现的数据源实体、消息服务以及本机平台服务和组件。Web 应用程序还能够通过各种各样的工具和组件,访问现有的平台相关的数据和应用程序组件。
后端数据源和现有的非 Java 应用程序和应用程序组件:对于本路标图来说,这些资产通常是您不能或不想移植到 Java 或 J2EE,但是又必须与整体 Java 技术或 J2EE 解决方案集成的 Windows 应用程序组件。新的 Java 或 J2EE 应用程序服务还能够直接或间接地访问 Windows 应用程序组件所连接到的关键数据源。例如,您可以将 Microsoft SQLServer 客户机组件替代为使用 JDBC API 来实现的 Java 数据访问组件。
Web 应用程序编程环境和开发工具 ,它们提供用于创建、装配、部署和管理动态和健壮的电子商务应用程序的服务器端 Java 编程和 J2EE 编程环境。
图 1. MVC 与 J2EE Web 应用程序
访问本机 Windows 组件
Java 编程环境为大多数平台无关的常用系统服务提供类和库,包括 TCP/IP 网络和套接字、文件系统资源,以及打印。(我们将在本文后面的 用户界面考虑因素中讨论窗口处理和用户界面。)由于您已决定使用 Java 技术来实现应用程序的 Web 支持特性,因此应该使用对应的支持 Java 的服务和功能来代替任何平台 /Windows 相关的服务和功能。例如,将使用了 NetBios 或 Windows 网络的通信服务代替为使用 java.net 包的 TCP/IP 和套接字,以及将 Windows 文件系统功能代替为 java.io 包中的等价特性。
Java 本机接口(Java Native Interface,JNI)
JNI 提供了用于整合本机 C/C++ 和 Java 代码的机制。它是一个具有文档和受支持的 API,JVM 本身就使用该实现来访问底层的本机平台操作系统服务。这些服务包括大多数平台上可用的公共功能,比如通信、网络、应用程序配置、进程管理,以及文件系统。本机平台服务还可以包括特定平台相关的服务,比如基于 Windows 的网络、设备管理、打印,以及 COM/DCOM。
如果选择使用 JNI 来访问本机 Windows 组件,您就剥夺了自己使用 Java 语言的主要价值诉求 —— 平台无关性。然而,在某些情况下使用 JNI 是必要的。例如:
您在原型化应用程序的 Java 实现,并且能够提供瘦客户机接口和 Java Web 组件作为 Web 应用程序原型的前端。
您在分阶段地对应用程序组件向 Java 语言或 J2EE 的移植进行阶段测试。作为权宜之计,您将剩余的非 Java 组件的服务作为它们当前的 Windows 实现来访问。
应用程序访问的服务或数据的接口将是平台相关的(例如 COM/DCOM 组件)。
应用程序访问的服务或数据是通过某个组件或外部应用程序来提供的,在向 Java 的移植过程中包括该组件或应用程序是不切实际的。(例如,源代码不可用是因为另一家公司或合作伙伴拥有它,因而不允许修改它或再分发它。)
您计划将应用程序的组件移植到 Java,或者在以后某个时间将它们替代为外部可用的 Java 组件,并且您必须使用本机平台组件。
其他工具和技术
除了 JNI 之外,还有其他几种工具和技术有助于从 Java 语言中访问本机 Windows 组件。与 JNI 一样,您应该将那些技术的使用限制到以下情况,即您不能使用 Java 语言或 J2EE 实现该集成的本机代码组件,或者您计划以后才这样做。
JNI++ 项目
JNI++ 项目提供了两个代码生成实用程序和一个旨在简化用 C++ 进行 JNDI 编程的核心 C++ 库。使用仅具有相对简单的数据结构的本机 C/C++ 类来编写简单的 JNI 程序,即这些程序仅访问字段和使用简单的访问方法(accessor)和修改方法(modifier method),这是相当容易的。但是使用 JNI 来提供对如下这样的 C/C++ 类的访问就更具挑战了:这些类具有复杂的数据结构、异常处理以及需要从 C/C++ 中回调 Java 代码。JNI++ 代码生成实用程序旨在把从 C/C++ 世界和 Java 语言之间的映射所花的时间减到最少。JNI++ 生成许多从 Java 语言访问 C++ 类(或反之)所必需的大量代码。核心本机代码库为 JVM 和每个运行线程的 JNI 环境提供了简化的接口。这个库还包含用于原始 JNI 类型的包装类,并且还处理 Java 代码和 C++ 异常之间的转换。
Interface Tool for Java
IBM alphaWorks Interface Tool for Java(以前名为 Bridge2Java)允许 Java 程序与 ActivX 对象通信,以便将 ActiveX 对象容易地集成到 Java 环境中。Interface Tool for Java 使用 JNI 和 COM 技术来允许您将 ActiveX 对象完全当作 Java 对象来处理。 您只需运行一个代理生成工具,根据 ActiveX 控件的 typelib 创建 Java 代理。然后您可以使用这些代理来允许 Java 程序与该 ActiveX 对象通信。Interface Tool for Java 包括的示例展示了 Java 应用程序代码如何访问该工具所生成的那些 Java 代理,从而与各种各样的 COM 对象交互,包括 Lotus 1-2-3 电子表格、Microsoft Excel、Microsoft Word、Microsoft Outlook、Microsoft Calendar,以及 Microsoft Internet Explorer。
Codemesh JunC++ion
JunC++ion 允许您混合和匹配 Java 组件与 C++ 组件,以便解决诸如使 C++ 应用程序支持 Web、将 C++ 客户机集成到 EJB 环境中、使用 C++ 实现 EJB 组件以及同时允许 Java 和 C++ 组件使用单个 API 集等问题。
JNIWrapper
JNIWrapper 通过 JNI 提供一个针对平台相关(比如 Windows 和 Linux)的特性和服务的 Java 接口,这是平台无关的 Java 环境所没有提供的。有些平台相关的操作,比如 Windows 下的注册表项,在 Java 语言中不受支持。为了使用 JNI 来提供这个功能,您需要实现一个本机库和一些 Java 类,并与它配合工作,同时在 Java 调试器和本机端的调试器中测试和调试它。JNIWrapper 提供了用于调用本机库函数的 Java 库。使用 JNIWrapper,您可以在 Java 应用程序中访问本机系统的操作和特性(比如托盘图标或者定制的启动屏幕)。
Jawin 集成项目
Jawin(Java/Win32)是一个免费的、开放源代码的体系结构,用于 Java 组件和通过 Windows COM 对象或 Windows DLL 来公开的组件之间的互操作。Jawin 允许 Java 应用程序调用任何基于 COM 或 DLL 的遗留代码,而不需要编写任何 JNI 代码。您的代码将能与诸如 Microsoft Office 套件等可编写脚本的应用程序交互,调用诸如 Microsoft 的基于 COM 的 XML 解析器和工具等可编写脚本的逻辑组件,以及访问诸如 Windows 注册表、安全 API 和事件日志等 Win32 API 特性。Jawin 包括一个用于可编写脚本的 COM 组件的代码生成器。该代码生成器读取类型库,并自动产生从 Java 应用程序调用该组件所需要的 Java 存根(stub)。
Stryon i-HUB
Stryon Corp. 在其 iHUB 服务器产品中提供了许多技术,允许 Java 和 J2EE 应用程序访问现有的 Windows .NET 和 COM 应用程序。iHUB 还包括一个 Java 到 COM+ 的桥接中间件,它将 Microsoft ActiveX、DLL 和 COM+ 与运行在支持 Java 的平台上的 Java 应用程序联系起来。使用 Java2COM 桥接,Java applet 和应用程序能够支持现有的本机代码。
用户界面考虑因素
移植用户界面是从客户机-服务器转向基于 Web 的应用程序所面临的最大挑战。客户机-服务器应用程序通常具有丰富的用户界面,而这可能难于在基于 Web 的应用程序中重现。简单地尝试将每个屏幕改写为 Web 页面通常是徒劳无益的。一个很好的类比是使用逐个短语的字面翻译将一本书从一种语言翻译为另一种语言,这种方法通常会导致无法准确传达作者原意的译文。显而易见,更好的方法是使用新的语言来表达作者的本意。用户界面也是如此:如果使用 Web 设计的非常好的实践而不是使用屏幕到 Web 页面的简单映射来设计 Web 界面,这样移植到基于 Web 的应用程序的客户机-服务器应用程序会更好。下面让我们研究一些可用的 J2EE 应用程序用户界面选项。
基于 Web 的界面
大多数基于 Web 的应用程序都能够利用 Web 浏览器或基于浏览器的扩展(比如 MIME 处理程序和 Java applet)作为它们的界面。 这种方法的一个主要优点是,在客户机需要运行应用程序的任何内容都包含在浏览器中,或在应用程序运行时下载到本地,从而使得应用程序易于管理。具有 Web 浏览器用户界面的应用程序可以使用 Java Servlets、JSP 技术、HTML 以及 JavaScript 来编写。
Java Server Faces(JSF)是一个相关的规范,它允许 J2EE 提供商为基于 Web 的 J2EE UI 开发提供拖放功能。它还允许厂商开发可供他们的开发工具使用的自定义组件。要看到 JSF 的实际应用,可考察一下 WebSphere Studio 5.11 版中的系列工具(请参阅 参考资料)。WebSphere Studio 有一个名为 Page Designer 的完全集成的工具,可以使用它通过拖放操作来可视化地开发 HTML 页面和 JSP 页面。Page Designer 已实现了 JavaServer Faces 规范,并且允许您将诸如 HTML 表单元素以及更高级的组件(这些组件允许您将 HTML 表格绑定到后端数据源)之类的内容拖放到页面上。
从这里开始了解 Web 界面设计须知:
useit.com
Jakob Nielsen 是公认的 Web 用户界面设计专家,他在这个站点上提供了丰富的信息 -- 包括非常好的实践。您还会找到指向他关于这个主题的书籍和文章的链接。
从这里开始了解关于 JavaServer Faces 的更多信息:
JavaServer Faces 主页
从 Sun 获得关于 JSF 的官方信息。
Magical Web Interface Development
这篇文章展示了如何提供基于服务器的强大能力和构建基于 Web 的用户界面的灵活性,以使得组件与您的业务对象保持同步。
Developing JSF Applications using WebSphere Studio V5.1.1 -- Part 1
这篇文章提供了 JSF 开发的实际练习。
JSF Central
这是一个 JavaServer Faces 社区站点,它允许访问用于开发 JSF 相关技术的资源。
Applets
applet 是缩微的 Java 应用程序,用户从网络上下载它们并在浏览器中执行。applet 通常被认为是不受信任的,因为它们匿名地通过网络发送,并在一个安全的、受限制的“沙箱(sandbox)”中执行,这个沙箱将它们与当前浏览器会话之外的本地系统的所有部分隔离。然而,创建者可以对他们的 applet 签名,并且浏览器用户可以允许来自受信任的提供商的经过签名的 applet 拥有特权 —— 比如访问本地文件系统和其他资源,这些特权通常与完整的 Java 应用程序联系在一起。
applet 最初在 Java SDK 的第一个版本中是使用原先的窗口工具包来实现的。Java 2 SDK 引入了允许 applet 使用 Swing 组件而不是使用原先的窗口组件的能力,从而赋予它们更好的响应能力和平台无关的外观。
浏览器“帮助程序”
Java applet 的一种替代技术是浏览器帮助程序和 MIME 类型处理程序。当基于浏览器的 GUI 需要访问文件和资源,而 Web 浏览器又不能在本地输出(可视资源)或“播放”(媒体类型)时,这些技术就可能很有用。这样的例子包括专用的可视化格式的应用程序内容,比如地图、电子表格、字处理文件类型(Adobe Acrobat、Microsoft Word、Lotus WordPro,等等)、诸如 PostScript 等设备类别相关的文件、诸如视频、音频、语音等多媒体,以及应用程序相关的格式(可以是任意格式)和诸如外部应用程序安装程序、软件清单实用程序、病毒扫描工具、金融或税务软件格式等功能。
从这里开始了解关于浏览器帮助程序和 MIME 处理程序的更多信息:
Extending the Browser with Plug-ins 这篇文章介绍了插件、客户机 Java 编程、客户机脚本编写,以及 DOM 和 dHTML。
Web 服务
Web 服务提供了将客户机-服务器 Windows 应用程序移植到 J2EE 的替代方法。Web 服务是自包含的、模块化的应用程序,您使用标准的 Internet 和 XML 技术来构建它们,并描述和发布到网络上以供其他程序使用。Web 服务是分布式系统演进过程中的下一步。它们允许企业使其资产可在全球范围内通过 Internet 访问,并且通过开放的 Internet 协议动态地连接和执行事务。
通过使用诸如 SOAP 这样的 Web 服务标准,您可以保留当前的 Windows 用户界面,并使用 Web 服务来访问基于 J2EE 的业务逻辑。使用这种方法,你需要使用 J2EE 标准来改写业务逻辑,以利用基于服务器的 J2EE 代码的平台无关性、远程访问以及可扩展性,同时保持 Windows 客户机界面不变,从而最小化对终端用户的影响。
从这里开始了解关于 Web 服务的更多信息:
Speed-start Web services
Speed-start Web services 计划拥有包含 IBM 的最新软件开发工具和中间件(试用版)的 DVD、大量的在线教程和文章、实用的研讨会和技术简报,以及由 IBM Web 服务专家主持的在线讨论论坛以帮助回答您的问题。
Java 客户机接口
如果需要保持应用程序的丰富外观和感觉,又要使其能在各种各样的操作系统平台(比如 Linux)上运行,那么您应该考虑为 J2EE 应用程序构建一个 Java 客户机界面。然后应用程序就能够利用像 RMI 这样的标准 API、Web 服务 和 EJB 技术,与 J2EE 服务器代码以及诸如文件访问、网络、多媒体和图形窗口功能等系统服务交互。
这种方法要求您或者通过手工过程,或者通过集中管理的自动化分发或共享机制,向用户分发应用程序的初始版本和后续更新。(使用 applet、Web 浏览器插件和 MIME 类型“帮助程序”的基于“瘦客户机”浏览器的界面通常更容易管理,因为代码下载是在每次代码被激活时从 Web 服务器下载的。)
从这里开始了解关于 Java 客户机应用程序的更多信息:
" The Java 2 user interface"
这篇 developerWorks文章探讨了 JFC/Swing 的各种特性,包括可插入的外观和感觉特性、拖放支持、Java 2D 图形支持、音频 API、辅助特性、使用 AWT Robot 的自动化事件,以及媒体支持(包括 3D 图形、高级图像处理、Java Media Framework,以及语音) 。
Eclipse.org
Eclipse 是一个基于 Java 的开放和可扩展的 IDE。遵循开放源代码范型,并具有一个提供免费源代码和全球再分发权的公共公开许可证,Eclipse 平台给开发人员提供了灵活性和控制能力。
" Using the Eclipse GUI outside the Eclipse Workbench, Part 1: Using JFace and SWT in stand-alone mode"
使用 Standard Widget Toolkit(SWT)和 JFace,您能够开发与本机应用程序紧密相似的独立 Java 应用程序。SWT 是一个窗口工具包,它具有一个与本机操作系统紧密集成的平台独立的 API。JFace 是一组建立在 SWT 基础上的实用工具,用于使得基于 SWT 编写 GUI 应用程序代码更加容易。SWT 和 JFace 都是开放源代码的 Eclipse 项目的组成部分,可用于将 Java 应用程序与独立应用程序中的本机平台组件(比如 ActiveX 控件)集成。标准 Java 用户界面和窗口系统(Swing)对于开发客户端应用程序用户界面来说是足够的和经得起考验的;然而,它缺乏本机应用程序所具有的外观和风格,而 SWT 则直接使用操作系统的本机窗口环境。
" 将 ActiveX 控件集成到 SWT 应用程序 -- 如何将 ActiveX 与 Eclipse 一起使用" 使用 SWT,您能够开发感觉和操作起来都像本机应用程序的独立 Java 应用程序。这篇 developerWorks展示了如何在独立的 SWT 应用程序中利用和集成 ActiveX 控件。
J2EE 应用程序的部署环境
J2EE 应用程序并不是直接在 Web 服务器器(比如 IIS 或 Apache Web 服务器)上运行。相反,运行它们的是一个称为 应用服务器的单独组件。应用服务器与您选择的平台上的选择的 Web 服务器和平共处。有各种各样商业化和开放源代码的应用服务器可供使用。您可以从以下这些资源了解其中两种应用服务器的更多信息:
Apache 软件基金会
Apache 是一个开放源代码的项目,它提供几种广泛使用的 Web 部署技术。这其中包括一个 Web/HTTP 服务器(Apache Web Server)和一个 servlet 容器/JSP 处理器(Tomcat)。Apache Web Server 自从 1996 年 4 月以来一直是 Internet 上最流行的 Web 服务器,现在它比其他所有 Web 服务器加起来都还使用得更广泛。Tomcat 是 Java Servlet 规范和 JavaServer Pages 标准的官方参考实现中所使用的 servlet 容器。
WebSphere Application Server V5.1 - Express
IBM 提供了一个管理简单而又动态的 Web 站点的高本益比的现成解决方案,它具有一个简化的基于 J2EE 的 Web 应用程序服务器和一个基于 WebSphere Studio 的开发环境。
WebSphere for newcomers
这个路标图介绍了 IBM WebSphere 系列产品,还提供了帮助您开始使用 WebSphere 产品来开发和部署 J2EE 解决方案的参考资料。
结束语
我们希望本路标图为您提供了相关任务和技术的有用概述,从而帮助您将客户机-服务器 Windows 应用程序移植到 Java 和 J2EE Web 应用程序。