java面向对象--对象初始化

重载(Overload)是指在一个类里面,多个方法的名字相同,而参数列表(参数的类型、个数和顺序)不同。返回值类型和修饰符与重载无关。解决相同意义的行为使用不同名字的问题。
构造器最大的作用就是在创建对象时执行初始化。
默认构造器又名无参构造器,作用是创建一个默认对象。如果类中没有任何构造器,则编译器会自动创建一个默认构造器。如果已经自行定义了任意一个构造器,编译器就不会自动创建无参构造器。

所有实例共用一份方法,编译器会把当前对象的引用作为第一个参数传入方法内部,以区分不同的调用对象。
this:在方法内部使用,this表示对“调用方法的那个对象”的引用。最大的作用就是让类中的一个方法,访问该类的另一个方法或属性。
static方法是没有this的方法,所以静态成员不能访问非静态成员。
static关键字:修饰的成员表明它是属于类共有的,既可以通过类访问,也可以通过实例访问。
在方法内部访问同一个类的成员,不需要使用this。
如果参数变量和成员变量同名时,可以用this.field=field加以区分。
在构造器中调用构造器:this可以指代重载的其他构造器方法以减少代码重复。注意此时this(arg)必须作为构造器中的第一句代码。

public class Teacher {
    private String name; 
    private int age;
    public Teacher(String name,int age ){
       this(name);
       this.age=age;
    }
    public Teacher(String name){
        this.name = name;
    }
}

静态数据、成员变量及构造器的初始化顺序
无继承时:
假设有个名为Dog的类,创建过程如下:
1.当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类的静态方法/静态域首次被访问时,Java解释器必须查找类路径,以定位Dog类的编译代码(在Dog.class文件中)。
2.然后载入Dog.class(这将创建一个Class对象),执行有关静态初始化的动作。因此,静态初始化只在Class对象首次加载的时候进行一次。 静态变量和静态初始块化块按定义的顺序进行初始化。
3.当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
4.这块存储空间会被清零,这就自动地将Dog中的所有基本类型数据设置成了默认值(对数字来说就是0,对布尔型和字符型也相同),而引用则被置成了null。 (默认初始化)
5.执行所有出现于成员变量定义处的初始化动作。(按定义的先后顺序进行显示初始化,包括成员变量和初始化块
6.执行构造器。
7.对象构造完毕,将地址赋给引用类型变量。

有继承时:
假设有个名为Golden的类,继承自Dog,Dog又继承自Animal,初始化过程如下:
1.当首次创建类型为Golden的对象时,Java加载器查找Golden类的编译代码(在Golden.class文件中)。
2.Java加载器会加载Golden.class文件,同时根据Golden.class(extends关键字)加载其基类Dog.class文件,再根据Dog.class加载根基类Animal.class文件。然后从根基类Animal到基类Dog再到子类Golden依次执行静态数据的初始化。 (子类的静态数据在父类的成员变量之前初始化)
3.当你用new Golden ()创建对象的时候,首先将在堆上为Golden对象(包括本类Golden、基类Dog和根基类Animal中的所有成员变量)分配足够的存储空间。由于子类的构造器不能访问父类的private成员,所以必须利用super关键字来初始化这部分成员变量。
4.这块存储空间会被初始化默认值即二进制的零。Golden中的所有基本类型数据(包括其基类Dog和Animal中的)设置成了默认值(对数字来说就是0,对布尔型和字符型也相同),而引用(包括其基类Dog和Animal中的)则被置成了null。也就是说在执行任何初始化代码之前,所有实例变量都已设置完默认的初值。
5.执行根基类Animal中所有成员变量的初始化动作。
6.执行基根类Animal构造器。
7.执行基类Dog中所有成员变量的初始化动作。
8.执行基类Dog构造器。
9.执行导出类Golden中所有成员变量的初始化动作。
10.执行导出类Golden构造器。
简单来说,就是从子类到基类依次加载类的class文件,然后从根基类到子类依次进行静态数据的初始化。再为对象分配存储空间,在存储空间清零后,依次从父类到子类进行实例初始化(包括成员变量初始化,调用构造器2步)。

Java在开发者没有定义别的super语句时会自动在子类的构造器中插入对父类构造器的调用,该调用并不是简单地执行父类构造函数,具体顺序如下(省略类初始化过程):
1、进入当前类的构造方法,调用super()(会被编译器改名为<init>,不执行别的代码)直接进入父类构造方法,并递归到java.lang.Object类构造方法。
2、执行java.lang.Object类的初始化,顺序为先初始化成员变量/初始化块(按照定义顺序),再调用构造方法(super部分不再执行)进行初始化。
3、运用步骤2的方法初始化java.lang.Object类的直接子类,并递归这个过程到当前类。

 

注意:
对象只能通过new constructor()的方式创建,所以编译器在程序员没有编写构造器时会提供默认的无参构造器。Constructors are meant for initializing the members of objects.
类的加载发生在:new 对象时,访问类的静态数据时,main方法所在的类在运行java应用时。而在类被加载时就会初始化静态数据。
对象会取得所有实例变量所需的空间,包括一路继承下来的东西。
在创建对象时,所有父类的构造函数都会被执行:构造函数在执行的过程中,第一件事是调用它父类的构造函数(编译器通过super调用或开发者自己调用有参数的父类构造函数),直到连锁反应到Object这个类为止,即“构造函数链”。
创建对象过程中,子类对象可能访问从父类继承下来的数据,所以super()必须是构造函数的第一个语句,父类构造函数必须在子类构造函数之前结束。
this(..)指代当前类的其他构造函数,也必须是构造函数中的第一句语句,所以super()和this()只能二选一。
每个对象除了保存类的实例变量之外,还保存着实际类信息的引用,存放类信息的内存区,在Java中称为方法区。
虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。

class Glyph {
    void draw() {
        System.out.println("Glyph.draw()");
    };
    Glyph() {
        System.out.println("Glyph() before draw()");
        draw(); // 调用重写了的子类型方法
        System.out.println("Glyph() after draw()");
    }
}

class RoundGlyph extends Glyph {
    private int radius = 1;
    RoundGlyph(int r) {
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }
    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}

public class PolyConstructors {
    public static void main(String[] args) {
        new RoundGlyph(5);
    }
}
/*
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*/

 

posted @ 2017-04-09 22:53  开发之路  阅读(986)  评论(0编辑  收藏  举报