Java 中的static关键字

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的CollectionsMath实用程序类,Apache的StringUtils或Spring框架的CollectionUtils,并注意到它们的所有实用程序都是static的。

4.2. 使用static方法的令人信服的理由

让我们看一下为什么我们要使用static方法的几个原因:

  • 访问/操作static变量和其他不依赖于对象的static方法。
  • static方法广泛用于实用程序和帮助程序类。

4.3. 要记住的要点

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

抽象方法不能是static的。

static方法不能使用此关键字或超级关键字。

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

  1. 实例方法可以直接访问实例方法和实例变量
  2. 实例方法也可以直接访问static变量和static方法
  3. static方法可以访问所有static变量和其他static方法
  4. 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变量”的原因。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值