这里有新鲜出炉的Java函数式编程,程序狗速度看过来!
java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE(j2ee), JavaME(j2me), JavaSE(j2se))的总称。
在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,网上关于Java中对象初始化顺序的文章很多,这篇文章我们将详细介绍Java中对象初始化顺序。有需要的可以参考学习。
前言
在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。最近我发现了一个有趣的问题,这个问题的答案乍一看下骗过了我的眼睛。看一下这三个类:
- package com.ds.test;
- public class Upper {
- String upperString;
- public Upper() {
- Initializer.initialize(this);
- }
- }
- package com.ds.test;
- public class Lower extends Upper {
- String lowerString = null;
- public Lower() {
- super();
- System.out.println("Upper: " + upperString);
- System.out.println("Lower: " + lowerString);
- }
- public static void main(final String[] args) {
- new Lower();
- }
- }
- package com.ds.test;
- public class Initializer {
- static void initialize(final Upper anUpper) {
- if (anUpper instanceof Lower) {
- Lower lower = (Lower) anUpper;
- lower.lowerString = "lowerInited";
- }
- anUpper.upperString = "upperInited";
- }
- }
运行
这个类可以得到什么输出?在这个极简的例子中可以更容易地看到整个形势,但是这个情形发生在现实中会有非常多的代码分散一个人的注意力。
- Lower
不管怎么样,输出是像这样的:
- Upper: upperInited
- Lower: null;
虽然小示例中使用了
类型,
- String
类的实际代码中有一个用于注册的委托对象,与
- Initializer
类的功能是相同的 — 至少
- Lower
类是这个意图。但由于某些原因在运行应用程序时没有工作。取而代之的是,使用了默认路径,委托对象没有被设置 (null)。
- Lower
现在稍微改变一下
的代码:
- Lower
- package com.ds.test;
- public class Lower extends Upper {
- String lowerString;
- public Lower() {
- super();
- System.out.println("Upper: " + upperString);
- System.out.println("Lower: " + lowerString);
- }
- public static void main(final String[] args) {
- new Lower();
- }
- }
现在的输出是这样的:
- Upper: upperInited
- Lower: lowerInited
发现代码中的区别了吗?
是的,这个
字段不再明确地设置为空。为什么这么做会有不同。不管怎样参考类型字段(例如这里的
- lowerString
)的默认值不是为空的吗?当然是空的。事实证明,虽然这种微小的变化显然不会以任何方式改变代码行为,但是却让结果变的不同。
- String
那么,到底发生了什么?当查看初始化顺序的时候一切就变的清晰了:
1.
函数调用了
- main()
构造器。
- Lower
2.
的一个实例被准备好了。意味着所有的字段都被创建并且填充了默认值,例如,引用类型的默认值为空,布尔类型的默认值为
- Lower
。在这个时候,任何的对字段的内联赋值都没有发生。
- false
3.父类构造器被调用了。这是被语言的特性所强制执行的。所以在其他任何事发生之前,Upper 的构造器被调用了。
4.Upper 这个构造器运行并且指定了一个引用,指向
方法新创建的的实例。
- Initializer.initialize()
5.
类为两个字段(
- Initializer
和
- upperString
)附上新字符串。通过使用有点肮脏的
- lowerString
实例检查做到为那两个字段赋值 – 这不是一个特别好的 设计模式 ,但是也有可行的,不用管那么多。一旦发生了,
- instanceof
和
- upperString
的引用都不再为空。
- lowerString
6.
的调用完成,
- Initializer.initialize()
构造器也同样完成。
- Upper
7.现在变得有趣了:
实例的构造在继续。假设在
- Lower
字段的声明中没有明确地
- lowerString
赋值,
- =null
构造器恢复执行并且打印出两个连接到字段的字符串。
- Lower
然而,如果有一个明确地赋值 null 的操作,执行流程会略有不同:当父类构造器完成后,在其余的构造器运行前,任何变量初始化都会执行(参见java语言规范12.5节)。在这种情况下,之前赋值给
的字符串引用不会再一次被赋予 null 。然后继续执行其余的函数构造,现在打印
- lowerString
的值为: null 。
- lowerString
这是一个很好的例子,不仅方便我们如何注意一些创建对象的细节(或者知道去哪里查看 Java 编码规范,打印的或者在线的),还显示了为什么像这样写初始化是很糟糕的。我们一点都不应该关心 Upper 的子类。相反的,如果因为一些原因对某些字段的初始化不能在子类本身被完成,它将只需要它自己的某些初始化帮助类的变体。在这种情况下,如果你使用
或者
- String lowString
是真的没有任何区别的,它应该是什么就会是什么。
- String lowerString = null
总结
以上就是这篇文章的全部内容了,希望这篇文章的内容对大家的学习或者工作能带来一定的帮助,如果有问题大家可以留言交流。
来源: http://www.phperz.com/article/17/1112/359796.html