65.9K
CodeProject 正在变化。 阅读更多。
Home

Java 中的 Static 关键字及其用法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (10投票s)

2013年12月30日

CPOL

8分钟阅读

viewsIcon

62204

了解如何以及何时在应用程序中使用 static 关键字。

引言

学习 Java 时,人们常被 static 字段和方法所困扰。尽管 static 关键字的用法非常简单,但初学者在学习“static”时很容易感到困惑。在本文中,我们将详细探讨 static 关键字,并澄清让开发人员感到困惑的那些问题。

类变量和对象变量

正如您所知,类的每个实例在内存中都会单独创建字段。如果您有一个包含姓名、姓氏和年龄变量的类,那么这些变量将为类的每个实例单独创建并赋值。这类变量被称为“对象变量”,因为它们属于特定对象。在 Java 中,也存在不属于对象而属于类的变量。这就是 static 关键字发挥作用的地方。

使用 static 关键字定义的变量被称为“类变量”。这类变量保存与类相关的数值,而不是特定对象,并且即使没有创建该类的任何对象,它们也会占用内存空间。另一方面,对象变量会随着实际对象的创建而创建。对象变量和类变量之间的另一个区别是,无论创建多少个对象,类变量只有一个实例。让我们看下面的代码示例。

package com.seckintozlu.article;

public class Student{

    private String name;        // Object variable
    private int age;            // Object variable
    private String studentId;   // Object variable
    public static int numberOfStudents = 0; // Class variable

    public Student(String name, int age, String studentId) {

        this.name= name;
        this.age= age;
        this.studentId= studentId;

        // Increase the number of students whenever an object is created
        numberOfStudents++;
    }
}

在这里,我们有 3 个对象变量和一个类变量,在构造函数中,每创建一个对象,我们都会增加类变量的值。现在让我们看下面的代码。

package com.seckintozlu.article;

public class Program {

    public static void main(String args[]) {

        System.out.println("Number of student in the beginning: " + Student.numberOfStudents);

        // create two student objects
        Student student1 = new Student("Obi-Wan Kenobi", 19, "2010001");
        Student student2 = new Student("Anakin Skywalker", 16, "2010002");

        int num = Student.numberOfStudents;
        System.out.println("Number of students (Using class name): " + num );

        num = student1.numberOfStudents;
        System.out.println("Number of students (Using first object): " + num );

        num = student2.numberOfStudents;
        System.out.println("Number of students (Using second object): " + num );
    }
}

这段代码将产生以下结果。

Number of student in the beginning: 0
Number of students (Using class name): 2
Number of students (Using first object): 2
Number of students (Using second object): 2

结果的第一行是 0,因为我们在创建任何对象之前打印 static 变量的值。如前所述,这是可行的,因为即使我们没有任何对象,static 变量也会在内存中创建。在接下来的几行中,我们创建了两个对象,将 static 变量的值增加到 2。然后,我们分别使用类名和两个对象引用再次打印 static 变量。Static 变量可以通过类名或对象引用访问,但在每种情况下我们都会得到相同的结果,因为类变量只有一个实例,它们都指向该实例。对象变量为每个对象单独创建,而 static 变量只创建一次,即使我们没有对象也存在。

静态方法

现在让我们看看如何将 static 关键字与方法一起使用。通常,当您需要调用方法时,您只需创建一个对象并使用对象引用调用该方法。但是,对于变量来说,可以创建独立于对象的方​​法。我们不需要创建对象来调用 static 方法。我们可以直接使用类名来完成。让我们看下面的代码。

package com.seckintozlu.article;

public class Student {

    private String name;        // Object variable
    private int age;            // Object variable
    private String studentId;   // Object variable
    private static int numberOfStudents = 0; // Class variable

    public Student(String name, int age, String studentId) {

        this.name= name;
        this.age= age;
        this.studentId= studentId;

        // Increase the number of students whenever an object is created
        numberOfStudents ++;
    }

    public static int getNumberOfStudents () {
       return numberOfStudents ;
    }
}

这段代码与之前的 Student 类唯一的区别是增加了一个新的 static 方法。在第一个示例中,我们使用类名 numberOfStudents 来引用公共类变量,但这次我们使类变量成为 private,并使用一个 public static 方法来指向它。为了测试这段代码,我们将使用以下内容:

package com.seckintozlu.article;

public class Program {

    public static void main(String args[]) {

        System.out.println("Number of students in the beginning: " + Student.getNumberOfStudents());

        // Create two student object
        Student student1 = new Student("Obi-Wan Kenobi", 19, "2010001");
        Student student2 = new Student("Anakin Skywalker", 16, "2010002");

        int numberOfStudents = Student.getNumberOfStudents();
        System.out.println("Number of students: " + numberOfStudents);
    }
}

这段代码将生成以下输出。

Number of students in the beginning: 0
Number of students: 2

因为 getNumberOfStudents 方法是 static 的,所以可以在不创建 Student 类的对象的情况下调用它。如果您已经创建了 Student 类的对象,您可以使用对象引用来调用 getNumberOfStudents(或任何 static 方法),但为了提高可读性,建议使用类名。同样,在访问 static 变量时,我们也可以使用对象引用,但这会降低代码的可读性。

关于 static 方法,最重要的一点是:我们不能在 static 方法中引用非 static 元素。如果示例中的 numberOfStudents 字段不是 static 变量,我们将在编译时收到错误消息。此外,我们不能在 static 方法中调用非 static 方法。举个例子:

package com.seckintozlu.article;

public class Student{

    private String name; // Object variable
    private int age; // Object variable
    private String studentId; // Object variable
    private static int numberOfStudents = 0; // Class variable

