“ static”关键字在课程中做什么?


444

具体来说,我正在尝试以下代码:

package hello;

public class Hello {

    Clock clock = new Clock();

    public static void main(String args[]) {
        clock.sayTime();
    }
}

但是它给了错误

无法在静态方法main中访问非静态字段

所以我将声明更改为clock

static Clock clock = new Clock();

而且有效。将关键字放在声明之前是什么意思?就可以对对象执行的操作而言,它将确切地执行和/或限制什么?


再次记住,每个CLASSLOADER每个类都有一个静态实例。
Javamann

Answers:


633

static 成员属于类而不是特定实例。

这意味着即使您创建了该类的一百万个实例,也没有创建任何实例,该static字段仅存在一个实例[1]。它将被所有实例共享。

由于static方法也不属于特定实例,因此它们不能引用实例成员。在给定的示例中,main不知道应引用该类的Hello哪个实例(以及该类的哪个实例Clock)。static成员只能引用static成员。实例成员当然可以访问static成员。

旁注:当然,static成员可以通过对象引用访问实例成员。

例:

public class Example {
    private static boolean staticField;
    private boolean instanceField;
    public static void main(String[] args) {
        // a static method can access static fields
        staticField = true;

        // a static method can access instance fields through an object reference
        Example instance = new Example();
        instance.instanceField = true;
    }

[1]:根据运行时的特性,每个ClassLoader或AppDomain或线程可以是一个,但是那不是重点。


5
在.NET中,您还可以使用[ThreadStatic]属性来修改此行为-该属性使静态局部于特定线程。
TheSoftwareJedi,2009年

4
我知道这是旧帖子,但是对于像我这样的初学者来说,这可能会有所帮助。stackoverflow.com/questions/7026507/...
user3526905

因为它是一个私有变量,您将无法访问instance.instanceField吗?还是因为您实例化了自己类中的对象而有效?对我来说,这听起来像是一个递归的噩梦,但我是Java新手。
马特·科比

如果一个类的静态成员被2个不同的线程引用,那么该静态成员有多少个实例?我觉得它是2,但是如果您想在线程中使用相同的实例,则必须使用volatile关键字。那是对的吗?

..如果没有剩余的类实例,该值是否保留?
mckenzm

130

这意味着Hello中只有一个“ clock”实例,而“ Hello”类的每个单独实例中没有一个实例,或者更多,因此,这意味着在Hello的所有实例中将有一个共同共享的“ clock”引用。 “ Hello”类。

因此,如果您要在代码中的任何位置进行“新Hello”操作:A-在第一种情况下(更改之前,不使用“静态”),每次调用“新Hello”都会产生一个新时钟,但是B-在第二种情况下(更改后,使用“静态”),每个“新Hello”实例仍将共享并使用最初创建的初始且相同的“时钟”引用。

除非您需要在main以外的地方设置“时钟”,否则它将同样有效:

package hello;
public class Hello
{
    public static void main(String args[])
    {
      Clock clock=new Clock();
      clock.sayTime();    
    }
}

这是更常用的方法。该main()例程应是独立的。
詹森·S,2009年

1
在第二个实例中,它将在每次调用main方法时创建一个Clock的新实例,对吗?
点击Upvote

2
在第二种情况下,时钟是静态的,它只会创建一次。在我的示例中,时钟位于主时钟内,然后是,它将在每次调用主时钟时创建新时钟。但是通常main在程序启动时仅被调用一次,退出时,所有内容都被释放。
Paul Tomblin,2009年

我不知道如何在main方法中制作一个新时钟?就像您说的那样,每次调用main时都会创建新的,但是只有一种main方法。该主要方法如何引用不同的时钟实例?很难理解如何在主时钟中创建新的时钟实例并使用方法sayTime(),但是很难在主时钟之外使实例使用sayTime()。main一次调用后,所有内容如何免费?@PaulTomblin
ShakibaZar

@ user5621266我仅使用该main方法,因为OP确实使用过。相反,如果它是从其他地方调用的公共方法,并且Hello类被多次实例化,则它可以为每个Hello实例创建一个Clock实例,除非它clock是静态的。
Paul Tomblin

97

static关键字的装置的东西(字段,方法或嵌套类)是有关类型,而不是任何特定的实例的类型。因此,例如,一个调用Math.sin(...)没有Math类的任何实例,实际上您无法创建Math该类的实例。

有关更多信息,请参见Oracle Java Tutorial相关部分


边注

不幸的是 Java 允许您访问静态成员,就好像它们是实例成员一样,例如

// Bad code!
Thread.currentThread().sleep(5000);
someOtherThread.sleep(5000);

这使它看起来好像sleep是一个实例方法,但实际上是一个静态方法-它始终使当前线程处于休眠状态。最好在调用代码中对此加以说明:

// Clearer
Thread.sleep(5000);

1
另一个示例:System.out.println()看起来像一个类方法,但实际上是一个实例方法。由于out是System类中的PrintStream实例。
张家辉

@LeslieCheung:不,对我来说,这看起来不像类方法,对我来说,它System.out也不像类型名称。
乔恩·斯基特

42

staticJava中的关键字表示变量或函数在该类的所有实例之间共享,因为它属于类型,而不是实际对象本身。

因此,如果您有一个变量:private static int i = 0;并且i++在一个实例中将其递增(),则更改将反映在所有实例中。i现在在所有情况下均为1。

可以使用静态方法而无需实例化对象。


4
IMO,“在所有实例之间共享”给人的印象是错误的-它表明您确实需要具有该对象的实例。
乔恩·斯基特

1
(实际上不需要任何实例,因为静态字段等属于该类型。)
Jon Skeet

@Jon Skeet静态属于类型,不是对象?你能说出更多细节吗?像数据类型一样的类型:int,double,...?
truongnm '16

@truongnm:在声明变量/方法的类中输入。
乔恩·斯基特

26

静态成员的基本用法...

public class Hello
{
    // value / method
    public static String staticValue;
    public String nonStaticValue;
}

class A
{
    Hello hello = new Hello();
    hello.staticValue = "abc";
    hello.nonStaticValue = "xyz";
}

class B
{
    Hello hello2 = new Hello(); // here staticValue = "abc"
    hello2.staticValue; // will have value of "abc"
    hello2.nonStaticValue; // will have value of null
}

这样,您就可以在所有类成员中共享值,而无需将类实例Hello发送给其他类。而静态的则不需要创建类实例。

Hello hello = new Hello();
hello.staticValue = "abc";

您可以按类名称调用静态值或方法:

Hello.staticValue = "abc";

22

静态意味着您不必创建类的实例即可使用与该类关联的方法或变量。在您的示例中,您可以调用:

Hello.main(new String[]()) //main(...) is declared as a static function in the Hello class

直接,而不是:

Hello h = new Hello();
h.main(new String[]()); //main(...) is a non-static function linked with the "h" variable

从静态方法(属于一个类)内部,您不能访问任何非静态的成员,因为它们的值取决于您对类的实例化。作为实例成员的非静态Clock对象对于Hello类的每个实例将具有不同的值/引用,因此您无法从该类的静态部分访问它。


静态上下文的很好的解释:)
Abdel-Raouf

