http://www.chineselinuxuniversity.net/articles/43910.shtml
对象实例何时被创建,这个问题也许你用一句话就能回答完了。但是它的潜在陷阱却常常被人忽视,这个问题也许并不像你想的那么简单,不信请你耐心看下去。
我前几天问一个同学,是不是在调用构造函数后,对象才被实例化?他不假思索的回答说是。
请看下面代码:
Java代码
Date date=new Date();
em.out.println(date.getTime());
新手在刚接触构造函数这个概念的时候。他们常常得出这样的结论:对象实例是在调用构造函数后创建的。因为调用构造函数后,调用引用(date)的实例方法便不会报NullPointerException的错误了。
二、经验者的观点
然而,稍稍有经验的Java程序员便会发现上面的解释并不正确。这点从构造函数中我们可以调用this关键字可以看出。
请看下面代码:
Java代码
public class Test{ public Test() { this.DoSomething(); } private void DoSomething() { System.out.println("do init"); } }
这段代码中我们在构造函数中已经可以操作对象实例。这也就证明了构造函数其实只是用于初始化,早在进入构造函数之前。对象实例便已经被创建了。
三、父类构造函数
当创建一个有父类的子类的时候。对象的实例又是何时被创建的呢?我们也许接触过下面经典的代码:
Java代码
public class BaseClass{ public BaseClass() { System.out.println("create base"); } } public class SubClass{ public SubClass() { System.out.println("create sub"); } public static void main(String[] args) { new SubClass(); } }
结果是先输出create base,后输出create sub。这个结果看起来和现实世界完全一致,先有老爸,再有儿子。因此我相信有很多程序员跟我一样会认为new SubClass()的过程是:实例化BaseClass->调用BaseClass构造函数初始化->实例化SubClass->调用SubClass构造函数初始化。然而非常不幸的是,这是个错误的观点。
四、奇怪的代码
以下代码是为了驳斥上面提到的错误观点。但是这种代码其实在工作中甚少出现。
Java代码
public class BaseClass
{
public BaseClass()
{
System.out.println("create base");
init();
}
protected void init()
{
System.out.println("do init");
}
}
//
public class SubClass
{
public SubClass()
{
System.out.println("create sub");
}
@Override
protected void init()
{
assert this!=null;
System.out.println("now the working class is:"+this.getClass().getSimpleName());
System.out.println("in SubClass");
}
public static void main(String[] args)
{
new SubClass();
}
}
这段代码运行的结果是先调用父类的构造函数,再调用子类的init()方法,再调用子类的构造函数。
这是一段奇妙的代码,子类的构造函数居然不是子类第一个被执行的方法。我们早已习惯于通过super方便的调用父类的方法,但是好像从没这样尝试从父类调用子类的方法。
再次声明,这只是个示例。是为了与您一起探讨对象实例化的秘密。通过这个示例,我们再次印证了开头的观点:早在构造函数被调用之前,实例便已被创造。若该对象有父类,则早在父类的构造函数被调用之前,实例也已被创造。这让java显得有些不面向对象,原来老子儿子其实是一块儿出生的。
五、奇怪但危险的代码
本篇是对上篇奇怪代码的延续。但是这段代码更加具有疑惑性,理解不当将会让你出现致命失误。
请看下面代码:
Java代码
public class BaseClass {
public BaseClass()
{
System.out.println("create base");
init();
}
protected void init() {
System.out.println("in base init");
}
}
public class SubClass extends BaseClass{
int i=1024;
String s="13 leaf";
public SubClass()
{
System.out.println("create sub");
init();
}
@Override
protected void init() {
assert this!=null;
System.out.println("now the working class is:"+this.getClass().getSimpleName());
System.out.println("in SubClass");
/////////////great line/////////////////
System.out.println(i);
System.out.println(s);
}
public static void main(String[] args) {
new SubClass();
//oh!my god!!
}
}
这段代码相比上一篇,只是在子类中添加了一些成员变量。而我们的目标正是集中在讨论成员变量初始化的问题上。
这段代码的执行顺序是:父类、子类实例化->调用父类构造函数->调用子类init()方法->调用子类构造函数->调用子类init()方法。最终的输出结果向我们揭示了成员变量初始化的秘密。
当父类构造函数调用子类的init()方法的时候。子类的成员变量统统是空的,这个空是指的低级初始化。(值类型为0,布尔类型为false,引用类型为null)。而当子类构造函数调用init()方法的时候,成员变量才真正被初始化。这是一个危险的讯息,那就是使用父类构造函数调用子类时存在成员变量未初始化的风险。
我们的讨论也到此为止了。再次回顾,总结一下实例何时被创建这个问题。我得出了以下结论:
本文到此便结束了。鉴于本人才疏学浅,若是专业术语有错误,或是哪里讲的不对,也欢迎各位高手拍砖。
附上第五篇中SubClass的部分字节码,方便大家深入理解:
Java代码
public SubClass();
aload_0 [this] //aload_0是啥?
invokespecial ques.BaseClass() [26] //调用父类构造函数
aload_0 [this]
sipush 1024 //初始化i成员变量
putfield ques.SubClass.i : int [28]
aload_0 [this]
ldc "13 leaf"> [30] //初始化s成员变量
putfield ques.SubClass.s : java.lang.String [32]
getstatic java.lang.System.out : java.io.PrintStream [34]
ldc "create sub"> [40]
invokevirtual java.io.PrintStream.println(java.lang.String) : void [42]
aload_0 [this]
invokevirtual ques.SubClass.init() : void [48] //调用init
return
相关推荐
通过学习这两份资源,你不仅可以理解实例化需求的基本理念,还能掌握如何在实际工作中应用这些知识,从而提升软件开发项目的成功率。记住,实例化需求不仅仅是一套技术方法,更是一种思维方式,旨在促进团队之间的...
这里,我们主要探讨的是Spring如何通过其IoC(Inversion of Control)容器来实例化Bean,并理解其背后的逻辑。 首先,我们要知道Spring IoC容器是通过XML配置文件、注解或Java配置类来管理Bean的生命周期。Bean的...
通过阅读和分析这些代码,你可以深入理解WebGL实例化的实现细节,以及如何在实际项目中应用这种技术来优化性能。 总的来说,WebGL实例化是一个强大的工具,能够帮助开发者在Web环境中高效地渲染大量相似对象,对于...
在Java编程语言中,实例化对象是创建类的实例,使其具有特定状态和行为的过程。这个过程涉及到几个关键步骤,让我们深入探讨一下。 首先,我们需要一个类,它是创建对象的蓝图。在Java中,我们使用`class`关键字来...
实例化需求是一种确保需求被正确理解和实现的有效方法。通过将抽象的需求转化为具体的例子,可以帮助团队成员更好地理解业务场景和技术细节,从而减少误解和偏差。 #### 二、实例化需求的概念与重要性 ##### 1. ...
实例化对于理解和使用类非常重要,因为它允许我们创建多个具有相同属性和方法的对象,但各自的值可以不同。这种复用性是面向对象编程的一大优势。例如,你可以创建多个`MyClass`实例,每个实例的`attr1`和`attr2`都...
在Unity3D游戏开发中,Prefabs(预设)和Instantiate(实例化)是两个非常重要的概念,它们是构建游戏对象和动态管理游戏世界的基石。本文将深入探讨这两个概念,帮助初学者理解并掌握它们在实际项目中的应用。 ...
通过深入理解WCF的单调服务模式,开发者可以更好地根据应用场景选择合适的实例化策略,优化服务性能和资源利用。在处理需要跨客户端共享状态或维持全局计数的应用时,单调服务模式尤其有用。然而,对于不需要保留...
由于抽象类自身不能被实例化,这意味着你不能使用`new`关键字直接创建一个抽象类的对象。然而,尽管不能直接实例化,抽象类在特定情况下依然可以间接地创建对象。以下将详细介绍如何在Java中处理抽象类实例化的问题...
在Python编程语言中,类是面向...理解这些概念并熟练应用在类的定义和实例化中,能帮助我们编写更健壮和易于维护的代码。在Python中,类和对象是实现面向对象编程的关键工具,正确地实例化类是确保代码正常运行的基础。
本篇将深入讲解Spring IOC如何实现bean的实例化。 首先,理解IOC的概念是至关重要的。在传统的编程模式中,开发者需要手动创建和管理对象,而在Spring IOC中,这个过程被反转,由Spring容器负责对象的创建、初始化...
为了更好地理解实例化的过程,这里提供了一个更简单的示例: ```python class Luffy_Stu: def __init__(self, name, age, sex): # 初始化方法 self.name = name # 实例变量 self.age = age # 实例变量 self.sex...
### Spring 实例化Bean的三种方式详解 ...理解并掌握这些实例化方式,对于高效地使用Spring框架进行企业级应用开发至关重要。通过合理选择和运用这些实例化策略,可以显著提高代码的可读性、可维护性和扩展性。
### Spring 延迟实例化的理解与应用 #### 一、Spring框架简介 Spring框架是一个开源的Java平台,它提供了一种全面的方式来构建应用程序。Spring的核心特性包括依赖注入(Dependency Injection, DI)和面向切面编程...
然而,当涉及到多个Fragment之间的切换时,可能会遇到一个问题:每次切换时,Fragment的布局可能会被重新实例化,导致用户体验下降,例如丢失用户在Fragment中的状态。为了解决这个问题,我们需要采取一系列策略来...
在Spring框架中,Bean的实例化是核心概念之一,它涉及到如何管理和控制应用程序中的对象生命周期。本篇文章将深入探讨Spring项目中Bean的实例化...理解并掌握这些实例化方法对于开发高效且可维护的Spring应用至关重要。
理解并熟练掌握各种实例化方式对于编写高效、可维护的代码至关重要。在实践中,根据项目的具体需求选择合适的实例化策略是至关重要的。通过不断学习和实践,你可以更好地理解和应用这些概念,提升你的编程技能。
《实例化需求》的概念和实践对提高软件开发的效率和质量具有重大意义,它强调了需求的可验证性、可读性和可维护性,使得开发团队能够更准确地理解和实现业务需求,从而构建出更符合用户期望的软件产品。
在这个"opengl实例化与拾取"的主题中,我们将深入探讨这两个关键概念。 **实例化(Instancing)** 实例化是OpenGL中提高渲染效率的一种技术,特别是在处理大量相似物体时。通过实例化,我们可以一次绘制一个基础几何...
在Java编程语言中,了解如何计算内存中实例化对象的数量是一项重要的技能,这对于优化程序性能、理解和管理资源分配至关重要。本篇文章将详细讲解如何利用静态成员变量来追踪和计算一个类在运行时创建的实例数量。 ...