总结一下Java的初始化,本文涉及的内容

  1. 自动初始化
  2. 成员指定初始化
  3. 静态初始化
  4. 实例初始化
  5. 数组的初始化
  6. 引用的初始化

1、自动初始化

对于在类中定义成员变量(又称为成员属性、字段),基本数据类型会自动初始化,引用类型——对象变量,会被初始化为null。注意,必须是字段,对于局部变量,必须指定初始化为其赋值,局部变量不初始化是程序员的疏忽。自动初始化是Java为了所有变量在使用前都能得到初始化,它要在构造器之前执行。

Java初始化对象 java初始化数据怎么写_初始化

package myblogs;
/**
 * 自动初始化
 * @author 小锅巴
 */
class A{
    private int i;
    private String s;
    A(int i){
        System.out.println("i的自动初始化: "+this.i);
        this.i = i;
    }

    public void print(){
        System.out.println("i: "+i+"   s: "+s);
    }
}
public class AutoIni {

    public static void main(String[] args) {
        A a = new A(1);
        a.print();
    }
}
/**
 * 输出:
 *  i的自动初始化: 0
 *  i: 1   s: null
 */

2、成员指定初始化

即在字段定义时将其初始化,最常用的初始化方式,可以通过调用函数来初始化,注意被调用的函数里面的参数必须先被初始化。在我看来是自动初始化的升级版,还是保证在使用前被初始化,执行要先于构造器,构造器不一定要为所有的成员变量赋值。

package myblogs;
/**
 * 成员指定初始化
 * @author 小锅巴
 */
class A1{
//  private int k = f(i);//k放在i之前会报错:Cannot reference a field before it is defined
    private int i;
    private int j = f(i);
    private int h = 3;

    private int f(int i){
        return ++i;
    }
    A1(int i, int h){
        System.out.println("i: "+this.i+"   j: "+j+"   h: "+this.h);
        this.h = h;
        this.i = i;
        System.out.println("i: "+i+"   j: "+j+"   h: "+h);
    }

    public void print(){
        System.out.println("i: "+i+"   j: "+j+"   h: "+h);
    }
}
public class SpecifiedIni {

    public static void main(String[] args) {
        A1 a = new A1(1, 1);
        a.print();
    }
}
/**
 * 输出:
 *  i: 0   j: 1   h: 3
 *  i: 1   j: 1   h: 1
 *  i: 1   j: 1   h: 1  
 */

3、静态初始化

(1)静态成员变量的初始化。静态成员变量,应该称为类变量,因为是用static修饰,不论被修饰的是变量,还是方法,表示是属于类的,不属于类的对象,在不创建对象时就可以使用,这也是不能在非静态方法中调用静态方法或者访问静态数据的原因。被static修饰的静态成员变量,在创建该类的第一个对象或者该静态成员变量被第一次调用时被初始化,如果没有,则永不会初始化。因为是类的属性,故只执行一次,且在构造器之前执行。

静态成员变量和静态方法

package myblogs;
/**
 * 测试静态成员变量和静态方法
 * @author 小锅巴
 */
public class TestStatic {
    public static String s = "TestStatic";
    public static void TestStatic(){
        System.out.println("Test static method.");
    }
    public static void main(String[] args) {
        System.out.println(TestStatic.s);
        TestStatic.TestStatic();
        new TestStatic();
    }
}
/**输出:
 *  TestStatic
 *  Test static method.
 */

静态成员变量的初始化:

package myblogs;
/**
 * 静态成员变量初始化
 * @author 小锅巴
 */
class Bowl{
    Bowl(int marker){
        System.out.println("Bowl(" + marker + ")");
    }

    void f1(int marker){
        System.out.println("f1(" + marker + ")");
    }
}

class Table{
    static Bowl bowl1 = new Bowl(1);//静态成员变量
    Table(){
        System.out.println("Table()");
        bowl2.f1(1);
    }

    void f2(int marker){
        System.out.println("f2(" + marker + ")");
    }

    static Bowl bowl2 = new Bowl(2);//此处只是为了展示,对于良好的编程习惯而言,作为静态成员变量,应该放在首部
}

class Cupboard{
    Bowl bowl3 = new Bowl(3);//成员变量
    static Bowl bowl4 = new Bowl(4);//静态成员变量
    Cupboard(){
        System.out.println("Cupboard()");
        bowl4.f1(2);
    }

    void f3(int marker){
        System.out.println("f3(" + marker + ")");
    }

    static Bowl bowl5 = new Bowl(5);//静态成员变量
}
public class StaticIni {
    static Table table = new Table();//静态成员变量
    static Cupboard cupboard = new Cupboard();//静态成员变量

    public static void main(String[] args) {
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }
}
/**
 *  Bowl(1)
    Bowl(2)
    Table()
    f1(1)
    Bowl(4)
    Bowl(5)
    Bowl(3)
    Cupboard()
    f1(2)
    Creating new Cupboard() in main
    Bowl(3)
    Cupboard()
    f1(2)
    Creating new Cupboard() in main
    Bowl(3)
    Cupboard()
    f1(2)
    f2(1)
    f3(1)
 */

从上面的例子可以看出,即使变量的定义散布于方法定义之间,在被调用之前均会得到初始化,如Bowl4和Bowl5。
(2)静态初始化块,Thinking In Java上又称显示的静态初始化,即将多个静态初始化动作组织成一个代码块。

package myblogs;
/**
 * 静态初始化块
 * @author 小锅巴
 */
class Cup{
    Cup(int marker){
        System.out.println("Cup marker "+marker);
    }
    void f(int marker){
        System.out.println("f marker");
    }
}

class Cups{
    static Cup cup1;
    static Cup cup2;
    //静态初始化块
    static{
        cup1 = new Cup(1);
        cup2 = new Cup(2);
    }
}
public class StaticIniBlocks {
    public static void main(String[] args) {
        System.out.println("Inside main");
        Cups.cup1.f(99);
    }
}
/**
 * 输出:
    Inside main
    Cup marker 1
    Cup marker 2
    f marker
 */

4、实例初始化

又称非显示的静态初始化,用来初始化每一个对象的非静态变量。从下面例子的执行结果来看,每创建一个对象,就执行一次,初始化顺序在静态成员变量的后面,构造器的前面。

package myblogs;
/**
 * 实例初始化
 * @author 小锅巴
 */
class Mug{
    Mug(int marker){
        System.out.println("Mug(" + marker + ")");
    }

    void f(int marker){
        System.out.println("f(" + marker + ")");
    }
}

class Mugs{
    /**
     * 印证静态成员变量的初始化顺序在实例初始化的前面
     */
    static private Mug mug0 = new Mug(0);
    Mug mug1;
    Mug mug2;

    Mugs(){
        System.out.println("Mugs()");
    }

    Mugs(int i){
        System.out.println("Mugs(int)");
    }
    /**
     * 实例初始化块,初始化对象的每个非静态变量,在本例中即初始化对象引用
     * 为了验证实例化初始化的执行顺序与其所在的位置无关,相比TIJ中的原例,我把实例化初始化块放在了构造器的后面
     */
    {
        mug1 = new Mug(1);
        mug2 = new Mug(2);
        System.out.println("mug1 & mug2 initialization");
    }
}
public class InstanceIni {

    public static void main(String[] args) {
        System.out.println("Inside main()");
        new Mugs();
        System.out.println("new Mugs() Completed");
        new Mugs(1);
        System.out.println("new Mugs(1) Completed");
    }
}
/**
输出 
Inside main()
Mug(0)
Mug(1)
Mug(2)
mug1 & mug2 initialization
Mugs()
new Mugs() Completed
Mug(1)
Mug(2)
mug1 & mug2 initialization
Mugs(int)
new Mugs(1) Completed
 */

5、数组的初始化

