编程技能就像生活中的许多其他技能,需要不断改进:如果我们不展望未来,我们将会落后。 站仍然不是一个选择。 在这第三安装4编写更好的Java技术系列中,我们将介绍四个重要主题:(1)验证参数使用标准Java库所提供的方法,(2)理解很重要Object类,(3)实验和学习玩jshell,和(4)查找和阅读最编写良好的代码我们可以,在这两本书和Java的源代码本身。这些技术是纯粹的编程技术,可以帮助在一个特定的压力而其他重点围绕Java生态系统和环境的工具。 不管各个性质的技术,当应用与勤奋和声音判断,每个可以帮助改善Java代码开发人员写的,新手和专家。
1。 验证参数与标准方法
验证输入是一个不可避免的任何计划的一部分。 例如,如果我们将一个对象作为参数传递给方法,希望对该对象调用一个方法,我们必须首先验证所提供的对象不是空。 此外,我们可以通过这个对象到另一个方法(可能是一个我们不开发)和第二个方法可能期望其参数不为空,导致一个错误如果一个null参数传递。
这种情况就会变得更复杂时,执行一个无效的语句可能会导致一个错误的点在程序执行的不同对象提供。 更糟糕的是,没有证据的错误可能发生在堆栈跟踪。 例如,如果我们创建一个不可变的类存储一个对象,并使用该对象的方法叫做在另一个线程,一个NullPointerException(肺水肿)可能被调用线程,没有标志的作业对象的发生。 这样一个类的一个例子如下图所示。
请点击此处输入图片描述
由于错误可能发生在不同的位置从最初的任务,必须在作业现场和我们验证参数快速失败如果无效的参数提供。 要做到这一点,我们可以添加一个null检查确保如果一个空参数传递给作业地点,立即拒绝了,结果在一个抛出神经性肺水肿:
请点击此处输入图片描述
如果提供的参数是不空,没有异常和正常功能的类。 尽管这是一个简单的解决问题的办法,其缺陷突出显示多个参数时必须经过验证。 例如,如果我们提供一个Engine和一个Transmission对象的Car构造函数,我们班增加如下:
请点击此处输入图片描述
随着null检查数量的增长,我们的代码的清晰开始减少。 等常见的问题,这是Java开发工具包(JDK)7中,添加了一个新的类(称为Objects),包括requireNonNull方法允许开发人员来检查一个对象不是零。 如果提供的对象requireNonNull方法是空的,一个是神经性肺水肿。 这种方法也返回所提供的对象允许紧凑的作业是:如果提供的对象为空,抛出一个肺水肿,但如果提供的对象不是空,它返回,可以分配给一个变量。 JDK使用这个方法,我们可以减少的null检查逻辑Car构造函数如下:
请点击此处输入图片描述
使用本标准方法,我们的代码的目的是更清晰:商店提供的Engine和Transmission如果他们不是null对象。 的requireNonNull方也足够灵活,允许一个定制的消息提供密切的表妹,requireNonNullElse,可以为其提供一个默认值。 的requireNonNullElse
将返回所提供的默认值时,所提供的对象为空,而不是抛出肺水肿。 总的来说,有三个重载的requireNotNull和两个重载的方法requireNotNullElse方法:
1:requireNonNull(T obj):抛出肺水肿如果提供的对象为空2:requireNonNull(T obj, String messag:抛出一个肺水肿与提供的消息如果提供的参数是null3:requireNonNull(T obj, Supplier<String> messageSupplier):抛出一个肺水肿与生成的消息messageSupplier参数如果提供的对象为空; 消息产生肺水肿时抛出; 这种方法时应该使用异常的消息可以是昂贵的创建(并应只被创建如果抛出肺水肿)4:requireNonNullElse(T obj, T defaultObj):返回所提供的对象如果不是null或返回所提供的默认值5:requireNonNullElseGet(T obj, Supplier<? extends T> supplier):返回所提供的对象如果不是null或生成一个默认值并返回它否则; 默认值是生成只有提供的对象为空; 这种方法时应该使用默认值可能是昂贵的创建(和只能时生成提供的对象为空)
JDK 7还包括两种方法,isNull和nonNull,上面的方法,而是同行密切回报true和false分别,如果提供的对象都是零。 这些boolean-based方法应该使用每当肺水肿并不期望和一些自定义的异常应该使用或处理逻辑。 注意Java环境中的习惯行为是抛出肺水肿时提供的参数是null(而不是一个IllegalArgumentException或一些自定义异常)和抛出异常的不同类型应该适当的勤勉和谨慎。JDK 9日发布的三个方法,介绍了允许开发人员检查提供的索引或一组指标在允许范围内:JDK 7还包括两种方法,isNull和nonNull,上面的方法,而是同行密切回报true和false分别,如果提供的对象都是零。 这些boolean-based方法应该使用每当肺水肿并不期望和一些自定义的异常应该使用或处理逻辑。 注意Java环境中的习惯行为是抛出肺水肿时提供的参数是null(而不是一个IllegalArgumentException或一些自定义异常)和抛出异常的不同类型应该做适当的勤勉和谨慎。
JDK 9日发布的三个方法,介绍了允许开发人员检查提供的索引或一组指标在允许范围内:
1:checkFromIndexSize(int fromIndex, int size, int length)::抛出一个IndexOutOfBoundsException(IOOBE)如果提供的总和fromIndex(包容),size(独家)的范围内0来length(独家)或回报fromIndex如果有效; 该方法用于验证访问n元素(size),开始在内地的fromIndex是有效的,与一个给定的集合或数组length
2:checkFromToIndex(int fromIndex, int toIndex, int length)::抛出一个IOOBE如果提供的fromIndex(包括)来提供的toIndex(独家)范围内0来length(独家)或回报fromIndex如果有效; 这个方法是有用的对于一些范围的验证,在内地fromIndex专门toIndex,有效期为一个给定的集合或数组length
3:checkIndex(int index, int length):抛出一个IOOBE如果提供的index小于0或大于或等于所提供的length,或返回index如果有效; 这个信息是非常有用的一个给定的验证index有效期为给定的集合或数组吗length
我们可以使用这些索引检查方法来确保提供索引是正确的对于一个给定的对象集合,如下面的清单中描述:
请点击此处输入图片描述
不幸的是,该指数检查方法不允许自定义异常甚至提供一个自定义异常消息。 在某些情况下,较低的抽象级别IOOBE不适合应用程序,并需要更高级的例外。 例如,根据上下文,我们可能不希望客户的Garage
知道我们存储类Car对象列表中(相对于数据库或一些远程服务),因此,抛出IOOBE可能透露太多信息或把我们的接口与施。 相反,NoSuchElementException可能是更合适的(或者如果需要一个自定义异常)。
考虑到这些缺点,我们可以制定以下规则关于null检查和索引方法(包括构造函数)参数的检查:
2。 了解对象类
最常见的一种上市首日的教训在Java面向对象是默认的所有类的超类:Object。 这个类构成了整个Java类型层次结构的根,包括常见的方法在所有Java类型,定义和那些包含在标准的Java库。 虽然这些基础知识几乎是普遍的Java开发人员的曲目之一,很多细节崩溃。 事实上,许多细节的,即使对于中间和先进的Java开发人员。
在总,Object类有十一个方法继承了所有类在Java环境。 而其中的一些方法,如finalize弃用,不应该被覆盖或显式地调用时,其他等equals和hashCode对于日常用Java编程至关重要。 而错综复杂的的深度Object类超出了本文的范围,我们将关注在这个终极类中最重要的两个方法:equals和hashCode。
=
的equals在理论和方法是一个简单的方法在实践中一个更微妙的。 这种方法允许平等比较两个对象,返回true如果对象是相等的false否则。 尽管这个概念听起来简单,实际上远非如此。 例如,两个不同类型的对象能平等吗? 可以两个对象在内存中存储在不同的位置(即不同实例)相等,如果他们国家是平等的吗? 平等是如何影响其他方法和特点Object课吗?
默认情况下,等于方法返回true如果两个实例是相等的。 这是显而易见的如果我们看看JDK 9的实现Object#equals方法:
请点击此处输入图片描述
虽然这个定义是非常简单的,它隐藏了一些=方法的重要特征。 总的来说,整个Java环境使五个基本假设=方法是如何实现的任何类,包括用户定义的类,这些假设是记录在文档中Object类。 这些假设,引用上述文档,如下:
1:它是反射性:对于任何非空的参考值x,x.equals(x)应该返回true。
2:它是对称的:对于任何非空引用值x和y,x.equals(y)应该返回true当且仅当y.equals(x)返回true。
3:它是传递:对于任何非空引用值x,y,z,如果x.equals(y)返回true和y.equals(z)返回true,然后x.equals(z)应该返回true。
:4它是一致的:对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或持续回报false没有提供信息用于=比较对象被修改。
5:对于任何非空的参考值x,x.equals(null)应该返回false。
应该注意的是,这些限制是复合平等的目的:回报true如果两个对象或被认为是相等的false
否则。 在大多数情况下,默认等于实现就足够了,但是在可能的情况下需要更多的调整实现。 例如,如果我们创建一个不可变类,这个类的两个对象应该是平等的,如果所有的字段都是平等的。 在实践中,压倒一切的equals方法结果在随后的实现结构:
检查提供的对象是这个对象
检查提供的对象有相同类型对象
检查是否提供对象的字段是一样的这个对象的字段
例,如果我们想要创建一个不可的Exam类,记录收到的年级学生在一个特定的考试,我们可以定义类,连同它的equals方法,如下:
请点击此处输入图片描述
这个实现可以确保得到以下结果:
请点击此处输入图片描述
有更多的equals方法比初看起来和中级和高级Java开发人员应该熟悉这一重要方法通过阅读其官方文档。 深入研究equals
方法,包括许多相关的特质与定制实现,可以找到10项(37-49页。)有效的Java,第3版约书亚·布洛赫。
hashCode
第二条Object串联是hashCode方法,该方法生成一个整数散列码对应于一个对象。 这个散列码作为哈希摘要时插入在基于散列的数据结构,如HashMap。 就像equals方法,整个Java环境会假设的行为hashCode通过编程的方法没有反映:
1:对象的哈希码必须是常数,同时考虑到的数据散列码保持不变; 一般来说,这意味着一个对象的哈希代码保持不变,如果物体的状态不变
2:相等的对象的哈希码必须是平等的equals方法:3:两个对象的哈希码不需要是不平等的,如果两个对象是根据他们的不平等equals方法,尽管依赖哈希码的算法和数据结构通常表现得更好当导致不平等不平等的对象的哈希码
例如,任意的哈希代码设置的字段通常是计算在实践中使用以下系列的计算:
请点击此处输入图片描述
在代码中,这结果hashCode类似以下的定义:
请点击此处输入图片描述
为了减少实现的沉闷hashCode与众多领域的一个类的方法,Objects类包含一个静态方法hash允许任意数量的值一起散列:
请点击此处输入图片描述
而Objects#hash减少了杂波的方法hashCode米ethod并提高其可读性,它不带有价格:自hash方法使用变量参数,Java虚拟机(JVM)创建一个数组来保存它的参数,需要拳击的参数的一个原始类型。 经过全面的考虑,散列方法是一个很好的默认当覆盖hashCode方法,但如果需要更好的性能,应该实现手动multiply-and-sum操作或哈希代码应该被缓存。 最后,由于连体的equals和hashCode方法(即哈希码相等,如果equals方法返回true),只要覆盖的方法之一,其他应该覆盖。
3所示。 实验jshell
有无数次当一个开发人员变得好奇或类声明将如何工作在他或她的应用程序和运行时不会想尝试这在真实的应用程序中。 例如,如果我们循环这些指标,多少次这循环得到执行? 或者,如果我们使用这有条件的,这逻辑是否执行? 有时,好奇心可能是更普遍的,如想知道一条语句的返回值将或质疑语言功能是如何在实践中(即如果我提供一个空值Objects#requireNonNull吗?)
伴随的许多其他重要的功能在JDK 9中,Read-Evaluate-Print循环(REPL)工具称为jshell。 jshell是一个命令行工具,允许Java语句的执行和评估,显示的结果。 开始jshell(假设bin/目录JDK 9安装操作系统路径),简单地执行jshell命令,如下所示(版本号将取决于版本的JDK安装在机):
请点击此处输入图片描述
一旦jshell>提示,我们可以输入任何可执行Java语句和看到它的评价。 注意单个语句不需要分号之后,虽然他们可以包含(如果需要的话)。 例如,我们可以看到Java和4和5 jshell使用下面的命令:
请点击此处输入图片描述
虽然这可能是简单,重要的是掌握,我们能够执行Java代码不创建一个全新的项目,并编写一个样板public static void main方法。 Whatsmore,我们也可以执行与jshell相对复杂的逻辑,如所示。
请点击此处输入图片描述
是jshell迅速创建一个完整的项目,它不再变得繁琐的评价小块的代码(即需要秒写和分钟来创建一个可运行的项目)。 例如,如果我们想看到的Objects#requireNonNull方法将应对各种参数(方法1),我们可以尝试使用jshell出来,看看实际的结果,如下描述。
请点击此处输入图片描述
重要的是要注意,虽然我们可以执行单个语句没有后面的分号,语句的范围(即那些花括号包围,单行的条件,等等)必须包含分号。 例如,离开了后面的分号的类定义导致jshell语法错误:
请点击此处输入图片描述
尽管jshell比本节中的例子更有能力给它的功劳,完全公开其功能超出了本文的范围。 好奇的读者可以找到大量的信息Java Shell用户指南。 尽管简单的例子在这一节中,jshell的易用性和力量为我们提供了一个方便的技术改善我们如何在Java开发应用程序:
4所示。 编写良好的代码阅读
最好的方法之一,成为一个熟练的工匠是看一个更有经验的工匠在起作用。 例如,为了成为一个更好的画家,一个有抱负的艺术家可以观看视频(即专业画家。绘画的乐趣通过鲍勃罗斯),甚至研究现有的大师如伦勃朗或莫奈的画作。 同样,曲棍球球员可以研究视频如何最好的国家冰球联盟(NHL)玩家滑冰或仍然处理在一个游戏或雇佣一个有经验的球员作为一个教练。
编程也不例外。 很容易忘记,编程是一种技巧,必须磨练和最好的方法来提高这种技能是对历史上最好的程序员。 在Java领域,这意味着研究语言的原始设计者如何运用语言。 例如,如果我们希望知道如何编写干净、简单的代码,我们可以看看JDK源代码和找出Java编写Java代码的发明家。 (注意,JDK可以找到源代码lib/src.zip下一个标准JDK安装在Windows或下载OpenJDK在任何操作系统)。
我们也可以获得大量的信息关于一个特定类通过观察它的实现工作。 例如,假设我们关心的是如何Collection删除一个元素使用AbstractCollection#remove(Object)方法。 而不是猜测实现,我们可以直接到源和实现(如下描述)。
请点击此处输入图片描述
通过简单地看着这个方法的源代码,我们可以看到,如果一个nullObject我传递给这个方法,第一个零了Collection(使用Iterator
为Collection删除)。 否则,equals方法是用来寻找一个匹配的元素,如果存在,离开Collection。 如果有任何改变的Collection,true返回; 否则,false返回。 虽然我们可以理解什么JavaDocs相关的方法做的,我们可以看到如何它是通过直视的源的方法。
除了看到特定的方法是如何工作的,我们还可以看到一些最经验丰富的Java开发人员编写的代码。 例如,我们可以看到如何有效地通过查看连接字符串AbstractCollection#toString方法:
请点击此处输入图片描述
许多新的Java开发人员可能使用简单的字符串连接,但开发人员的AbstractCollection#toString(他是一个原始的Java前身)决定使用StringBuilder。 这至少应该求问:为什么? 有什么这开发人员知道我们不? (很有可能因为这是不太常见的找到一个缺陷或错误的JDK源代码)。
然而,值得注意的是,仅仅因为代码写某种方式在JDK并不意味着它在大多数Java应用程序是这样写的。 很多时候,成语使用广泛的Java开发人员,但没有出现在JDK(一些JDK的代码已经写了很久以前)。 同样,JDK的开发人员可能没有做出正确的决定(甚至一些原始的Java开发人员承认,一些原始的实现是一个错误,但这些实现中使用太多不同的应用程序返回和改变它们),它将是明智的不重复这些错误,而是向他们学习。
作为JDK的补充源代码,一个经验丰富的Java开发人员应该读尽可能多的著名的Java开发人员编写的代码。 例如,阅读代码写的马丁重构相当令人瞠目结舌。 可能存在的方式编写代码,我们从未想过但最滋味的实践者是常见的。 虽然它是几乎不可能制定一个全面的书籍列表包含大部分文字代码(就是编写良好的非常主观的),一些最知名的书如下:
有效的Java
干净代码
务实程序员
释放它!
Java并发性在实践中
虽然有无数,这些书提供了一个良好的基础,包括一些历史上最多产的Java开发人员。 正如JDK源代码,代码写在上面的书是特定风格的作家写的。 每个开发人员是一个个体,每一个有他或她自己的风格(比如一些发誓通过打开括号的一条线,而另一些人则要求他们自己都放在一个新行),但关键是不要陷入琐事。 相反,我们应该学习一些最好的Java开发人员写代码,应该渴望写代码一样简单,干净的阅读。
结论
有无数的技术提高开发人员的技术水平,以及他或她开发人员的代码。 在这第三安装4编写更好的Java技术系列中,我们覆盖验证方法参数使用提供的标准方法Objects类,理解Object
类、试验jshell和维护一个贪得无厌的胃口资源best-written代码。 使用这些技术,搭配健康的剂量的良好的判断力,可以导致更好的Java开发人员在任何层面上,从新手到专家。
最后给大家分享我自己收集的架构学习资料,关注我,回复“JAVA”即可获取以下学习资料: