一.
上一节,谈论了this的核心思想,以及this怎么使用。
现在,this还有另外一个应用。直接介绍细节的时候,说到构造函数可以直接调用一般函数,一般函数不能直接调用构造函数。一般函数需要对象创建进行初始化。那么,构造函数和构造函数之间如何进行访问呢?
说到构造函数间的调用,让我想起在定义功能化函数时,为了提高复用性,函数之间进行的调用。
上面截图的两个方法中,一个功能是初始化姓名,另一个的功能是初始化姓名和年龄的,姓名的初始化存在重复的问题。
既然已经重复存在,那么我们就可以直接调用前一个方法,
当我们在构造函数中调用speak方法时,speak需要被对象调用,因为speak打印的是对象的name,age,所以下面的截图中,应该写成对象的speak,即this.speak()(这里不是写成p.speak,而是写成this.speak,我省略了小括号,在描述类的函数中书写调用函数,都是用this代替对象么?这不同于在主函数中调用函数的书写。)
背后的原理:一个类当中,它的所有的成员,只要想运行,都必须要用对象调用(当然除了静态以外)。类中的成员想要被执行,就必须被对象调用。比如说描述类中的一般方法speak里,name想要执行(就是输出),前面必须加上this.
如果想在speak方法中,调用method方法,其实前面省略的还是this.(也就是说,可以省略不写?)
回到开始的设想,构造函数间相互调用,(下面的截图中)如果下面的person方法想要调用上面的person方法的话,按照前面讲解的思路(前面是操作的成员变量时,加上的指定对象),也要写上对象(不调用时,自己书写语句也是加上了对象,this.name=name;现在看调用其它构造函数了,是否也要这么干?),也就是说,要写成this.Person()。但是这样写是不对的,Person()方法是对对象进行初始化的,this就是对象,还没有进行初始化,对象怎么就开始调用函数了?要改成this(name),上面的方法是给对象进行初始化的,写成这样的格式,就不会有矛盾冲突了,意思就是说给哪个对象进行name初始化。
二.
展现一下内存的图解,
(本节想谈论的是什么,就是描述类中,构造函数之间的调用应该怎么写,内存中的图解是怎样的)
我就分析一下,对象调用Person(String name, int age)那部分的过程。
在堆中开辟空间,分配地址编号,name和age默认初始化后,对象就开始调用构造函数进行构造函数初始化。为什么会这么说,程序中表明的就是new Person("旺财", 30),括号里写了字符串和int类型的具体数据,这就表明在调用(String name,int age)形式的构造函数(为什么对象的这种形式会调用构造函数,原因是什么?)。现在开始调用对应的构造函数,将构造函数压栈,同时要将对象的地址标号赋值给构造中的this,表明对应关系,接着将具体的数据赋值给name和age,这里将构造函数的定义执行完了(我猜这个this=0x0067这句话是隐藏的)。接着,开始执行构造函数里面的内容,第一句,this(name),这又是对象在调用另一个构造函数,(为什么调用的不是一般函数,写的是name),对当前对象又进行了一次初始化。这相当于分步初始化。从Person(String name ,int age)到this(name),已经初始化一部分了,再调用其他构造函数,没必要再初始化,提高了复用性。当执行到this.name时,当前对象又开始调用新的构造函数,即具有一个String类型参数的构造函数,将当前对象中的确定输入数据“旺财”赋值给Person(String name)中的name,当然,在赋值前,将对象地址给this,标定对应关系,接着执行Person(String name)内部的语句,this.name=name,将Person(String name)中的旺财赋值给对象中的默认值的name,最后一个return将Person(String name)弹栈,接着继续执行Person(String name ,int age)中的this.age=age的语句,最后,又执行return将Person(String name ,int age)弹栈,这样一来,对象就创建完成。对象初始化结束后,将地址编号赋值给栈中的局部变量p,p完成对特定对象的指向。
到目前为止,this有两个作用,一个是代表对应对象,起到栈中内容和堆中内容相互对应的作用。还有一个作用是在构造函数中调用其它构造函数的作用,构造函数是给对象初始化的,哪个对象不确定,用this来表示,后面用括号的形式,就可以区分它是代表哪个构造函数,因为构造函数的区分是通过参数列表来完成的。
一般函数调用不了构造函数。
三. 细节
上面的说完以后,说点小细节,将语句调换了一下位置。DOS的结果显示错误。为什么呢?因为初始化动作要先执行。
举例来说明,调用Person(String name,int age)构造函数后,里面两条语句已经按照对象调用的要求new Person("旺财", 30),对对象中的name和age进行了初始化赋值,紧接着最后一句,又将name改成了"哈哈",这就是问题所在。我们调用的最终结果是想得到name,age的值为旺财和30,将再次调用的初始化语句放在最后有错误的风险。调用构造函数初始化语句定义在第一行,是java的规则。
下面再举一个例子,展现递归,new Person(); 依据括号里的形参来决定调用哪个构造函数,这里是空参数。调用Person()构造函数,接着里面的this("哈哈"),又调用Person(String name)的构造函数,进入函数后,里面的this()又调用Person()构造函数。这样轮流往复进行。最终要会导致程序停止,栈内存溢出了。