C/C++/Java static 对比
之前对static的作用一直是一知半解,对于静态没有很好的理解,面试的时候问到static的作用也没有答好,于是特意学习了一下并记录下来,希望对其他困惑于此的同学有所帮助。
C
C的static
有三种用途。C++的static
用法包括了C的,因此先说C。
1. 静态局部变量
于函数内部修饰变量。
首先回顾一下C/C++的内存分配:
- 全局数据区 (data area):存放全局变量,静态数据和常量
- 代码区(code area):存放代码
- 栈 (stack):存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等
- 堆 (heap):余下的空间都是堆,一般手动分配,如malloc、new等
静态局部变量就存放于全局数据区。
程序第一次运行到该变量的声明处时初始化,之后即使再调用该函数也不会被再初始化。
如果声明处没有显式初始化,它将被初始化为0。(一般的局部变量在声明时不会强制初始化)
它始终保留在全局数据区直到程序结束,而局部变量在栈区,函数结束后就会释放占用的内存。
与全局变量的区别:全局变量不属于函数,不利于代码维护。虽然静态局部变量在全局数据区,但它只能被它所在的函数调用。
2. 静态全局变量
定义在函数体外,用于修饰全局变量,表示该变量仅在本文件可见。
如果不使用static
修饰全局变量,其他文件可以使用extern
获取该值。其他文件也可以定义相同的变量名而不冲突。
它在这里起到了文件隔离的作用。
3. 静态函数
静态函数与静态全局变量类似,静态函数不能被其他文件所用。其它文件可以定义相同名字的函数,不会冲突。
C++
除了以上三种,C++还有额外的两种用法:
1. 静态数据成员
用于修饰类 (class)的数据成员,这些成员叫静态成员。每个类有一份静态成员数据,而普通数据是每个实例 (instance)都有。因此静态数据成员也叫类变量,普通数据成员也叫实例变量。
静态数据被分配到全局数据区中,即使创建的类对象全部被摧毁,这些数据依然保留。与静态局部变量类似,静态数据成员也只被初始化一次。静态数据成员定义时要分配空间,所以不能在类声明中定义。
2. 静态成员函数
在类函数定义前加上static
使它变成一个静态函数成员,可以用类名::函数名
进行访问。
特点:
- 静态成员之间可以互相访问,包括静态成员函数访问静态数据成员和静态函数访问静态函数。
- 非静态成员函数可以任意访问静态成员函数和静态数据成员。
- 反过来不行,即静态成员函数不能访问非静态成员函数与非静态数据成员。
- 调用静态成员函数可以使用成员访问操作符(.)/(->),也可以用类名::函数名
Java
Java中的static
与C/C++十分相似,但叫法不同。
1. 静态字段(类字段)
被标记为static
的字段,每个类只持有一个。
例如:
class Employee
{
private static int nextID = 1;
private int id;
...
}
每个Employee对象都有一个id,但所有的Employee对象共享一个nextID字段。
注意:即使没有Employee对象,静态字段也存在。它属于类,不属于任何一个实例。
2. 静态常量
静态常量使你不需要通过实例就可以访问到类中的常量。例如:
public class Math
{
...
public static final double PI = 3.1415926;
...
}
在程序中可以使用Math.PI来访问该常量。如果没有static,PI就成了Math的实例字段,每个实例都持有一个PI副本,必须通过实例访问PI。这显然不是我们想要的。
3. 静态方法
与C++一样,静态方法指不在对象上执行的方法。如Math类的pow方法。静态方法是没有this参数的方法。
以下两种情况可以使用静态方法:
- 方法不需要访问对象状态,因为它需要的所有参数都通过显示参数提供
- 方法只需要访问类的静态字段
参考文章: C/C++中 static关键字.原博有代码辅助理解
Java参考的Core Java Volume I