Java组件设计(txt+pdf+epub+mobi电子书下载)


发布时间:2021-08-02 00:32:51

点击下载

作者:孔德生

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Java组件设计

Java组件设计试读:

前言

设计,决定了软件质量!

组件化设计和构建软件系统,是实现快速发布高质量软件产品之梦的最佳实践!

组件设计,凝聚了需求分析、面向对象、设计模式、数据结构、多线程等一系列高阶领域的核心精髓!

Java语言,是互联网上的卓越语言,从诞生的那一天开始,就被很多软件工程师所青睐,不断在世界的每个角落开花结果,应用日益广泛。

Java开源世界,如火如荼地发展,无数的框架、组件如雨后春笋般涌现,极大地推进了Java技术在各个领域的商业应用。

然而,由于设计者的眼界、经验和水平等的限制,Java开源框架、组件参差不齐,优秀者并不多见。在企业软件开发中,Java往往成了臃肿、笨拙、低效等的代名词。功能最简单的Java组件也要几十、上百个类文件,使用组件需要引入的Jar文件少则几兆字节,多则几十兆、上百兆字节。因此,所谓鼓吹的轻量级组件,不过是个噱头而已。

笔者从事企业软件开发十余载,专注平台和组件开发领域,深知组件设计与开发过程的“高处不胜寒”。因此,在本书中,笔者将自己对组件技术的认识和心得进行总结和提炼,为读者奉上一份Java组件设计领域的精彩技术大餐。

本书定位

本书面向软件架构师、设计师、高级开发人员,讲解企业应用中核心组件的设计原则与最佳实践。

本书将澄清设计模式、数据结构、多线程、接口设计等多个高阶技术领域中“流行”的认知误区,通过大量的组件实例分析,为读者精彩讲解组件设计这一最具技术含量的领域需要考虑的核心问题、设计方案与最佳实践。

本书的内容

这本书主要涵盖三部分的内容:

第1篇,主要讲解了应用程序的架构、组件的定义和组件核心设计原则。这些原则,都是在大量的企业软件实践中浓缩提炼的精华。

第2篇,对Java语言的高级主题进行了详尽的阐述,作为组件实战的必需必备基础功力。

第3篇,对企业应用的核心组件,进行需求分析、设计和实现。核心组件包括:

● 第4章,配置组件

● 第7章,数据库访问组件

● 第5章,Socket 通信组件

● 第8章,JSON转换器

● 第6章,日志组件

● 第9章,权限组件

本书代码下载地址为http://www.broadview.com.cn。

特别致谢

衷心感谢周坚先生,在华为公司期间对我无私的指导,使我有幸步入设计领域的高级殿堂!

衷心感谢我的父亲、母亲和兄弟姐妹,是你们的关怀照顾和言传身教,让我懂得人生的意义!

衷心感谢我的妻子,默默忍受我长期的熬夜,不断给我关心、支持和鼓励!

衷心感谢我的儿子,给我带来无尽的欢乐!

衷心感谢在我的生活、工作中,帮助过我的每一个人!

孔德生

2009年2月第1篇 组件设计总括

第1章 组件设计概述.................................................................1

面对复杂而日益多变的客户需求,如何应对?如何快速产出高质量的软件?经过多年的软件工程实践,组件技术被认为是快速发布高质量软件产品的关键技术。通过一系列基础组件的组装,可以像搭积木一样,迅速生成所需软件的主体结构和核心功能,应用程序开发者只需要编写特定的应用逻辑即可,这样大大加快了软件产品的开发速度。另外,组件都是经过了充分测试的,因此在质量上也提供了坚实的保障。只有这样,才能实现快速、高质量的软件开发。

第2章 组件设计原则.................................................................7

本章将对目前业界存在的诸多技术争论、误区进行澄清,让读者从所谓的“业界潮流”、教条、“黄金定律”中走出来,真正理解组件设计过程的核心原则。第1章 组件设计概述

当前,软件在各行各业得到了广泛的应用。随着需求的快速膨胀,软件规模的不断扩大,软件开发活动也变得日益复杂。这种情况,在企业应用系统开发中表现得更为突出。企业应用系统中,对并发、数据量、性能、可靠性要求很高,这些都对软件系统的设计和开发提出了严峻的挑战。

