Java static keyword
Java static keyword
静态变量
静态变量简介
在类中创建变量的使用static关键字修饰,那么这个变量就是静态变量。
静态变量的特点:
- 静态变量属于类,而不属于类实例对象。
- 静态变量只在类加载的时候获取一次内存空间,这使得静态变量很节省空间;
- 静态变量可以通过类名直接访问,也可以通过类实例对象访问。
- 静态变量可以直接赋值,也可以在静态代码块中赋值。
- 静态变量一旦赋值后,就可以在类的任意方法中访问。
使用静态静态变量的好处就是可以节省内存空间,假设某大学录取了一万名新生,那么在创建一万个 Student 对象的时候,
所有的字段(name、age 和 school)都会获取到一块内存。学生的姓名和年纪不尽相同,但都属于这个大学,
如果每创建一个对象,school 这个字段都要占用一块内存的话,就很浪费,因此,最好将 school 这个字段设置为 static,这样就只会占用一块内存,而不是一万块。
package com.basic.keywords.static_;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Student {
private int age;
private String name;
private static String school = "加里敦大学";
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getSchool() {
return this.school;
}
@Override
public String toString() {
return String.format("Student(age=%s,name=%s,school=%s)", this.age, this.name, this.school);
}
public static void main(String[] args) {
Student s1 = new Student("Tom", 18);
Student s2 = new Student("Jory", 19);
System.out.println(String.format("%s\n%s", s1.toString(), s2.toString()));
}
}
注意
- lombook的@Getter、@Setter、@ToString、@Data等注解,会忽略静态变量,如果需要使用静态变量,需要单独编写get方法;
静态变量在jvm中的存储位置如下图所示:

s1和s2两个引用变量存放在栈中(stack),s1和s2指向的对象存放在堆中(heap),school变量存放在方法区(静态区)中(method area)。
s1和s2共享同一个school变量,即s1和s2的school变量指向同一个内存地址。
静态变量的特性
由于静态变量只会获取一次内存空间,所以任何对象对它的修改都会得到保留。
举例说明:
类中定义一个静态变量,然后创建三个对象,分别修改静态变量的值,然后打印静态变量的值。
package com.basic.keywords.static_;
public class StudentCounter {
private static int count = 0;
public StudentCounter() {
count++;
System.out.println(String.format("count=%s", count));
}
public int getCount() {
return this.count;
}
public static void main(String[] args) {
StudentCounter s1 = new StudentCounter();
StudentCounter s2 = new StudentCounter();
StudentCounter s3 = new StudentCounter();
}
}
运行结果:
count=1
count=2
count=3
由于静态变量只会获取一次内存空间,所以任何对象对它的修改都会得到保留,所以每创建一个对象,count 的值就会加 1,所以最终的结果是 3。这就是静态变量和成员变量之间的差别。
另外,需要注意的是,由于静态变量属于一个类,所以不要通过对象引用来访问,而应该直接通过类名来访问,否则编译器会发出警告。
静态方法
如果方法上加了 static 关键字,那么它就是一个静态方法。
静态方法的特点:
- 静态方法属于类,而不属于类实例对象。
- 调用静态方法的时候不需要创建这个类的对象;
- 静态方法只能访问静态变量和静态方法,不能访问成员变量和成员方法。
实例代码如下:
package com.basic.keywords.static_;
import lombok.*;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class staticMethodStudent {
private String name;
private int age;
private static String school = "加里敦大学";
public static void changeSchool(String newSchool) {
school = newSchool;
}
@Override
public String toString() {
return String.format("Student(age=%s,name=%s,school=%s)",this.age,this.name,this.school);
}
public static void main(String[] args) {
staticMethodStudent s1 = new staticMethodStudent("Tom", 20);
System.out.println(String.format("s1: %s",s1));
staticMethodStudent.changeSchool("社会大学");
staticMethodStudent s2 = new staticMethodStudent("Jory", 20);
System.out.println(String.format("s2: %s",s2));
}
}
运行结果:
s1: Student(age=20,name=Tom,school=加里敦大学)
s2: Student(age=20,name=Jory,school=社会大学)
从运行结果可以看出,s2 的 school 都是社会大学,这是因为静态方法 changeSchool 是属于类的,另外,需要注意的是,由于静态方法属于一个类,所以不要通过对象引用来访问,而应该直接通过类名来访问,否则编译器会发出警告。
另外,main 方法也是静态方法,所以可以直接通过类名来访问。如果 main 方法不是静态的,就意味着 Java 虚拟机在执行的时候需要先创建一个对象才能调用 main 方法,而 main 方法作为程序的入口,创建一个额外的对象显得非常多余。
静态代码块
用一个 static 关键字,外加一个大括号括起来的代码被称为静态代码块。
静态代码块是在类加载的时候执行的,并且只会执行一次。
静态代码块的作用是:
- 初始化静态变量
- 执行一些静态的初始化操作
实例代码如下:
package com.basic.keywords.static_;
public class staticBlock {
static {
System.out.println("static block");
}
public static void main(String[] args) {
System.out.println("main 方法");
}
}
运行结果:
static block
main 方法
从运行结果可以看出,静态代码块是在类加载的时候执行的,并且只会执行一次。
静态代码块通常用来初始化一些静态变量,它会优先于 main() 方法执行。
静态代码块适合在无法在声明静态变量时完成初始化操作,比如List、Map等。在实际的项目开发中,通常使用静态代码块来加载配置文件到内存当中。
静态代码块的执行顺序是:
- 静态代码块
- 构造代码块
- 构造函数
实例代码如下:
package com.basic.keywords.static_;
import java.util.ArrayList;
import java.util.List;
public class staticBlock {
public static List<String> writes = new ArrayList<>();
public staticBlock(){}
public List<String> getWrites(){
return this.writes;
}
static {
writes.add("Tom");
writes.add("Jory");
writes.add("Suxin");
System.out.println("第一块");
}
static {
writes.add("Tom2");
writes.add("Jory2");
writes.add("Suxin2");
System.out.println("第二块");
}
public static void main(String[] args) {
staticBlock s1 = new staticBlock();
System.out.println(s1.getWrites());
}
}
执行结果:
第一块
第二块
构造函数
[Tom, Jory, Suxin, Tom2, Jory2, Suxin2]
静态内部类
除了以上,static 还有一个不太常用的功能——静态内部类。静态内部类是指在一个类中定义一个静态的内部类,这个内部类可以直接访问外部类的静态成员,但是不能访问外部类的非静态成员。
静态内部类的特点:
- 静态内部类不能访问外部类的所有成员变量;
- 静态内部类可以访问外部类的所有静态变量,包括私有静态变量。
- 外部类不能声明为 static。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
第一次加载 Singleton 类时并不会初始化 instance,只有第一次调用 getInstance() 方法时 Java 虚拟机才开始加载 SingletonHolder 并初始化 instance,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。
静态内部类练习案例,访问静态内部类的静态变量不会触发构造函数的执行。
public class staticClass {
private staticClass(){
System.out.println("staticClass constructor");
}
private static staticClass getInstance(){
return staticClassInner.instance;
}
public static class staticClassInner{
public static final staticClass instance = new staticClass();
public staticClassInner(){
System.out.println("staticClassInner constructor");
}
}
public static void main(String[] args) {
staticClass staticClass = new staticClass();
staticClass.getInstance();
}
}
运行结果:
staticClass constructor
staticClass constructor
从运行结果可以看出,静态内部类的静态变量不会触发构造函数的执行。
getInstance()方法返回的是staticClassInner.instance(一个static final字段)。
staticClassInner.instance是一个静态字段,它的值是在类加载时初始化的。 因此,staticClassInner.instance的值是在类加载时初始化的,而不是在getInstance()方法被调用时初始化的。
根据Java类加载的规则,静态内部类是一个独立的类,它会在第一次被主动使用时加载。主动使用包括:
- 创建类的实例
- 访问类的静态变量(非final)或静态方法
注意:在Java中,如果一个静态变量是final的,并且它的值是编译时常量,那么它在编译时就会被内联,不会导致类的初始化。但是,如果静态变量是final的,但它的值不是编译时常量(比如通过new创建对象),那么访问这个静态变量会导致类的初始化。
在这个例子中,public static final staticClass instance = new staticClass(); 这个静态变量是final的,但它的值是通过new来创建的,所以它属于运行时常量。因此,在第一次访问这个静态变量时,会触发静态内部类staticClassInner的初始化。
然而,在getInstance()方法中,我们访问了staticClassInner.instance,这会导致静态内部类staticClassInner被加载和初始化。在类初始化的过程中,会执行静态变量的赋值,即new staticClass(),所以会再次调用staticClass的构造方法(输出"staticClass constructor")。但是,静态内部类staticClassInner的构造方法并没有被调用,因为我们没有创建它的实例。
静态内部类的构造函数仅在显式创建内部类对象(使用new)时执行。访问其静态字段或方法只会初始化类(处理静态成员),不会实例化对象,因此构造函数不会运行。

浙公网安备 33010602011771号