转载

Java对象初始化

自动初始化(默认值)

一个类的所有基本数据成员都会得到初始化,运行下面的例子可以查看这些默认值:

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)。

对于非基本数据类型而言,对象的句柄也会被初始化:

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();  } } 

【运行结果】:

Person<----->null 

可见,句柄初始化值为null。这就是说,如果没有为p指定初始化值就调用类似于 p.setName 的方法,就会出现异常。

规定初始化

如果需要自己为变量赋一个初始值,可以在定义变量的同时赋值。

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();     //... }

这些方法也可以使用自变量:

class Person {     int i;     int j = set(i);     //... }

构建器初始化

构建器进行初始化的优点是可以在运行期决定初始化值。例如:

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)<br/> show----running 

上例中,虽然t1、t2、t3的定义遍布于类中,但是初始化的先后顺序是由t1、t2、t3的定义顺序决定的(自己动手调换t1、t2、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指定基类的构建器)。基础类构建器完成后,衍生类实例变量就会按本来的顺序得到初始化,然后执行构建器的剩余的主体部分。

总结对象创建的过程:

  • 静态只在类加载的时候执行且只执行一次;
  • 非静态只有在实例化的时候执行,每次创建对象都执行;
  • 静态在非静态之前执行,基类静态优先于衍生类静态执行;
  • 静态属性和静态代码块的执行属性取决于它们在类中的位置,谁在前先执行谁;
  • 非静态属性和构造块的执行顺序取决于它们在类中的位置,谁在前执行谁。
正文到此结束
Loading...