静态变量
静态变量也称为类变量,类所有的实例都共享静态变量,可以直接通过类名来访问它,静态变量在内存中只存在一份。
在并发或后端接口的开发中,需要注意静态变量对于所有线程或用户都是共享的,可能会导致线程安全问题或互相影响,实现单个线程或用户拥有独立的静态变量,应使用 ThreadLocal
实现。
静态方法
静态方法是属于类的方法,不依赖于类的具体实例,而是直接通过类名来调用。静态方法在内存中只存在一份,无论类有多少个对象实例,它们共享相同的静态方法。由于静态方法不依赖于实例状态,通常用做定义工具函数。
首先回顾一下面向对象中的两个概念:
- 多态性:指同一方法调用可以根据调用的对象不同而表现出不同的行为。
- 重写:指子类在继承父类的方法时,对该方法进行重新定义,调用该方法时将执行子类的实现而不是父类的实现。【重写的本质是为了实现多态性。】
静态方法属于类而不属于对象实例,所以在继承关系中不会表现出多态性,于是我们可以容易得到:
- 静态方法不能被子类重写,因为重写的本质是多态性,而静态方法不支持多态性,所以静态方法不能被子类重写。
- 静态方法不能是抽象方法,因为其不能被继承,所以必须实现。
如下代码可以看到这里并没有实现重写,只是覆盖了父类的方法, childRef.staticMethod();
调用的依然是父类的防范。不过之类通过对象去调用静态方法是一个不建议的行为。
class Parent {
public static void staticMethod() {
System.out.println("Parent's static method");
}
}
class Child extends Parent {
public static void staticMethod() {
System.out.println("Child's static method");
}
}
public class StaticMethodDemo {
public static void main(String[] args) {
Parent.staticMethod(); // Output: Parent's static method
Child.staticMethod(); // Output: Child's static method
Parent parentRef = new Parent();
Parent childRef = new Child();
parentRef.staticMethod(); // Output: Parent's static method
childRef.staticMethod(); // Output: Parent's static method
}
}
静态语句块
静态代码块是用于在类加载时执行的代码块,它在类的生命周期中只会执行一次。静态代码块通常用于进行一次性的初始化操作,例如加载配置文件、初始化静态变量等。
public class AppConfig {
// 静态变量
public static int staticVar1 = 10;
// 静态变量和静态代码量会按照它们在代码中的顺序进行初始化。
static {
// 例如加载配置文件,初始化静态变量等
}
// 静态变量
public static int staticVar2 = 10;
}
静态内部类
静态内部类(也称为嵌套静态类),它不依赖于外部类的实例,因此可以直接创建静态内部类的对象,无需先创建外部类的实例,理所当然的也就无法访问外部类的非静态的成员变量和方法。
public class OuterClass {
// 外部类的成员和方法
// 静态内部类
public static class InnerStaticClass {
// 静态内部类的成员和方法
}
}
// 创建静态内部类的实例
OuterClass.InnerStaticClass inner = new OuterClass.InnerStaticClass();
加载顺序
它们都是在类加载时完成初始化的,也就是说,在类被首次使用之前,它们会被初始化。在 Java 中,在类加载和实例化过程中,遵循以下顺序:
- 父类(静态变量和静态语句块):首先,会初始化父类的静态变量和执行父类的静态语句块,按照它们在代码中的顺序进行初始化。
- 子类(静态变量和静态语句块):接着,会初始化子类的静态变量和执行子类的静态语句块,也是按照它们在代码中的顺序进行初始化。
- 父类(实例变量和普通语句块):在创建子类实例之前,会先执行父类的实例变量初始化和普通语句块。
- 父类(构造函数):然后调用父类的构造函数,完成父类实例的初始化。
- 子类(实例变量和普通语句块):接着,会执行子类的实例变量初始化和普通语句块。
- 子类(构造函数):最后调用子类的构造函数,完成子类实例的初始化。