preface

第一章 前言

Java是一门通用型(General Purpose)的编程语言。Java语言支持许多特性、例如:面向对象程序设计(Object-Oriented Programming),泛型程序设计(Generic Programming),跨平台,安全性等。根据TIOBE2019年的调查结果,在所有的编程语言中,Java的使用率排名第一。

本系列文章将由Java语言的特性开始,逐一介绍Java语言的特点、Java语言的特性和使用方法、Java虚拟机以及运行环境、流行的Java开源代码库的用法等。在每个重要的知识点,都会配有详细的例子解释。在读完本系列文章后,读者应能较全面地掌握Java语言。如果读者刚刚阅读完Java入门书籍的话,本系列文章能够帮助读者进一步提高Java技术能力。

1 Java语言的特性

1.1 面向对象程序设计(Object-Oriented Programming)

面向对象程序设计是一种编程思想、方法、或者模式。在面向对象的程序设计思想下,一切事物被抽象成类(Class)/对象(Object)。其中,事物的数据被抽象为类(Class)/对象(Object)的成员变量;对于事物的操作或者逻辑处理被抽象为成员方法(Method)。封装(Encapsulation)特性将数据和逻辑处理封装在类中,对内建立了数据与操作的联系,对外隐藏了不必要暴露的细节。因此,与过程程序设计(Procedural Programming)(例如:C语言)相比,面向对象程序设计能帮助开发人员减轻程序开发和维护的难度,降低程序之间、模块之间的耦合。

通过良好的设计,过程程序设计语言(例如:C语言)也能实现部分面向对象程序设计的特性(例如:封装、继承(Inheritance),多态(Polymorphism)等)。但是,与过程程序设计语言不同的是,面向对象程序设计在语言设计这个层面提供了封装、继承和多态的特性,无需开发人员额外的开发。其实,在面向对象程序设计语言面世之前,开发人员已将面向对象程序设计的思想应用于C语言程序开发之中。

1.2 函数式程序设计(Functional Programming)

函数式程序设计是另一种编程思想、方法、或者模式。在函数式程序设计思想下,数据被抽象成函数的输入与输出,而对数据的处理则抽象为函数。在Java 8中加入了Lambda函数的概念,极大的增强了函数式程序设计在Java中的应用。大致的讲,在计算能力方面,函数式程序设计、与面向对象程序设计、过程程序设计是等效的。即,使用面向对象程序设计、过程程序设计处理的数据或者逻辑过程都可以用函数式程序设计处理,反之亦然。使用函数式程序设计方法能有效简化程序的逻辑复杂度,降低程序维护的负担。因为在函数调用和运行的过程中,不会产生副作用(Side Effect),所以,函数式程序设计的代码易于测试,易于程序并行化(因为每个函数的执行是相互独立的)。一篇著名的介绍函数式编程的文章是由图灵奖获得者John Backus于1977年在获得图灵奖的典礼上发表的演讲:Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs. 中文:可以从冯·诺依曼式的编程风格中解放出来的程序设计和功能风格及其程序代数

1.3 泛型编程(Generic Programming)

泛型编程最初设计的目的是为了更好的重用代码(Code Reuse)。泛型编程能为开发人员带来诸多好处。当数据类型可作为参数传递时,开发人员只需要开发一次通用算法,并可将此通用算法应用于不同数据类型之上。在该通用算法的实现中,无需使用强制类型转换(cast),编译器会依据上下文环境推断出当前使用的数据类型,并且进行数据类型检查。这使得程序更加精简,易于维护。当与反射机制(Reflection)同时使用时,Java代码既能获得泛型编程的通用机制的好处,也能通过使用反射机制获得动态处理的能力。Java支持泛型函数(Generic Method)和泛型类(Generic Class)。开发人员还可通过继承关系为泛型数据设定上界(Upper Bound)或者下界(Lower Bound),为程序提供更严格的类型检查。