面对复杂而日益多变的客户需求,如何应对?如何快速产出高质量的软件?经过多年的软件工程实践,组件技术被认为是快速发布高质量软件产品的关键技术。通过一系列基础组件的组装,可以像搭积木一样,迅速生成所需软件的主体结构和核心功能,应用程序开发者只需要编写特定的应用逻辑即可,这样大大加快了软件产品的开发速度。另外,组件都是经过了充分测试的,因此在质量上也提供了坚实的保障。只有这样,才能实现快速、高质量的软件开发。

在组件技术中,如何捕捉和定义组件的需求,如何设计和开发组件,是组件技术的最核心部分。本章对组件技术进行概要的阐述,帮助读者澄清一系列组件技术领域的概念。1.1 应用软件的总体架构

通常,应用软件的总体架构,可以分为两大部分:应用层和平台层,如图1-1所示。图1-1 应用软件架构

平台层提供基础框架和大量可重用组件,这些组件以一定的接口方式暴露出来,供应用层来调用。平台层通常不提供与具体业务相关的逻辑处理,而是提供:

● 与业务无关的功能组件,比如日志、安全、线程池、连接池、告警监控等。

● 多种业务之间可共用的机制,如工作流、事件通知机制、远程通信等,这部分也与具体的业务无关。

应用层提供具体应用相关的逻辑处理部分,包括页面、应用逻辑、应用数据等。

平台层和应用层,是个逻辑划分的概念,在实际的软件实现中,平台层和应用层都可以由多个层来实现,也可以合并到一个程序中,这要视软件系统的规模和具体需求而定。

从图1-1可以看出,构建一个高度可重用的平台层,可以使应用开发人员只需集中精力关注业务逻辑,而与业务无关的功能组件和机制都由平台层提供,供直接调用,并且多个应用程序可以充分共享这些组件,这样就极大地简化应用开发,缩短软件交付周期,并保障软件质量。

而构建一个高度可重用的平台层,最核心的挑战就是设计和开发高度可重用的组件,使其能够提取应用的共性需求,简化接口,真正做到应用开发时可以直接拿来就用,而且非常简单易用。1.2 组件定义及与其相关概念的澄清

那么,到底什么是组件呢?框架又是什么意思?类是组件吗?控件又指什么?元件、构件这些概念又如何理解?

这些概念,都没有一个统一的标准答案,因此在软件开发过程中,这些术语经常被混淆,笔者根据自己的工作体会,对这些概念解释如下:

对象

面向对象语言(Object-Oriented Language)是一种以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。对象都具有状态和行为。对象,也被翻译为实例、物件。

面向对象的概念。类是一种对包括数据成员、函数成员和嵌套类型进行封装的数据结构。在程序运行期间,由类创建成对象的过程,被称为类的实例化。因此,对象是个运行期的术语,类是个编译期的术语。类本身就是可重用的直接实现手段。

控件

控件的英文是Control,控件就是具有用户界面的组件。要说得具体一点,就得回顾早期 Windows的历史根源,当时控件指任何子窗口:按钮、列表框、编辑框或者某个对话框中的静态文本。从概念上讲,这些窗口——控件——类似用来操作收音机或小电器的旋钮和按钮。随着控件数量的增加(组合框、日期时间控件等),控件逐渐成为子窗口的代名词,无论用在对话框中还是用在其他种类的主窗口中。没过多久 Basic 程序员开始编写他们自己专用的控件,自然而然地,人们便想到共享这些控件。共享代码的方法之一是通过磁盘拷贝,但显然效率低下。必须要有一种机制使开发者建立的控件能够在其他程序员的应用中轻而易举地插入,这便是VBA控件、OLE控件、OCX及至最终ActiveX控件产生的动机。因此,控件是组件的一个主要样本(并且历史上曾驱动着组件的开发),控件又不仅仅是唯一的一种组件。

元件

元件是个电子行业的术语,是电子元件的简称。也有一些软件借用这个术语,指特定的可重用控件。

构件