20

Java中的静态:

静态是非访问修饰符。static关键字属于该类,而不是该类的实例。可用于将变量或方法附加到类。

静态关键字可以用于:

方法

变量

嵌套在另一个类中的类

初始化块

不能用于:

类(未嵌套)

建设者

介面

方法本地内部类(与嵌套类不同)

内部类方法

实例变量

局部变量

例:

想象以下示例,该示例具有一个名为count实例变量,该实例变量在构造函数中递增:

package pkg;

class StaticExample {
    int count = 0;// will get memory when instance is created

    StaticExample() {
        count++;
        System.out.println(count);
    }

    public static void main(String args[]) {

        StaticExample c1 = new StaticExample();
        StaticExample c2 = new StaticExample();
        StaticExample c3 = new StaticExample();

    }
}

输出:

1 1 1

由于实例变量在创建对象时会获得内存,因此每个对象都将具有实例变量的副本,如果递增,则不会反映到其他对象。

现在,如果我们将实例变量计数更改为静态变量,那么程序将产生不同的输出:

package pkg;

class StaticExample {
    static int count = 0;// will get memory when instance is created

    StaticExample() {
        count++;
        System.out.println(count);
    }

    public static void main(String args[]) {

        StaticExample c1 = new StaticExample();
        StaticExample c2 = new StaticExample();
        StaticExample c3 = new StaticExample();

    }
}