1.4 垃圾自动回收(Garbage Collection)

垃圾回收机制将开发人员从内存与指针管理的困境中解放出来。因为Java虚拟机提供了内存自动释放回收的功能,Java开发人员只需要在使用内存/对象时,动态申请一个新的对象即可,而无需考虑何时何地释放这个内存/对象。对象的释放由Java虚拟机的垃圾回收机制决定(开发人员也可主动发起垃圾回收过程)。因此,在Java程序中,指向对象的变量被称为“引用”(Reference)。

1.5 安全性(Security)

因为Java程序是在Java虚拟机内解释执行的,Java语言在Java虚拟机内添加了安全性机制。目前,Java语言至少在如下几个方面提供了安全检查,增强了Java程序的安全性。其一,Java是强类型语言(Strongly typed)。在编译过程中,Java编译器会检查数据类型;Java虚拟机提供了垃圾回收机制,增强了Java程序的鲁棒性(Robustness)。其二,Java虚拟机在运行代码之前会检查代码的合法性。因为Java提供动态加载机制,和“一次编译、到处运行”的特性,在运行代码之前,Java虚拟环境需要对代码进行合法性检查,因为加载的代码可能是不被信任的代码(Untrusted Codes)。其三,Java标准库实现了多种与安全相关的算法,例如,加解密算法等,为上层程序提供了可靠安全的服务接口(Security Service Interface)。

1.6 高性能(High Performance)

因为Java语言是解释执行的,与C/C++语言相比,其运行速度较慢。经过多年的优化,Java语言运行的速度得到了很大的提升。与此同时,CPU的处理能力也在不断上升。目前,在大型程序设计项目中,Java语言的运行速度已不再是整个项目的性能瓶颈。在性能优化方面,开发人员将更多的精力放在了算法优化,逻辑处理流程优化等方面。

实际上,Java是在高性能、开发效率、安全性等多个方面选取的一个折中方案。C/C++虽然运行的速度快,但是因为C/C++代码最终需编译成机器代码,直接在机器上运行,这将整个机器暴露在C/C++应用程序面前,不仅安全性得不到保证,而且,因为不同机型的机器代码不同,不同操作系统的系统调用接口也不同,C/C++开发人员往往需要花费大量的精力去维护和适配各种平台。所以,为了解决这个问题,Java语言在设计上着重突出了跨平台和安全性特性。另一方面,与纯的解释运行的语言相比(例如:Perl),Java将源代码编译成二进制文件,加快了程序的运行速度。而且,在编译过程中进行数据类型检查,帮助开发人员检测错误,提高了开发效率。

1.7 跨平台(Cross-Platform or Platform Independence)

因为Java语言是解释执行的,Java语言只依赖于Java虚拟机提供的运行环境,而不依赖于所运行的平台(例如:硬件、操作系统等)。所以,Java语言支持“一次编译,到处运行”的特性。

1.8 编译解释运行(Compiled and Interpreted)

Java语言的运行需要经过两个过程。首先,一个Java程序需要经过编译,将源代码文件翻译(Translate)成Java虚拟机能够识别的二进制文件(即class文件)。然后,再由Java虚拟机解释运行这个二进制文件。这样做的好处是,在编译阶段,编译器能够帮助开发人员检查源代码,提高源代码的正确性与性能。在解释阶段,Java提供的虚拟运行环境能够将同一代码应用、适配到不同的平台之上,增强了Java语言的适用性。这样做的坏处是牺牲了程序运行的性能。

2 批评的声音

然而,Java编程语言并不是完美无瑕的。在Java的发展过程中,一些被列为Java特性的内容,同样也受到了许多质疑与批评。在这里,我们也列出一些反对的观点,供读者思考。

2.1 支持基本数据类型(Primitive Data Type)