构件是系统中实际存在的可更换部分,它实现特定的功能,符合一套接口标准并实现一组接口。构件代表系统中的一部分物理实施,包括软件代码(源代码、二进制代码或可执行代码)或其等价物(如脚本或命令文件)。

通常认为,构件是特定软件开发环境中、满足指定规范的软件部分,涵盖了设计、开发、物理等范畴。

框架

框架的英文是Framework,框架是一个应用程序的半成品。框架提供了可在应用程序之间共享的可复用的公共结构。开发者把框架融入他们自己的应用程序,并加以扩展,以满足他们特定的需要。框架和工具包的不同之处在于,框架提供了一致的结构,而不仅仅是一组工具类(摘自《JUnit in action中文版》)。

子系统

子系统是个在设计中使用的术语,英文是SubSystem。在软件总体架构中,按照功能划分子系统。通常一个子系统包含一个或多个进程。

模块

模块也是个在设计中使用的术语,英文是Module。模块指一个子系统内部,按照软件结构分解的功能部分。模块会包含多个类、使用多个组件,也会与框架交互。

那么组件是什么呢?

组件

类本身是个细粒度的可重用实现,为了解决功能或机制层面更大粒度重用的问题,又引入了组件的概念。组件的英文是Component,其逻辑结构如图1-2所示。图1-2 组件逻辑结构图

组件对外暴露一个或多个接口,供外界调用。组件内部由多个类来协同实现指定的功能。对于复杂的组件,会包括很多类,还可能包含配置文件、界面、依赖的库文件等,组件也可以包含或者使用其他的组件,构成更大粒度的组件。

一些特定范畴的组件,由软件厂家或者国际权威组织制定并颁布了组件规范,如COM、ActiveX、EJB、JavaBean等。本书讨论的组件,指一般意义的自定义组件,不包括这些规范化的组件。

一个真正的软件系统,会涉及以上多个概念,典型的软件系统静态结构如图1-3所示。图1-3 软件系统静态结构

图1-3展示了一个软件系统,包括2个子系统:子系统1和子系统2,其中,子系统1调用子系统2。

子系统1包含2个模块:模块1和模块2。模块1由两个类(Class 1 和Class 2)和一个组件(组件1)构成,模块2由两个类(Class3和Class4)和两个组件(组件2和组件3)构成,模块2提供一个接口给模块1调用。模块1和模块2都使用了框架1。

子系统2包含2个模块:模块3和模块4。模块3由两个类(Class5和Class6)和一个组件(组件4)构成,模块4由一个类(Class7)和一个组件(组件5)构成。模块4提供一个接口给模块3调用。模块3和模块4都使用了框架2。1.3 总结

本章对组件技术进行了概括的阐述。经过长期的软件工程实践,组件技术被认为是实现快速高质量产出软件的最关键技术。软件系统从逻辑上可以划分为平台层和应用层,平台层提供大量可重用的组件,保障了应用系统的快速开发。另外,本章对组件领域各种易于混淆的概念进行了讲解和区分,使读者可以清晰理解到底什么是组件。第2章 组件设计原则

Java阵营一直倡导开源,开源运动如火如荼展开,催生了无数组件。但是,坦率地讲,这些开源的组件中,能够直接拿过来,不做任何改造,就能用于商业软件的构建,满足功能和性能的要求,这样的优秀组件并不多见。因此,核心软件开发者时常面对的尴尬局面是:大量的开源资源,都不满足我的要求。

实际上,组件设计是软件设计开发最精髓所在,凝聚了面向对象、数据结构、设计模式、线程并发同步、网络通信、操作系统等诸多领域的最核心技术,一直是设计开发领域彰显技术水准的最高领地。

一个组件,要想被广泛重用,满足不同系统的使用要求,就要求组件有足够的能力适应不同的应用场合,提供满足需要的功能,并表现出优秀的性能。因此,组件设计过程中,组件设计者要考虑的问题非常广泛而复杂,组件设计者需要具备的知识、技能和经验要求非常高,主要包括如下三个层次的核心能力。(1)第一层次:编程能力。主要包括:

● 编程语言的熟练使用;

● 数据结构的精准运用;

