跳至主要內容

14. Java static关键字

LiuSongLing大约 8 分钟javajava

在本教程中,我们将详细探索Java语言的静态关键字。

我们将了解如何将静态关键字应用于变量、方法、块和嵌套类,以及它有什么不同。

1.static关键字

在Java编程语言中,关键字static意味着特定成员属于类型本身,而不是该类型的实例。

这意味着我们将只创建一个静态成员的实例,该实例在类的所有实例之间共享。

我们可以将关键字应用于变量、方法、块和嵌套类。

2.静态字段(类变量)

在Java中,当我们声明一个字段静态时,恰好会创建该字段的单个副本,并在该类的所有实例之间共享。

我们实例化一个类多少次并不重要。始终只有一个属于它的静态字段副本。这个静态字段的值在同一类的所有对象之间共享。

从内存的角度来看,静态变量存储在堆内存中。

2.1静态字段示例

假设我们有一个具有多个属性(实例变量)的Car类。

每当我们从这个汽车蓝图中实例化新对象时,每个新对象都会有这些实例变量的不同副本。







然而,假设我们想要一个变量,它保存实例化Car对象的数量,并在所有实例之间共享,以便他们可以访问它并在初始化时增加它。

这就是静态变量的用处:

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);
}

2.2使用静态字段的原因

以下是我们何时想使用静态字段的一些原因:

  • 当变量的值独立于对象时

  • 当值应该在所有对象之间共享时

由于静态变量属于一个类,我们可以使用类名直接访问它们。因此,我们不需要任何对象引用。

我们只能在类级别声明静态变量。

我们可以访问静态字段,而无需对象初始化。

最后,我们可以使用对象引用(如ford.numberOfCars++)访问静态字段。但我们应该避免这种情况,因为很难弄清楚它是实例变量还是类变量。

相反,我们应该始终使用类名(Car.numberOfCars++)引用静态变量。

3.静态方法(类方法)

与静态字段类似,静态方法也属于类而不是对象。因此,我们可以调用它们,而无需创建它们所在的类的对象。

3.1静态方法示例

我们通常使用静态方法来执行不依赖于实例创建的操作。

为了在该类的所有实例之间共享代码,我们用静态方法编写它:

static void setNumberOfCars(int numberOfCars) {
    Car.numberOfCars = numberOfCars;
}

我们还通常使用静态方法来创建实用程序或辅助类,以便我们无需创建这些类的新对象即可获得它们。







例如,我们可以看看JDK的Collections或Math实用程序类,Apache的StringUtils,或Spring框架的CollectionUtils,并注意到它们的所有实用程序方法都是静态的。

3.2使用静态方法的原因

让我们看看我们想要使用静态方法的几个原因:

  • 访问/操作静态变量和其他不依赖于对象的静态方法。

  • 静态方法广泛用于实用程序和辅助类。

Java中的静态方法在编译时解析。由于方法重写是运行时多态性的一部分,因此静态方法无法重写。

抽象方法不能是静态的。

静态方法不能使用thissuper关键字。

以下实例、类方法和变量的组合是有效的:

  • 实例方法可以直接访问实例方法和实例变量

  • 实例方法也可以直接访问静态变量和静态方法

  • 静态方法可以访问所有静态变量和其他静态方法

  • 静态方法不能直接访问实例变量和实例方法。他们需要一些对象引用才能做到这一点。

3.3在Java中调用静态方法中的非静态方法

为了在静态方法中调用非静态方法,我们必须使用包含非静态方法的类实例。例如,在main()静态方法中调用非静态方法时,这是一个常见的用例。

让我们考虑一下我们在本文前面介绍的汽车类的例子,它定义了以下方法:







public String getName() {
    return name;
}

public String getEngine() {
    return engine;
}

public static String getCarsInformation(Car car) {
    return car.getName() + "-" + car.getEngine();
}

正如我们所看到的,我们在getCarsInformation()静态方法中调用getName()和getEngine()方法,它们是非静态方法。这之所以可能,只是因为我们使用Car对象的实例来访问这些方法。否则,我们会收到此错误消息“非静态方法'getName()'无法从静态上下文引用”。

4,静态块

我们使用静态块来初始化静态变量。虽然我们可以在声明期间直接初始化静态变量,但在某些情况下,我们需要进行多行处理。在这种情况下,静态块会派上用场。

如果静态变量在初始化期间需要额外的多陈述逻辑,我们可以使用静态块。

4.1静态块示例

例如,假设我们想用一些预定义的值初始化一个List对象。

使用静态块,这变得很容易:

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");
    }
}

不可能用所有初始值和声明初始化列表对象。因此,这就是我们在这里使用静态块的原因。

以下是使用静态块的几个原因:

  • 如果静态变量的初始化除了分配之外还需要一些额外的逻辑

  • 如果静态变量的初始化容易出错,并且需要异常处理

一个类可以有多个静态块。 静态字段和静态块以与类中相同的顺序解析和运行。







5.静态类

Java允许我们在类中创建一个类。它提供了一种我们只在一个地方使用的元素分组方式。这有助于保持我们的代码更有条理和可读性。

一般来说,嵌套类架构分为两种类型:

  • 我们声明静态的嵌套类称为静态嵌套类

  • 非静态的嵌套类称为内部类

两者之间的主要区别在于,内部类可以访问封闭类的所有成员(包括私有成员),而静态嵌套类只能访问外部类的静态成员。

事实上,静态嵌套类的行为与任何其他顶级类完全相同,但包含在唯一可以访问它的类中,以提供更好的打包便利。

5.1静态类示例

创建单项对象最广泛使用的方法是通过静态嵌套类:

public class Singleton  {
    private Singleton() {}

    private static class SingletonHolder {
        public static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

我们使用这种方法,因为它不需要任何同步,并且易于学习和实现。

另一个嵌套静态类示例,其中显示父成员和嵌套成员之间的可见性,反之亦然:







public class Pizza {

    private static String cookedCount;
    private boolean isThinCrust;

    public static class PizzaSalesCounter {

        private static String orderedCount;
        public static String deliveredCount;

        PizzaSalesCounter() {
            System.out.println("Static field of enclosing class is "
              + Pizza.cookedCount);
            System.out.println("Non-static field of enclosing class is "
              + new Pizza().isThinCrust);
        }
    }

    Pizza() {
        System.out.println("Non private static field of static class is "
          + PizzaSalesCounter.deliveredCount);
        System.out.println("Private static field of static class is "
          + PizzaSalesCounter.orderedCount);
    }

    public static void main(String[] a) {
           new Pizza.PizzaSalesCounter();
    }
}

当我们运行主方法时,结果是:

Static field of enclosing class is null
Non private static field of static class is null
Private static field of static class is null
Non-static field of enclosing class is false

让我们看看在代码中使用静态内部类的几个原因:

  • 仅在一个地方使用的分组类增加了封装

  • 我们让代码更接近唯一会使用它的地方。这增加了可读性,代码也更易于维护。

  • 如果嵌套类不需要对其封闭的类实例成员进行任何访问,则最好将其声明为静态。这样,它就不会与外部类耦合,因此更理想,因为它们不需要任何堆或堆栈内存。

基本上,静态嵌套类无法访问封闭外类的任何实例成员。它只能通过对象的引用访问它们。

静态嵌套类可以访问封闭类的所有静态成员,包括私有成员。

Java编程规范不允许我们将顶级类声明为静态。 只有类中的类(嵌套类)可以作为静态的。