输出:

1 2 3

在这种情况下,静态变量将仅获得一次内存,如果任何对象更改了静态变量的值,它将保留其值。

静态与最终:

在整个执行过程中,声明为final和static的全局变量保持不变。因为,静态成员存储在类内存中,并且在整个执行过程中仅加载一次。它们是该类的所有对象共有的。如果将静态变量声明为final,则任何对象都无法更改其值,因为它是final。因此,声明为final和static的变量有时称为常量。接口的所有字段都称为常量,因为默认情况下它们是最终的和静态的。

在此处输入图片说明

图片资源:最终静态


15

要添加到现有答案中,让我尝试一下图片:

所有储蓄账户的利率为2%。因此它是静态的

余额应为个人,因此不是静态的。

在此处输入图片说明


13

到目前为止,该讨论都忽略了类加载器的考虑。严格来说,对于给定的classloader,Java静态字段在类的所有实例之间共享。


1
Apocalisp在对Merhdad的回答的评论中提到了这一点。
Zach Langley,

1
好点子。许多人不知道这一点,但是一旦您开始使用类加载器,它就变得非常重要。
sleske

2
都是如此,但并不能回答问题。它应该已经发布为评论。
罗恩侯爵

7

可以将字段分配给类或类的实例。默认情况下,字段是实例变量。通过使用static该字段将成为一个类变量,因此只有一个clock。如果您在一个地方进行更改,则随处可见。实例变量彼此独立更改。


6

关键字static用于表示属于类本身而非实例的字段或方法。使用您的代码,如果对象Clock是静态的,则Hello该类的所有实例将共享该Clock数据成员(字段)。如果您将其设为非静态,则每个的实例都Hello可以具有唯一的Clock字段。

问题是您在类中添加了main方法,Hello以便您可以运行代码。这里的问题是main方法是静态的,因此,它不能引用非静态字段或其中的方法。您可以通过两种方式解决此问题:

  1. 将类的所有字段和方法设为Hello静态,以便可以在main方法内部引用它们。这确实不是一件好事(或将字段和/或方法设为静态的错误原因)
  2. Hello在main方法内创建类的实例,并首先访问它们的所有字段和方法。

对您来说,这意味着对代码进行以下更改:

package hello;

public class Hello {

    private Clock clock = new Clock();

    public Clock getClock() {
        return clock;
    }

    public static void main(String args[]) {
        Hello hello = new Hello();
        hello.getClock().sayTime();
    }
}

6

在Java中,static关键字可以简单地视为指示以下内容:

“不考虑或与任何特定情况无关”

如果您static以这种方式考虑,那么会更容易理解它在遇到的各种情况下的用法:

  • static字段是属于类,而不是任何特定的实例的字段

  • static方法是不具有的概念的方法this; 它在类上定义,并且除非对该类传递引用,否则不知道该类的任何特定实例

  • static构件类是没有其外围类的一个实例的概念任何或知识嵌套类(除非到一个封闭类实例的引用传递给它)


5

静态使时钟成员成为类成员,而不是实例成员。如果没有static关键字,则需要创建Hello类的实例(具有时钟成员变量)-例如

Hello hello = new Hello();
hello.clock.sayTime();