● 多线程并发、同步的准确理解和实现;

● Socket 网络编程技术的全面掌握和运用;

● 数据库概念的精准理解和熟练应用。(2)第二个层次:设计能力。主要包括:

● 面向对象思想的精准理解和正确应用;

● 设计模式的精准理解和正确应用;

● 优秀的框架、中间件、数据库的深入洞悉和优化能力;

● 数据备份、安全、可维护性等方面的技术知识掌握;

● 设计方案的分析、比较、权衡和抉择。(3)第三个层次:需求分析能力。主要包括:

● 对客户明确的功能需求进行分析的能力;

● 对客户明确的性能、数据备份、安全、可维护性等方面需求进行分析的能力;

● 推理、提取、归纳客户隐性需求的能力;

● 对需求方案进行分析、比较、权衡和抉择的能力。

每个层次的完成标准,是对本层次内的产出物,可以精确地阐述,为什么一定要这样,而不能采用别的方案或实现的理由。比如,在第二个层次——设计阶段,必须要能精确地说出:这样设计软件的依据在哪里,而且为什么一定要这样设计,而不能用其他的方案呢;这种设计方案与别的设计方案相比较时优势在哪里,不足在哪里;选择这种设计方案的理由在哪里。在深厚的技术和经验积累的基础上,能够横向的对各种方案进行深入的优缺点分析、比较、权衡和抉择,是相应阶段修炼完成的重要标志。

对一个本科毕业生来说,走上工作岗位后,在项目实战中进行学习和积累,到达第一个层次,至少需要2~3年的时间。修炼第二个层次,还需要3~4年的时间。修炼第三个层次,还需要2~3年的时间。

考虑实际工作中,一个软件开发人员经常在一个项目开发过程中,对以上三个层次的相关活动都会参与,相当于三个层次同时进行修炼,因此通常工作经验至少在5年以上才有可能涉足组件设计这个领域。另外,逐步地完善和全面提升,日臻精纯,需要天赋、勤奋、深入的思考和岁月的积累。这也就是,为什么在软件领域,组件到处泛滥,但优秀组件却不多的原因所在。

本章将对组件设计过程中要考虑的核心要素、设计中要遵循的核心原则进行总体阐述,使读者能从总体上掌握如何发现、评判、设计一个优秀的组件。

本章将对目前业界存在的诸多技术争论、误区进行澄清,让读者从所谓的“业界潮流”、教条、“黄金定律”中走出来,真正理解组件设计过程的核心原则。这些核心原则如下:

原则一:精准地解决共性问题

原则二:无配置文件

原则三:与使用者概念一致

原则四:业务无关的中立性

原则五:对使用环境无依赖

原则六:单类设计和实现

下面来详细讲解每个核心原则。2.1 组件定位:精准地解决共性问题

组件来源于软件工程实践中,是对重复、反复出现、普遍的、有相似性的问题进行分析,剥离掉各个问题的特性,而提取各个问题之间的共性。此共性就确定了组件要解决的问题。如果不是共性的问题,那么写完后的代码就只能在一种或有限的几种情况下才能被使用,就无法实现大规模复用。不能广泛地复用,就不能被叫做组件。

因此,组件首先是业务驱动的,不是技术驱动的。不能因为我们有了新的技术,就想着要写几个新的组件。

共性的业务问题,是组件的产生来源。

另外,即使确定了要解决的共性问题,组件也还必须提供解决问题最合理、最有效的方式和方法。如果提供的方法不是最好的,那么也很难得到使用者的喜爱,最终也难以推广和复用。因此,组件设计上,必须提供解决问题的精准思路和方案。这是决定同类别的组件中,哪个组件能胜出的最主要原因。

一个组件提供的问题解决方法,是否最有效,主要评价原则如下:

1.技术与业务对齐

技术结构和技术概念要与业务上的模型、概念保持一致,在组件对外暴露的接口上,最好不要引入新的技术术语和概念,这样的组件最吻合业务的需求,也最容易理解。

技术与业务对齐,这是组件设计的第一位重要原则。

2.技术方案的优势

