实例变量初始化的时机:
定义变量时指定初始值 非静态初始化块中对实例变量指定初始值 构造器中对实例变量指定初始值
类所包含的非静态初始化块将会在构造器之前获得执行。
class Cat{
public Cat(String name,int age){
System.out.println("执行构造器");
this.name =name;
this.age =age;
}
{
System.out.println("执行非静态初始化块");
weight=2.0;
}
}
#介绍一种java工具 在编译Java文件后产生class文件 接下来使用 javap -c 类名看到详细编译信息
三种方法为变量指定初始值作用完全类似,它们对应的赋值语句都将被合并到构造器中。在合并过程中,定义变量语句转换得到的赋值语句,初始化块里语句的转换为的赋值语句,总是位于构造器的所有语句之前,合并后,两种赋值语句的顺序保持为它们在源代码中的顺序。
类变量初始化的时机:
定义类变量时指定初始值 静态初始化块中指定初始值
在类进行初始化时:先为所有的类变量分配存储空间,然后在按源代码中的顺序执行静态初始化块中所指定的初始值和定义类变量中所指定的初始值。
super()用于显示的调用父类的构造器,this()用来显示调用本类的另外一个构造器。而且super()和this()只能在构造器中的第一行使用,而且只能使用其中之一。
澄清一个概念,java对象是由构造器创建的吗?不是,构造器只负责对java类中实例变量赋初值,在执行构造器之前,Java对象所占的内存空间就已经分配出来了,也就是为这些实例变量都分配了内存空间,而且这些变量都是空值。
package aaaaa;
class Base {
int i= 2;
public void display(){
System.out.println(this.i);
}
}
class D extends Base{
int i =22;
public void display(){
System.out.println(this.i);
}
}
public class Test{
public static void main(String args[]){
Base b =new Base();
System.out.println(b.i);
b.display();
System.out.println("========================");
D d = new D();
System.out.println(d.i);
d.display();
System.out.println("========================");
Base bd =new D();
System.out.println(bd.i);
bd.display();
System.out.println("========================");
Base d2b =d;
System.out.println(d2b.i);
}
}
这段代码的输出结果是什么?
2
2
========================
22
22
========================
2
22
========================
2
当变量的编译类型和运行类型不同时,通过该变量访问它引用对象的实例变量时,该变量的值由声明该变量的值所决定,但通过该变量访问它所引用对象的实例方法将有它所引用的对象所决定。
package aaaaa;
class Animal{
private String desc;
public Animal(){
this.desc=getDesc();
}
public String getDesc(){
return "Animal";
}
public String toString(){
return desc;
}
}
class Wolf extends Animal{
private String name;
private double weight;
public Wolf(String name,double weight){
this.name=name;
this.weight=weight;
}
public String getDesc(){
return "Wolf[name]:"+name+"Wolf[weight]:"+weight;
}
}
public class Test{
public static void main(String args[]){
System.out.println(new Wolf("灰太狼",10));
}
}
代码运行结果:
Wolf[name]:nullWolf[weight]:0.0
解释:如果父类构造器调用了子类重写的方法,且通过子类构造器来创建子类对象,调用了父类的构造器,就会导致子类重写的方法在子类构造器的所有代码之前被运行,从导致出现子类重写的方法访问不到子类的实例变量的情形。
如果子类重写了父类的方法,那么意味着子类方法完全覆盖父类的方法。而对于实例变量则不是如此,即使子类中声明并赋值了与父类同名的变量,子类中的这个变量也不会完全覆盖父类中的变量。
内存中的子类实例:
package aaaaa;
class Base {
int i= 2;
}
class D extends Base{
int i =22;
}
class E extends D{
int i =222;
}
public class Test{
public static void main(String args[]){
E e =new E();
D d = e;
Base b =e;
System.out.println(b.i);
System.out.println(d.i);
System.out.println(e.i);
}
}
代码运行结果:
2
22
222
内存中的子类实例对象不仅保存了子类本身所有的实例变量,而且保存了父类所有的实例变量。
类变量:
类变量最好使用类来访问,因为类变量在类初始化阶段完成,而实例变量在对象初始化阶段完成初始化。
final修饰符:
final修饰的实例变量赋初值的三个位置:
声明时赋值 在非静态初始化块中赋值 在构造器中赋值
final修饰的类变量赋初值的两个位置:
声明时赋值 在静态初始化块中赋值。
对于一个final修饰的变量,并且在定义时就指定初始值,那么该初始值在编译时就可以确定下来了,这个变量就相当于一个直接量。其实质为“宏变量”。但只有在定义final类变量时指定初始值,系统才会对该final类变量执行宏替换。
如果程序需要在匿名类中使用局部变量,那么这个局部变量必须使用final值来修饰。