Java 中的static关键字使用指南-Java快速入门教程
1. 概述
在本教程中,我们将详细探讨 Java 语言的 static 关键字。
我们将了解如何将 static 关键字应用于变量、方法、块和嵌套类,以及它有什么不同。
2. static关键字剖析
在 Java 编程语言中,关键字 static 意味着特定成员属于类型本身,而不是该类型的实例。
这意味着我们将只创建该static成员的一个实例,该实例在类的所有实例之间共享。
我们可以将static关键字应用于变量、方法、块和嵌套类。
3. static字段(或类变量)
在 Java 中,当我们声明一个字段是static的时,只会创建该字段的单个副本并在该类的所有实例之间共享。
我们实例化一个类多少次并不重要。始终只有一个属于它的static字段副本。此static字段的值在同一类的所有对象之间共享。
从内存的角度来看,static变量存储在堆内存中。
3.1. static字段示例
假设我们有一个具有多个属性(实例变量)的 Car 类。
每当我们从这个 Car 类实例化新对象时,每个新对象都会有其这些实例变量的不同副本。
但是,假设我们想要一个变量来保存实例化 Car 对象数量的计数,并在所有实例之间共享,以便它们可以访问它并在初始化时递增它。
这就是static变量的用武之地:
public class Car {
private String name;
private String engine;
public static int numberOfCars;
public Car(String name, String engine) {
this.name = name;
this.engine = engine;
numberOfCars++;
}
// getters and setters
}
现在,对于我们实例化的此类的每个对象,都会递增 numberOfCars 变量的相同副本。
因此,对于这种情况,这些将是真的:
@Test
public void whenNumberOfCarObjectsInitialized_thenStaticCounterIncreases() {
new Car("Jaguar", "V8");
new Car("Bugatti", "W16");
assertEquals(2, Car.numberOfCars);
}
3.2. 使用static字段的令人信服的理由
以下是我们想要使用static字段的一些原因:
- 当变量的值与对象无关时
- 当值应该在所有对象之间共享时
3.3. 要记住的要点
由于static变量属于一个类,我们可以使用类名直接访问它们。因此,我们不需要任何对象引用。
我们只能在类级别声明static变量。
我们可以在没有对象初始化的情况下访问static字段。
最后,我们可以使用对象引用(例如ford.numberOfCars++)访问static字段。但是我们应该避免这种情况,因为很难弄清楚它是实例变量还是类变量。相反,我们应该始终使用类名(Car.numberOfCars++)引用static变量。
4. static方法(或类方法)
与static字段类似,static方法也属于类而不是对象。因此,我们可以在不创建它们所在的类的对象的情况下调用它们。
4.1. static方法示例
我们通常使用static方法来执行不依赖于实例创建的操作。
为了在该类的所有实例之间共享代码,我们用static方法编写它:
static void setNumberOfCars(int numberOfCars) {
Car.numberOfCars = numberOfCars;
}
我们还通常使用static方法来创建实用程序或帮助程序类,以便我们可以在不创建这些类的新对象的情况下获取它们。
例如,我们可以查看JDK的Collections或Math实用程序类,Apache的StringUtils或Spring框架的CollectionUtils,并注意到它们的所有实用程序都是static的。
4.2. 使用static方法的令人信服的理由
让我们看一下为什么我们要使用static方法的几个原因:
- 访问/操作static变量和其他不依赖于对象的static方法。
- static方法广泛用于实用程序和帮助程序类。
4.3. 要记住的要点
Java 中的static方法在编译时解析。由于方法重写是运行时多态性的一部分,因此无法重写static方法。
抽象方法不能是static的。
static方法不能使用此关键字或超级关键字。
实例、类方法和变量的以下组合有效:
- 实例方法可以直接访问实例方法和实例变量
- 实例方法也可以直接访问static变量和static方法
- static方法可以访问所有static变量和其他static方法
- static方法不能直接访问实例变量和实例方法。它们需要一些对象引用才能执行此操作。
5. static块
我们使用static块来初始化static变量。虽然我们可以在声明期间直接初始化static变量,但在某些情况下我们需要进行多行处理。在这种情况下,static块会派上用场。
如果static变量在初始化期间需要额外的多语句逻辑,我们可以使用static块。
5.1. static块示例
例如,假设我们想用一些预定义的值初始化一个 List 对象。
使用static块,这变得很容易:
public class StaticBlockDemo {
public static List<String> ranks = new LinkedList<>();
static {
ranks.add("Lieutenant");
ranks.add("Captain");
ranks.add("Major");
}
static {
ranks.add("Colonel");
ranks.add("General");
}
}
不可能使用所有初始值和声明初始化 List 对象。所以,这就是我们在这里使用static块的原因。
5.2. 使用static块的令人信服的理由
以下是使用static块的几个原因:
- 如果static变量的初始化除了赋值之外还需要一些额外的逻辑
- 如果static变量的初始化容易出错并且需要异常处理
5.3. 要记住的要点
一个类可以有多个static块。
static字段和static块的解析和运行顺序与它们在类中存在的顺序相同。
6. static类
Java允许我们在类中创建一个类。它提供了一种对元素进行分组的方法,我们只会在一个地方使用。这有助于使我们的代码更有条理和可读性。
通常,嵌套类体系结构分为两种类型:
- 我们声明 static 的嵌套类称为static嵌套类
- 非static的嵌套类称为内部类
这两者之间的主要区别在于,内部类可以访问封闭类的所有成员(包括私有成员),而static嵌套类只能访问外部类的static成员。
事实上,static嵌套类的行为与任何其他顶级类完全相同,但包含在将访问它的唯一类中,以提供更好的打包便利性。
6.1. 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;
}
}
我们使用此方法是因为它不需要任何同步,并且易于学习和实现。
6.2. 使用static内部类的令人信服的理由
让我们看一下在代码中使用static内部类的几个原因:
- 对仅在一个位置使用的类进行分组可增加封装
- 我们使代码更接近唯一使用它的地方。这增加了可读性,并且代码更易于维护。
- 如果嵌套类不需要对其封闭类实例成员的任何访问权限,则最好将其声明为static。这样,它就不会耦合到外部类,因此更优化,因为它们不需要任何堆或堆栈内存。
6.3. 要记住的要点
基本上,static嵌套类无法访问封闭外部类的任何实例成员。它只能通过对象的引用访问它们。
static嵌套类可以访问封闭类的所有static成员,包括私有成员。
Java 编程规范不允许我们将顶级类声明为static。只有类中的类(嵌套类)才能设置为static。
7. 理解错误“无法从static上下文引用非static变量”
通常,当我们在static上下文中使用非static变量时,会发生此错误。
正如我们之前看到的,static变量属于类,并在类加载时加载。另一方面,我们需要创建一个对象来引用非static变量。
因此,Java 编译器抱怨是因为需要对象调用或使用非static变量。
现在我们知道了导致错误的原因,让我们用一个例子来说明它:
public class MyClass {
int instanceVariable = 0;
public static void staticMethod() {
System.out.println(instanceVariable);
}
public static void main(String[] args) {
MyClass.staticMethod();
}
}
正如我们所看到的,我们在static方法staticMethod中使用了实例变量,这是一个非static变量。
结果,我们将收到错误非static变量无法从static上下文引用。
8. 结论
在本文中,我们看到了 static 关键字的实际应用。
我们还讨论了使用static字段、static方法、static块和static内部类的原因和优势。
最后,我们了解了导致编译器失败并显示错误“无法从static上下文引用非static变量”的原因。