Từ khóa 'tĩnh' làm gì trong một lớp?


444

Để cụ thể, tôi đã thử mã này:

package hello;

public class Hello {

    Clock clock = new Clock();

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

Nhưng nó đã báo lỗi

Không thể truy cập trường không tĩnh trong phương thức tĩnh chính

Vì vậy, tôi đã thay đổi tuyên bố clocknày:

static Clock clock = new Clock();

Va no đa hoạt động. Việc đặt từ khóa đó trước khi khai báo nghĩa là gì? Chính xác thì nó sẽ làm gì và / hoặc hạn chế về những gì có thể được thực hiện cho đối tượng đó?


Một lần nữa hãy nhớ rằng có một trường hợp tĩnh cho mỗi lớp trên mỗi lớp.
Javamann

Câu trả lời:


633

static các thành viên thuộc về lớp thay vì một trường hợp cụ thể.

Điều đó có nghĩa là chỉ có một thể hiện của một statictrường tồn tại [1] ngay cả khi bạn tạo một triệu thể hiện của lớp hoặc bạn không tạo bất kỳ trường hợp nào. Nó sẽ được chia sẻ bởi tất cả các trường hợp.

staticcác phương thức cũng không thuộc về một thể hiện cụ thể, nên chúng không thể tham chiếu đến các thành viên thể hiện. Trong ví dụ đã cho, mainkhông biết trường hợp nào của Hellolớp (và do đó, trường hợp nào của Clocklớp) mà nó nên tham chiếu. staticthành viên chỉ có thể tham khảo staticcác thành viên. Tất nhiên các thành viên có thể truy cập staticcác thành viên.

Lưu ý bên lề: Tất nhiên, staticcác thành viên có thể truy cập các thành viên thể hiện thông qua một tham chiếu đối tượng .

Thí dụ:

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]: Tùy thuộc vào các đặc điểm thời gian chạy, nó có thể là một cho mỗi ClassLoader hoặc AppDomain hoặc luồng, nhưng đó là bên cạnh điểm.


5
Trong .NET, bạn cũng có thể sửa đổi hành vi này bằng cách sử dụng thuộc tính [ThreadStatic] - làm cho tĩnh cục bộ thành các luồng cụ thể.
TheSoftwareJedi

4
Tôi biết đây là bài viết cũ nhưng đối với những người mới bắt đầu như tôi thì điều này có thể hữu ích. stackoverflow.com/questions/7026507/
trộm

Bạn sẽ không thể truy cập instance.instanceField vì đó là var riêng tư? Hoặc nó có hợp lệ không vì bạn đã khởi tạo đối tượng bên trong lớp của chính nó? Nghe có vẻ như một cơn ác mộng đệ quy với tôi nhưng tôi là một người mới sử dụng Java.
Matt Corby

Nếu thành viên tĩnh của một lớp được tham chiếu bởi 2 luồng khác nhau thì có bao nhiêu trường hợp có thành viên tĩnh đó? Tôi cảm thấy như là 2 nhưng nếu bạn muốn cùng một ví dụ trên các chủ đề thì từ khóa dễ bay hơi phải được sử dụng. Đúng không?
Dan

..và giá trị được bảo toàn nếu không còn trường hợp nào của lớp?
mckenzm

130

Điều đó có nghĩa là chỉ có một phiên bản "đồng hồ" trong Hello, không phải là một phiên bản cho mỗi phiên bản riêng biệt của lớp "Hello" hoặc hơn thế nữa, điều đó có nghĩa là sẽ có một tham chiếu "đồng hồ" được chia sẻ chung trong tất cả các trường hợp của lớp "Xin chào".

Vì vậy, nếu bạn định thực hiện "Hello mới" ở bất kỳ đâu trong mã của mình: A- trong kịch bản đầu tiên (trước khi thay đổi, không sử dụng "tĩnh"), nó sẽ tạo một đồng hồ mới mỗi khi gọi "Hello mới", nhưng B- trong kịch bản thứ hai (sau khi thay đổi, sử dụng "tĩnh"), mọi phiên bản "Hello mới" vẫn sẽ chia sẻ và sử dụng tham chiếu "đồng hồ" ban đầu và tương tự được tạo trước tiên.