既然提倡“万物皆对象”,Java应该只使用类/对象来抽象数据,而不应该支持基本数据类型。对于小型项目而言,使用基本数据类型的确能加快开发速度,然而,基本数据类型与很多特性是不兼容的。例如,在反射机制中,代码需要区分基本数据类型(例如:int)和类(例如:Integer)。在一些动态场景下,如果函数接口只支持传入或者传出Object对象,那么,在使用该函数之前,程序不得不额外的将int转化为Integer类。这些都会让代码看起来很奇怪。另外,为了在泛型编程中支持基本数据类型,Java语言社区不得不专门提出议案JEP 218: Generics over Primitive Types,增强泛型编程的应用范围。

2.2 语言设计问题(不支持多重继承(Multiple Inheritance)、不支持操作符重载(Operator Overloading))

虽然Java与C++都支持面向对象的程序设计,但是,在一些设计理念方面却存在着不同。例如,C++支持多重继承,而Java不支持多重继承。Java设计人员坚信多重继承会极大地增加代码的复杂度,使得代码难以维护。他们相信多重继承的逻辑能够转化为使用单继承的方法实现。

操作符重载也存在着不同的设计理念。Java语言更加鼓励通过使用接口(Interface)来提供个性化(Customize)的操作服务。毕竟,在实现方面,操作符也是通过函数调用或者类似的方法实现的。

2.3 性能问题

性能问题是C/C++开发人员难以接受Java语言的一个重要原因。与Java语言相比,C/C++程序更多的应用于对性能要求很高的场景中(Performance-Critical Scenarios)。例如,操作系统、数据库、网络通信应用、图形图像应用等。

2.4 缺乏针对特定平台的优化(Lack of Platform-Specific Optimization)

与性能问题相似,在一些场景下,使用C/C++语言能够更好的与运行环境融合,进一步优化系统的性能与安全性(Platform-Specific Optimization)。例如:在Linux环境下,C/C++可以使用系统调用与标准库函数直接与Linux系统交互。从实现方面来讲,Java语言也可以提供类似的库函数。但是,Java语言并没有这样做,原因是Java需要提供一个跨平台的运行环境。如果一些功能只能在Linux环境上运行的话,那么,这些功能是不支持跨平台特性的。所以,这些功能是不应该加入到Java运行环境中的。因此,为了提供跨平台特性,Java语言放弃了对于具体平台优化的支持。

2.5 Java系统过于复杂

因为Java提供了许多特性,所以,为了支持这些特性,Java的运行环境也承受着额外的负担。例如,Java虚拟机是一个多线程的应用程序。即使用户开发的是一个单线程的应用程序,但是,当这个程序运行时,它实际上是运行在一个多线程的环境中。例如,Java虚拟机会运行一些系统线程,用于提供定时器(Timers)功能,支持垃圾自动回收(Garbage Collection),分发信号(Signals),以及支持JIT(Just In Time Compilation)特性。

由于Java系统过于复杂,导致运行Java程序需要消耗大量的系统资源。因此,Java程序不适合在资源紧缺(Resource-limited)或者性能要求较高(Performance Critical)的环境中运行。

2.6 Java代码库过于驳杂

绝大部分的Java代码库是由开源社区开发和维护的。开源社区的优势是,志愿者热衷于软件开发,并且能提供较为可靠的软件质量。但是,其劣势是各自为战。针对相似的功能,可能会有多个团体或者个人开发出不同的代码库,其实现方式也不尽相同。例如,Simple-JsonGSON, 和Jackson都支持JSON格式的操作功能。

3 结语

总之,Java是一门非常流行的编程语言,它的设计特性抓准了软件设计的发展趋势,在无数开发人员的共同努力下逐渐发展、壮大。随着软件开发规模的增长,Java在计算性能上的劣势已变得微不足道。另一方面,因为Java降低了软件开发的难度,设计了更为丰富的组件集成方式,为开发大规模软件,尤其是分布式软件,提供了更为方便的软件开发环境。因此,Java在企业和开发人员中受到了广泛的欢迎。

下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.