概述
包含在 Java 中的新功能和升级改变了编程环境的面貌,并为面向对象编程 (简称为 OOP) 给出了新的定义。但与它的前辈不同,Java 需要与标准功能捆绑在一起,并且独立于主机平台。
创建 Java 语言的主要目标
- 它很简单。
- 它是面向对象的。
- 它独立于主机平台。
- 它包含用于网络的语言设施和库。
- 它旨在安全地执行来自远程来源的代码。
Java 语言引入了一些在其他语言(如 C 和 C++)中不存在的新功能。
面向对象 ("OO") 指的是一种编程方法和语言技术。OO 的主要思想是围绕它操作的“事物”(即对象)来设计软件,而不是围绕它执行的操作。
随着计算机硬件的进步,它带来了创建更好的软件技术的必要性,以便能够创建不断增加的复杂应用程序。其目的是使大型软件项目更容易管理,从而提高质量并减少项目失败的数量。面向对象解决方案是最新的软件技术。
- 汇编语言
- 软件技术始于汇编语言,汇编语言接近机器指令,易于转换为可执行代码。每台硬件都有自己的汇编语言。汇编语言包含低级指令,例如将数据从内存移动到硬件寄存器、执行算术运算并将数据移回内存。程序员必须了解计算机的详细体系结构才能编写程序。
- 过程式语言
- 在汇编语言之后,开发了高级语言。在这里,语言编译器用于将高级程序转换为机器指令,使程序员摆脱了了解计算机硬件体系结构的负担。为了促进代码的重复使用并最大限度地减少 GOTO 指令的使用,引入了“过程式”技术。这简化了软件控制流的创建和维护,但忽略了数据的组织。调试和维护具有许多全局变量(即包含可以在应用程序的任何位置修改的数据的变量)的程序变成了噩梦。
- 面向对象语言
- 在 OO 语言中,数据与信息隐藏密切相关。特定于对象的 data 只能通过该对象中的过程访问。因此,对象包含 data 以及控制流,程序成为对象之间的一系列交互。
在这种情况下,机器代码文件及其执行特定于为其编译的平台(Windows、Linux、macOS 等),即 目标平台
... 因为编译后的文件是为在特定平台和硬件上运行而设计的机器代码文件。它会在另一个平台上产生不同的结果/输出。因此,如果希望您的程序在多个平台上运行,则必须多次编译您的程序
它带来了更大的漏洞风险。请注意,当某段代码编译成可执行格式时,可执行文件无法动态更改。需要从更改后的代码重新编译,才能使更改反映在最终的可执行文件中。模块化(将代码分成模块)在 Java 的前辈中不存在。如果输出应用程序不是单个可执行文件,而是模块形式,那么可以轻松更改单个模块并查看应用程序中的更改。另一方面,在 C/C++ 中,代码的细微更改需要重新编译整个应用程序。
Java 的理念是将源代码编译成将要解释的中间语言。
源代码 | 中间文件 | 解释器 |
中间语言是 字节码。解释器是 Java 虚拟机 (JVM)。字节码文件是通用的,而 JVM 是特定于平台的
因此,每个平台都应该编写一个 JVM。情况就是这样。因此,您只需生成一个唯一的字节码文件(一个 .class
文件)。
该语言的最初实现使用解释型虚拟机来实现可移植性,许多实现仍然如此。这些实现产生的程序运行速度比典型的 C++ 编译器创建的完全编译的程序慢,因此该语言因产生缓慢的程序而声名狼藉。从 Java 1.2 开始,Java VM 产生了运行速度快得多的程序,使用了多种技术。
其中第一个是简单地直接编译成本地代码,就像更传统的编译器一样,完全跳过字节码。这实现了极高的性能,但代价是可移植性。这实际上不再使用。
另一种技术,即时 (JIT) 编译器,在程序运行时将 Java 字节码编译成本地代码,并保留编译后的代码以供反复使用。更复杂的 VM 甚至使用 动态重新编译,其中 VM 可以分析正在运行的程序的行为并选择性地重新编译和优化程序的关键部分。这两种技术都使程序能够利用本地代码的速度而不损失可移植性。
可移植性是一个技术上难以实现的目标,Java 在该目标上的成功存在一些争议。虽然确实有可能为 Java 平台编写在许多主机平台上表现一致的程序,但大量平台存在小错误或不一致性,导致有些人将 Sun 的“一次编写,随处运行”的口号戏称为“一次编写,随处调试”。
C++ 是建立在 C 语言之上的,因此在语言周围出现了多种不同的方法来做相同的事情。例如,在 C++ 中,创建对象可以通过三种不同的方式完成。此外,C++ 并没有随其编译器捆绑提供标准库。相反,它依赖于其他程序员创建的资源;这些代码很少能很好地融合在一起。
在 Java 中,提供了标准化的库,以允许以统一的方式访问主机机器的功能(例如图形和网络)。Java 语言还包括对多线程程序的支持——这是许多网络应用程序的必需品。
然而,平台无关的 Java 在服务器端应用程序方面非常成功,例如 Web 服务、Servlet 或 Enterprise JavaBeans。
Java 在客户端也取得了进展:首先是 抽象窗口工具包 (AWT),然后是 Swing,最新的客户端库是 标准小部件工具包 (SWT)。有趣的是,它们试图如何处理两种相互对立的消费力量。这些是
- 高效、快速的代码;移植到最流行的硬件(一次编写,随处测试)
- 使用底层本机子例程创建 GUI 组件。AWT 和 SWT 采用了这种方法。
- 可移植性到任何移植了 JVM 的硬件(一次编写,随处运行)
- 为了实现后者,Java 工具包不应该依赖于底层本机用户界面。Swing 采用了这种方法。
有趣的是,这种方法是如何来回切换的。AWT → Swing → SWT。
安全执行
[edit | edit source]凭借语言中内置的高级控制来操作硬件,C/C++ 程序员可以访问系统上的几乎任何资源,无论是硬件还是软件。这原本是该语言的优势之一,但这种灵活性却导致了混乱和复杂的编程实践。
错误处理
[edit | edit source]传统的错误处理方法是让每个函数返回一个错误代码,然后让调用者检查返回的内容。这种方法的问题是,如果返回代码充满了错误检查代码,就会影响到正在执行实际工作的原始代码,从而降低代码的可读性。
在新的错误处理方法中,函数/方法不返回错误代码。相反,在发生错误时,会抛出一个 异常。异常可以通过 catch
关键字在 try
块的末尾进行处理。这样,调用该函数的代码就不需要被错误检查代码所破坏,从而使代码更易读。这种新的错误处理方法称为“异常处理”。
异常处理也被添加到 C++ 中。但是,Java 和 C++ 异常处理之间存在两个区别
- 在 Java 中,抛出的异常就像 Java 中的任何其他对象一样是一个 Java 对象。它只需要实现
Throwable
接口。 - 在 Java 中,编译器会检查是否可以捕获异常。如果对抛出的异常没有 catch 块,编译器就会给出错误。
Java 前身中可选的异常处理会导致开发人员不关心错误处理。因此,经常发生意外错误。Java 强制开发人员处理异常。程序员必须处理异常,或者声明用户必须处理它。必须有人处理它。
网络功能
[edit | edit source]尽管功能强大,但 Java 的前身缺乏与其他计算机联网的标准功能,通常依赖于平台复杂的网络功能。由于几乎所有网络协议都已标准化,Java 技术的创建者希望将其作为该语言的一项旗舰功能,同时忠于之前在标准化 远程过程调用 方面取得的进展。Java 团队关注的另一个功能是其在万维网和互联网中的集成。
Java 平台是最早提供广泛支持从远程来源执行代码的系统之一。Java 语言的设计考虑了 网络计算。
Applet 可以运行在用户的浏览器中,执行从远程 HTTP 服务器下载的代码。远程代码在高度受限的“沙箱”中运行,这保护用户免受行为不端或恶意代码的侵害;发布者可以申请证书,以便使用该证书对 Applet 进行数字签名,将其标记为“安全”,从而允许它们突破沙箱并访问本地文件系统和网络,这很可能是由用户控制的。
动态类加载
[edit | edit source]在 C 和 C++ 等传统语言中,所有代码都必须在执行之前编译并链接到一个可执行程序。在 Java 中,类按需编译。如果在执行阶段不需要某个类,则该类甚至不会被编译成字节码。
此功能在网络编程中特别有用,因为我们事先不知道将执行哪些代码。正在运行的程序可以从文件系统或远程服务器加载类。
此外,此功能理论上使 Java 程序可以在执行过程中更改自己的代码,以实现一些自我学习行为。然而,更现实的想象是,Java 程序会在执行之前生成 Java 代码,然后执行该代码。通过一些反馈机制,生成的代码可以随着时间的推移而改进。
自动内存垃圾收集
[edit | edit source]在 C 和 C++ 等传统语言中,程序员必须确保释放所有分配的内存。在程序员必须手动分配系统内存资源的情况下,内存泄漏成为了一种常见的麻烦。
内存资源或缓冲区具有特定的操作模式,以实现最佳性能。缓冲区一旦充满数据,就需要在不再使用其内容后进行清理。如果程序员忘记在代码中清理它,内存很容易过载。用 C/C++ 语言编程变得很繁琐且不安全,因为存在这些缺陷,用这些语言构建的程序容易出现内存泄漏和系统崩溃——有时甚至会损害硬件本身。释放内存对于服务器来说尤其重要,因为它必须连续运行几天。如果在使用后没有释放一块内存,而服务器只是不断分配内存,那么这种内存泄漏可能会导致服务器宕机。
在 Java 中,释放内存的工作从程序员手中解放出来;Java 虚拟机跟踪所有使用的内存。当内存不再使用时,它会自动释放。JVM 在后台运行一个单独的任务,释放未被引用的、未使用的内存。该任务称为“垃圾回收”。
垃圾回收器始终在运行。这种自动内存垃圾收集功能使用 Java 编写健壮的服务器端程序变得容易。程序员唯一需要关注的是对象创建的速度。如果应用程序创建对象的速率快于垃圾回收器释放对象的速率,则会导致内存问题。根据 JVM 的配置方式,应用程序要么会因抛出 NotEnoughMemoryException
而耗尽内存,要么会暂停以让垃圾回收器执行其工作。
Applet
[edit | edit source]Java 的创建者提出了applet的概念。Java 程序可以在客户端浏览器程序中运行。Java 于 1995 年发布;当时互联网正变得越来越普及,人们也越来越熟悉它。Java 的承诺在于客户端浏览器方面,代码将被下载并在客户端浏览器程序中作为 Java applet 执行。
- 另请参见 Java 编程/小程序。
多年来,C/C++ 编程中的一些特性被程序员滥用。虽然语言允许这样做,但它被称为坏习惯。因此,Java 的创建者将其从语言中排除。
- 运算符重载
- 多重继承
- 友元类(访问另一个对象的私有成员)
- 显式类型转换的限制(与内存管理相关)
在大多数人的观点中,Java 技术在所有这些目标上都相当不错。然而,该语言并非没有缺点。Java 往往比类似语言(如 C++)更高级,这意味着 Java 语言缺乏硬件特定数据类型、指向任意内存地址的低级指针或运算符重载等编程方法。虽然这些特性经常被程序员滥用或误用,但它们也是强大的工具。然而,Java 技术包括 Java 本地接口 (JNI),这是一种从 Java 语言代码调用本地代码的方式。使用 JNI,仍然可以使用其中的一些功能。
一些程序员也抱怨 Java 缺乏多重继承,这是 C++ 等其他面向对象语言的一项强大功能。Java 语言将类型和实现的继承分开,允许通过接口继承多个类型定义,但只允许通过类层次结构单继承类型实现。这在避免多重继承的许多危险的同时,提供了大多数多重继承的好处。此外,通过使用具体类、抽象类以及接口,Java 语言程序员可以选择对其定义的对象类型进行完全、部分或零实现,从而确保应用程序设计中的最大灵活性。
有些人认为,对于某些项目,面向对象会使工作变得更困难而不是更容易。这种特定的抱怨并不独特,它也适用于其他面向对象语言。