Trừ khi bạn cần "đồng hồ" ở đâu đó bên ngoài chính, điều này cũng sẽ hoạt động tốt:

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

Đây là cách thông thường hơn để làm điều đó. Các main()thói quen nên được khép kín.
Jason S

1
Trong trường hợp thứ hai, nó sẽ tạo một thể hiện mới của Đồng hồ mỗi khi phương thức chính được gọi, phải không?
Nhấp vào Upvote

2
Trong trường hợp thứ hai, đồng hồ tĩnh, nó sẽ chỉ tạo một lần. Trong ví dụ của tôi, trong đó đồng hồ nằm trong chính, thì có, nó sẽ tạo ra nó mới mỗi khi gọi chính. Nhưng thông thường main chỉ được gọi một lần khi bắt đầu chương trình và khi nó thoát, mọi thứ đều miễn phí.
Paul Tomblin

Tôi không thể hiểu làm thế nào có thể tạo ra một chiếc đồng hồ mới trong phương pháp chính? như bạn nói nó sẽ tạo ra nó mới mỗi lần gọi chính, nhưng chỉ có một phương thức chính. Làm thế nào phương pháp chính có thể đề cập đến các trường hợp đồng hồ khác nhau? Có một chút khó hiểu làm thế nào có thể tạo phiên bản mới của đồng hồ trong chính và sử dụng phương thức sayTime (), nhưng không thể tạo ra thể hiện ra khỏi chính và sử dụng sayTime (). Làm thế nào là mọi thứ miễn phí khi chính được gọi một lần? @PaulTomblin
ShakibaZar

@ user5621266 Tôi chỉ sử dụng mainphương thức này vì OP đã làm. Nếu thay vào đó, nó là một phương thức công khai được gọi từ nơi khác và lớp Hello được khởi tạo nhiều lần, thì nó có thể tạo một thể hiện Đồng hồ cho mỗi phiên bản Hello, trừ khi clocklà tĩnh.
Paul Tomblin

97

Các staticphương tiện từ khóa đó một cái gì đó (một lĩnh vực, phương pháp hoặc lớp lồng nhau) có liên quan đến các loại chứ không phải bất kỳ đặc biệt ví dụ của các loại. Vì vậy, ví dụ, một cuộc gọi Math.sin(...)mà không có bất kỳ phiên bản nào của Mathlớp và thực sự bạn không thể tạo một thể hiện của Mathlớp.

Để biết thêm thông tin, hãy xem bit có liên quan của Hướng dẫn Java của Oracle .


Sidenote

Thật không may, Java cho phép bạn truy cập các thành viên tĩnh như thể họ là thành viên thể hiện, vd

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

Điều đó làm cho nó trông giống như sleeplà một phương thức cá thể, nhưng thực ra nó là một phương thức tĩnh - nó luôn làm cho luồng hiện tại ngủ. Thực hành tốt hơn để làm rõ điều này trong mã gọi:

// Clearer
Thread.sleep(5000);

1
Một ví dụ khác: System.out.println () trông giống như một phương thức lớp, nhưng thực ra nó là một phương thức cá thể. Vì out là một ví dụ PrintStream trong lớp System.
Huệ Zhang

@LeslieCheung: Không, nó không giống như một phương thức lớp đối với tôi, vì System.outnó không giống như một tên loại đối với tôi.
Jon Skeet

42

Các statictừ khóa trong Java có nghĩa là biến hoặc chức năng được chia sẻ giữa tất cả các trường của lớp đó vì nó thuộc loại , không phải là đối tượng thực tế bản thân.

Vì vậy, nếu bạn có một biến: private static int i = 0;và bạn tăng nó ( i++) trong một trường hợp, thay đổi sẽ được phản ánh trong tất cả các trường hợp. ibây giờ sẽ là 1 trong tất cả các trường hợp.

