一个类的全部基本数据成员都会获得初始化,运行下面的例子能够查看这些默认值:ide
class Default{ boolean t; char c; byte b; short s; int i; long l; float f; double d; public void show() { System.out.println("基本类型 初始化值\n"+ "boolean<----->" + t +"\n" + "char<----->" + c +"\n" + "byte<----->" + b + "\n" + "short<----->" + s + "\n" + "int<----->" + i + "\n" + "long<----->" + l + "\n" + "float<----->" + f + "\n" + "double<----->" + d + "\n" ); } } public class InitValue { public static void main(String[] args) { Default d = new Default(); d.show(); } }
【运行结果】:函数
基本类型 初始化值 boolean<----->false char<-----> byte<----->0 short<----->0 int<----->0 long<----->0 float<----->0.0 double<----->0.0
其中,char类型的默认值为空(null)。编码
对于非基本数据类型而言,对象的句柄也会被初始化:spa
class Person { private String name; // setter } class Default { Person p; public void show() { System.out.println("Person<----->" + p); } } public class InitValue { public static void main(String[] args) { Default d = new Default(); d.show(); } }
【运行结果】:code
Person<----->null
可见,句柄初始化值为null。这就是说,若是没有为p指定初始化值就调用相似于p.setName
的方法,就会出现异常。对象
若是须要本身为变量赋一个初始值,能够在定义变量的同时赋值。blog
class Default{ boolean t = true; char c = 'A'; byte b = 47; short s = 0xff; int i = 24; long l = 999; float f = 1.2f; double d = 1.732; public void show() { System.out.println( "boolean<----->" + t +"\n" + "char<----->" + c +"\n" + "byte<----->" + b + "\n" + "short<----->" + s + "\n" + "int<----->" + i + "\n" + "long<----->" + l + "\n" + "float<----->" + f + "\n" + "double<----->" + d + "\n" ); } } public class InitValue { public static void main(String[] args) { Default d = new Default(); d.show(); } }
甚至能够经过一个方法来进行初始化;继承
class Person { int i = set(); //... }
这些方法也可使用自变量:ci
class Person { int i; int j = set(i); //... }
构建器进行初始化的优势是能够在运行期决定初始化值。例如:it
class Person { int age; Person() { age = 89; } }
age首先会初始化为0,而后变成89。对于全部基本类型以及对象的句柄,这种状况都是成立的。
在一个类里,初始化的顺序是由变量在类内的定义顺序决定的。即便变量定义大量遍及于方法定义的中间,那么变量仍然会在调用任何方法(包括构造函数)以前获得初始化。例如:
class Pet { Pet(int age) { System.out.println("Pet(" + age + ")"); } } class Person { Pet t1 = new Pet(1); Person() { System.out.println("---Person()---"); t3 = new Pet(33); } Pet t2 = new Pet(2); void show() { System.out.println("show----running"); } Pet t3 = new Pet(3); } public class OrderOfInitialization { public static void main(String[] args) { Person p = new Person(); p.show(); } }
【运行结果】:
Pet(1) Pet(2) Pet(3) ---Person()--- Pet(33) show----running
上例中,虽然t一、t二、t3的定义遍及于类中,可是初始化的前后顺序是由t一、t二、t3的定义顺序决定的(本身动手调换t一、t二、t3看看结果),且初始化优先于构建器执行,当调用Person的构建器时,t3从新初始化。
若是数据是静态的(static),一样的过程也会执行。若属于基本类型,并且未对其进行初始化,就会自动得到本身的标准基本类型初始值;若它是指向一个对象的句柄,除非建立一个对象同它链接起来,不然获得一个空值(null)。若是在定义时初始化,采起的方式与非静态值是不一样的,这是由于static只有一个存储区域。例如:
class Bowl { Bowl(int marker) { System.out.println("Bowl(" + marker + ")"); } void f(int marker) { System.out.println("f(" + marker + ")"); } } class Table { static Bowl b1 = new Bowl(1); Table() { System.out.println("Table()"); b2.f(1); } void f2(int marker) { System.out.println("f2(" + marker + ")"); } static Bowl b2 = new Bowl(2); } class Cupboard { Bowl b3 = new Bowl(3); static Bowl b4 = new Bowl(4); Cupboard() { System.out.println("Cupboard()"); b4.f(2); } void f3 (int marker) { System.out.println("f3(" + marker + ")"); } static Bowl b5 = new Bowl(5); } public class StaticInitialization { 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(); t2.f2(1); t3.f3(1); } static Table t2 = new Table(); static Cupboard t3 = new Cupboard(); }
【运行结果】:
Bowl(1) Bowl(2) Table() f(1) Bowl(4) Bowl(5) Bowl(3) Cupboard() f(2) Creating new Cupboard() in main Bowl(3) Cupboard() f(2) Creating new Cupboard() in main Bowl(3) Cupboard() f(2) f2(1) f3(1)
Java容许将其余static初始化工做划分到类内一个特殊的代码块中,这种代码块的形式为static关键字,后面跟着一个方法主体,称为静态代码块。静态代码块只有在第一次生成那个类的对象或首次访问属于那个类的static成员时执行。例如:
class Person { Person(int age) { System.out.println("Person(" + age + ")"); } void f(int age) { System.out.println("f(" + age + ")"); } } class Persons { static Person p1; static Person p2; static { p1 = new Person(1); p2 = new Person(2); } Persons() { System.out.println("Persons()"); } } public class ExplicitStatic { public static void main(String[] args) { System.out.println("Inside main()"); Persons.p1.f(18);//1 } static Persons x = new Persons();//2 static Persons y = new Persons();//2 }
在标记为1的行内访问static对象p1的时候,或在行1被注释而行2未被注释是,用于Persons的static初始化模块就会运行。若1和2都被注释掉,则用于Persons的静态代码块不会执行。
class Person { Person(int age) { System.out.println("Person("+age+")"); } } class Persons { static Person p = new Person(2); // 1 static { p = new Person(3); } static Person p = new Person(2); // 2 } public class CompStaticInit { public static void main(String[] args) { } static Persons x = new Persons(); }
根据注释1保留2,注释2保留1的结果分析可知,静态属性和静态代码块的执行顺序取决于编码的顺序。谁在前面就先执行谁。
class Animal { Animal(int age) { System.out.println("Animal(" + age + ")"); } void f(int age) { System.out.println("f(" + age + ")"); } } public class NotStaticInit { Animal a1; Animal a2; { a1 = new Animal(1); a2 = new Animal(2); System.out.println("a1 & a2 initialized"); } NotStaticInit() { System.out.println("NotStaticInit"); } public static void main(String[] args) { System.out.println("Inside main()"); NotStaticInit x = new NotStaticInit(); } }
相似于静态代码块,匿名代码块与非静态属性的初始化顺序取决于编码顺序。
class Insect { int i = 1; int j; Insect() { prt("i = " + i + ", j = " + j); j = 2; } static int x1 = prt("static Insect.x1 initialized"); static int prt(String s) { System.out.println(s); return 3; } } public class Beetle extends Insect { int k = prt("Beeklt.k initialized"); Beetle() { prt("k = " + k); prt("j = " + j); } static int x2 = prt("static Bootle.x2 initialized"); static int prt(String s) { System.out.println(s); return 4; } public static void main(String[] args) { prt("Beetle constructor"); Beetle b = new Beetle(); } }
【运行结果】:
static Insect.x1 initialized static Bootle.x2 initialized Beetle constructor i = 1, j = 0 Beeklt.k initialized k = 4 j = 2
对Beetle运行Java时,发生的第一件事情是装载程序到外面找到那个类。在装载过程当中,装载程序发现一个基础类,因此随之将其载入。不管是否生成基础类的对象,这一过程都将执行。若是基础类含有另外一个基础类,则另外一个基础类随即也会载入,以此类推。接下来就在根基础类中执行static初始化,再在下一个衍生类中执行,以此类推。这是由于衍生类的初始化可能要依赖于对基础类成员的初始化。
当类都装载完毕,就能建立对象。首先,这个对象中的全部基本数据类型都会设置成为他们的默认值,对象句柄设为null。而后执行基础类的构建器。这种状况是自动完成的(衍生类的构造函数中默认调用了super()
,也能够经过super指定基类的构建器)。基础类构建器完成后,衍生类实例变量就会按原本的顺序获得初始化,而后执行构建器的剩余的主体部分。
总结对象建立的过程:
- 静态只在类加载的时候执行且只执行一次;
- 非静态只有在实例化的时候执行,每次建立对象都执行;
- 静态在非静态以前执行,基类静态优先于衍生类静态执行;
- 静态属性和静态代码块的执行属性取决于它们在类中的位置,谁在前先执行谁;
- 非静态属性和构造块的执行顺序取决于它们在类中的位置,谁在前执行谁。