簡體   English   中英

類中的“靜態”關鍵字有什么作用?

[英]What does the 'static' keyword do in a class?

具體來說,我正在嘗試以下代碼:

package hello;

public class Hello {

    Clock clock = new Clock();

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

但它給出了錯誤

無法訪問靜態方法 main 中的非靜態字段

所以我把clock的聲明改成這樣:

static Clock clock = new Clock();

它奏效了。 將關鍵字放在聲明之前是什么意思? 就可以對該對象執行的操作而言,它究竟會做什么和/或限制什么?

static成員屬於類而不是特定實例。

這意味着只有一個static字段的實例存在[1] ,即使您創建了一百萬個該類的實例或者您沒有創建任何實例。 它將由所有實例共享。

由於static方法也不屬於特定實例,因此它們不能引用實例成員。 在給出的示例中, main不知道應該引用Hello類的哪個實例(因此也不知道Clock類的哪個實例)。 static成員只能引用static成員。 實例成員當然可以訪問static成員。

Of course, static members can access instance members through an object reference .當然, 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 或線程一個,但這不是重點。

這意味着 Hello 中只有一個“時鍾”實例,而不是每個單獨的“Hello”類實例都有一個,或者更多,這意味着在所有實例之間將有一個共同共享的“時鍾”引用“你好”類。

因此,如果您要在代碼中的任何位置執行“新 Hello”: A- 在第一個場景中(在更改之前,不使用“靜態”),每次調用“新 Hello”時都會生成一個新時鍾,但是 B- 在第二種情況下(更改后,使用“靜態”),每個“新 Hello”實例仍將共享並使用最初創建的相同“時鍾”引用。

除非您在 main 之外的某個地方需要“時鍾”,否則這也可以:

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

static關鍵字意味着某些東西(字段、方法或嵌套類)與類型相關,而不是與該類型的任何特定實例相關。 例如,在沒有任何Math類實例的情況下調用Math.sin(...) ,實際上您無法創建Math類的實例。

有關更多信息,請參閱Oracle 的 Java 教程的相關部分。


邊注

不幸的是,Java允許您像訪問實例成員一樣訪問靜態成員,例如

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

這使它看起來好像sleep是一個實例方法,但它實際上是一個靜態方法 - 它總是使當前線程休眠。 最好在調用代碼中明確這一點:

// Clearer
Thread.sleep(5000);

Java 中的static關鍵字意味着變量或函數在該類的所有實例之間共享,因為它屬於type ,而不是實際的對象本身。

所以如果你有一個變量: private static int i = 0; 並且您在一個實例中增加它( i++ ),更改將反映在所有實例中。 i現在在所有情況下都是 1。

無需實例化對象即可使用靜態方法。

靜態成員的基本用法...

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

靜態意味着您不必創建類的實例即可使用與類關聯的方法或變量。 在您的示例中,您可以調用:

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

從靜態方法(屬於類)內部,您無法訪問任何非靜態成員,因為它們的值取決於您對類的實例化。 作為實例成員的非靜態時鍾對象對於 Hello 類的每個實例都有不同的值/引用,因此您無法從類的靜態部分訪問它。

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

由於實例變量在對象創建時就獲得了內存,所以每個對象都會有實例變量的副本,如果遞增,就不會反映到其他對象。

現在,如果我們將實例變量 count 更改為靜態變量,那么程序將產生不同的輸出:

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 的變量有時稱為常量。 接口的所有字段都稱為常量,因為默認情況下它們是最終的和靜態的。

在此處輸入圖像描述

圖片資源:最終靜態

要添加到現有答案,讓我嘗試使用圖片:

2% 的利率適用於所有儲蓄賬戶。 因此它是靜態的。

平衡應該是個體的,所以它不是靜態的。

在此處輸入圖像描述

到目前為止,這個討論忽略了類加載器的考慮。 嚴格來說,Java 靜態字段在給定類加載器的類的所有實例之間共享。

關鍵字static用於表示屬於類本身而不是實例的字段或方法。 使用您的代碼,如果對象Clock是靜態的,則Hello類的所有實例將共享此Clock數據成員(字段)。 如果將其設為非靜態,則Hello的每個單獨實例都可以有一個唯一的Clock字段。

您向Hello類添加了一個main方法,以便您可以運行代碼。 問題在於main方法是靜態的,因此它不能引用其中的非靜態字段或方法。 您可以通過兩種方式解決此問題:

  1. Hello類的所有字段和方法設為靜態,以便可以在main方法中引用它們。 這真的不是一件好事(或者使字段和/或方法靜態的錯誤原因)
  2. 在 main 方法中創建Hello類的實例,並按照最初的預期方式訪問它的所有字段和方法。

對您而言,這意味着對您的代碼進行以下更改:

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

可以將字段分配給類或類的實例。 默認情況下,字段是實例變量。 通過使用static字段成為一個類變量,因此只有一個clock 如果您在一個地方進行更改,它在任何地方都可見。 實例變量彼此獨立地更改。

在 Java 中, static關鍵字可以簡單地認為表示以下內容:

“不考慮或與任何特定實例有關”

如果您以這種方式考慮static ,則更容易理解它在遇到它的各種上下文中的使用:

  • static字段是屬於類而不是任何特定實例的字段

  • static方法是沒有this概念的方法; 它是在類上定義的,並且不知道該類的任何特定實例,除非將引用傳遞給它

  • static成員類是一個嵌套類,對其封閉類的實例沒有任何概念或知識(除非將對封閉類實例的引用傳遞給它)

我已經對“助手”類中的靜態方法(僅在可能的情況下)產生了興趣。

調用類不需要創建幫助類的另一個成員(實例)變量。 您只需調用助手類的方法。 輔助類也得到了改進,因為您不再需要構造函數,並且不需要成員(實例)變量。

可能還有其他優點。

靜態使時鍾成員成為類成員而不是實例成員。 如果沒有 static 關鍵字,您將需要創建 Hello 類的實例(它有一個時鍾成員變量) - 例如

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

靜態方法不使用定義它們的類的任何實例變量。可以在此頁面上找到對差異的很好解釋

//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
    }
}

還可以考慮沒有“this”指針的靜態成員。 它們在所有實例之間共享。

了解靜態概念

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

main()是一個靜態方法,它有兩個基本限制:

  1. 靜態方法不能使用非靜態數據成員或直接調用非靜態方法。
  2. this()super()不能在靜態上下文中使用。

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

輸出:編譯時錯誤

靜態變量只能在靜態方法中訪問,所以當我們聲明靜態變量時,getter和setter方法將是靜態方法

靜態方法是我們可以使用類名訪問的類級別

以下是靜態變量 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;
    }

}

這里提出了一個關於為這個概念選擇“靜態”這個詞的問題。 這是對這個問題的欺騙,但我認為詞源沒有得到明確解決。 所以...


這是由於關鍵字重用,從 C 開始。

考慮 C 中的數據聲明(在函數體內):

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

變量 foo 在進入函數時在堆棧上創建(並在函數終止時銷毀)。 相比之下, bar 總是在那里,所以它在普通英語的意義上是“靜態的”——它不會去任何地方。

Java 和類似的語言對數據有相同的概念。 可以為每個類的實例(每個對象)分配數據,也可以為整個類分配一次。 由於 Java 旨在為 C/C++ 程序員提供熟悉的語法,因此“靜態”關鍵字在這里是合適的。

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

最后,我們來談談方法。

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

從概念上講,類 C 的每個實例都有一個 foo() 實例。整個 C 類只有一個 bar() 實例。這與我們討論的數據情況類似,因此使用 'static ' 又是一個明智的選擇,特別是如果您不想在您的語言中添加更多保留關鍵字。

在運行某些項目時,首先要加載靜態內容(變量,方法,塊..)。

運行此項目時,首先加載主方法。 因為它是static method 然后,它看起來是對象"a" object 。但是對象a尚未定義。 因為它是非靜態的。 然后出現此錯誤。

Java 程序中的成員可以使用其聲明/定義之前的關鍵字“static”聲明為靜態的。 當一個成員被聲明為靜態時,它本質上意味着該成員由一個類的所有實例共享,而無需為每個實例制作副本。

因此 static 是 Java 中使用的非類修飾符,可以應用於以下成員:

  • 變量
  • 方法
  • 類(更具體地說,嵌套類)

當一個成員被聲明為靜態的,那么它就可以在不使用對象的情況下被訪問。 這意味着在類被實例化之前,靜態成員是活動的並且是可訪問的。 與當類的對象超出范圍時不再存在的其他非靜態類成員不同,靜態成員顯然仍然是活動的。

Java中的靜態變量

