initializationauto是什么意思思

  在我刚刚接触现在这个产品的时候,我就在我们的代码中接触到了对Double Brace Initialization的使用。那段代码用来初始化一个集合:

  相信第一次看到这种使用方式的读者和我当时的感觉一样:这是在做什么?当然,通过在函数add()的调用处加上断点,您就会了解到这实际上是在使用add()函数向刚刚创建的集合exclusions中添加元素。

  可为什么我们要用这种方式来初始化集合呢?作为比较,我们先来看看通常情况下我们所编写的具有相同内容集合的初始化代码:

  这些代码很繁冗,不是么?在编写这些代码的时候,我们需要重复键入很多次exclusions。同时,这些代码在软件开发人员需要检查到底向该集合中添加了哪些元素的时候也非常恼人。反过来,使用Double Brace Initialization对集合进行初始化就十分简单明了:

  因此对于一个熟悉该使用方法的人来说,Double Brace Initialization清晰简洁,代码可读性好维护性高,自然是初始化集合时的不二选择。而对于一个没有接触过该使用方法而且基础不是很牢靠的人来说,Double Brace Initialization实在是有些晦涩难懂。

  从晦涩到熟悉实际上非常简单,那就是了解它的工作原理。如果将上面的Double Brace Initialization示例稍微更改一下格式,相信您会看出一些端倪:

2 // 匿名派生类的各个成员

  而内层的花括号实际上是在匿名派生类内部所声明的instance initializer:

4 // 实际上等于构造函数,用来执行对当前匿名类实例的初始化

  在通过Double Brace Initialization创建一个集合的时候,我们所得到的实际上是一个从集合类派生出的匿名类。在该匿名类初始化时,它内部所声明的instance initializer就会被执行,进而允许其中的函数调用add()来向刚刚创建好的集合添加元素。

  其实Double Brace Initialization并不仅仅局限于对集合类型的初始化。实际上,任何类型都可以通过它来执行预初始化:

  看到了吧。这和我中所提及的Fluent Interface模式有异曲同工之妙。

  下一步,我们就需要了解Double Brace Initialization的优缺点,从而更好地对它进行使用。

  Double Brace Initialization的优点非常明显:对于熟悉该使用方法的人而言,它具有更好的可读性以及更好的维护性。

Initialization的时候,我们实际上创建了一个匿名类。匿名类有一个性质,那就是该匿名类实例将拥有一个包含它的类型的引用。如果我们将该匿名类实例通过函数调用等方式传到该类型之外,那么对该匿名类的保持实际上会导致外层的类型无法被释放,进而造成内存泄露。

  而在用户通过该Builder创建一个产品实例的时候,他将会使用如下代码:

  上面的代码没有保持任何对NutritionFacts.Builder的引用,因此在执行完这段代码后,该段程序所实际使用的内存应该仅仅增加了一个NutritionFacts实例,不是么?答案是否定的。由于在build()函数中使用了Double Brace

  另外一个缺点则是破坏了equals()函数的语义。在为一个类型实现equals()函数的时候,我们可能需要判断两个参与比较的类型是否一致:

8中说它违反了里氏替换原则。反驳这种观点的人则主要认为维护equals()函数返回结果正确性的责任需要由派生类来保证。而且从语义上来说,如果两个类的类型都不一样,那么它们之间还彼此相等本身就是一件荒谬的事情。因此在某些类库的实现中,它们都通过检查类型的方式强行要求参与比较的两个实例的类型需要是一致的。

  而在使用Double Brace Initialization的时候,我们则创建了一个从目标类型派生的匿名类。就以刚刚所展示的build()函数为例:

  在build()函数中,我们所创建的实际上是从NutritionFacts派生的匿名类。如果我们在该段代码之后添加一个断点,我们就可以从调试功能中看到该段代码所创建实例的实际类型是NutritionFacts$1。因此,如果NutritionFacts的equals()函数内部实现判断了参与比较的两个实例所具有的类型是否一致,那么我们刚刚通过Double Brace

  好,既然我们刚刚提到了匿名类在调试器中的表示,那么我们就需要慎重地考虑这个问题。原因很简单:在较为复杂的Double Brace Initialization的使用中,这些匿名类的表示会非常难以阅读。就以下面的代码为例:

  而在使用调试器进行调试的时候,您会看到以下一系列类型:

  在查看这些数据的时候,我们常常无法直接理解这些数据到底代表的是什么。因此软件开发人员常常需要查看它们的基类到底是什么,并根据调用栈去查找这些数据的初始化逻辑,才能了解这些数据所具有的真正含义。在这种情况下,Double Brace Initialization所提供的不再是较高的维护性,反而变成了维护的负担。

  而且在使用Double Brace Initialization之前,我们首先要问自己:我们是否在使用一系列常量来初始化集合?如果是,那么为什么要将数据和应用逻辑混合在一起?如果这两个问题中的任意一个是否定的,那么就表示我们应该使用独立的文件来记录应用所需要的数据,如*.properties文件等,并在应用运行时加载这些数据。

  可以说,Double Brace Initialization虽然在表意上具有突出优势,它的缺点也非常明显。因此软件开发人员需要谨慎地对它进行使用。

  在前面的介绍中我们已经看到,Double Brace Initialization最大的问题就是在表达复杂数据的时候反而会增加的维护成本,在equals()函数方面不清晰的语义以及潜在的内存泄露。

  第一个缺点非常容易避免,那就是在创建一个复杂的数据集合时,我们不再考虑使用Double Brace Initialization,而是将这些数据存储在一个专门的数据文件中,并在应用运行时加载。

  而后两个缺点则可以通过限制该部分数据的使用范围来完成。

  那在需要初始化复杂数据的时候,我们应该怎么办?为此业内也提出了一系列解决方案。这些方案不仅可以提高代码的表意性,还可以避免由于使用Double Brace Initialization所引入的一系列问题。

  最常见的一种解决方案就是使用第三方类库。例如由Apache Commons类库提供的ArrayUtils.toMap()函数就提供了一种非常清晰的创建Map的实现:

  如果说您不喜欢引入第三方类库,您也可以通过创建一个工具函数来完成类似的事情:

转载请注明原文地址并标明转载:

商业转载请事先与我联系:

}

本章将详细深入地探讨Spring框架的控制反转实现(Inversion of Control,IoC)[]原理。Spring框架所提供的众多功能之所以能成为一个整体正是建立在IoC的基础之上,因此对这一内涵简单、外延丰富的技术我们有必要进行详细的介绍。

sales=sales@.URL,可被用来从大多数位置以透明的方式获取底层的资源,包括从classpath、文件系统位置、任何以标准URL描述的位置以及其它一些变种。如果资源位置串是一个没有任何前缀的简单路径,这些资源来自何处取决于实际应用上下文的类型。

部署在应用上下文的bean可能会实现一个特殊的标志接口ResourceLoaderAware,它会在初始化时自动回调将应用上下文本身作为资源加载器传入。

为了让bean能访问静态资源,可以象其它属性一样注入Resource。被注入的Resource属性值可以是简单的路径字符串,ApplicationContext会使用已注册的PropertyEditor,来将字符串转换为实际的Resource对象。

ApplicationContext构造器的路径就是实际的资源串,根据不同的上下文实现,字符串可视为不同的形式(例如:ClassPathXmlApplicationContext会把路径字符串看作一个classpath路径)。然而,它也可以使用特定的前缀来强制地从classpath或URL加载bean定义文件,而不管实际的上下文类型。

一个应用中的大多数代码最好写成依赖注入(控制反转)的风格,这样代码就和Spring IoC容器无关,它们在被创建时从容器得到自己的依赖,并且完全不知道容器的存在。然而,对于少量需要与其它代码粘合的粘合层代码来说,有时候就需要以一种singleton(或者类似singleton)的方式来访问Spring IoC容器。例如,第三方的代码可能试图(以Class.forName()的方式)直接构造一个新的对象,但无法强制它们从Spring IoC容器中得到这些对象。如果第三方代码构造的对象只是一个小stub或proxy,并且使用singleton方式访问Spring IoC容器来获得真正的对象,那么大多数的代码(由容器产生的对象)仍然可以使用控制反转。因此大多数的代码依然不需要知道容器的存在,或者它如何被访问,并保持与其它代码的解耦,这样所带来的益处是很显然的。EJB也可以使用这种stub/proxy方案代理到由Spring IoC容器产生的普通的Java实现对象。虽然理想情况下Spring

另一个例子,在一个多层的复杂的J2EE应用中(比如不同的JAR,EJB,以及WAR打包成一个EAR),每一层都有自己的Spring IoC容器定义(有效地组成一个层次结构),如果顶层只有一个web-app(WAR)的话,比较好的做法是简单地创建一个由不同层的XML定义文件组成的组合Spring IoC容器。所有不同的Spring IoC容器实现都可以以这种方式从多个定义文件构造出来。但是,如果在顶层有多个兄弟web-apps,为每一个web-app创建一个Spring IoC容器,而每个ApplicationContext都包含大部分相同的底层的bean定义。因而就会因内存使用,建bean的多个复本会花很长时间初始化(比如Hibernate SessionFactory),以及其它可能产生的副作用而产生问题。作为另一可选的方案,象和的类可以在需要的时候以有效的singleton方式,加载多层次的(比如一个是另一个的父亲)Spring IoC容器,这些将会作为web应用的Spring IoC容器的父容器。由此底层的bean定义只在需要的时候加载(并且只被加载一次)。

}

我要回帖

更多关于 Initialization 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信