这是结构设计、技术选型范畴内要考虑的问题。实现同一个功能,可以有很多不同的结构设计,也可以采用很多不同的技术。优秀的组件,一定要在技术方案上有明显的技术优势。

3.接口简单

组件暴露出来供外界使用的接口,必须足够简单。在概念和模型层面上与业务保持一致,另外接口封装、抽象上要仔细推敲,保证接口提供给别人使用时,调用者足够简单地使用。

4.功能强大

组件如果提供的功能太少,意味着能帮用户解决的问题比较少,这样的组件实用性大打折扣。而如果组件的功能过多,就会让用户觉得这个组件非常臃肿和庞大,用户只用了其中一小部分功能,其他大部分功能都用不上或者很少用,这样用户要在一本厚厚的使用手册中,仔细寻找自己需要的部分,去除自己不需要的部分,这也算不上一个优秀的组件。因此,一个组件提供的功能,既要能满足多种场景下不同客户的需求,还要在不同的需求中进行取舍和合并,使提供的功能集不会超出客户实际使用需求太多。2.2 组件设计:无配置文件

组件不带任何配置文件,组件所依赖的各种库也不带任何配置文件。

这个原则极其重要!!!

如果一个组件,自己定义了配置文件,无论是XML,Properties,还是其他的文件类型,内部的格式都是组件设计者自己定义的。我们假设一个中等的项目,会使用10个组件,那么就会有10个配置文件。这些配置文件的格式各不相同,那么我们的软件安装手册、维护手册就要仔细描述这10个配置文件的结构,我们的实施维护人员就要学会如何配置这10个文件。如果项目再大,需要集成50个组件,那就需要50个配置文件,对手册编写人员和实施维护人员,这是怎样的灾难啊!

归纳起来,如果一个组件自己带配置文件,带来的不良影响主要有:(1)不同组件的配置文件格式不统一,软件开发人员、手册编写人员、实施人员、维护人员等要学习不同格式、不同结构、不同的配置方式,学习的成本大大增加;(2)同一个配置(比如数据库连接串,IP地址等),由于各个组件的配置方式不同,则实施人员要在多个配置文件中进行配置,既增加了工作量,又难于保证数据始终一致;(3)如果后续发现更好的组件,要用其替换原有的组件,则新的组件的配置文件与原组件的配置文件不同,则即使这两个组件的功能一致,我们也必须更新软件的实施维护手册,相关人员又要重新学习。

因此,基于配置文件的组件,是非常不容易被集成的。一个应用软件,无论其使用多少个组件,应该最多只有一个配置文件,用于集中配置这个应用的所有参数。而每个组件,都是以接口或者类的方式提供配置方法,应用软件集中读取所有配置信息,然后通过组件的配置方法,将配置参数设置到组件上。这样,将从根本上解决组件集成过程中由组件配置文件引起的各种问题。2.3 组件设计:与使用者概念一致

给应用组件提供接口。在接口上表现出的概念术语、使用方式都应与使用者的概念术语、使用方式完全对应,也就是说,技术和业务对齐。

组件设计者常犯的毛病是,组件暴露的接口,引入了新的技术术语,这些术语在使用者的概念体系中根本就不存在,因此使用者理解起来非常困难,造成极大的学习障碍。与使用者概念不一致的技术术语,突出表现在接口命名、类名、函数名、参数名、返回值的含义等。

从本质上讲,组件接口上暴露的与使用者不一致的概念,要么是这个概念本身就是错误或不恰当的,其不应该存在,要么这是个内部实现的概念,不应该暴露在接口上。2.4 组件设计:业务无关的中立性

一个组件,要在不同应用、不同场景下都能广泛地重用,这是组件设计者必须实现的目标。但一个组件的产生,往往来源于一个特定的项目、一个特定的业务场景,在实现业务和项目功能的时候,组件设计者意识到,这个功能部分可以进行抽取,形成一个可重用的组件。因此,这个抽取的过程,组件设计者务必把与当前这个项目、这个业务特有的部分剥离掉,保留一般、共性的功能,并重新封装和调整接口,以满足通用的使用场景。这样,这个组件可以与业务无关,保持自己的中立性,后续可以在其他的应用中被重用。

