写出相应的输出,同时完成所要求的程序的书写。
class Example3
{
public int x; //非静态变量
public static int y; //静态变量
void method() //非静态方法
{
x = 1; //正确,非静态方法可以访问非静态成员
y = 1; //正确,非静态方法可以访问静态成员
System.out.println("实例方法访问:x="+x+" y="+y);
}
static void smethod() //静态方法
{
//x = 3; 错误,静态方法不能非静态成员
y = 3; //正确,静态方法可以访问静态成员
System.out.println("静态方法访问:y="+y);
}
public static void main(String[] args)
{
Example3 prog3 = new Example3();//生成类的实例
prog3.method(); //非静态方法通过实例来调用
Example3.smethod(); //静态方法通过类名来调用
}
}
创建一个名称为StaticDemo的类 并声明一个静态变量和一个普通变量。对变量分别赋予10和5的初始值。在main()方法中输出变量值。
创建类,然后定义变量,再输出即可,只不过访问静态成员变量和普通成员变量的方式有点不同。
代码如下:
public class StaticDemo {
static int staticVar ;
int normalVar ;
public static void main(String[] args) {
// TODO Auto-generated method stub
Example3 prog3 = new Example3();//生成类的实例
prog3.method(); //非静态方法通过实例来调用
Example3.smethod(); //静态方法通过类名来调用
StaticDemo sd = new StaticDemo();
StaticDemo.staticVar = 10; // 类的静态变量,可以直接通过 类名.静态成员名 来访问
sd.normalVar = 5; // 类的普通变量,需要先创建对象,然后再通过 对象名.非静态成员名 来访问
System.out.println("静态变量的值为:"+sd.staticVar); // 类的静态变量,也可以通过 对象名.静态成员名 来访问
System.out.println("非静态变量的值为:"+sd.normalVar);
}
}
class Example3
{
public int x; //非静态变量
public static int y; //静态变量
void method() //非静态方法
{
x = 1; //正确,非静态方法可以访问非静态成员
y = 1; //正确,非静态方法可以访问静态成员
System.out.println("实例方法访问:x="+x+" y="+y);
}
static void smethod() //静态方法
{
//x = 3; 错误,静态方法不能非静态成员
y = 3; //正确,静态方法可以访问静态成员
System.out.println("静态方法访问:y="+y);
}
}
一般程序把新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。
看下面的例子:
//example: #include <stdio.h> #include <stdlib.h> int k1 = 1; int k2; static int k3 = 2; static int k4; int main() { static int m1 = 2, m2; int i = 1; char*p; char str[10] = "hello"; char*q = "hello"; p = (char *)malloc(100); free(p); printf("栈区-变量地址 i:%p\n", &i); printf("栈区-变量地址 p:%p\n", &p); printf("栈区-变量地址 str:%p\n", str); printf("栈区-变量地址 q:%p\n", &q); printf("堆区地址-动态申请:%p\n", p); printf("全局外部有初值 k1:%p\n", &k1); printf(" 外部无初值 k2:%p\n", &k2); printf("静态外部有初值 k3:%p\n", &k3); printf(" 外静无初值 k4:%p\n", &k4); printf(" 内静态有初值 m1:%p\n", &m1); printf(" 内静态无初值 m2:%p\n", &m2); printf(" 文字常量地址:%p, %s\n", q, q); printf(" 程序区地址:%p\n", &main); return 0; }
输出结果如下:
static 关键字最基本的用法是:
被 static 修饰的变量、被 static 修饰的方法统一属于类的静态资源,是类实例之间共享的,换言之,一处变、处处变。
在 C++ 中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用。所以在所有对象中都可以共享它。使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。
静态成员的定义或声明要加个关键 static。静态成员可以通过双冒号来使用即 <类名>::<静态成员名>。
通过类名调用静态成员函数和非静态成员函数:
class Point { public: void init() { } static void output() { } }; void main() { Point::init(); Point::output(); }
报错:
'Point::init' : illegal call of non-static member function
结论 1:不能通过类名来调用类的非静态成员函数。
通过类的对象调用静态成员函数和非静态成员函数。
class Point { public: void init() { } static void output() { } }; void main() { Point pt; pt.init(); pt.output(); }
编译通过。
结论 2:类的对象可以使用静态成员函数和非静态成员函数。
在类的静态成员函数中使用类的非静态成员。
#include <stdio.h> class Point { public: void init() { } static void output() { printf("%d\n", m_x); } private: int m_x; }; void main() { Point pt; pt.output(); }
编译出错:
error C2597: illegal reference to data member 'Point::m_x' in a static member function
因为静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,所以这个调用就出错了,就好比没有声明一个变量却提前使用它一样。
结论3:静态成员函数中不能引用非静态成员。
在类的非静态成员函数中使用类的静态成员。
class Point { public: void init() { output(); } static void output() { } }; void main() { Point pt; Pt.init(); pt.output(); }
编译通过。
结论 4:类的非静态成员函数可以调用用静态成员函数,但反之不能。
使用类的静态成员变量。
#include <stdio.h> class Point { public: Point() { m_nPointCount++; } ~Point() { m_nPointCount--; } static void output() { printf("%d\n", m_nPointCount); } private: static int m_nPointCount; }; void main() { Point pt; pt.output(); }
按 Ctrl+F7 编译无错误,按 F7 生成 EXE 程序时报链接错误。
error LNK2001: unresolved external symbol "private: static int Point::m_nPointCount" (?m_nPointCount@Point@@0HA)
这是因为类的静态成员变量在使用前必须先初始化。
在 main() 函数前加上 int Point::m_nPointCount = 0; 再编译链接无错误,运行程序将输出 1。
结论 5:类的静态成员变量必须先初始化再使用。
思考总结:静态资源属于类,但是是独立于类存在的。从 J 类的加载机制的角度讲,静态资源是类初始化的时候加载的,而非静态资源是类实例化对象的时候加载的。 类的初始化早于类实例化对象,比如 Class.forName("xxx") 方法,就是初始化了一个类,但是并没有实例化对象,只是加载这个类的静态资源罢 了。所以对于静态资源来说,它是不可能知道一个类中有哪些非静态资源的;但是对于非静态资源来说就不一样了,由于它是实例化对象出来之后产生的,因此属于类的这些东西它都能认识。所以上面的几个问题答案就很明确了:
(static 修饰类:这个用得相对比前面的用法少多了,static 一般情况下来说是不可以修饰类的, 如果 static 要修饰一个类,说明这个类是一个静态内部类(注意 static 只能修饰一个内部类),也就是匿名内部类。像线程池 ThreadPoolExecutor 中的四种拒绝机制 CallerRunsPolicy、AbortPolicy、DiscardPolicy、 DiscardOldestPolicy 就是静态内部类。静态内部类相关内容会在写内部类的时候专门讲到。)
一般总结:在类中,static 可以用来修饰静态数据成员和静态成员方法。
静态数据成员
静态成员函数
再给一个利用类的静态成员变量和函数的例子以加深理解,这个例子建立一个学生类,每个学生类的对象将组成一个双向链表,用一个静态成员变量记录这个双向链表的表头,一个静态成员函数输出这个双向链表。