构造器

[color=red]构造器的调用过程:[/color]
首先要注意的是Java的构造器并不是函数,所以他并不能被继承,这在我们extends的时候写子类的构造器时比较的常见,即使子类构造器参数和父类的完全一样,我们也要写super就是因为这个原因。
  构造器的修饰符比较的有限,仅仅只有public private protected这三个,其他的例如任何修饰符都不能对其使用,也就是说构造器不允许被成名成抽象、同步、静态等等访问限制以外的形式。
  因为构造器不是函数,所以它是没有返回值的,也不允许有返回值。但是这里要说明一下,构造器中允许存在return语句,但是return什么都不返回,如果你指定了返回值,虽然编译器不会报出任何错误,但是JVM会认为他是一个与构造器同名的函数罢了,这样就会出现一些莫名其妙的无法找到构造器的错误,这里是要加倍注意的。
  在我们extends一个子类的时候经常会出现一些意想不到的问题,我在这里说一些和构造器有关的。
  首先说一下Java在构造实例时的顺序(不讨论装载类的过程)
  构造的粗略过程如下
  1、分配对象空间,并将对象中成员初始化为0或者空,java不允许用户操纵一个不定值的对象。
  2、执行属性值的显式初始化(这里有一点变化,一会解释,但大体是这样的)
  3、执行构造器
  4、将变量关联到堆中的对象上
  介绍一下准备知识,以备一会来详细说明这个的流程
  this()super()是你如果想用传入当前构造器中的参数或者构造器中的数据调用其他构造器或者控制父类构造器时使用的,在一个构造器中你只能使用this()或者super()之中的一个,而且调用的位置只能在构造器的第一行,在子类中如果你希望调用父类的构造器来初始化父类的部分,那就用合适的参数来调用super(),如果你用没有参数的super()来调用父类的构造器(同时也没有使用this()来调用其他构造器),父类缺省的构造器会被调用,如果父类没有缺省的构造器,那编译器就会报一个错误,注意此处,我们经常在继承父类的时候构造器中并不写和父类有关的内容,此时如果父类没有缺省构造器,就会出现编译器添加的缺省构造器给你添麻烦的问题了哦。例如:Classbextendsa{publicb(){}}就没有任何有关父类构造器的信息,这时父类的缺省构造器就会被调用。你必须在构造器的第一行放置super或者this构造器,否则编译器会自动地放一个空参数的super构造器的,其他的构造器也可以调用super或者this,调用成一个递归构造链,最后的结果是父类的构造器(可能有多级父类构造器)始终在子类的构造器之前执行,递归的调用父类构造器
  在具体构造类实例的过程中,上边过程的第二步和第三步是有一些变化的,这里的顺序是这样的,分配了对象空间及对象成员初始化为默认值之后,构造器就递归的从继承树由根部向下调用,每个构造器的执行过程是这样的:
  1、Bind构造器的参数
  2、如果显式的调用了this,那就递归调用this构造器然后跳到步骤5
  3、递归调用显式或者隐式的父类构造器,除了Object以外,因为它没有父类
  4、执行显式的实例变量初始化(也就是上边的流程中的第二步,调用返回以后执行,这个步骤相当于在父构造器执行后隐含执行的,看样子像一个特殊处理)
  5、执行构造器的其它部分
 
  从这个步骤中可以很明显的发现这个实例初始化时的递归调用过程,估计看过这个你应该能够理解这个递归构造链是怎么样回事了。

public class Object {
...
public Object() {}
...
}
public class Employee extends Object {
private String name;
private double salary = 15000.00;
private Date birthDate;
public Employee(String n, Date DoB) {
// implicit super();
name = n;
birthDate = DoB;
}
public Employee(String n) {
this(n, null);
}
}

public class Manager extends Employee {
private String department;
public Manager(String n, String d) {
super(n);
department = d;
}
}


在创建Manager("Joe Smith","Sales"):时,步骤如下
0 basic initialization

0.1 allocate memory for the complete Manager object

0.2 initialize all instance variables to their default values (0 or null)

1 call constructor: Manager("Joe Smith", "Sales")

1.1 bind constructor parameters: n="Joe Smith", d="Sales"

1.2 no explicit this() call

1.3 call super(n) for Employee(String)

1.3.1 bind constructor parameters: n="Joe Smith"

1.3.2 call this(n, null) for Employee(String, Date)

1.3.2.1 bind constructor parameters: n="Joe Smith", DoB=null

1.3.2.2 no explicit this() call

1.3.2.3 call super() for Object()

1.3.2.3.1 no binding necessary

1.3.2.3.2 no this() call

1.3.2.3.3 no super() call (Object is the root)

1.3.2.3.4 no explicit variable initialization for Object

1.3.2.3.5 no method body to call