如何识别哪些是业务特性,哪些是一般的共性,这需要依赖组件设计者多年的经验。2.5 组件设计实现:对使用环境无假设

组件的内部实现,是否可以对将来组件被使用的环境做些假设?比如,是在 Servlet 容器中运行,或仅使用一个组件的实例,或仅处理一个目录…….

如果组件设计者对组件将来被使用的环境做了一些假设,认为这些条件必然被满足,那么组件的代码实现就会和这些假设条件相关。如果这些条件不成立,则组件无法被使用。比如,组件设计者认为,将来应用中一个组件实例就能满足其需要,因此组件就设计成了单实例。当一个特殊的应用出现,需要使用两个组件实例来处理不同的场景时,发现组件已经被设计成单实例,无法满足应用的这种“特殊”需求。

这种需求,真的很特殊吗?

客观上讲,需求来自于具体的应用,来自客户的使用场景和要解决的问题。需求,是独立于设计和实现的,尤其与组件的设计实现无关。组件设计者之所以认为这种需求很“特殊”,是因为这个需求超出了组件设计者原来所做的假设。根本上讲,组件设计者不应该对组件将来的使用环境做任何假设,这样组件的使用场合仅受限于组件的能力集,只要应用需要的功能在组件的能力集范围内,各种环境下的不同应用都可以使用这个组件。

这样,组件才可以被广泛重用。2.6 组件设计实现:单类设计和实现

怎样的组件,才是一个优秀的组件?从组件使用者的角度,组件要简单,易于使用,并且功能强大。那么组件怎样才能简单,易于使用?

首先,前面讲过,组件提供出来的接口,要与使用者的概念完全一致,这样使用者就非常容易理解组件的各个类、接口、方法和参数,这样就会易于使用。

但组件本身怎么才能简单呢?

最简单的组件,就是一个类。

一个类,就会比两个类简单,两个类就会比四个类简单。这个道理显而易见,其核心精髓是面向对象的基本设计原则:高内聚、低耦合。要严格控制类的数量,逻辑相关度很高的,就不允许拆成多个类。不支持将来变化的部分,就不提供接口。

组件,作为可重用的软件,不会承载太多的功能,组件的规模不会很大。因此,最理想的情况,组件就是单独的一个类。组件使用者用起来,将会是极致的简单。

我们从网上下载开源的项目,打开源代码一看,豁!好家伙,一大堆的类和接口,不写几十个、几百个类好像就显不出作者的水平。其实真有必要写那么多的类吗?

高内聚!高内聚!单类组件,简单到极致!2.7 总结

本章对组件设计原则进行了精辟的阐述,对业界软件设计浪潮中频繁出现、习以为常、甚至被公认或默认的做法,笔者提出了自己不同的设计理念。在后续的各个章节中,将在这些原则的指导下,对各个领域的组件进行分析、设计和实现,让读者充分理解这些设计原则的重要性和正确性。第2篇 组件设计的基础知识

第3章 预备知识......................................................................16

本章主要讲解一些组件开发中常用的技术,作为后续章节讲解组件设计的预备知识。本章假设读者已经熟悉了Java语言的基本语法,并且熟练使用Java语言至少2年,因此,简单的语法知识不再讲述,仅讲解一些高级主题。第3章 预备知识

本章主要讲解一些组件开发中常用的技术,作为后续章节讲解组件设计的预备知识。

本章假设读者已经熟悉了Java语言的基本语法,并且熟练使用Java语言至少2年,因此,简单的语法知识不再讲述,仅讲解一些高级主题。3.1 Java语法深入讲解

本节对Java语法中一些高级主题进行讲解。3.1.1 static

1.static变量

本质上讲,static关键字声明了一个全局变量。

尽管Java是一门纯面向对象的语言,语言规范中没有全局变量这种说法,但static 声明的变量,从本质上就等同于C或C++语言中的全局变量。整个应用程序只有一个变量实例,任何对static变量进行的更改,在程序的其他地方都可见。

举个例子如下:

counter 是个整型变量,前面用static 关键字修饰后,就变成了一个全局变量,在程序的代码执行之前,这个counter 变量就已经存在于内存中了,而且是唯一的实例。

