当前位置:编程学习 > JAVA >>

java基础:类变量与实例变量的初始化过程

1)java中类变量和实例变量的声明顺序:
java要求定义成员变量时必须采用合法的向前引用,比如:
public class ErrorDef {
int num1 = num2 +2 ;//非法向前引用num2
int num2 = 10 ;
}
上述代码无法通过编译。num1非法向前引用num2。
同样的道理,两个类变量也不能采用非法向前引用,例如:
public class ErrorDef {
static int num1 = num2 +2 ;//非法向前引用num2
static int num2 = 10 ;
}
但是如果一个是实例变量一个是类变量,则实例变量总是可以引用类变量,例如:
public class ErrorDef {
int num1 = num2 +2 ;//合法
static int num2 = 10 ;
}
上述代码是合法的。虽然num2位于num1的后面,但num1的初始化却可以根据num2得到,因为,num2是类变量,num1是实例变量。
类变量的初始化总是处于实例变量初始化之前。类变量的使用可以通过类直接使用,也可以通过实例来调用,不管采用哪种方法
调用,低层都是转化为通过类名来调用类变量。


2)实例变量的初始化时机:
程序可以在3个地方对实例变量进行初始化:定义实例变量的时候指定初始值、非静态初始化代码块中对实例变量进行初始化、
构造器中队实例变量进行初始化。例如:
public class Cat {
String name ;
int age ;

public Cat(String name , int age){
System.out.println("构造方法中");
this.name = name ;
this.age  = age ;
}

{//非静态代码块,里面的变量weight没有先后顺序,不会出现向前引用错误。
System.out.println("非静态代码块正在执行");
weight = 2.0 ;
}

double weight = 2.3;

public String toString(){
return "Cat[name=" + name + ",age=" + age + ",weight=" + weight + "]" ;
}
}
测试代码如下:
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("kitty" , 2) ;
System.out.println(cat);
Cat c2 = new Cat("tom" , 3) ;
System.out.println(c2);
}
}
程序的运行结果为:
非静态代码块正在执行
构造方法中
Cat[name=kitty,age=2,weight=2.3]
非静态代码块正在执行
构造方法中
Cat[name=tom,age=3,weight=2.3]
当调用类构造器来创建java对象的时候,该类所包含的非静态代码块总是在构造器执行之前获得执行。从程序的运行结果可以看出
两只猫的weight都是2.3,这是因为在定义weight的时候指定了初始值,它和非静态代码块中的操作与他们在源代码中的排列顺序有
关,在程序中,先将weight初始化为2.0,然后又将weight初始化为2.3,所以最后weight的值为2.3。
如果将double weight = 2.3;此行代码放到非静态代码块的前面,则程序的运行结果为:
非静态代码块正在执行
构造方法中
Cat[name=kitty,age=2,weight=2.0]
非静态代码块正在执行
构造方法中
Cat[name=tom,age=3,weight=2.0]
由此可以看出,定义变量时候的初始值和非静态代码块中的值的先后顺序会影响变量最后的值。


为什么weight的最终值会和定义变量声明的值与非静态代码块中的值的先后顺序有关呢?其实可以使用javap工具来查看jvm编译java
程序的过程。


例如:javap -c Cat

\

将double weight = 2.3;此行代码放到非静态代码块前面,继续使用javap命令编译Cat,得到的结果为:
\

从上面两个结果中我们可以清楚的看到,jvm在初始化变量的时候会将定义变量的时候设置的初始值以及在非静态代码块中设置的值到
移动到构造器中,在构造器中执行的顺序就是代码在源程序中的出现的先后顺序,所以变量的值和定义变量的初始化值以及非静态代码
块中设置值的先后顺序有关。


3)类变量的初始化时机:
程序可以在两个地方对类变量进行初始化:定义类变量时指定初始值、静态初始化块中对类变量指定初始值。这两种方式的执行顺序与
他们在源程序中的排列顺序有关。
例如:
public class StaticInitTest {
static int count = 2 ;
static {
System.out.println("StaticInitTest的静态初始化块中");
count = 5 ;
}
public static void main(String[] args){
System.out.println("count=" + count );
}
}
程序执行结果为:
StaticInitTest的静态初始化块中
count=5
当把static int count = 2 ;放到静态代码块的后面的时候:
public class StaticInitTest {
static {
System.out.println("StaticInitTest的静态初始化块中");
count = 5 ;
}
static int count = 2 ;
public static void main(String[] args){
System.out.println("count=" + count );
}
}
程序的执行结果为:
StaticInitTest的静态初始化块中
count=2
由此可见,类变量的值与定义变量时指定的值与在静态代码块中指定的值的先后顺序有关。
为了更清楚的观察类变量的初始化过程,看下面的例子:
public class Price {
final static Price INSTANCE = new Price(2.8) ;//静态实例
static double initPrice = 20  ;//初始化价格为20
double currentPrice ;
public Price(double discount){//格局折扣discount得到当前价格currentPrice
currentPrice = initPrice - discount ;
}
}
测试程序为:
public class Test {
public static void main(String[] args) {
System.out.println(Price.INSTANCE.currentPrice);
Price p = new Price(2.8) ;
System.out.println(p.currentPrice);
}
}
程序运行结果为:
-2.8
17.2
为了解释这个结果,我们需要深入理解程序对Price类进行初始化的过程。该过程可以分为两个阶段:
(1)jvm为Price的两个变量分配内存空间。
(2)按初始化代码的排列顺序对类变量执行初始化。
在第一阶段中,jvm为INSTANCE和initPrice分配内存空间,此时INSTANCE为null,initPrice的值为0.0.接着进入第二阶段,按照顺序先
初始化INSTANCE,调用构造方法计算currentPrice的值,此时currentPrice=0.0-2.8 = -2.8,执行完以后再初始化initPrice的值为20,
所以第一条输出语句的结果为-2.8,第二条语句的结果为17.2

 

摘自 xxqi1229的专栏

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,