    public Student(String name, int age, String studentId) {

        this.name = name;
        this.age = age;
        this.studentId = studentId;

        // Increase the number of students whenever an object is created
        numberOfStudents++;
    }

    public int findNumberOfStudents() {
        return numberOfStudents;
    }

    public static int getNumberOfStudents() {
        return findNumberOfStudents();
    }
}

我们在 Student 类中添加了一个名为 findNumberOfStudents 的新的非 static 方法。它只返回 numberOfStudents 变量的值。请注意:我们不能在 static 方法中引用非 static 元素。但在这里我们做的是相反的操作,我们在非 static 方法中访问一个 static 字段,这是完全有效的。然而,static getNumberOfStudents 方法正试图调用 findNumberOfStudents,它不是 static 的。根据我们刚刚提到的 static 访问规则,这是无效的。当我们尝试编译这个类时,我们会收到一个错误,如:“Cannot make a static reference to the non-static method findNumberOfStudents() from the type Student”(“无法从 Student 类型对非静态方法 findNumberOfStudents() 进行静态引用”)如果我们尝试引用非 static 字段(例如“name”),也会收到相同的错误消息。

让我尝试解释一下这个规则背后的原因。我们知道 static 变量和方法独立于对象。这意味着即使没有创建对象,我们也可以调用 static 方法并为 static 变量赋值。然而,非 static 元素只有在创建对象时才能存在。基于此,如果我们尝试在 static 方法中引用非 static 变量,我们怎么知道我们引用的是哪个对象的字段?在那个时刻,可能根本没有对象。当我们尝试在 static 方法中调用非 static 方法时,这个陈述也为 true。因此,我们只能在 static 方法中引用 static 元素。但是,我们可以从非 static 方法中引用 static 元素,因为在创建任何对象之前,static 元素就已经被创建了。

当我们尝试在 static 方法中调用非 static 方法时收到错误消息,将另一个方法也设为 static 会起作用,但这可能不是一个好主意。如果您最终创建了许多 static 方法,请停下来思考一下您的方法是否需要是 static 的。我们将在文章的最后讨论这一点。现在让我们看看 static 关键字的另一个用法。

静态代码块

Static 代码块用于为 static 变量赋值。它们也称为“static 初始化程序”。Static 代码块在 static 变量加载到内存后立即执行。JVM 保证 static 代码块在创建该类的对象之前执行。

package com.seckintozlu.article;
import java.util.Random;

public class StaticInitializer {

    private static int array[];
    private static int length = 10;

    static {

        array = new int[length];
        Random rnd = new Random();

        for(int i=0; i < length; i++)
        array[i] = rnd.nextInt(100);
    }
}

查看上面的代码,我们创建了一个具有随机赋值的整数数组。使用 static 代码块,一旦创建了“array”变量,数组就会被随机整数填充。还有一点,我们也无法在 static 代码块中访问非 static 元素。由于 static 代码块并不常用,因此我不想详细说明。

静态导入

我们已经知道,我们使用类名来引用 static 方法。例如,在 Java 中,java.lang 包中的 Math 类充满了 static 方法,我们需要使用 Math.nameOfMethod() 来调用这些方法。使用 static 导入,我们可以仅使用方法名来调用它们。让我们看看它是如何使用的。

package com.seckintozlu.article;

import static java.lang.Math.*;

public class Program {

    public static void main(String args[]) {

        System.out.println("Square root 25: " + sqrt(25));
        System.out.println("Log(100): " + log(100));
        System.out.println("Pi: " + PI);
    }
}

正如您在代码中看到的,当我们在代码中添加 static 导入时,我们可以不使用类名来引用 static 方法和变量。如果我们没有 static 导入,我们通常会使用 Math.sqrt()Math.PI

何时使用 Static 变量?

决定使用类变量还是对象变量可能会让初学者在 Java 或任何其他具有“static”功能的语言中感到困惑。实际上,区别非常明显。类变量(静态变量)对该类的每个对象都是通用的,并且只有一个实例。另一方面,对象变量属于一个对象,其值取决于对象本身。在我开始时给出的 student 类的第一个示例中,您可以轻松地看出每个 Student 对象必须对 namestudentId 等变量有单独的值。

另一方面,由于 numberOfStudents 变量保存了创建的 student 对象的数量,因此它必须有一个单一的值。换句话说,它需要是一个 static 变量。我们可以将其存储为对象变量,但那样的话,每个对象将拥有一个单独的副本,并且当其值更改时,我们需要更新所有副本。正如您所料,这不是处理此问题的合适方法。

何时使用 Static 方法?

这是一个比前一个问题更难回答的问题。在面向对象编程语言中,对象拥有行为和状态。我们拥有的变量指定了状态,而方法代表了行为。如果一个方法依赖于对象的状态,或者受到对象状态的影响,那么该方法需要是一个对象方法(非 static)。如果它完全独立于状态,换句话说,如果对象的状态不影响方法生成的结果,那么我们可以将其定义为 static 方法。

例如,java.lang.Math 中的所有方法都是 static 的。为了计算一个数的平方根,我们只需要将该数作为参数传递给方法。从 Math 类创建的对象的状态与计算数字的平方根无关。因此,我们不需要创建对象来计算平方根。在开发过程中,您可能会在寻找如此明显的例子时遇到困难,但了解区别将有助于您找到答案。

最后说明:public static void main(String args[])

在 Java 中,main 方法的签名是预定义的。Main 方法必须是 publicstatic 的。它必须是 public 的,因为 JVM 需要从外部调用它。它也必须是 static 的,因为 JVM 不会实例化 main 方法所在的类。为了能够在不创建对象的情况下调用方法,它必须定义为 static

© . All rights reserved.