counter 静态变量是在StaticExample 类中声明的,但实际上counter 变量是独立于StaticExample 的任何实例的。也就是说,程序没有创建任何StaticExample 实例时,counter 已经存在。程序创建100个StaticExample实例时,couner 在内存中仍然是一个,而不是100个。当程序中创建的StaticExample 类的实例都被虚拟机垃圾回收了,counter 还存在。因此静态变量的生命周期,可以认为程序的第一行代码执行之前,就已经在内存中准备好,在程序的最后一行代码执行完毕,静态变量还存在于内存中。静态变量独立于任何对象,包括声明静态变量的类实例对象。

对简单类型是这样,对复杂类型(对象类型)也是这样。

静态变量的这种特性,经常被用在单实例的类实现中,例子如下:

2.static方法

如果一个方法,仅依赖方法的传入参数、其他static变量,则此方法不依赖于声明方法的类实例,应该声明为static。表示此方法是类方法,而非实例方法。例子如下:

toint 方法的实现,仅依赖于方法传入的参数s,不依赖ConvertUtil 对象的成员变量,因此toint方法是个类方法,不是个实例方法,因此用static来修饰toint方法的声明。这样,调用者调用时的代码就像下面这样:

如果不用static修饰,则调用者的代码就需要修改为如下:

但实际上,toint 方法根本就不需要创建一个ConvertUtil类的实例,创建这个对象是个浪费。

组件设计中,常用的工具类方法,基本都是static方式声明的。

3.static类

一个普通的Java类声明,用static修饰,是非法的,编译器会提示出错。这一点,与C++的静态类的概念是完全不同的。在C++中,一个类的声明用static修饰,则这个类中的所有成员变量和成员函数都是静态方法,独立于对象存在。

对于嵌套类的声明,可以用static 修饰,但这有另外的含义,在后续的嵌套类中进行讲解。3.1.2 嵌套类

一个类的声明,是在另外一个类中,这种在类中声明的类叫嵌套类,也叫类中类、内部类。例子如下:

总结如下:(1)内部类可以访问外部类的任何成员变量,包括外部类实例的私有成员变量。(2)内部类可以调用外部类的任何成员函数,包括外部类实例的私有成员函数。(3)外部类可以访问内部类的任何成员变量,包括内部类实例的私有成员变量。(4)外部类可以调用内部类的任何成员函数,包括内部类实例的私有成员函数。

因此,对于内部类的成员变量和成员函数,用private,protected,public,package 修饰,就如同没有这些修饰词一样。

另外,内部类是不能独立于外部类而单独存在的,对象构造的顺序是由外向内,先生成外部类,然后生成内部类。3.1.3 静态嵌套类

嵌套类,在类声明的时候用static 修饰,就成了静态嵌套类。静态嵌套类与普通嵌套类是完全不同的。这里,static的唯一作用,就相当于一个分隔符,将内部类和外部类隔离开来,使内部类独立于外部类单独存在。也就是说,内部类对外部类没有任何的依赖关系。而普通的内部类,是必须依赖于外部类而存在的。

因此,外部类与静态内部类之间的关系,就和两个独立的类之间的关系相同,二者之间互相访问,与两个独立类之间的访问规则相同。因此,用private,protected,public,package修饰,将直接影响可访问性。示例如下:3.2 反射

我们通常写的程序都是静态的代码,比如:调用class A示例的put 方法:

第二行,a.put 就是一种静态的写法。在编译阶段,就完成对a类型的解析,对a是否具有put方法进行了判断。如果a对象没有put方法,则编译不通过。

可是,在一些特殊情况下,我们需要在运行时动态地对一个对象的指定方法进行调用,比如我们想写一个通用的函数,传入一个对象,把这个对象的属性名和属性值都打印出来,这就需要使用反射技术。

反射,是在JDK 1.5引入的,JDK 1.4以及以前的版本不支持。

在java.lang.reflect 包中,定义了一些类,用于运行时动态解析和调用对象,常用的如下:

● Constructor:构造函数

● Field:成员变量

● Method:成员方法

● Type:类型

