Java——static关键字

Java——static关键字

概述:在面向对象编程中,static既可以用来修饰的成员变量成员方法,也可以用来修饰的代码块,被修饰的内容是属于类的,而不是单单是属于某一个对象,所以就可以直接通过类本身来调用。

静态成员

概述:简单来说静态内容成员就是由关键字static修饰的成员,包含静态变量静态方法静态代码块三部分。
静态成员有如下性质:

  1. 是随着类的加载而加载的,且只加载一次。
  2. 存储于一块固定的内存区域(静态区),所以可以直接被类名调用。
  3. 它优先于对象存在,所以可以被所有对象共享。

接下来对静态变量静态方法静态代码块三部分进行解释。

静态变量

概述:当static修饰成员变量时,该变量称为静态变量。该类的每个对象都共享同一个静态变量的值,任何对象都可以修改静态变量的值,也可以直接通过类本身对静态变量进行操作。
演示案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public 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
22
public 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
19
public 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
15
public 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
31
public 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
2
3
4
5
6
7
8
9
10
11
这是父类的静态代码块
这是子类的静态代码块
这是父类的普通代码块
这是父类的构造方法
这是子类的普通代码块
这是子类的构造方法
-------------
这是父类的普通代码块
这是父类的构造方法
这是子类的普通代码块
这是子类的构造方法

由结果不难发现当第二次创建子类对象时静态代码块已不再运行,即只执行一次。