对象的四种初始化方式:
1、在定义对象的地方
2、在类的构造其中
3、在正要使用这些对象之前,也称为惰性初始化。
4、使用实例(代码块)初始化
下面是这四种方法的具体使用:
1 class Soap{ 2 private String s; 3 public Soap(){ 4 System.out.println("soap()"); 5 s="constucted"; 6 } 7 public String toString(){return s;} 8 } 9 public class Bath { 10 private String s1="Happy"; //The method 1. 11 private String s2,s3; 12 private Soap castille; 13 private int i; 14 public Bath(){ 15 System.out.println("Inside Bath()"); 16 s2="Joy"; //The method 2. 17 castille=new Soap(); 18 } 19 {i=100;} //The method 4. 20 public String toString(){ 21 if(s3==null){s3="Jone";} //The method 3. 22 return "s1="+s1+"\n"+"s2="+s2+"\n"+"s3="+s3+"\n"+"i="+i+"\n"+"castille="+castille; 23 } 24 public static void main(String[] args) { 25 Bath b=new Bath(); 26 System.out.println(b); 27 } 28 } 29 //output: 30 Inside Bath() 31 soap() 32 s1=Happy 33 s2=Joy 34 s3=Jone 35 i=100 36 castille=constucted
基类的自动初始化:
1 class Cleaner{ 2 private String s="cleaner"; 3 public Cleaner(){ 4 System.out.println("Cleaner Constructor."); 5 } 6 public void append(String str){s+=str;} 7 public void dilute(){append("\tCleaner.dilute");} 8 public String toString(){return s;} 9 } 10 class Geli extends Cleaner{ 11 public Geli(){ 12 System.out.println("Geli Constructor."); 13 } 14 public void dilute(){ 15 append("\tGeli.dilute"); 16 super.dilute(); 17 } 18 } 19 class Hair extends Geli{ 20 public Hair(){ 21 System.out.println("Hair Constructor."); 22 } 23 public void dilute(){ 24 append("\tHair.dilute"); 25 super.dilute(); 26 } 27 public void ster(){ 28 append("\nster()"); 29 } 30 } 31 public class demo01 { 32 public static void main(String[] args) { 33 Cleaner c=new Hair(); 34 c.dilute(); 35 System.out.println(c); 36 //c.ster(); illegal 37 } 38 } 39 //output: 40 Cleaner Constructor. 41 Geli Constructor. 42 Hair Constructor. 43 cleaner Hair.dilute Geli.dilute Cleaner.dilute
从上面的代码可以看出,java会自动在导出类的构造器中插入对基类构造器的调用。构造过程是从基类”向外“扩散的,所以基类在导出类构造器可以访问它之前,就已经完成了初始化,这是为了保证父类的完整性。
子类对象向上转型后会丢失自己的方法,但是会调用父类的方法时,会调用已经被子类覆写的方法。子类独有的方法对父类来说是不可见的,即使通过向上转型。
初始化的实际过程:
1、在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零
2、调用基类的构造器
3、按照声明的顺序调用成员的方法
4、调用导出类的构造器主体
1 class Cycle{ 2 public Cycle(){ 3 System.out.println("base constructor."); 4 this.ride(); 5 } 6 public void ride(){ 7 System.out.println("this is a cycle."); 8 } 9 } 10 class Unicycle extends Cycle{ 11 private int i=9; 12 @Override 13 public void ride(){ 14 System.out.println("this is a Unicycle."+i); 15 } 16 } 17 public class demo02 { 18 public static void main(String[] args) { 19 Unicycle un=new Unicycle(); 20 un.ride(); 21 } 22 } 23 //output: 24 base constructor. 25 this is a Unicycle.0 26 this is a Unicycle.9
第29行和第30行不同的原因就是因为初始化步骤1。
这说明了编写构造器时的一条简单有效的准则:用尽可能简单的方法是对象进入正常状态;如果可以的话,避免调用其他方法。
常见的错误:
如果你已经定义了一个构造器(无论是否有参数),编译器就不会帮你自动创建默认构造器,那么在子类的构造器中自然就应该使用自定义的构造器,而不是默认构造器。
1 class Toy{ 2 Toy(int i){} 3 } 4 class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{ 5 FancyToy(){ 6 super(4); //不可省略 7 //super(); illegal. 8 } 9 }