Java中的实例变量和类变量易混点(2)

Java 1224℃

使用static 修饰的成员变量是类变量,属于该类本身;没有使用static 修饰的成员变量是实例变量,属于该类的实例。在同一个 JVM内,每个类只对应一个Class对象,但每个类可以创建多个Java对象。

由于同一个JVM内每个类只对应一个Class对象,因此同一个JVM内的一个类的类变量只需一块内存空间;但对于实例变量而言,该类每创建一次实例,就需要为实例变量分配一块内存空间。也就是说,程序中有几个实例,实例变量就需要几块内存空间。 下面程序可以很好地表现出实例变量属于对象,而类变量属于类的特性。

class Person 
{ 
       String name; 
       int age; 
       static int eyeNum; 
       public void info() 
       { 
              System.out.println("我的名字是:" + name  
                   + ", 我的年龄是:" + age); 
       } 
} 
public class FieldTest 
{ 
       public static void main(String[] args)  
       { 
              //类变量属于该类本身,只要该类初始化完成,程序即可使用类变量 
              Person.eyeNum = 2;                       //① 
              //通过 Person 类访问 eyeNum类变量 
              System.out.println("Person 的 eyeNum 属性:" + Person.eyeNum); 
              //创建第1个 Person 对象 
              Person p = new Person(); 
              p.name = "猪八戒"; 
              p.age = 300; 
              //通过 p访问 Person 类的 eyeNum类变量 
              System.out.println("通过p变量访问 eyeNum 类变量:" + p.eyeNum);   //② 
              p.info(); 
              //创建第2个 Person 对象 
              Person p2 = new Person(); 
              p2.name = "孙悟空"; 
              p2.age = 500; 
              p2.info(); 
              //通过 p2修改 Person 类的 eyeNum 类变量 
              p2.eyeNum = 3;                        //③ 
              //分别通过p、p2和 Person 访问 Person 类的 eyeNum 类变量 
              System.out.println("通过 p变量访问 eyeNum 类变量:" + p.eyeNum); 
              System.out.println("通过 p2变量访问 eyeNum类变量:" + p2.eyeNum); 
              System.out.println("通过 Person类访问 eyeNum类变量:" + Person.eyeNum); 
       } 
}

上面程序中①行代码直接对 Person 类的 eyeNum 类变量赋值。这没有任何问题,因为eyeNum类变量是属于 Person类的,当 Person类初始化完成后,eyeNum类变量也随之初始化完成。因此,程序既可对该类变量赋值,也可访问该类变量的值。

一旦 Person类初始化完成,程序即可通过 Person类访问eyeNum类变量。除此之外,Java还允许通过Person类的任意实例来访问eyeNum类变量–这是笔者认为Java设计得非常不合理的地方:既然eyeNum本质上属于 Person类,而不是属于 Person类的实例,那就应该禁止通过 Person实例来访问eyeNum类变量。

虽然 Java 允许通过 Person对象来访问 Person 类的 eyeNum类变量,但由于 Person 对象本身并没有 eyeNum类变量(只有实例变量才属于 Person 实例),因此程序通过 Person 对象来访问 eyeNum类变量时,底层依然会转换为通过 Person 访问 eyeNum类变量。也就是说,不管通过哪个 Person对象来访问eyeNum类变量,都与通过 Person类访问eyeNum类变量的效果完全相同。因此,在②行代码处通过p来访问eyeNum变量将再次输出2。

执行完②行代码时,程序创建 Person对象,系统不再为 eyeNum类变量分配内存空间,执行初始化,而只为 Person对象的实例变量执行初始化–因为实例变量才属于 Person实例,而类变量属于 Person类本身。

当 Person类初始化完成之后,类变量也随之初始化完成,以后不管程序创建多少个 Person对象,系统不再为 eyeNum类变量分配内存;但程序每创建一个 Person对象,系统将再次为name、age实例变量分配内存,并执行初始化。

当程序执行完③行代码之后,内存中再次增加了一个 Person 对象。当程序通过 p2 对eyeNum 类变量进行赋值时,实际上依然是对 Person 类的 eyeNum 类变量进行赋值。

当 Person 类的 eyeNum类变量被改变之后,程序通过 p、p2、Person 类访问 eyeNum 类变量都将输出 3。这是由于,不管通过哪个 Person 对象来访问 eyeNum类变量,底层都将转换为通过 Person 来访问 eyeNum类变量。由于 p和 p2 两个变量指向不同的 Java 对象,当通过它们访问实例变量时,程序将输出不同的结果。

转载请注明:零五宝典 » Java中的实例变量和类变量易混点(2)