Java中的数组和C/C++中的不同,Java中的数组是对象。因为是对象,所以数组名是对象引用;是引用,并不是数组本身,所以在声明时也不能指定大小;因为是引用,所以对数组进行复制操作,直接对数组名进行复制是可以的。

Java初始化对象 java初始化数据怎么写_对象_02

数组的长度是数组的固有成员,不能改变,附上数组的内存结构,以int类型的数组为例:

Java初始化对象 java初始化数据怎么写_java_03


其中数组对象引用占16个字节,数组大小(肯定是int类型的)占4个字节,后面就是数组里面的元素了。PS:图片来源,《ALgorithms fourth edition》第一章第四节

数组的大小a.length是数组的固有大小,不能改变

package myblogs;

import java.util.Arrays;
/**
 * 数组的初始化
 * @author 小锅巴
 */
public class ArrayIni {

    public static void main(String[] args) {
        /**
         * 声明了数组引用,并指定其长度为2,
         */
        Integer[] a = new Integer[2];
        /**
         * 试图修改数组a的长度,编译器报错,提示:The final field array.length cannot be assigned
         * 说明数组的长度是被final修饰的,新建后就不能改变了,如果可以改变,那么程序很显然有隐藏的隐患,
         */
//      a.length = 3;
        /**
         * TIJ上没写这种,我尝试了下,编译器报错:Cannot define dimension expressions when an array initializer is provided
         * 很简单,如果初始化了数组里面的元素,可自动获取数组大(因为数组大小是数组的固有成员),新建数组时加上大小,画蛇添足
         */
//      Integer[] a1 = new Integer[2]{1,2};
        Integer[] b = {
                new Integer(1),
                new Integer(2),
                3,//antuoboxing
        };
        Integer[] c = new Integer[]{
            new Integer(1),
            new Integer(2),
            3,
        };
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));
        System.out.println(Arrays.toString(c));
    }
}
/**
 输出:
[null, null]
[1, 2, 3]
[1, 2, 3]
 */

6、引用的初始化

TIJ上面的例子,直接贴代码吧。

package chapter7;
/**
 * 引用初始化的四种方式
 *  1、在定义时初始化
 *  2、在构造器中初始化
 *  3、实例初始化
 *  4、惰性初始化,即在对象不值得初始化和不必每次都创建对象的情况下,就在正要使用对象前将其初始化
 * @author 小锅巴
 */
class Soap{
    private String s;
    Soap(){
        System.out.println("Soap()");
        s = "Constructed";
    }

    /**
     * 任何非基本类型的对象都有toString方法,需要String时传入对象名,将自动调用toString方法
     */
    public String toString(){
        return s;
    }
}

public class Bath {
    /**
     * 在定义时初始化
     */
    private String
        s1 = "Happy",
        s2 = "Happy",
        s3, s4;
    private Soap castille;
    private int i;
    private float toy;

    /**
     * 在构造器中初始化
     */
    public Bath(){
        System.out.println("Inside Bath()");
        s3 = "Joy";
        toy = 3.14f;
        castille = new Soap();
    }

    /**
     *  实例初始化
     */
    {   i = 47;}

    public String toString(){
        /**
         * 惰性初始化
         */
        if(s4 == null)
            s4 = "Joy";
        return 
                "s1 = " + s1 + "\n" +
                "s2 = " + s2 + "\n" +
                "s3 = " + s3 + "\n" +
                "s4 = " + s4 + "\n" +
                "i = " + i + "\n" +
                "toy = " + toy + "\n" +
                "castille = " + castille;
    }

    public static void main(String[] args){
        Bath b = new Bath();
        /**
         * 自动调用toString方法
         */
        System.out.println(b);
    }   
}

3月21日更新,成员指定初始化发生在当前类的构造器之前,当前类的基类构造器之后。若基类构造器中调用了具有多态的方法,且该方法中使用了当前类的成员变量,则该方法中的成员变量的值为0或者null,详情请点击这里


3月29日更新,更多关于实例初始化,请移步内部类之匿名内部类