Java——static关键字
概述:在面向对象编程中,static
既可以用来修饰的成员变量和成员方法,也可以用来修饰的代码块,被修饰的内容是属于类的,而不是单单是属于某一个对象,所以就可以直接通过类本身来调用。
静态成员
概述:简单来说静态内容成员就是由关键字static
修饰的成员,包含静态变量、静态方法、静态代码块三部分。
静态成员有如下性质:
- 是随着类的加载而加载的,且只加载一次。
- 存储于一块固定的内存区域(静态区),所以可以直接被类名调用。
- 它优先于对象存在,所以可以被所有对象共享。
接下来对静态变量、静态方法、静态代码块三部分进行解释。
静态变量
概述:当static
修饰成员变量时,该变量称为静态变量。该类的每个对象都共享同一个静态变量的值,任何对象都可以修改静态变量的值,也可以直接通过类本身对静态变量进行操作。
演示案例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class StaticVar {
static int num = 100;
}
public class Main {
public static void main(String[] args) {
System.out.println("通过类的对象调用静态变量:" + new StaticVar().num);
System.out.println("通过类本身调用静态变量:" + StaticVar.num);
new StaticVar().num += 5;
System.out.println("通过对象修改静态变量之后");
System.out.println("通过类的对象调用静态变量:" + new StaticVar().num);
System.out.println("通过类本身调用静态变量:" + StaticVar.num);
StaticVar.num += 5;
System.out.println("通过类本身修改静态变量之后");
System.out.println("通过类的对象调用静态变量:" + new StaticVar().num);
System.out.println("通过类本身调用静态变量:" + StaticVar.num);
}
}
运行结果如下:1
2
3
4
5
6
7
8通过类的对象调用静态变量:100
通过类本身调用静态变量:100
通过对象修改静态变量之后
通过类的对象调用静态变量:105
通过类本身调用静态变量:105
通过类本身修改静态变量之后
通过类的对象调用静态变量:110
通过类本身调用静态变量:110
静态方法
概述:当static
修饰成员方法时,该方法称为静态方法 。静态方法在声明中有static
,可以通过类的对象来调用静态方法,也可直接通过类来调用,建议通过类直接访问。(注:静态方法中只能访问类的静态成员!)
演示案例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22public class StaticMethod {
int num = 10;
static int staticNum = 10;
static void print() {
System.out.println("调用静态方法!");
}
static void printNum() {
//错误,不可调用非静态成员
//System.out.println(num);
//错误,不能使用this关键字
//System.out.println(this.staticNum);
}
}
public class Main {
public static void main(String[] args) {
System.out.print("通过类的对象调用静态方法:");
new StaticMethod().print();
System.out.print("通过类本身调用静态方法:");
StaticMethod.print();
}
}
运行结果如下:1
2通过类的对象调用静态方法:调用静态方法!
通过类本身调用静态方法:调用静态方法!
静态代码块
概述:位于类内、类方法之外的由关键字static
修饰的代码块,其随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
执行顺序:在面向对象编程中,代码块的执行顺序为:静态代码块→非静态代码块→构造函数。
演示案例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class StaticCode {
public StaticCode() {
System.out.println("这是构造方法");
}
static {
System.out.println("这是静态代码块");
}
{
System.out.println("这是普通代码块");
}
}
public class Main {
public static void main(String[] args) {
new StaticCode();
System.out.println("-------------");
new StaticCode();
}
}
运行结果如下:1
2
3
4
5
6这是静态代码块 //只执行一次,且优先级最高
这是普通代码块
这是构造方法
-------------
这是普通代码块
这是构造方法
扩展:如果将main函数直接写在静态代码块所在类中,则执行顺序为:静态代码块→main函数→非静态代码块→构造函数。
案例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class StaticCode {
public StaticCode() {
System.out.println("这是构造方法");
}
static {
System.out.println("这是静态代码块");
}
{
System.out.println("这是普通代码块");
}
public static void main(String[] args) {
System.out.println("这里是main函数代码块");
//new StaticCode();
}
}
运行结果如下:
main函数中不创建对象时,运行结果:1
2这是静态代码块
这里是main函数代码块
创建对象时,运行结果:1
2
3
4这是静态代码块
这里是main函数代码块
这是普通代码块
这是构造方法
注:静态部分代码只在对象创建时调用,而非静态代码块在每次创建对象时都会运行。
扩展(结合继承关系,代码块的执行顺序)
具体执行顺序为:父类静态代码块→子类静态代码块→父类非静态代码块→父类构造方法→子类非静态代码块→子类构造方法,静态代码块只执行一次。
直接上代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public class Fa { //父类
public Fa() {
System.out.println("这是父类的构造方法");
}
static {
System.out.println("这是父类的静态代码块");
}
{
System.out.println("这是父类的普通代码块");
}
}
public class Zi extends Fa{ //子类
public Zi() {
System.out.println("这是子类的构造方法");
}
static {
System.out.println("这是子类的静态代码块");
}
{
System.out.println("这是子类的普通代码块");
}
}
public class Main {
public static void main(String[] args) {
new Zi();
System.out.println("-------------");
new Zi();
}
}
运行结果如下:
1 | 这是父类的静态代码块 |
由结果不难发现当第二次创建子类对象时静态代码块已不再运行,即只执行一次。