1.3.2.4 initialize explicit Employee variables: salary=15000.00;注意:在父构造器返回后子类才会初始化实例变量的值。

1.3.2.5 execute body: name="Joe Smith"; date=null;

1.3.3 - 1.3.4 steps skipped

1.3.5 execute body: no body in Employee(String)

1.4 no explicit initializers for Manager

1.5 execute body: department="Sales"

这个例子很好的表示了构造器的执行过程。还要注意以下几个方面:

一、构造器中一定不要创建自身的实例,否则会造成调用栈溢出错误。这个规则也适用于对象的实例变量,如果对象中有自身的引用,这个引用一定不能在定义中或者构造器中初始化。
class a
{
a _a = new a();

public a()
{
_a = new a();
a _b = new a();
}
}

以上三种情况都会造成栈溢出,呵呵,这样会造成一个无穷递归的调用栈。

二、如果父类是一个抽象类,那通过调用父类的构造器,也可以将它初始化,并且初始化其中的数据。
  三、如果你要在构造器中调用一个方法时,将该方法声明为private。
  对于这个规则是需要一些说明的,假使你的父类构造器中要调用一个非静态方法,而这个方法不是private的又被子类所重载,这样在实际创建子类的过程中递归调用到了父类的构造器时,父类构造器对这个方法的调用就会由于多态而实际上调用了子类的方法,当这个子类方法需要用到子类中实例变量的时候,就会由于变量没有初始化而出现异常(至于为什么子类中的实例变量没有初始化可以参考上边的实例初始化过程),这是Java不想看到的情况。而当父类构造器中调用的方法是一个private方法时,多态就不会出现,也就不会出现父类构造器调用子类方法的情况,这样可以保证父类始终调用自己的方法,即使这个方法中调用了父类中的实例变量也不会出现变量未初始化的情况(变量初始化总是在当前类构造器主体执行之前进行)。

[size=large][color=red]构造器与方法的不同:[/color][/size]

功能和作用的不同
构造器是为了创建一个类的实例。这个过程也可以在创建一个对象的时候用到:Platypus p1 = new Platypus();
相反,方法的作用是为了执行java代码。
修饰符,返回值和命名的不同
构造器和方法在下面三个方便的区别:修饰符,返回值,命名。和方法一样,构造器可以有任何访问的修饰: public, protected, private或者没有修饰(通常被package 和 friendly调用). 不同于方法的是,构造器不能有以下非访问性质的修饰: abstract, final, native, static, 或者 synchronized。
返回类型也是非常重要的。方法能返回任何类型的值或者无返回值(void),构造器没有返回值,也不需要void。
最后,谈谈两者的命名。构造器使用和类相同的名字,而方法则不同。按照习惯,方法通常用小写字母开始,而构造器通常用大写字母开始。构造器通常是一个名词,因为它和类名相同;而方法通常更接近动词,因为它说明一个操作。
"this"的用法
构造器和方法使用关键字this有很大的区别。方法引用this指向正在执行方法的类的实例。静态方法不能使用this关键字,因为静态方法不属于类的实例,所以this也就没有什么东西去指向。构造器的this指向同一个类中,不同参数列表的另外一个构造器。在构造器中,如果要使用关键字this,那么,必须放在第一行,如果不这样,将导致一个编译错误。
"super"的用法
构造器和方法,都用关键字super指向超类,但是用的方法不一样。方法用这个关键字去执行被重载的超类中的方法。构造器使用super去调用超类中的构造器。同this一样,这行代码必须放在第一行,否则编译将出错。

编译器自动加入代码
编译器自动加入代码到构造器,对于这个,java程序员新手可能比较混淆。当我们写一个没有构造器的类,编译的时候,编译器会自动加上一个不带参数的构造器,例如:public class Example {}
编译后将如下代码:
public class Example {
Example() {}
}
在构造器的第一行,没有使用super,那么编译器也会自动加上,例如:
public class TestConstructors {
TestConstructors() {}
}
编译器会加上代码,如下:
public class TestConstructors {
TestConstructors() {
super;
}
}
继承
构造器是不能被继承的。子类可以继承超类的任何方法。
[size=medium]
[color=darkred]总结[/color][/size]

主题 构造器 方法
功能 建立一个类的实例 java功能语句
修饰 不能用bstract, final, native, static, or synchronized 能
返回类型 没有返回值,没有void 有返回值,或者void
命名 和类名相同;通常为名词,大写开头 通常代表一个动词的意思,小写开头
this 指向同一个类中另外一个构造器,在第一行 指向当前类的一个实例,不能用于静态方法
super 调用父类的构造器,在第一行 调用父类中一个重载的方法
继承 构造器不能被继承 方法可以被继承
编译器自动加入一个缺省的构造器 自动加入(如果没有)
编译器自动加入一个缺省的调用到超类的构造器 自动加入(如果没有) 不支持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值