
5.6 static关键字
static是一个用于声明程序结构的关键字,此关键字可以用于全局属性和全局方法的声明,主要特点是可以避免对象实例化的限制,在没有实例化对象的时候直接进行此类结构的访问。
5.6.1 static属性

在一个类中,主要的组成就是属性和方法(分为构造方法与普通方法两种),而每一个对象都分别拥有各自的属性内容(不同对象的属性保存在不同的堆内存中)。如果想要类中的某个属性定义为公共属性(所有对象都可以使用的属性),则可以在声明属性前加上static关键字。
范例:定义static属性(现在假设定义的Person类描述的全部都是中国人的信息)


本程序定义了一个描述中国人的类Chinese,类中定义了一个static类型的country属性,这样该属性就成为公共属性,该属性会保存在全局数据区中,所有的对象都可以获取到相同的对象内容,当有一个对象修改了static属性内容后将影响到其他所有对象,此时的内存关系如图5-11所示。

图5-11 static属性内存关系
提问:可以不将country属性定义为static吗?
在本程序中,如果在Chinese类中定义时没有将country属性设置为static,不是也可以实现同样的效果吗?
回答:使用static才表示公共。
首先,在本程序中country是一个公共属性,这是使用static关键字的主要原因。如果此时在代码中不使用static定义country属性,则每个对象都会拥有此属性。
范例:不使用static定义country属性

由于country属性没有使用static,所以在进行对象实例化操作时,内存关系如图5-12所示。

图5-12 使用非static声明的内存关系
可以想象一下,如果现在每一个对象都拥有各自的country属性的话,那么此属性不再是公共属性,而当进行country属性更新时,就必然要修改所有类的对象,这样在实例化对象较多的时候一定会带来性能与操作上的问题。
static描述的是全局属性,对于全局属性除了可以利用实例化对象调用外,最大的特点在于可以直接利用类名称并且在没有实例化对象产生的情况下进行调用。
范例:通过类名称直接调用static属性

本程序在没有产生实例化对象的时候就直接利用了类名称输出和修改static属性的内容,通过本程序可以发现,static虽然定义在类中,但是不受实例化对象的使用限制。
注意:通过本程序,就应该清楚以下几点。
使用static定义的属性内容不在堆内存中保存,而是保存在全局数据区。
使用static定义的属性内容表示类属性,类属性可以由类名称直接进行调用(虽然可以通过实例化对象调用,但是在Java开发标准中不提倡此类格式)。
static属性虽然定义在了类中,但是其可以在没有实例化对象的时候进行调用(普通属性保存在堆内存里,而static属性保存在全局数据区之中)。
另外需要提醒读者的是,在以后进行类设计的过程中,首要的选择还是普通属性,而是否需要定义static属性是需要根据实际的设计条件选择的。
5.6.2 static定义方法

static除了可以进行属性定义之外,也可以进行方法的定义,一旦使用static定义了方法,那么此方法就可以在没有实例化对象的情况下调用。
范例:定义static方法

本程序对静态属性country进行了封装处理,这样类的外部将无法直接进行此属性的调用,为了解决country属性的修改问题,所以设置了一个static方法setCountry()。由于static定义的方法和属性均不受到实例化对象的限制,这样就可以直接利用类名称进行static方法调用。
注意:关于方法的调用问题。
此时类中的普通方法实际上就分为了两种:static方法和非static方法,而这两类方法之间的调用也存在着以下的限制。
static定义的方法不能调用非static的方法或属性。
非static定义的方法可以调用static的属性或方法。
以上两点,读者可以自行编写代码验证,或者是参考本书附赠的学习视频。而之所以会存在这样的限制,主要原因如下。
使用static定义的属性和方法,可以在没有实例化对象的时候使用(如果没有实例化对象,也就没有了表示当前对象的this,所以static方法内部无法使用this关键字原因就在于此)。
非static定义的属性和方法,必须实例化对象之后才可以进行调用。
在第4章讲解Java方法定义的格式时提出:如果一个方法在主类中定义,并且由主方法直接调用,那么前面必须有public static,即使用以下的格式。
格式:在主类中定义,由主方法直接调用的普通方法定义格式。

范例:观察代码

按照之前所学习的概念来讲,此时范例表示的是一个static方法调用其他的static方法,但是如果这时print()方法的定义没有使用static呢?则必须使用实例化对象来调用非static方法,即所有的非static方法几乎都有一个特点:方法要由实例化对象调用。
范例:实例化本类对象调用非static方法

因为在讲解本章概念前,考虑到知识层次的问题,并没有强调static这个关键字,所以才给出了一个简单的格式用于定义方法。而在本章,由于方法是通过实例化对象调用就没有在方法中使用static关键字定义。
同时需要提醒读者的是,在实际项目开发的过程中,类里面的组成方法在大部分情况下都是非static型的,也就是说大部分类的方法都是需要通过实例化对象调用的,所以设计中非static方法应该作为首选,而static方法只有在不考虑实例化对象的情况下才会定义。
5.6.3 static应用案例

static关键字最为重要的使用特点就是避免实例化对象的限制而直接进行属性或方法的调用,同时static属性还可以描述公共数据的特点,下面将通过此特性讲解static属性的使用案例。
范例:编写一个程序类,这个类可以实现实例化对象个数的统计,每创建一个新的实例化对象就实现一个统计操作

在进行对象个数统计的时候,肯定需要一个公共的属性进行个数的保存,所以本程序定义了一个静态属性count,在通过构造方法调用进行对象实例化过程中都会进行个数的累加处理。实际上对于此时的程序只需稍加修改就可以实现一个属性内容自动命名的处理操作,现在假设Book类中有两个构造方法:无参构造和单参构造(设置title属性),但是要求不管调用哪一个构造方法都可以为title属性设置一个内容,这样就可以通过static属性进行自动命名处理。
范例:实现属性自动命名


本程序利用static属性的共享特点,定义了一个对象的计数操作,这样每当调用无参构造方法时都可以保证自动设置一个title属性内容。
提示:在多线程中存在此类操作。
在第14章中讲解多线程开发的时候也会出现有线程名称的自动命名处理操作,这样做的好处是保证每一个线程都有对应的唯一名称,而实现机制与本程序相同。