
我们已经为QCon纽约准备了好几个月了,终于快到了!它是软件架构师、开发人员和技术团队了解最新技术的主要专业会议之一。沐鸣注册平台官网
在为期2天的研讨会之后,杏耀注册平台官网将有3天来自行业领袖的密集会议。从众多的演讲中选择最好的可能是压倒性的,所以如果你正在寻找帮助,看看我们特别兴奋强调的这七个演讲。沐鸣平台沐鸣娱乐
我们已经为QCon纽约准备了好几个月了,终于快到了!它是软件架构师、开发人员和技术团队了解最新技术的主要专业会议之一。沐鸣注册平台官网
在为期2天的研讨会之后,杏耀注册平台官网将有3天来自行业领袖的密集会议。从众多的演讲中选择最好的可能是压倒性的,所以如果你正在寻找帮助,看看我们特别兴奋强调的这七个演讲。沐鸣平台沐鸣娱乐
完美的编程语言并不存在。如果没有别的问题,我希望我们能在这一点上达成一致。新语言的发展往往是对另一种语言的缺点的回应,每一种语言在某些方面不可避免地更强,而在另一些方面则更弱。沐鸣登录测速
c#和Java都起源于C/ c++语言,除了都是面向对象的,它们还有很多共同点。除了Java的JVM和c#的。net CLR在结构上的一些相似之处之外,每个人都有自己的发展道路,他们各自的开发团队专注于语言应该是什么样子的不同观点。沐鸣注册网站
我们不想迷失在哪种语言更好的争论中,我们只想概述一些c#开发人员正在使用的特性,这些特性在Java中是没有的。沐鸣登录平台
谷歌前雇员詹姆斯•达莫尔(James Damore)最近泄露的一份有关其前雇主雇佣活动的备忘录,引发了有关IT招聘实践的讨论。在阅读了大量关于这个话题的文章后,很多人似乎认为职场多样性和性别代表是可以互换的。他们当然不是,这样做不仅在智力上不诚实,而且非常不诚实,以至于这样做实际上阻碍了在劳动力中实现性别和种族平衡这一重要目标的进程。沐鸣注册平台
你如何定义多样性?
25年前,我竞选过学生会主席。其中一名候选人是开明的进步人士,其主要纲领是促进和改善大学各方面的多元化。这个信息在社会科学、法律和人文学科的建筑中都很受欢迎,但当它被用卡车运送到工程领域时,却遇到了瓶颈。
按照所有先见之明的刻板印象,当时工程部门的性别平等有点欠缺,但那些未来的列车长中,有一些人对不断被“缺乏多样性”的大棒打败感到有点厌倦。在提问环节,一名学生走到麦克风前,问应试者是否觉得工程系缺乏多样性。在这位候选人结结巴巴地试图给出一个外交上的答案后,这位学生接着又说了一些更夸张的话。
“学校同性恋委员会的领导是一名工程师。我们在学生会的代表是印度人。获得全额奖学金的五名学生中,有三名是第二代中国人,就连我那些肤色较浅、你认为缺乏多样性的朋友们,也持澳大利亚、俄罗斯、以色列和东欧等国的签证来到这里。所以你怎么可能站在那里告诉我我们不是不同的?这个学生疯了,他完全有理由这么做。沐鸣首页
工程学院确实是多样化的,在各种美丽的,甚至鼓舞人心的方式。性别平等当然是缺乏的,我可以想到少数少数群体代表不足,但有人站在这群学生面前,告诉他们他们不是多元化的,这是一种不应该的和完全的侮辱。
面对知识不诚实
即使25年过去了,这种交流仍然让我产生共鸣。不仅因为看到一个社会正义斗士在智力上被彻底摧毁是一件很愉快的事,而且因为这个学生没有错。他有充分的权利站起来,反对那些对他引以为傲的教师们的侮辱和嘲笑。
我有参与中期咨询项目的历史,可以说,我在很多城市都参与过数量可观的项目。我不记得在任何一场活动中,项目室看起来都像是1950年的电视剧《广告狂人》(Mad Men)的场景,每个程序员都是白人男性,每个开发人员都是特权背景的产物。事实上,几年前我在多伦多的一个项目中,我在一个30多人的团队中的昵称是“白人家伙”。
我为多年来参与的所有这些项目感到骄傲,我与来自各种背景的人成为了朋友,他们的背景比我想象的更加多样化。我认识的朋友中有许多令人难以置信的女性程序员,尽管我承认我工作过的所有项目团队在性别平等方面都存在不足。但如果告诉我,我工作过的团队并不是由各种各样的人组成的,这对我和所有与我共事的人来说都是一种侮辱,因为他们本来就是。我看到了劳动力的多样性。我还没有看到性别平等。这是有区别的。沐鸣测速注册
修改翻译结果
试试有道人工翻译?
精选同行业资深译员,专家审校润色,让你尊享快捷又准确的人工翻译!
立即体验
在技术领域,妇女和某些明显的少数群体的代表性都不足,这肯定是一个问题。但是性别和种族平等并不等同于工作场所的多样性。争论说他们是不诚实的,并且使这种侮辱的智力上的不诚实持续下去将会比它将会做更多的阻碍在工作场所实现平衡的性别和种族代表的目标。
您是否应该实现一个自定义用户注册中心,以帮助减轻对各种LDAP服务器的访问,从而简化身份验证和组关联等安全任务?这个问题的答案是一个响亮的“不”。沐鸣注册平台官网
自定义用户注册表的简单之美
从表面上看,实现自定义用户注册中心非常简单。虽然略有不同从一个应用程序服务器,实现一个自定义用户注册表,你通常只需要写一个或两个Java类,它提供了一个实现半打左右的方法做事情像验证密码,或指示用户是否给定组的一部分。peasy很容易。
例如,要为WebSphere创建自定义用户注册表,这里是需要实现的IBM WebSphere UserRegistry接口,以及您需要编码的18种方法:沐鸣平台
com.ibm.websphere.security.UserRegistry
3.mapCertificate (X509Certificate [])
现在请记住,这里的目标不是发明一个存储用户的系统。在实现自定义用户注册中心时,通常有一个应用程序正在连接的底层数据存储。因此,自定义用户注册中心的目的可能是将对组合LDAP服务器和具有用户信息的数据库系统的访问组合起来。或者可能有三个不同的LDAP服务器需要进行合并访问。每个系统都有更新密码或检查用户是否属于给定组的机制。自定义用户注册表的代码只需利用这些底层系统的api即可。不需要重新发明自定义用户注册表。相反,您只需利用底层用户存储库已经提供的工具即可。
听起来很简单,不是吗?嗯,它不是。原因有很多。沐鸣开户
连通性的担忧
首先,仅仅连接到各种不同的系统就可能是一种痛苦。首先要解决的问题是获取凭证,绕过或至少通过现有的防火墙和安全系统进行身份验证。仅仅获得到不同用户注册中心系统的初始连接就可能是一件痛苦的事情,更不用说在SSL证书过期或网络拓扑结构发生更改时维护连接性了。保持网络连接既是一种预先的痛苦,也是一种长期的痛苦。
LDAP服务器的优化
然后是优化工作。针对单个用户存储库进行身份验证非常耗时,尤其是在登录高峰时。现在,假设有三到四个底层系统,在这些系统上,用户检查被连接到if..然后……其他的语句。这将是一个足够长的滞后引发用户反抗。因此,即使在合并了不同的LDAP服务器和数据库之后,仍然需要投入时间来确定如何优化访问。有时候,如果有一个后备的NoSQL数据库,其中用户id被映射到他们注册的系统,可以加快速度,尽管登录失败仍然可能需要查询每个子系统。性能优化成为构建用户注册中心的一个重要部分,因为每个用户在登录系统时都会注意到这一点。
数据质量问题
如果存在单独的子系统,那么确保数据质量也将成为首要任务。例如,如果相同的用户名(如cmckenzie)存在于两个子系统中,那么哪个子系统是真实记录?数据完整性问题可能会导致难以诊断的异常行为。例如,cmckenzie可能能够在低使用时间登录,但不能在高峰使用时间登录,因为在高峰使用时间,溢出请求被路由到不同的子系统。尽管这些问题可能源于LDAP服务器子系统中的数据质量问题,但是维护自定义用户注册表代码的开发人员将负责对问题进行故障排除和识别。
与Oracle OpenWorld一起在Moscone举行。
浪漫的爪哇9号石
这个会议被称为JavaOne,所以发现名为JDK 9 Hidden Gems的会议可以在满屋子的人面前播放也就不足为奇了。在莫斯康尼西方建筑,甲骨文的JVM建筑师米凯尔Vidstedt和英特尔公司的高级职员软件工程师Sandhya Viswanathan将避免Java 9跳过大件物品,如项目宣传的拼图和Java 9 multi-jar部署能力,相反,根据教学大纲,“谈论JDK 9优化跨越更大支持向量与增强的向量化、优化数学库,加密和压缩加速度,紧凑的字符串,带有相关优化实现的新api,以及更多帮助大数据、云、微服务、HPC和FSI应用程序的特性。沐鸣:对于Java迷来说,这样的会话描述比糖果对婴儿更有吸引力。希望他们能在规定的时间内尽可能多地完成这些话题。
让罗伊·菲尔丁的梦想成真
Java开发人员继续对开发RESTful web服务保持兴趣,因为e-Finance的企业架构师Mohamed Taman在题为“RESTful api的有效设计”的会议上将满负荷运行。除了在企业领域开发RESTful api, Taman的会议还讨论了如何创建与物联网组件、嵌入式设备、微服务甚至移动电话无缝交互的多通道RESTful web服务。毫无疑问,罗伊·菲尔丁会很高兴。
波雅尔斯基揭开了JUnit 5的神秘面纱
最后,需要注意的是,如果您想要与流行的CodeRanch Marshall Jeanne Boyarsky见面,您不可能在最后一分钟走进她关于可靠的软件测试实践的实践课程,因为希尔顿的吸引力已经被明显地超额预定了。与企业软件架构师Steve Moyer共同呈现的这节实践课名为“从JUnit 5开始”。
实际上,我对JUnit上的会话达到最大容量感到惊讶。让开发人员在最好的情况下编写良好的JUnit测试是非常困难的,更不用说在beer garden调用时参加有关这个主题的技术会议了。我假设波雅尔斯基的名声和网络形象负责收拾屋子。或者,这可能是因为课程大纲读起来更像是一个引起恐惧的警告,而不是一个简单的概述:“JUnit 4和JUnit 5之间的差异远远大于JUnit 3和JUnit 4之间的差异。”JUnit 5即将发布,所以是时候了解下一代JUnit了。”沐鸣娱乐业务:
以上就是2017年JavaOne今日热点的全部内容。这在很大程度上符合你对一群具有前瞻性思维的软件工程师的期望。这是一个学习新话题反应设计,得到的语言特性从JDK 8和Java 9,学习如何编写RESTful api,将整合多渠道设备,最后,学习如何编写测试来确保任何代码写入是可靠和健壮。就像我说的,这和你对JavaOne 2017的期望差不多。沐鸣代理
今天的客户对应用程序有很高的期望,要求个性化的体验、超高速的响应能力和每个事务的业务价值。塔塔咨询服务公司(TCS)的专家Sunder Singh和Kallol Basu表示,这种压力是企业启动数字转型项目的主要驱动力之一。来自Oracle OpenWorld 2017的TCS顾问本周在旧金山表示,另一个原因是IT创新的速度,它已经并正在实现所有业务流程的自动化。沐鸣总代
在我们最近的谈话中,TCS二人解释了以下三个企业应该开始进行数字转型项目的关键原因。Kallol是TCS Oracle Practice transformation change management顾问,Singh是Oracle Practice企业解决方案全球主管。在Oracle OpenWorld/JavaOne, Singh是“构建更智能的企业”和“提高速度、规模和效率”研讨会的联合演讲者。
数字转型项目的三个驱动力
巴苏认为,数字化转型将人重新置于公司的核心地位。“筒仓心态不适合依赖于开放性和横向方法的数字化转型。
辛格说:“有了数字技术和云技术,人们可以在任何时间、任何地点、几乎不需要任何东西的情况下创业,而且还会侵占你的生存空间。”“进入壁垒消失了。沉重的资本支出和多年的创业准备时间已经过去。”沐鸣首页
你的生意怎么样?
你的公司已经开始或正在计划一个数字转型项目,还有其他原因吗?或者,数字化转型不在你的议程上吗?或者,你的公司的自动化项目已经使其成为所谓的数字业务了吗?
“在数字转换项目中,开发团队通常能感觉到的痛点是什么?”这是我在Oracle OpenWorld/JavaOne会议“构建更智能的企业”之前向塔塔咨询服务(TCS)全球主管桑德•辛格提出的问题。沐鸣测速注册以下是他的回答,他在文中指出并讨论了数字化转型的五大挑战。
辛格说:“从复杂、耗时的过程转向简化是一种口头禅。”DevOps团队必须有这样的自我认识,即当前复杂的工作方式需要花费时间和金钱,并且最终会让公司付出生命的代价。辛格警告说,开发商必须学会“简化,否则就会灭亡”。沐鸣5网站
开发人员不能再与组织分开工作了。一个数字转型项目包括社交、移动、云、分析、人工智能、物联网等等,所有这些都以一种整合的方式进行。辛格说:“必须让所有员工都参与到变革中来,为他们提供正确的技能、行为和资源,让他们接受和接受变革。”
辛格说,如果不能在整个组织内整体地完成数字转型项目,就会导致“不断地追赶潮流”。“我们将继续进行部分建设和重建。”
在数字转换中,传统的IT角色将从侧重于操作转变为侧重于应用程序如何交付业务价值的方法。“需要对实现新的kpi提供变更管理支持,以帮助采用新的角色,”Singh说。“这种可操作的变更管理应该从董事会扩展到每个受变更影响的员工。”
3.辛格说,以敏捷的方式执行数字转型项目是成功的关键。他说:“关键是要在企业范围内(而不是孤立地)采用敏捷,以某种方式做出响应,推出产品和产品,获得先发优势。”沐鸣登录平台
最后,辛格指出,抵制变革是徒劳的。数字化颠覆现在是、将来也仍将是企业的常态。辛格说:“这个组织几十年来的文化和工作方式正在受到干扰。开发人员必须遵循这一流程,并在新的数字业务范式中找到自己的角色。
随着公司迅速进入数字化转型的领域,以极大地提高性能和扩展应用程序的覆盖范围,开发人员在开发和部署软件时需要新的方法来设计、构建、管理和通信。甲骨文在2017年JavaOne大会上证明,该组织正在采取正确的步骤来推动数字转型,他们展示了一系列令人印象深刻的开发工具和云相关服务。沐鸣开户
数字转换技术
在拉里·埃里森的公司里,哪些技术推动了数字转换?在Oracle自己的团队中,成千上万的开发人员使用Slack。在2017年JavaOne大会上,Slack平台产品负责人巴斯特·本森(Buster Benson)上台解释了原因,他将Slack描述为一个消息传递平台,同时也是一个内部使用的工作操作系统。但即便是slack也开始数字化转型。本森说:“我们正开始测试这种界限,并通过推出共享渠道和国际化,帮助你将Slack扩展到其他公司。”由于全服务生态系统的相互依赖性质,使公司之间能够进行更多的交流将是一项重大和有益的成就,有助于推动数字转型。
本森强调协作、对话和上下文决策,并指出Slack希望改善人与人之间以及人与人与软件之间的沟通。“大公司每天都在使用数百种甚至数千种不同的软件。您可能是使用终端或文本编辑器、设计应用程序或票务系统的人。你可能会用得很深。”本森说。“我们不想改变这一点。我们只是试着采用其他997产品,并给它一个轻巧易用的界面。以一种不需要你关注链接或输入密码的方式呈现,以友好的方式将内容带入对话中。”沐鸣平台注册登录
有了Slack从许多不同来源获取内容并直接在feed中显示的能力,用户可以通过单一界面与更多系统进行交互,减少了上下文切换和其他可能降低工作效率的活动。对于那些希望业绩指数级增长的公司来说,最开始要做的是让基层员工的工作更轻松。
新工具推动数字转换
进一步推动数字化转型的是深度学习和人工智能程序,这些程序可以与物联网设备、智能手机和其他各种能够捕捉数据(无论是二进制数据、文本数据,甚至是图像数据)的技术进行交互。在2017年OracleWorld和JavaOne大会的主题演讲中,甲骨文公司在许多现场演示中充分利用了这一趋势。
OracleWorld示范,Oracle开发人员建立一个服务牌照可以拍照,发送给后端microservice,从供应商获得与历史数据如交叉路口,并响应用户的信息在几秒钟内车辆里程,建议价格,等等。为了实现这一点,必须将大量不同的服务集成在一起,但是开放api使这一点变得越来越容易。“如果你看一下新一代的应用程序是如何构建的,他们首先考虑的是api的交互方式,而不是功能,”Amit Zavery说,他是Oracle的融合中间件和PaaS开发高级副总裁。沐鸣登录
1211/5000
为了帮助驱动基于API的数字转换,像Apiary这样的新工具正在出现,以帮助简化服务集成。Apiary允许开发人员快速、轻松地设计、记录、模拟和测试api。它解决了软件开发中反复出现的问题。“在过去,很多时候我会创建一个没人想用的API。我没有停下来思考他们为什么想要那个API或者他们需要它做什么,”Oracle产品开发和API服务副总裁Jakub Nesetril说。“有了Apiary,你可以预先记录一个API,获得一个模拟服务器来原型你的API,并在你开始开发之前把那个API给别人使用。”除了获得早期反馈之外,这种方法还确保文档已经就绪,并且不需要在最后进行反向工程。
从人工智能和深度学习到帮助促进应用程序集成的API创建工具,有许多令人兴奋的新工具正在涌现,它们正在帮助推动数字转换。如果说我们能从OracleWorld和JavaOne 2017中学到什么的话,那就是继续构建这些数字转换工具是像Oracle这样的大型科技公司的首要任务。
我将代码基从EclipseLink作为JPA提供者切换到Hibernate,并发现在Hibernate中您不能在事件监听器类中使用继承。JPA规范没有详细讨论这个主题,因此JPA提供者必须处理彼此不同的问题。沐鸣注册网站
我将实体侦听器从实体中分离出来,即使可以在实体类中直接添加注释如@PostPersist的方法。实体侦听器本身并不复杂,但也只是稍微复杂一些:我为所有实体侦听器创建了一个通用基类,并为每个实体创建了专门化子类。
公共类EntityListenerBase {
@PostPersist
public void postpersistent (T object) {
SomeEventBus.sendEvent (createPostPersistEvent(对象));
}
受保护的抽象事件createPostPersistEvent(T对象);
}
公共类ThingEntityListener扩展了EntityListenerBase {
@Override
受保护事件createPostPersistEvent(Thing Thing) {
返回新ThingAdded(的事);
}
}
将实体侦听器添加到实体非常简单:只需将@ entitylistener注释添加到实体并将其指向侦听器。沐鸣注册平台
@ entity
@EntityListeners (ThingEntityListener.class)
公共类ThingEntity实现了Thing {
…
}
EclipseLink在使用这样的事件侦听器时完全没有问题。问题是在我将JPA提供者切换到Hibernate之后开始的:在某个时候,我意识到应用程序显然不再生成任何响应持久性事件的事件。就好像实体监听器根本不存在一样!
在对Hibernate如何管理其事件监听器进行了较长时间的调试之后,我发现我以前的事件监听器方法不再可行,原因有二。
第一个原因是Hibernate坚持在实体监听器类中直接声明带注释的事件方法。从超类继承的方法不会扫描事件注释!这与实体类本身的方法被注释时的行为不同;在这种情况下,使用@MappedSuperclass注释的超类也将用于定位事件侦听器方法。但是,对于实体侦听器类则不是这样。沐鸣登陆地址
在将postpersistent方法向下移动到子类时,我偶然发现了第二个原因:对于每个事件注释,Hibernate只允许在实体监听器类中使用一个方法进行注释。现在,因为原始postpersistent方法有一个泛型参数,而子类有一个固定的类型参数,所以编译器创建了该方法的两个版本,一个是Object作为参数类型,另一个是我想要的实际类型Thing。其中一个方法被标记为synthetic,但是两个方法都获得注释,Hibernate不喜欢这样。不是。一个。一些。
因此,解决方案很简单,即使这意味着在所有实体侦听器上有一些代码重复:只需从事件侦听器中删除类型层次结构。
公共类ThingEntityListener {
@PostPersist
public void postPersist(Thing Thing) {
SomeEventBus。sendEvent(新ThingAdded(事情));
}
}
有人可能会说这实际上更好;更少的类层次结构和所有相关的侦听器方法都在类中,增加了可见性并降低了复杂性。
你显然知道什么是延迟加载,对吧?您肯定了解缓存。据我所知,在Java中没有优雅的方法来实现它们。以下是我在仙人掌原始人的帮助下自己发现的。
《现实》(Reality, 2012),沐鸣测速登录地址马泰奥·加罗内(Matteo GarroneReality)著(2012)
假设我们需要一个对象来加密一些文本。以一种更面向对象的方式来说,它将封装文本并成为其加密的形式。下面是我们将如何使用它(让我们先创建测试):
接口加密{
asString()抛出IOException;
}
enc = new EncryptedX(“Hello, world!”);
System.out.println (enc.asString ());
现在让我们以一种非常原始的方式实现它,使用一个主构造函数。加密机制将在传入数据的每个字节中添加+1,并假设加密不会破坏任何东西(一个非常愚蠢的假设,但为了这个例子,它会工作):沐鸣注册平台
类Encrypted1实现加密{
私有最终字符串文本;
Encrypted1(字符串txt) {
这一点。文本=三种;
}
@Override
公共字符串asString() {
最后一个字节[]in = this.text.getBytes();
final byte[] out = new byte[in.length];
for (int i = 0;我< in.length;+ + i) {
out[i] = (byte) (in[i] + 1);
}
返回新字符串(出);
}
}
看起来正确吗?我测试了一下,效果不错。如果输入是“Hello, world!”,输出将是“Ifmmp-!xpsme\”。
接下来,让我们假设我们想让类接受一个InputStream和一个字符串。我们想这样称呼它,例如:
enc = new Encrypted2(
新FileInputStream (“/ tmp / hello.txt”)
);
System.out.println (enc.toString ());
下面是最明显的实现,有两个主构造函数(同样,实现是基本的,但是可以工作):
类Encrypted2实现加密{
私有最终字符串文本;
Encrypted2(InputStream input)抛出IOException {
ByteArrayOutputStream包=
新ByteArrayOutputStream ();
而(真){
int one = int .read();
if (1 < 0) {
打破;
}
baos.write(一);
}
这一点。text =新字符串(baos.toByteArray());
}
Encrypted2(字符串txt) {
这一点。文本=三种;
}
// asString()与Encrypted1中的完全相同
}
从技术上讲,它是可行的,但是流读取就在构造函数内部,这是一种不好的做法。主构造函数只能执行属性赋值,而次构造函数只能创建新对象。沐鸣平台
让我们尝试重构和引入延迟加载:
类Encrypted3实现加密{
私人字符串文本;
私有最终输入流;
Encrypted3 (InputStream流){
这一点。文本=零;
这一点。输入=流;
}
Encrypted3(字符串txt) {
这一点。文本=三种;
这一点。输入=零;
}
@Override
公共字符串asString()抛出IOException {
如果这一点。text == null) {
ByteArrayOutputStream包=
新ByteArrayOutputStream ();
而(真){
int one = int .read();
if (1 < 0) {
打破;
}
baos.write(一);
}
这一点。text =新字符串(baos.toByteArray());
}
最后一个字节[]in = this.text.getBytes();
final byte[] out = new byte[in.length];
for (int i = 0;我< in.length;+ + i) {
out[i] = (byte) (in[i] + 1);
}
返回新字符串(出);
}
}
效果很好,但是看起来很丑。最丑陋的部分当然是这两行:
这一点。文本=零;
这一点。输入=零;
它们让对象可变,并使用NULL。它很丑,相信我。不幸的是,在经典的例子中,延迟加载和空引用总是同时出现。然而,有一个更好的方法来实现它。让我们重构我们的类,这次使用Cactoos中的标量:
类Encrypted4实现加密{
私有最终IoCheckedScalar text;
Encrypted4 (InputStream流){
这个(
()- > {
ByteArrayOutputStream包=
新ByteArrayOutputStream ();
而(真){
int one = stream.read();
if (1 < 0) {
打破;
}
baos.write(一);
}
返回新字符串(baos.toByteArray ());
}
);
}
Encrypted4(字符串txt) {
(()- > txt);
}
Encrypted4(标量<字符串>源){
这一点。text = new IoCheckedScalar<>(源);
}
@Override
公共字符串asString()抛出IOException {
最后一个字节[]in = this.text.value().getBytes();
final byte[] out = new byte[in.length];
for (int i = 0;我< in.length;+ + i) {
out[i] = (byte) (in[i] + 1);
}
返回新字符串(出);
}
现在看起来好多了。首先,只有一个主构造函数和两个辅助构造函数。其次,对象是不可变的。第三,仍然有很大的改进空间:我们可以添加更多的构造函数,这些构造函数将接受其他数据源,例如文件或字节数组。