到目前为止我们在Java程序的入口 - main方法多次使用到static关键字,它有什么作用呢?
public static void main(String[] args) {
}
Java中的static可以修饰类的成员变量、方法和一个代码块。分别称为静态变量、静态方法和静态代码块。它有以下几个特点:
- 在内存里不会和普通的变量和方法存在一块
- 在类首次被使用时就在内存分配空间和初始化
- 静态代码块在类首次使用时执行,只执行这一次且在构造代码块之前执行。
- 只属于类,可以被所有对象共享但不属于任何对象。
- 静态变量和方法既可以通过对象访问也可以用类名直接访问。
- 普通方法可以使用静态变量和静态方法,但是静态代码块里使用的变量和方法必须是静态的。
执行顺序问题##
我建一个StaticDemo,类里普通成员变量、静态变量、普通方法、静态方法、构造方法、构造代码块、静态代码块都有。
public class StaticDemo {
public static String ss = "静态变量";
public String s = "普通变量";
static {
System.out.println("静态代码块");
}
public static void sMethod() {
System.out.println("静态方法");
}
{
System.out.println("构造代码块");
}
public StaticDemo() {
System.out.println("构造方法");
}
public void method() {
System.out.println("普通方法");
}
}
再写个类测试一下:
public class Test {
public static void main(String[] args) {
System.out.println("main");
StaticDemo sd = new StaticDemo();
}
}
运行结果:
<pre>
main
静态代码块
构造代码块
构造方法
</pre>
可以看到在main方法里执行<code>System.out.println("main");</code>时,StaticDemo还没有“第一次使用”。然后<code>new StaticDemo();</code>是第一次使用会先调用静态代码块,然后执行构造代码块,最后执行构造方法。
所以执行顺序是:静态代码块 - 构造代码块 - 构造方法。
静态代码块和静态方法里的变量和方法必须也是静态的##
做个测试,我们在静态代码块和静态方法里使用非静态的变量和方法:
static {
System.out.println("静态代码块");
System.out.println(s); // 静态代码块不能使用非静态变量
method(); //也不能使用非静态方法
}
public static void sMethod() {
System.out.println("静态方法");
System.out.println(s); // 静态方法块不能使用非静态变量
method(); //也不能使用非静态方法
}
运行结果:
<pre>
StaticDemo.java:8: 错误: 无法从静态上下文中引用非静态 变量 s
System.out.println(s); // 静态代码块不能使用非静态变量
^
StaticDemo.java:9: 错误: 无法从静态上下文中引用非静态 方法 method()
method(); //也不能使用非静态方法
^
StaticDemo.java:14: 错误: 无法从静态上下文中引用非静态 变量 s
System.out.println(s); // 静态方法块不能使用非静态变量
^
StaticDemo.java:15: 错误: 无法从静态上下文中引用非静态 方法 method()
method(); //也不能使用非静态方法
^
4 个错误
</pre>
很显然,静态区域里使用非静态的东西是不允许的,但是非静态区域里使用静态的东西是可以的。像以下这样不会报错:
public void method() {
System.out.println("普通方法");
System.out.println(ss); // 非静态方法可以使用静态变量
sMethod(); //也可以使用静态方法
}
通过对象名调用静态变量和方法##
用程序来模拟一个简单的篮球比赛,首先有个比赛类,设计得简单点,有队伍1和队伍2得分属性,初始化分数、打印分数、重置分数这几个方法:
public class BasketballGame {
public static int score1; //队伍1的分数
public static int score2; //队伍2的分数
//初始化分数
static {
score1 = 0;
score2 = 0;
System.out.println("初始化完毕");
}
//打印分数
public static void printScore() {
System.out.println("队伍1的得分 --> " + score1);
System.out.println("队伍2的得分 --> " + score2);
}
//重置分数
public static void resetScore() {
score1 = 0;
score2 = 0;
System.out.println("分数重置完毕");
}
}
接着有个篮球队伍类,也很简单,就是有队伍编号和能得分:
public class BacketballTeam {
public int teamNo; //队伍编号
public BacketballTeam(int teamNo) {
this.teamNo = teamNo;
}
public void score() {
//根据队伍编号得分
if(teamNo == 1) {
BasketballGame.score1++;
} else {
BasketballGame.score2++;
}
}
}
测试一下:
//创建两个篮球队
BacketballTeam team1 = new BacketballTeam(1);
BacketballTeam team2 = new BacketballTeam(2);
//得分
team1.score();
team1.score();
team2.score();
team2.score();
//打印分数
BasketballGame.printScore();
//重置分数
BasketballGame.resetScore();
//再来一局
team1.score();
team2.score();
team2.score();
team2.score();
//打印分数
BasketballGame.printScore();
运行结果:
<pre>
初始化完毕
队伍1的得分 --> 2
队伍2的得分 --> 2
分数重置完毕
队伍1的得分 --> 1
队伍2的得分 --> 3
</pre>
例子逻辑很简单,然后我们从中可以看到几个static的特性:
- 从<code>BasketballGame.score1++</code>可以看到可以通过类名直接访问静态变量。
- 从<code>BasketballGame.printScore();</code>和<code>BasketballGame.resetScore();</code>可以看到可以通过类名直接访问静态方法。
因此静态的内容是属于类而不是属于任何一个对象,或者也可以说成静态的内容是共享的。
另外从上面的例子中可以看到静态代码块通常用于对静态变量初始化,因为静态代码块在第一次使用类的时候就会被执行。例如:
//初始化分数
static {
score1 = 0;
score2 = 0;
System.out.println("初始化完毕");
}
在上面的示例中什么时候是第一次使用类呢?
就是BacketballTeam里的
public void score() {
//根据队伍编号得分
if(teamNo == 1) {
BasketballGame.score1++;
} else {
BasketballGame.score2++;
}
}
<code>BasketballGame.score1++;</code>和<code>BasketballGame.score2++;</code>都可以触发静态代码块。并不一定需要new新对象才调用。
你也许有个疑问,构造代码块也可以啊做初始化啊。反正构造代码也可以访问静态变量。但你知道,构造代码块要new新对象时才会使用。像<code>BasketballGame.score1++;</code>根本没new新对象不会触发构造代码块但是会触发静态代码块。
本文代码下载:百度网盘
网友评论