Các phương thức tĩnh có thể được sử dụng mà không cần khởi tạo một đối tượng.


4
"Chung giữa tất cả các trường" cho ấn tượng sai, IMO - nó gợi ý rằng bạn làm cần phải có một thể hiện của đối tượng.
Jon Skeet

1
(Trong khi thực sự không cần phải có bất kỳ trường hợp nào , bởi vì trường tĩnh, v.v ... thuộc về loại .)
Jon Skeet

@Jon Skeet tĩnh thuộc loại, không phải đối tượng? Bạn có thể nói chi tiết hơn? Kiểu như kiểu dữ liệu: int, double, ...?
truongnm

@truongnm: Nhập như trong lớp khai báo biến / phương thức.
Jon Skeet

26

Cách sử dụng cơ bản của các thành viên tĩnh ...

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
}

Đó là cách bạn có thể chia sẻ các giá trị trong tất cả các thành viên lớp mà không cần gửi cá thể lớp Xin chào đến lớp khác. Và whit static bạn không cần tạo cá thể lớp.

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

Bạn chỉ có thể gọi các giá trị tĩnh hoặc phương thức theo tên lớp:

Hello.staticValue = "abc";

22

Tĩnh có nghĩa là bạn không phải tạo một thể hiện của lớp để sử dụng các phương thức hoặc biến được liên kết với lớp. Trong ví dụ của bạn, bạn có thể gọi:

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

trực tiếp, thay vì:

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

Từ bên trong một phương thức tĩnh (thuộc về một lớp), bạn không thể truy cập bất kỳ thành viên nào không tĩnh, vì các giá trị của chúng phụ thuộc vào việc khởi tạo lớp của bạn. Một đối tượng Đồng hồ không tĩnh, là thành viên thể hiện, sẽ có một giá trị / tham chiếu khác nhau cho mỗi phiên bản của lớp Hello của bạn và do đó bạn không thể truy cập nó từ phần tĩnh của lớp.


Giải thích tuyệt vời cho bối cảnh tĩnh :)
Abdel-Raouf

20

Tĩnh trong Java:

Tĩnh là một sửa đổi không truy cập. Từ khóa tĩnh thuộc về lớp hơn thể hiện của lớp. có thể được sử dụng để đính kèm Biến hoặc Phương thức vào Lớp.

Từ khóa tĩnh CÓ THỂ được sử dụng với:

phương pháp

Biến đổi

Lớp lồng trong lớp khác

Khối khởi tạo

KHÔNG THỂ được sử dụng với:

Lớp (Không lồng nhau)

Constructor

Giao diện

Phương thức Lớp bên trong cục bộ (Sự khác biệt sau đó lớp lồng nhau)

Phương thức lớp bên trong

Biến thể hiện

Biến cục bộ

Thí dụ:

Hãy tưởng tượng ví dụ sau có một biến đối tượng có tên là đếm tăng dần trong hàm tạo:

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

    }
}

Đầu ra:

1 1 1

Do biến đối tượng nhận được bộ nhớ tại thời điểm tạo đối tượng, mỗi đối tượng sẽ có bản sao của biến thể hiện, nếu nó được tăng lên, nó sẽ không phản ánh đến các đối tượng khác.

Bây giờ nếu chúng ta thay đổi số lượng biến thể hiện thành một biến tĩnh thì chương trình sẽ tạo ra đầu ra khác nhau:

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

    }
}

Đầu ra:

1 2 3

Trong trường hợp này, biến tĩnh sẽ chỉ nhận được bộ nhớ một lần, nếu bất kỳ đối tượng nào thay đổi giá trị của biến tĩnh, nó sẽ giữ lại giá trị của nó.

Tĩnh với Final:

