Static 的意义与实作方式

类别:Java 点击:0 评论:0 推荐:
「将某 class 产生出一个 instance 之后,此 class 所有的 instance field 都会新增一份,那么所有的 instance method 是否也会新增一份?」我常常看到网路上有人在讨论此问题,所以写了这篇文章,以为解释。

Member 的种类
类别(class)的内部组成统称为成员(member),如果依据成员是「资料」还是「程式」来区分,可以分成:

资料,被称为 field
程式,被称为 method
如果再依据有无使用static修饰而细分,则成员可细分成四种:

class field:有用static修饰的field
class method:有用static修饰的method
instance field:没有用static修饰的field
instance method:没有用static修饰的method
顾名思义,class field/method和class本身有密切的关系,而instance field/method则与instance(也就是物件)有密切的关系。请看下面的范例程式。

public class Class1 {

 public static int classField;

 public static void classMethod1() {

  // ...
 }
 public static void classMethod2() {
  // ...
 }
 public int instanceField;
 public void instanceMethod1() {
  // ...
 }
 public void instanceMethod2() {
  // ...
 }
}

public class Class2 {
 public static void classMethod () {
  // ...
 }
 public void instanceMethod() {
  // ...
 }
}

Field
宣告field时,如果前面加上static的修饰字,就会使得此field变成是class field。一个class field永远只占用一块记忆体,而且此记忆体空间是在此class一被载入(load)记忆体之后就立刻配置的(allocate),感觉就如同此field与该class本身相关,而不是与该class的instance相关。class field可以被同一个class的class method内部所直接使用,举例来说,上述的classMethod1()内部可以出现classField。如果Class1的class method或instance method欲使用到Class2的class field,就必须在前面冠上Class2,例如:Class2.classField。

宣告field时,如果前面「不」加上static的修饰字,就会使得此field变成是instance field。对instance field而言,每产生一个instance(物件)就多一块instance field的记忆体,每少一个instance就少一块instance field的记忆体。instance field可以被同一个instance的instance method内部所直接使用,举例来说,上述的instanceMethod1()内部可以出现instanceField。如果某class的class method或instance method欲使用到某instance的instance field,就必须在前面冠上instance名称,例如:obj.classField。

Method
宣告method时,如果前面加上static的修饰字,就会使得此method变成是class method。对class method而言,永远只占用一块记忆体,而且此记忆体空间是在此class一被载入进记忆体之后就立刻配置的,就如同此method是与该class本身相关,而不是与该class的instance相关。class method可以被同一个class的任何class method内部所直接使用,举例来说,上述的classMethod1()内部可以出现classMethod2()。如果Class1的class method或instance method欲使用到Class2的classMethod(),就必须在前面冠上Class2,也就是Class2.classMethod()。

宣告method时,如果前面「不」加上static的修饰字,就会使得此method变成是instance method。对instance method而言,每产生一个instance「并不会」多一块instance method的记忆体。同一个method不管被调用(invoke)几次,也不管被调用时的instance是何者,每次的程式码完全都一样,差别只在每次执行时资料不同,而资料是存放在call stack中,所以不会混淆。在instance method内,资料的来源包括了参数和instance field。参数被传进来变成call stack内的entry,所以不会混淆,这很容易理解,但是instance field是如何区隔开来的(前面刚提过:instance field会随着instance数目增加而增加),这是透过隐匿(implicit)的this参数来达到了,稍后会有说明。instance method可以被同一个instance的instance method内部所直接使用,举例来说,上述的instanceMethod1()内部可以出现instanceMethod2()。如果某class的class method或instance method欲使用到某instance的某instance method,就必须在前面冠上此instance名称,例如:obj.classMethod()。

隐匿的 this 参数
综合上面的叙述来看:

class field:共用一块记忆体
class method:共用一块记忆体
instance field:随着每个instance各有一块记忆体
instance method:共用一块记忆体
instance method为什么不是随着每个instance占有一块记忆体,反倒是共用一块记忆体?其实,让每个instance method如同instance field一样,随着每个instance占有一块记忆体,这么做当然是可以的,只是Java编译器和JVM都不这么做,因为太浪费记忆体空间了。一个field少则占用一个byte,多则占用数百Byte,但是method少则数个byte,多则数百Kilo Byte。Mehtod耗费的记忆体是field的数百倍,甚至数千倍,当然是能共用就尽量共用,比较不会消耗记忆体。既然JVM让一个class的所有instance共用相同的instance method,下面两行程式码在instanceMethod()内部时,如何区分是instance1或instance2?

instance1.instanceMethod();
instance2.instanceMethod();

因为编译器会帮我们在把instance1和instance2个别传入instanceMethod()中当作第一个参数。也就是说,任何instance method参数的实际个数都会比表面上多一个,这个多出来的参数是由Java编译器帮我们加上去的,用来代表对应的instance。此参数的变数名称为this,也是Java的一个关键字(keyword)。

当调用某个instance method或使用某个instance field时,你必须在前面加上该instance的名称,如果该instance method/field相关的instance和当时程式码所在的instance method的instance指的是同一个instance时,该instance的名称就是this,这种情况下,你也可以选择不在前面加上「this.」。

然而,在某些状况下,非得在前面加上「this.」不可。例如,当method中的参数或区域变数和instance field名称完全相同时,如果不在前面冠上「this.」,那么指的是参数或区域变数;如果在前面冠上「this.」,那么指的才是instance field。

本文地址:http://com.8s8s.com/it/it12088.htm