另外,java.lang 包中,Class 类是非常值得重视的,通常用它来动态创建对象。对于声明了无参数的构造函数,或者有默认构造函数的类,动态创建一个对象的代码如下:

这样,一个com.test.ServiceImpl 类的实例就创建出来了。

假如ServiceImpl 类有个registerUser 方法,实现用户注册功能,如下:

如何动态调用呢?代码示例如下:

以上代码,需用try{}包括起来,捕获异常。3.3 数据结构

本节对编程中经常使用的线性数据结构和非线性数据结构进行了简单介绍。线性数据结构介绍两种:ArrayList 和LinkedList。非线性数据结构介绍两种:HashSet和HashMap。这些数据结构,都在java.util 包中定义。3.3.1 ArrayList

ArrayList是个线性的数据结构,可以在任何位置对元素进行增加和删除。但请注意以下要点:(1)ArrayList是非同步的。也就是说,在多线程并发操作ArrayList的场景下,需要我们写代码对ArrayList 进行同步。(2)ArrayList内部是用数组来实现的,对元素的增加和删除都会引起数组的内存分配空间动态发生变化。因此,对其进行插入和删除速度较慢,但检索速度很快。(3)ArrayList中可以同时存放不同的对象。

ArrayList 常用的方法有:3.3.2 LinkedList

LinkedList 也是一个线性的数据结构,可以在任何位置对元素进行增加和删除。使用LinkedList 时,需注意以下:(1)LinkedList 是非同步的。也就是说,在多线程并发操作LinkedList的场景下,需要我们写代码对LinkedList 进行同步。(2)LinkedList 内部就是用动态链表来实现的,因此与ArrayList相比,其增加和删除元素速度很快,整个List不需要重新分配空间,但检索速度较慢。(3)LinkedList 实现了Queue接口,可以用做一个先进先出的队列来使用。

LinkedList 常用的方法与ArrayList 类似,增加了Queue 接口的方法如下:3.3.3 HashSet

HashSet 是个集合类型的数据结构,实现了Set接口,满足数学上对集合的定义:无序、不重复。使用HashSet 时需要注意:(1)HashSet是非同步的。也就是说,在多线程并发操作HashSet的场景下,需要我们来写代码对HashSet 进行同步。(2)HashSet内部是用HashMap来实现的,对元素的增加、删除、检索有很高的性能。(3)HashSet 中元素是无序的,因此对HashSet进行遍历,不保证遍历元素的顺序都一致。(4)HashSet中没有重复的元素,因此将相同的多个对象加入到HashSet中,则最终HashSet中只有一个对象。

HashSet 常用的方法如下:3.3.4 HashMap

HashMap,又称杂凑表、散列表,它是一个非线性的数据结构。其中每个元素由Key和Value 对构成,Key 用来做索引,Value是实际要存储的值。使用HashMap时需要注意:(1)HashMap是非同步的。也就是说,在多线程并发操作HashMap的场景下,需要我们来写代码对HashMap进行同步;(2)HashMap 对元素插入、删除、检索的速度都非常快,在高性能的情况下被大量使用,其缺点是内存占用较多。(3)HashMap中可以存放不同类型的对象,包括Null。

HashMap 常用的方法如下:3.4 泛型3.4.1 泛型简介

先举一个例子来说明什么是泛型。

有两个类StringFoo和DoubleFoo,要构造两个类的对象,并打印出各自的成员x,具体如下:

StringFoo 的代码:

DoubleFoo的代码:

调用StringFoo和DoubleFoo的代码:

如果要实现对Integer、Long、Date等类型的操作,还要写相应的类,实在是麻烦至极。

因此,对上面的两个类进行重构,写成一个类,考虑如下:

在上面的类中,成员和方法的逻辑都一样,就是类型不一样。Object是所有类的父类,因此可以考虑用Object作为成员类型,这样就可以实现通用了。

调用的代码如下:

以上,是在没有泛型的情况下,我们编写的代码,采用最顶层基类Object进行类型声明,然后将值传入,取出时要进行强制类型转换。

JDK从1.5版本开始引入了泛型的概念来优雅地解决此类问题。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载