Biến toàn cục được khai báo là cuối cùng và tĩnh không thay đổi cho toàn bộ thực thi. Bởi vì, các thành viên tĩnh được lưu trữ trong bộ nhớ lớp và chúng chỉ được tải một lần trong toàn bộ thực thi. Chúng là chung cho tất cả các đối tượng của lớp. Nếu bạn khai báo các biến tĩnh là cuối cùng, bất kỳ đối tượng nào cũng không thể thay đổi giá trị của chúng vì nó là cuối cùng. Do đó, các biến được khai báo là cuối cùng và tĩnh đôi khi được gọi là Hằng. Tất cả các trường của giao diện được gọi là hằng số, bởi vì chúng là cuối cùng và tĩnh theo mặc định.

nhập mô tả hình ảnh ở đây

Tài nguyên hình ảnh: Tĩnh cuối cùng


15

Để thêm vào câu trả lời hiện có, hãy để tôi thử với một hình ảnh:

Lãi suất 2% được áp dụng cho TẤT CẢ các tài khoản tiết kiệm. Do đó nó là tĩnh .

Một sự cân bằng nên là cá nhân , vì vậy nó không tĩnh.

nhập mô tả hình ảnh ở đây


13

Cuộc thảo luận này cho đến nay đã bỏ qua các cân nhắc của trình nạp lớp. Nói một cách chính xác, các trường tĩnh Java được chia sẻ giữa tất cả các phiên bản của một lớp cho một trình nạp lớp nhất định .


1
Điều này đã được Apocalisp đề cập trong các bình luận về câu trả lời của Merhdad.
Zach Langley

1
Điểm tốt. Nhiều người không biết điều này, nhưng một khi bạn bắt đầu nhắn tin với trình nạp lớp, nó trở nên rất quan trọng.
sleske

2
Đây là tất cả sự thật nhưng nó không trả lời câu hỏi. Nó nên được đăng như một bình luận.
Hầu tước Lorne

7

Một trường có thể được gán cho lớp hoặc một thể hiện của lớp. Theo mặc định các trường là các biến thể hiện. Bằng cách sử dụng statictrường trở thành một biến lớp, do đó có một và chỉ một clock. Nếu bạn thực hiện thay đổi ở một nơi, nó sẽ hiển thị ở mọi nơi. Biến thể sơ thẩm được thay đổi độc lập với nhau.


6

Từ khóa staticđược sử dụng để biểu thị một trường hoặc một phương thức thuộc về chính lớp đó chứ không phải thể hiện. Sử dụng mã của bạn, nếu đối tượng Clocklà tĩnh, tất cả các phiên bản của Hellolớp sẽ chia sẻ Clockthành viên dữ liệu này (trường) chung. Nếu bạn làm cho nó không tĩnh, mỗi trường hợp riêng lẻ Hellocó thể có một Clocktrường duy nhất .

Vấn đề là bạn đã thêm một phương thức chính vào lớp Hellođể bạn có thể chạy mã. Vấn đề ở đây là phương thức chính là tĩnh và do đó, nó không thể tham chiếu đến các trường không tĩnh hoặc các phương thức bên trong nó. Bạn có thể giải quyết điều này theo hai cách:

  1. Làm cho tất cả các trường và phương thức của Hellolớp tĩnh để chúng có thể được tham chiếu bên trong phương thức chính . Đây thực sự không phải là một điều tốt để làm (hoặc lý do sai t làm cho một trường và / hoặc một phương thức tĩnh)
  2. Tạo một thể hiện của Hellolớp của bạn bên trong phương thức chính và truy cập tất cả các trường và phương thức của nó theo cách chúng được dự định ở vị trí đầu tiên.

Đối với bạn, điều này có nghĩa là thay đổi sau đây đối với mã của bạn:

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

Trong Java, statictừ khóa có thể được coi đơn giản là chỉ ra những điều sau đây:

"không liên quan hoặc liên quan đến bất kỳ trường hợp cụ thể nào"

Nếu bạn nghĩ statictheo cách này, sẽ dễ hiểu hơn về việc sử dụng nó trong các bối cảnh khác nhau mà nó gặp phải:

  • Một statictrường là một trường thuộc về lớp chứ không phải bất kỳ trường hợp cụ thể nào

  • Một staticphương thức là một phương thức không có khái niệm this; nó được định nghĩa trên lớp và không biết về bất kỳ trường hợp cụ thể nào của lớp đó trừ khi tham chiếu được truyền cho nó

  • Một staticlớp thành viên là một lớp lồng nhau mà không có bất kỳ khái niệm hay kiến ​​thức nào về một thể hiện của lớp kèm theo của nó (trừ khi một tham chiếu đến một thể hiện của lớp kèm theo được truyền cho nó)