聲明為靜態的類的成員變量稱為靜態變量。 它也被稱為“類變量”。 一旦變量被聲明為靜態,內存只分配一次,而不是每次實例化類時。 因此,您可以在不引用對象的情況下訪問靜態變量。

以下 Java 程序描述了靜態變量的用法:

class Main
{
// static variables a and b
static int a = 10;
static int b;

static void printStatic()
{
    a = a /2;
    b = a;

    System.out.println("printStatic::Value of a : "+a + " Value of b : 
 "+b);
}  

public static void main(String[] args)
{
   printStatic();
   b = a*5;
   a++;

System.out.println("main::Value of a : "+a + " Value of b : "+b);
   }
 }

輸出::

printStatic::Value of a : Value of b : 5
main::Value of a : 6 Value of b : 25

在上面的程序中,我們有兩個靜態變量,即 a 和 b。 我們在函數“printStatic”和“main”中修改這些變量。 請注意,即使函數范圍結束,這些靜態變量的值也會在函數中保留。 輸出顯示兩個函數中的變量值。

靜態方法

當 Java 中的方法前面帶有關鍵字“static”時,它就是靜態的。

關於靜態方法,您需要記住的一些要點包括:

  • 與使用類的實例調用的其他非靜態方法相比,靜態方法屬於該類。
  • 要調用靜態方法,您不需要類對象。
  • 靜態方法可以訪問類的靜態數據成員。 靜態方法甚至可以更改靜態數據成員的值。
  • 靜態方法不能引用“this”或“super”成員。 即使靜態方法嘗試引用它們,也會出現編譯器錯誤。
  • 就像靜態數據一樣,靜態方法也可以調用其他靜態方法。 靜態方法不能引用非靜態數據成員或變量,也不能調用非靜態方法。

以下程序顯示了 Java 中靜態方法的實現:

class Main
{
  // static method
  static void static_method()
{
    System.out.println("Static method in Java...called without any 
object");
}

public static void main(String[] args)
{
    static_method();
   }
 }

輸出:

Static method in Java...called without any object

Java中的靜態塊

就像您在 Java 中的 C++、C# 等編程語言中具有功能塊一樣,也有一個稱為“靜態”塊的特殊塊,它通常包含與靜態數據相關的代碼塊。

此靜態塊在創建類的第一個對象時(准確地說是在類加載時)或使用塊內的靜態成員時執行。

以下程序顯示了靜態塊的用法。

class Main
{
  static int sum = 0;
  static int val1 = 5;
  static int val2;

// static block
 static {
    sum = val1 + val2;
    System.out.println("In static block, val1: " + val1  + " val2: "+ 
val2 + " sum:" + sum);
    val2 = val1 * 3;
    sum = val1 + val2;
}

 public static void main(String[] args)
{
    System.out.println("In main function, val1: " + val1  + " val2: "+ val2 + " sum:" + sum);
  }
}

輸出:

In static block, val1: 5 val2: 0 sum:5
In main function, val1: val2: 15 sum:20

靜態類

在 Java 中,您有靜態塊、靜態方法,甚至是靜態變量。 因此很明顯,您也可以擁有靜態類。 在 Java 中,可以在另一個類中包含一個類,這稱為嵌套類。 包含嵌套類的類稱為 Outer 類。

在 Java 中,雖然可以將嵌套類聲明為靜態,但不能將外部類聲明為靜態。

現在讓我們探索 Java 中的靜態嵌套類。

靜態嵌套類

如前所述,您可以在 Java 中將嵌套類聲明為靜態。 靜態嵌套類在某些方面不同於非靜態嵌套類(內部類),如下所示。

與非靜態嵌套類不同,嵌套靜態類不需要外部類引用。

靜態嵌套類只能訪問外部類的靜態成員,而非靜態類可以訪問外部類的靜態成員和非靜態成員。

下面給出了一個靜態嵌套類的示例。


class Main{
  private static String str = "SoftwareTestingHelp";

     //Static nested class
     static class NestedClass{
        //non-static method
            public void display() {

            System.out.println("Static string in OuterClass: " + str);
            }

    }
   public static void main(String args[])
   {
            Main.NestedClassobj = new Main.NestedClass();
            obj.display();
   }
}

輸出

Static string in OuterClass: SoftwareTestingHelp

我認為這就是 static 關鍵字在 java 中的工作方式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM