java对象的初始化

主要内容:

  • 构造方法
  • 成员初始化
  • 初始化顺序

一、 构造方法

  • 语法调用
  • 返回值
  • 内部逻辑
  • 注意事项

1.1、构造方法的语法调用

与类的普通方法调用不同,构造方法的调用语法是用关键字new,告诉解释器为即将要创建的对象分配足够的堆空间,如new Dog()。

1.2、构造方法的返回值

构造方法是类的一种特殊的方法,它没有返回值(其语法强制要求不能有返回值类型,用以区分它是构造方法,构造方法的执行目的与返回值为void类型的方法相同,都是只要执行方法的“副作用”,而不是为了返回某个值),如果为其添加了void返回类型,则该方法仅仅是一个普通的类方法,只不过方法名称与类名称相同而已。

public class SonClass{
    public SonClass(){//构造方法
        System.out.println("SonClass construtor(default)...");
    }

    public void SonClass(){//普通方法
        System.out.println("SonClass common(default)...");
    }
}

1.3、构造方法的内部逻辑

super()与this():java类中的构造器如果未用super()方法显式调用父类的对应构造方法,会隐式自动去调用父类的默认构造方法,即无论你在方法中是否写super(),构造器会先去调用父类的构造方法,即子类构造器去调用父类构造器,父类构造器去调用爷爷类构造器, 一直往上追溯,直到追溯到祖先类Object。

public class SonClass extends ParentClass{
    public SonClass(){
        System.out.println("SonClass construtor(default)...");
    }
    public SonClass(String s){
        System.out.println("SonClass construtor..."+s); 
    }

    public static void main(String[] args) {
        new SonClass("apple");      
    }
}

class ParentClass extends GrandClass{
    public ParentClass(){
        System.out.println("ParentClass construtor(default)...");
    }

    public ParentClass(String s){
        System.out.println("ParentClass construtor..."+s);
    }
}

class GrandClass{
    public GrandClass(){
        System.out.println("GrandClass construtor(default)...");
    }

    public GrandClass(String s){
        System.out.println("GrandClass construtor..."+s);
    } 
}

SonClass继承ParentClass,ParentClass继承GrandClass,虽然在子类构造器中未显式调用父类构造器,但其默认会执行父类的默认无参构造方法。
执行结果如下:

GrandClass construtor(default)…
ParentClass construtor(default)…
SonClass construtor…apple

1.4、注意事项

如果父类中没有默认无参构造方法,则子类的构造器必须显式调用父类对应的一个构造方法,否则会报编译异常;一个类中的构造器不能相互调用,否在会报递归调用的编译异常。

二、成员初始化

  • 默认值
  • 变量空间大小

2.1、成员变量默认值

package javabasic;

public class InitValue {

    boolean bo;
    byte by;
    short sh;
    char ch;
    int in;
    float fl;
    long lo;
    double du;
    String st;

    public static void main(String[] args) {
        InitValue iv = new InitValue();
        System.out.println("boolean: "+iv.bo);
        System.out.println("byte: "+iv.by);
        System.out.println("short: "+iv.sh);
        System.out.println(('\u0000')==iv.ch);
        System.out.println("int: "+iv.in);
        System.out.println("float: "+iv.fl);
        System.out.println("long: "+iv.lo);
        System.out.println("double: "+iv.du);
        System.out.println("String: "+iv.st);
    }
}

InitValue中并未对类的成员变量显式赋值,在new创建对象分配堆空间时,java解释器会为其所有类成员变量赋初值(其中char类型的变量初值为’\u0000’,打印为一个空格),引用类型的变量默认值为null,main方法执行结果如下:

boolean: false
byte: 0
short: 0
true
int: 0
float: 0.0
long: 0
double: 0.0
String: null

2.2、基本类型变量的空间大小

变量类型

默认值


字节

boolean

false

-

-

byte

0

8bits

1 byte

char

\u0000

16bits

2 bytes

short

0

16bits

2 bytes

int

0

32bits

4 bytes

float

0.0f

32bits

4 bytes

long

0L

64bits

8 bytes

double

0.0d

64bits

8 bytes

对于静态成员的初始化的默认值与分配空间大小与普通的成员变量并没有区别。

三、初始化顺序

  • 初始化顺序

3.1、初始化顺序

package javabasic;

public class InitOrder {

    private int p;
    private static int a = 1;

    public InitOrder(){
        System.out.println(InitOrder.a);
        System.out.println(InitOrder.st);
        System.out.println("in InitOrder construtor...");
    }

    private static String st = "static string";
    private static InnerClass ic = new InnerClass();
    static{
        //! System.out.println(p); //静态方法中不能引用非静态域,因为此时java解释器还没有为即将创建的对象分配空间
        System.out.println("in static...");
        System.out.println(InitOrder.stf);
    }

    private static String stf = "after static string...";

    public static void main(String[] args) {
        new InitOrder();
    }

}

class InnerClass{
    public InnerClass(){
        System.out.println("in InnerClass construtor...");
    }
}

可以看到,当调用一个类的构造方法时,先执行的是所有static域的初始化(无论代码是在构造方法前面,还是构造方法后面),再执行构造器方法;对于static代码块与声明static域的执行优先级相同,即如果static{}代码块在申明static变量之前,那么先执行static,如果static{}代码块在申明static变量之后,那么先执行static申明。

对于类成员变量,无论是在申明时就初始化,还是申明之后再初始化,其变量的值都是经过2次修改,先是java解释器自动给其赋的默认值,然后再是我们人为的赋值。

控制台打印结果如下:

in InnerClass construtor…
in static…
null
1
static string
in InitOrder construtor…

总结一下对象的创建过程,假设有个Person类:

  1. 首次创建类型为Person的对象,或者访问Person类的static域,java解释器先要找到Person类的路径,以定位Person.class文件。
  2. java虚拟机载入Person.class文件,并创建一个类型为Class的对象,这个时候类的所有静态初始化的动作都将执行。因此,静态初始化只有在Class对象首次加载时进行一次。
  3. 当用new Person()创建对象时,java虚拟机将在堆内容上分配足够的内存空间。而这块内存空间会被清零(内存中原来的数据会擦除),并自动将Person对象中的所有基本类型数据都设置成默认值,而引用类型设置成null。
  4. 执行所有出现在定义处的初始化操作。
  5. 最后执行构造器。