在定义一个类时,只是在描述某事物的特征和行为,并没有产生具体的数据。只有通过new关键字创建该类的实例对象时,才会开辟栈内存和堆内存,在堆内存中每个对象会有自己的属性。如果希望某些属性被所有对象共享,就必须将其声明为static属性。如果属性使用了static关键字进行修饰,则该属性可以直接使用类名称进行调用。除了修饰属性,static关键字还可以修饰成员方法。
1.静态属性
如果在Java程序中使用static修饰属性,则该属性称为静态属性(也称全局属性),静态属性可以使用类名直接访问,访问格式如下:
类名.属性名
在学习静态属性之前,先来看一个案例,如图1所示。
图1:
图1运行结果:
在图1中,第5~7行代码声明了Student类的有参构造方法,第9~11行代码输出了name和age属性的值。第16~20行代码分别定义了Student类的3个实例对象,并分别使用3个实例对象调用info()方法。
在图1运行结果中,3名学生均来自于A大学。下面考虑一种情况:假设A大学改名为B大学,而且此Student类已经产生了10万个学生对象,那么意味着,如果要修改这些学生对象的学校信息,就需要把这10万个对象中的学校属性全部修改,共修改10万遍,这样肯定是非常麻烦的。
为了解决上述问题,可以使用static关键字修饰school属性,将其变为公共属性。这样,school属性只会分配一块内存空间,被Student类的所有对象共享,只要某个对象进行了一次修改,全部学生对象的school属性值都会发生变化。
下面修改图1代码,使用static关键字修饰school属性,具体代码如图2所示。
图2:
图2运行结果:
在图2中,第4行代码使用static关键字修饰了school属性,第22行代码使用stu1对象为school属性重新赋值。
在图2运行结果中可以发现,只修改了一个stu1对象的学校属性,stu1和stu2对象的school属性内容都发生了变化,说明使用static声明的属性是被所有对象共享的。图2代码的内存分配图如图3所示。
图3:
小提示:static不能修饰局部变量
static关键字只能修饰成员变量,不能修饰局部变量,否则编译器会报错。例如,下列代码是非法的。
2.静态方法
如果想要使用类中的成员方法,就需要先将这个类实例化,而在实际开发时,开发人员有时希望在不创建对象的情况下,通过类名就可以直接调用某个方法,要实现这样的效果,只需要在成员方法前加上static关键字,使用static关键字修饰的方法通常称为静态方法。
同静态变量一样,静态方法也可以通过类名和对象访问,具体格式如下:
类名.方法
或者
实例对象名.方法
下面通过一个案例来学习静态方法的使用,如图4所示:
图4:
图4运行结果:
在图4中,Student类将所有的属性都进行了封装,所以想要更改属性就必须使用setter方法。第12~29行代码声明了name、age和school属性的getter和setter方法,第36~38行代码分别对name、age和school属性的值进行修改,但是school属性是使用static声明的,所以可以直接使用类名Student进行调用。
注意:
静态方法只能访问静态成员,因为非静态成员需要先创建对象才能访问,即随着对象的创建,非静态成员才会被分配内存。而静态方法在被调用时可以不创建任何对象。
3.静态代码块
在Java类中,用static关键字修饰的代码块称为静态代码块。当类被加载时,静态代码块会执行,由于类只加载一次,因此静态代码块只执行一次。在程序中,通常使用静态代码块对类的成员变量进行初始化。
下面通过一个案例学习静态代码块的使用,如图5所示。
图5:
图5运行结果:
在图5中,第2~4行代码声明了一个构造代码块,第5~7行声明了一个静态代码块,第14~16行代码分别实例化了3个Student对象。
从图5运行结果可以看出,代码块的执行顺序为静态代码块、构造代码块、构造方法。static修饰的成员会随着class文件一同加载,其优先级最高。在main()方法中创建了3个Student对象,但在3次实例化对象的过程中,静态代码块中的内容只输出了一次,这就说明静态代码块在类第一次使用时才会被加载,并且只会加载一次。