【Java面向对象】5-7 static 关键字详解
§5-7 static
关键字详解
在前面的章节(变量作用域、[非]静态成员)中已经了解了 static
关键字的用法和作用。本节将继续讨论 static
关键字的用途。
5-7.1 static
修饰的变量
我们来看一个例子:
public class Test {
private static int num; //静态变量:类变量
private double figure = 0.0; //非静态变量:实例变量
public static void main(String args[]) {
Test obj = new Test(); //实例化当前类
Sout.println(Test.num); //静态变量可以通过类名访问
Sout.println(obj.num); //静态变量还可以通过对象访问
Sout.println(obj.figure); //非静态变量仅可以通过对象访问
}
}
运行结果:
0
0
0.0
可以看到,静态变量隶属于类,随类的加载而加载,被存储在静态方法区,只有一个,被所有线程共享,且具有初始值。因此,一般而言,可直接通过类名访问静态变量以显式区分该变量为静态变量。若需要多个类访问该变量,我们可以考虑使用静态变量,静态变量在多线程中也有很大的作用。
而非静态变量可分为实例变量和局部变量,前者隶属于对象,仅在类被实例化成对象后可通过对象访问,存储在堆中;后者属于局部变量,随着方法的运行一并被压入栈中,仅由当前线程使用,不共享,且不具有初始值,使用时必须初始化。
5-7.2 static
修饰方法
static
关键字还可以用于修饰方法,使其变为静态方法。还是上述同样的例子:
public class Test {
public static void run() {
//方法实现
}
public void go() {
//方法实现
}
public static void main(String args[]) {
Test.run(); //静态方法调用
Test obj = new Test();
obj.go(); //非静态方法调用
}
}
同样地,静态方法随着类的加载而加载,隶属于类,可直接通过类名调用该方法,也可以通过对象调用该方法。若主方法也在该类中,还可以直接调用该方法而无需类名:
run();
且值得注意的是,静态方法进可以调用静态成员,不可以调用非静态成员。但非静态成员二者皆可调用。
并且,main(String args[])
方法也是一个静态方法,若想在其中调用非静态成员,则必须先实例化才可调用。
5-7.3 静态代码块
代码还可以放在一个语句块当中,如:
public class Test {
{
//匿名代码块(不推荐)
Sout.println("匿名代码块");
}
static {
//静态代码块
Sout.println("静态代码块");
}
public Test() {
Sout.println("构造器");
}
public static void main(String args[]) {
Test objA = new Test();
Sout.println("============");
Test objB = new Test();
}
}
代码可以放在一个代码块之中。在类中,匿名代码块、静态代码块和构造器的执行顺序和执行次数稍有不同。运行上述程序,得到结果:
静态代码块
匿名代码块
构造器
============
匿名代码块
构造器
程序执行时,类最先被加载,静态代码块随类的加载而加载并被执行,且只执行一次。而匿名代码块和构造器会随着对象的创建而执行,且有顺序:
静态代码块 -> 匿名代码块 -> 构造器
鉴于匿名代码块优先于构造器执行的性质,且在创建对象时执行,可以在匿名代码块中先对对象赋予初始值。
使用匿名代码块还可以用于限定局部变量的生命周期。匿名代码块中所声明的变量在该代码块执行完毕后立即被释放。这样可提高内存使用率。如:
public class Test {
int x = 10; //在匿名代码块外的局部变量
{
int y = 20;
System.out.println(x);
Sysyem.out.println(y);
}
System.out.println(x);
System.out.println(y); //报错,匿名代码块已经执行完毕,代码块内部局部变量被释放
}
不同于 C++,Java 不允许在匿名代码块中声明一个与代码块外重名的局部变量。即下列情况是不允许的:
public class Test {
public void main(String args[]) {
int x = 10; //匿名代码块外的局部变量
{
int x = 20; //匿名代码块内的重名变量 --->> 错误:作用域中已定义变量
int y = 30; //匿名代码块内的局部变量
}
}
}
若尝试编译,则会报错(不通过),原因为:
5-7.4 静态导入包
已知 java.lang.Math
包下含有静态方法 random();
,用于返回伪随机数。但每次调用时,需要语句:
Math.random();
但倘若可以省略类名,直接调用该类下的这个方法呢?这是我们则需要导入包:
import java.lang.Math.random; //直接导入该包下的某个方法
但发现会报错:
但此时,将语句修改为下述语句即可:
import static java.lang.Math.radom;
同样地,我们还可以通过静态导入该包下的常量:
import static java.lang.Math.PI;
我们还可以使用通配符静态导入该包下所有静态成员:
import static java.lang.Math.*;
但是,使用静态导入时,应当注意:
- 静态导入仅适用于导入静态成员;
- 当程序中大量调用某一包中的静态成员时,可用静态导入减少击键;
- 使用通配符可以导入包下所有静态成员;
- 语法必须为
import static
; - 提防成员二义性。若所导入的不同包中有同名静态成员,将会导致编译错误;