5

我对“ helper”类中的静态方法(仅在可能的情况下)很喜欢。

调用类无需创建辅助类的另一个成员(实例)变量。您只需调用助手类的方法。辅助类也得到了改进,因为您不再需要构造函数,也不需要成员(实例)变量。

可能还有其他优点。


4
//Here is an example 

public class StaticClass 
{
    static int version;
    public void printVersion() {
         System.out.println(version);
    }
}

public class MainClass 
{
    public static void main(String args[]) {  
        StaticClass staticVar1 = new StaticClass();
        staticVar1.version = 10;
        staticVar1.printVersion() // Output 10

        StaticClass staticVar2 = new StaticClass();
        staticVar2.printVersion() // Output 10
        staticVar2.version = 20;
        staticVar2.printVersion() // Output 20
        staticVar1.printVersion() // Output 20
    }
}

3

也可以想到静态成员没有“ this”指针。它们在所有实例之间共享。


3

了解静态概念

public class StaticPractise1 {
    public static void main(String[] args) {
        StaticPractise2 staticPractise2 = new StaticPractise2();
        staticPractise2.printUddhav(); //true
        StaticPractise2.printUddhav(); /* false, because printUddhav() is although inside StaticPractise2, but it is where exactly depends on PC program counter on runtime. */

        StaticPractise2.printUddhavsStatic1(); //true
        staticPractise2.printUddhavsStatic1(); /*false, because, when staticPractise2 is blueprinted, it tracks everything other than static  things and it organizes in its own heap. So, class static methods, object can't reference */

    }
}

二等

public class StaticPractise2 {
    public static void printUddhavsStatic1() {
        System.out.println("Uddhav");
    }

    public void printUddhav() {
        System.out.println("Uddhav");
    }
}

2

main() 是一种静态方法,它具有两个基本限制:

  1. 静态方法不能使用非静态数据成员,也不能直接调用非静态方法。
  2. this()并且super()不能在静态上下文中使用。

    class A {  
        int a = 40; //non static
        public static void main(String args[]) {  
            System.out.println(a);  
        }  
    }

输出:编译时错误


1

静态变量只能在静态方法中访问,因此,当我们声明静态变量时,那些getter和setter方法将是静态方法

静态方法是一个类级别,我们可以使用类名称进行访问

以下是静态变量获取器和设置器的示例:

public class Static 
{

    private static String owner;
    private static int rent;
    private String car;
    public String getCar() {
        return car;
    }
    public void setCar(String car) {
        this.car = car;
    }
    public static int getRent() {
        return rent;
    }
    public static void setRent(int rent) {
        Static.rent = rent;
    }
    public static String getOwner() {
        return owner;
    }

    public static void setOwner(String owner) {
        Static.owner = owner;
    }

}

1

在这里,有人问这个概念中“静态”一词的选择。解决了这个问题,但我认为词源没有明确解决。所以...


这是由于以C开头的关键字重用。

考虑一下C中的数据声明(在函数体内):

    void f() {
        int foo = 1;
        static int bar = 2;
         :
    }

进入函数时,将在堆栈上创建变量foo(并在函数终止时销毁)。相比之下,bar总是在那儿,所以就普通英语而言,它是“静态的”-它不会随处可见。

Java和类似的语言具有相同的数据概念。可以为每个类的实例(每个对象)分配数据,也可以为整个类分配一次数据。由于Java旨在为C / C ++程序员提供熟悉的语法,因此'static'关键字在此是合适的。

    class C {
        int foo = 1;
        static int bar = 2;
         :
    }

最后,我们来谈谈方法。

    class C {
        int foo() { ... }
        static int bar() { ... }
         :
    }

从概念上讲,对于类C的每个实例,都有一个foo()实例。对于整个类C,只有一个bar()实例。这与我们为数据讨论的情况类似,因此使用“静态”再次是明智的选择,尤其是当您不想在语言中添加更多保留关键字时。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.