5

Tĩnh làm cho thành viên đồng hồ là thành viên lớp thay vì thành viên thể hiện. Nếu không có từ khóa tĩnh, bạn sẽ cần tạo một thể hiện của lớp Hello (có biến thành viên đồng hồ) - vd

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

5

Các phương thức tĩnh không sử dụng bất kỳ biến thể hiện nào của lớp mà chúng được định nghĩa. Một lời giải thích rất tốt về sự khác biệt có thể được tìm thấy trên trang này


5

Tôi đã phát triển một ý thích cho các phương thức tĩnh (chỉ, nếu có thể) trong các lớp "người trợ giúp".

Lớp gọi không cần tạo biến thành viên (thể hiện) khác của lớp trình trợ giúp. Bạn chỉ cần gọi các phương thức của lớp người trợ giúp. Ngoài ra, lớp trình trợ giúp được cải thiện vì bạn không còn cần một hàm tạo nữa và bạn không cần các biến thành viên (thể hiện).

Có lẽ có những lợi thế khác.


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

Cũng có thể nghĩ về các thành viên tĩnh không có con trỏ "này". Chúng được chia sẻ giữa tất cả các trường hợp.


3

Hiểu các khái niệm tĩnh

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

    }
}

Lớp thứ hai

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

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

2

main() là một phương thức tĩnh có hai hạn chế cơ bản:

  1. Phương thức tĩnh không thể sử dụng một thành viên dữ liệu không tĩnh hoặc gọi trực tiếp phương thức không tĩnh.
  2. this()super()không thể được sử dụng trong bối cảnh tĩnh.

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

Đầu ra: Lỗi thời gian biên dịch


1

Biến tĩnh chỉ có thể được truy cập chỉ trong các phương thức tĩnh, vì vậy khi chúng ta khai báo các biến tĩnh, các phương thức getter và setter sẽ là các phương thức tĩnh

phương thức tĩnh là một cấp lớp chúng ta có thể truy cập bằng tên lớp

Sau đây là ví dụ cho Biến và Getters tĩnh:

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

Một câu hỏi đã được hỏi ở đây về sự lựa chọn từ 'tĩnh' cho khái niệm này. Nó đã được đặt ra cho câu hỏi này, nhưng tôi không nghĩ rằng từ nguyên đã được giải quyết rõ ràng. Vì thế...


Đó là do sử dụng lại từ khóa, bắt đầu bằng C.

Xem xét khai báo dữ liệu trong C (bên trong một thân hàm):

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

Biến foo được tạo trên ngăn xếp khi hàm được nhập (và bị hủy khi hàm kết thúc). Ngược lại, thanh luôn ở đó, vì vậy nó 'tĩnh' theo nghĩa tiếng Anh thông dụng - nó không đi đâu cả.

Java và các ngôn ngữ tương tự, có cùng khái niệm về dữ liệu. Dữ liệu có thể được phân bổ cho mỗi phiên bản của lớp (mỗi đối tượng) hoặc một lần cho toàn bộ lớp. Do Java nhằm mục đích có cú pháp quen thuộc cho các lập trình viên C / C ++, nên từ khóa 'tĩnh' thích hợp ở đây.

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

Cuối cùng, chúng ta đến với phương pháp.

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

Về mặt khái niệm, có một ví dụ về foo () cho mọi trường hợp của lớp C. Chỉ có một thể hiện của thanh () cho toàn bộ lớp C. Điều này song song với trường hợp chúng ta đã thảo luận về dữ liệu và do đó sử dụng 'static 'lại là một lựa chọn hợp lý, đặc biệt nếu bạn không muốn thêm các từ khóa dành riêng cho ngôn ngữ của mình.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.