Tại sao phương thức chính của Java là tĩnh?


505

Chữ ký phương thức của phương thức Java () là:

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

Có một lý do cho phương pháp này là tĩnh?


1
trong trường hợp này, chúng ta không nên nói chữ ký phương thức , vì thuật ngữ này chỉ đề cập đến tên phương thức và các tham số của nó
Andrew Tobilko

Java được thiết kế có chủ ý để trông quen thuộc với một lập trình viên C. Điều này rất gần với quy ước C.
Thorbjørn Ravn Andersen

Câu trả lời:


337

Phương thức này là tĩnh vì nếu không sẽ có sự mơ hồ: nên gọi hàm tạo nào? Đặc biệt nếu lớp của bạn trông như thế này:

public class JavaClass{
  protected JavaClass(int x){}
  public void main(String[] args){
  }
}

JVM có nên gọi new JavaClass(int)không? Nó nên vượt qua để làm xgì?

Nếu không, JVM có nên khởi tạo JavaClassmà không chạy bất kỳ phương thức constructor nào không? Tôi nghĩ là không nên, vì điều đó sẽ đặc biệt trong toàn bộ lớp học của bạn - đôi khi bạn có một cá thể chưa được khởi tạo và bạn phải kiểm tra nó trong mọi phương thức có thể được gọi.

Có quá nhiều trường hợp cạnh và sự mơ hồ đối với nó có nghĩa là JVM phải khởi tạo một lớp trước khi điểm vào được gọi. Đó là lý do tại sao mainlà tĩnh.

Tôi không biết tại sao mainluôn luôn được đánh dấu publicmặc dù.


4
Việc thực hiện một giao diện không giải quyết được vấn đề khởi tạo.
Jacob Krall

26
Cá nhân tôi thích điều đó public static void mainđóng vai trò là điểm đánh dấu của một điểm vào - một nhà xây dựng không tham số công khai không hét lên "Đây có lẽ là một điểm vào!" theo cùng một cách
Jacob Krall

5
@EdwinDalorzo - Điều gì sẽ đạt được bằng cách buộc lớp điểm vào phải được khởi tạo? Gọi một phương thức tĩnh đặt gánh nặng ít nhất lên lớp. Tự do khởi tạo chính nó nếu điều đó có ý nghĩa hơn cho thiết kế của bạn.
David Harkness

18
Nhà xây dựng nên được gọi là gì? Làm thế nào mà thậm chí có thể hình dung là một vấn đề? Vấn đề tương tự như thế còn tồn tại đối với quyết định maingọi. Thật kỳ lạ (đối với bạn), JVM quản lý việc này tốt.
Konrad Rudolph

9
Phương thức chính luôn được công khai vì nó phải được truy cập bởi công cụ thời gian chạy, JVM.
nhịp điệu

398

Đây chỉ là quy ước. Trong thực tế, ngay cả tên main () và các đối số được truyền vào hoàn toàn là quy ước.

Khi bạn chạy java.exe (hoặc javaw.exe trên Windows), điều thực sự xảy ra là một vài lệnh gọi Giao diện gốc Java (JNI). Các cuộc gọi này tải DLL thực sự là JVM (đúng vậy - java.exe KHÔNG phải là JVM). JNI là công cụ mà chúng ta sử dụng khi chúng ta phải kết nối thế giới máy ảo và thế giới của C, C ++, v.v ... Điều ngược lại cũng đúng - ít nhất là theo kiến ​​thức của tôi) JVM chạy mà không sử dụng JNI.

Về cơ bản, java.exe là một ứng dụng C siêu đơn giản, phân tích cú pháp dòng lệnh, tạo một chuỗi String mới trong JVM để giữ các đối số đó, phân tích tên lớp mà bạn đã chỉ định là chứa hàm main (), sử dụng các lệnh gọi JNI để tìm Chính phương thức main (), sau đó gọi phương thức main (), truyền vào mảng chuỗi vừa tạo như một tham số. Điều này rất, rất giống với những gì bạn làm khi bạn sử dụng sự phản chiếu từ Java - thay vào đó, nó chỉ sử dụng các lệnh gọi hàm gốc có tên khó hiểu.

Sẽ là hoàn toàn hợp pháp khi bạn viết phiên bản java.exe của riêng bạn (nguồn được phân phối với JDK) và để nó làm một cái gì đó hoàn toàn khác. Trên thực tế, đó chính xác là những gì chúng tôi làm với tất cả các ứng dụng dựa trên Java của chúng tôi.

Mỗi ứng dụng Java của chúng tôi có trình khởi chạy riêng. Chúng tôi chủ yếu làm điều này để chúng tôi có được biểu tượng và tên quy trình của riêng mình, nhưng nó có ích trong các tình huống khác mà chúng tôi muốn làm gì đó ngoài lệnh gọi chính () thông thường để thực hiện mọi thứ (Ví dụ: trong một trường hợp chúng tôi đang làm Khả năng tương tác của COM và chúng tôi thực sự chuyển một điều khiển COM vào hàm main () thay vì mảng chuỗi).

Vì vậy, dài và ngắn: lý do tĩnh là vì nó thuận tiện. Lý do nó được gọi là 'chính' là nó phải là một cái gì đó, và chính () là những gì họ đã làm trong những ngày xưa của C (và vào thời đó, tên của hàm quan trọng). Tôi cho rằng java.exe có thể cho phép bạn chỉ định một tên phương thức chính đủ điều kiện, thay vì chỉ lớp (java com.mycompany.Foo.some SpecialMain) - nhưng điều đó chỉ khiến IDE khó tự động phát hiện hơn ' các lớp có thể khởi chạy trong một dự án.


66
+1: Rất hấp dẫn (đặc biệt là phần viết về một tùy chỉnh java.exe)
Adam Paynter

9
Thú vị, tôi không đồng ý với "Đây chỉ là quy ước." Một phần của câu trả lời. Câu hỏi chính của OP là lý do tĩnh trong tuyên bố. Tôi không nghĩ rằng statictrong main()tuyên bố chỉ là vì lợi ích của quy ước. Tuy nhiên, thực tế là 'main ()' và không phải thứ gì khác khả thi.
Jared

2
@David Thế là xong. Tôi thực sự đã thích một câu trả lời từ một trong những người liên quan ban đầu - nhưng đó là một cú sút rất xa. Hầu hết các câu trả lời khác không may là một bài tập về lý luận đặc biệt. Điều này cung cấp các chi tiết khá thú vị, bên cạnh việc khiêm tốn không phát minh ra các chi tiết kỹ thuật sai để loại bỏ một nguyên nhân phi kỹ thuật (có thể).
Konrad Rudolph

2
@Jared - Họ có thể đã yêu cầu một nhà xây dựng không tranh luận công khai và làm cho mainkhông tĩnh và vẫn phù hợp với giới hạn của ngôn ngữ. Không cần nghe ý kiến ​​từ các nhà thiết kế, chúng tôi sẽ phải đồng ý không đồng ý. :)
David Harkness

4
@BenVoigt Bạn gọi LoadL Library () để lấy dv jvm. Sau đó, bạn gọi getprocaddress ("JNI_CreateJavaVM"), sau đó bạn gọi hàm JNI_CreateJavaVM ( docs.oracle.com/javase/1.4.2/docs/guide/jni/spec/ tựa ). Khi VM được tải, bạn sử dụng các lệnh gọi JNI tiêu chuẩn để tìm đúng lớp, tải phương thức chính tĩnh và gọi nó. Không có nhiều chỗ để giải thích sai ở đó. JNI hoàn toàn là cách bạn tải VM. Bạn có thể được sử dụng để chỉ viết JNI phía khách hàng bằng cách sử dụng từ khóa gốc, javah -jni, v.v ... nhưng đó chỉ là một nửa của JNI.
Ngày Kevin

188

Các main()phương pháp trong C++, C#Javalà tĩnh
Bởi vì họ sau đó có thể được gọi bởi các công cụ thời gian chạy mà không cần phải khởi tạo một đối tượng nào thì mã trong cơ thể của main()sẽ làm phần còn lại.


1
Được rồi nhưng không thể chạy ngay lập tức một đối tượng của lớp? Rồi gọi phương thức Chính? Tại sao?
Andrei Rînea

12
Làm thế nào JVM sẽ biết hàm tạo nào sẽ gọi, nếu lớp chính của bạn có các hàm tạo quá tải? Những thông số nào nó sẽ vượt qua?
Jacob Krall

1
@ Không khi bạn nói lớp cha có nghĩa là lớp chứa phương thức chính? Bởi vì nếu vậy, thuật ngữ "lớp cha mẹ" khá khó hiểu ở đây, và nếu không thì nó sẽ không có ý nghĩa với tôi. Ngoài ra, nếu theo quy ước chúng ta sử dụng public static void main..., tại sao quy ước không thể là lớp điểm vào ứng dụng nên có một hàm tạo mặc định công khai?
Edwin Dalorzo

2
@Jacob Làm thế nào để JVM biết quá tải static void mainđể gọi? Không phải là một vấn đề gì cả.
Konrad Rudolph

4
@Namratha: Vâng, bạn đang thiếu một cái gì đó. Điều đó không đúng khi "phương thức tĩnh không thể tham chiếu phương thức không tĩnh". Câu lệnh đúng là: "Mọi phương thức tĩnh phải cung cấp một đối tượng khi sử dụng bất kỳ phương thức không tĩnh nào". Và nhìn xem, staticcác phương thức như mainthường xuyên sử dụng newđể tạo ra một đối tượng như vậy.
Ben Voigt

38

Tại sao static static void main (String [] args)?

Đây là cách Ngôn ngữ Java được thiết kế và Máy ảo Java được thiết kế và viết.

Đặc tả ngôn ngữ Java của Oracle

Kiểm tra Thi hành Chương 12 - Phần 12.1.4 Gọi Test.main :

Cuối cùng, sau khi hoàn thành việc khởi tạo cho Kiểm tra lớp (trong đó có thể xảy ra tải, liên kết và khởi tạo hệ quả khác), phương thức chính của Kiểm tra được gọi.

Phương thức chính phải được khai báo công khai, tĩnh và void. Nó phải chấp nhận một đối số duy nhất là một chuỗi các chuỗi. Phương pháp này có thể được khai báo là một trong hai

public static void main(String[] args)

hoặc là

public static void main(String... args)

Đặc tả máy ảo Java Java

Kiểm tra các khái niệm ngôn ngữ lập trình Java Chương 2 - Phần 2.17 Thực thi :

Máy ảo Java bắt đầu thực thi bằng cách gọi phương thức chính của một số lớp được chỉ định và truyền cho nó một đối số duy nhất, đó là một chuỗi các chuỗi. Điều này làm cho lớp được chỉ định được tải (§2.17.2), được liên kết (§2.17.3) với các loại khác mà nó sử dụng và được khởi tạo (§2.17.4). Phương thức chính phải được khai báo công khai, tĩnh và void.

Nguồn OpenJDK của Oracle

Tải xuống và trích xuất jar nguồn và xem cách viết JVM, kiểm tra ../launcher/java.c, trong đó có chứa mã C gốc phía sau lệnh java [-options] class [args...]:

/*
 * Get the application's main class.
 * ... ...
 */
if (jarfile != 0) {
    mainClassName = GetMainClassName(env, jarfile);

... ...

    mainClass = LoadClass(env, classname);
    if(mainClass == NULL) { /* exception occured */

... ...

/* Get the application's main method */
mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                   "([Ljava/lang/String;)V");

... ...

{    /* Make sure the main method is public */
    jint mods;
    jmethodID mid;
    jobject obj = (*env)->ToReflectedMethod(env, mainClass,
                                            mainID, JNI_TRUE);

... ...

/* Build argument array */
mainArgs = NewPlatformStringArray(env, argv, argc);
if (mainArgs == NULL) {
    ReportExceptionDescription(env);
    goto leave;
}

/* Invoke main method. */
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

... ...

4
Vấn đề ở đây là đây thực sự là một câu trả lời rất hay cho câu hỏi ở dạng ban đầu, với rất nhiều tài liệu tham khảo (+1). Tuy nhiên, tôi muốn tìm hiểu về cơ sở lý luận cho quyết định thiết kế làm cho phương thức tĩnh trở thành điểm vào, thay vì phương thức xây dựng hoặc phương thức cá thể.
Konrad Rudolph

1
@KonradRudolph, đối với các câu hỏi liên quan đến ngôn ngữ và thiết kế đặc tả JVM, có lẽ bạn có thể thử liên hệ với nguồn gốc từ Oracle và xem liệu bạn có thể nhận được bất kỳ phản hồi tích cực nào không.
yorkw

2
Nói chung, khi tính toán kết quả phương thức chỉ phụ thuộc vào các tham số của nó, do đó nó không phụ thuộc vào trạng thái bên trong của đối tượng, nó có thể là tĩnh. Và nên đặt nó ở dạng tĩnh cho khả năng duy trì / tái sử dụng mã. Nếu phương thức mainkhông tĩnh, điều đó có nghĩa là phải biết trạng thái thể hiện của lớp và nó phức tạp hơn nhiều để định nghĩa, giống như hàm tạo nào được sử dụng trước.
Yves Martin

@KonradRudolph Thật thú vị, Oak (tiền thân của Java) đã yêu cầu phương thức chính để có một nguyên mẫu tương tự: public static void main(String arguments[])- Tham khảo: Oak 0.2 Spec .
assylias

2
@ Có thể là như vậy. Không cần thiết, nếu một thiết kế khác có ý nghĩa. Tôi đã nghe một số lập luận tốt trong các ý kiến ở đây, nhưng tôi vẫn nghĩ rằng một quá trình là một cách hiệu quả rất giống một sợi (nó ), và a thread trong Java thường được biểu diễn dưới dạng một thể hiện của Runnable. Đại diện cho toàn bộ quá trình theo cùng một cách (nghĩa là có Runnable.Runđiểm đầu vào) chắc chắn có ý nghĩa trong Java. Tất nhiên, Runnablebản thân nó được cho là một lỗ hổng thiết kế, do thực tế là Java không có các phương thức ẩn danh (chưa). Nhưng vì nó đã có sẵn
Konrad Rudolph

36

Chúng ta hãy giả vờ, điều đó staticsẽ không được yêu cầu như là điểm vào ứng dụng.

Một lớp ứng dụng sau đó sẽ trông như thế này:

class MyApplication {
    public MyApplication(){
        // Some init code here
    }
    public void main(String[] args){
        // real application code here
    }
}

Sự khác biệt giữa mã xây dựng và mainphương thức là cần thiết bởi vì trong OO, một hàm tạo sẽ chỉ đảm bảo rằng một thể hiện được khởi tạo đúng. Sau khi khởi tạo, thể hiện có thể được sử dụng cho "dịch vụ" dự định. Đặt mã ứng dụng hoàn chỉnh vào hàm tạo sẽ làm hỏng điều đó.

Vì vậy, cách tiếp cận này sẽ buộc ba hợp đồng khác nhau khi áp dụng:

  • phải là một constructor mặc định. Mặt khác, JVM sẽ không biết hàm tạo nào sẽ gọi và tham số nào sẽ được cung cấp.
  • phải là một mainphương pháp 1 . Ok, điều này không đáng ngạc nhiên.
  • Lớp học không được abstract. Mặt khác, JVM không thể khởi tạo nó.

Cách statictiếp cận mặt khác chỉ yêu cầu một hợp đồng:

  • Phải có mainphương pháp 1 .

Ở đây abstractcũng không có nhiều nhà xây dựng vấn đề.

Do Java được thiết kế để trở thành một ngôn ngữ đơn giản cho người dùng , không có gì đáng ngạc nhiên khi điểm nhập ứng dụng cũng được thiết kế theo cách đơn giản sử dụng một hợp đồng và không theo cách phức tạp khi sử dụng ba hợp đồng độc lập và dễ vỡ.

Xin lưu ý: Đối số này không phải là về sự đơn giản bên trong JVM hoặc bên trong JRE. Đối số này là về sự đơn giản cho người dùng .


1 Ở đây chữ ký đầy đủ được tính là chỉ một hợp đồng.


1
Trên thực tế, các yêu cầu phức tạp hơn: phải có một mainphương pháp đó là public, staticvà có chữ ký void main(String[]). Tôi đồng ý rằng, nếu phương thức là một phương thức ví dụ, JRE sẽ có nhiều công việc hơn một chút nhưng loại công việc sẽ giống nhau và độ phức tạp không cao hơn đáng kể (xem các cuộc thảo luận trong nhận xét của câu trả lời trước). Tôi không tin rằng sự khác biệt này chiếm quyết định làm cho điểm đầu vào tĩnh, đặc biệt vì các phương thức cần thiết cho độ phân giải của phương thức cá thể tồn tại và có thể sử dụng được.
Konrad Rudolph

3
@KonradRudolph: Quan điểm của tôi không phải là về công việc mà JRE sẽ phải làm. Quan điểm của tôi là về việc buộc mọi người sử dụng ngôn ngữ phải tuân theo nhiều hợp đồng hơn khi cần thiết. Theo nghĩa này, một static public main(String[])phương thức là một chữ ký và do đó một hợp đồng. Nếu không, ba hợp đồng độc lập phải được tuân theo.
AH

1
Ah. Tôi vẫn không đồng ý rằng điều này làm cho bất kỳ sự khác biệt mặc dù. Các lớp điểm đầu vào cũng có thể thực hiện Runnable. Rõ ràng, Java mong muốn các nhà phát triển tuân theo hợp đồng đó mọi lúc, tại sao nó phải quá nhiều cho điểm vào ứng dụng? Điều đó không có ý nghĩa.
Konrad Rudolph

3
@KonradRudolph: Không có mâu thuẫn: Trong một trường hợp, hệ thống sẽ buộc ba hợp đồng đối với người dùng. Các hợp đồng không rõ ràng, không thể kiểm tra được thông qua trình biên dịch và theo quan điểm của người dùng là độc lập. Trong trường hợp thông thường ThreadRunnablekhông có gì bị ẩn khỏi người dùng, anh ta có thể thấy rõ những gì đang diễn ra và anh ta có thay đổi để chỉ thực hiện những hợp đồng phù hợp với mình - anh ta đang kiểm soát chứ không phải hệ thống.
AH

2
Đây là câu trả lời tốt nhất ở đây. Thật đáng tiếc khi nhiều người dùng sẽ chỉ đọc 2 hoặc 3 câu trả lời hàng đầu trên trang; và điều này không có khả năng đến đó bất cứ lúc nào sớm. Nó đề cập đến điểm quan trọng của hàm tạo là CHỈ để khởi tạo - và do đó, không có ý nghĩa gì khi viết mã theo kiểu mà hàm tạo chạy toàn bộ ứng dụng.
Dawood ibn Kareem

14

Nếu không, hàm tạo nào sẽ được sử dụng nếu có nhiều hơn một?

Có nhiều thông tin hơn về việc khởi tạo và thực thi các chương trình Java có sẵn trong Đặc tả ngôn ngữ Java .


12

Trước khi phương thức chính được gọi, không có đối tượng nào được khởi tạo. Có từ khóa tĩnh có nghĩa là phương thức có thể được gọi mà không cần tạo bất kỳ đối tượng nào trước.


Sai lầm. Hoặc ít nhất là rất không chính xác. public class Main {static Object object = new Object () {{System.out.println ("object created"); }}; public static void main (String [] args) {System.out.println ("in main"); }}
eljenso

Nhận xét công bằng. Về mặt kỹ thuật, tôi nên nói rằng trước khi phương thức Main được gọi, lớp chứa phương thức chính không được khởi tạo.
BlackWasp

12

Bởi vì nếu không, nó sẽ cần một thể hiện của đối tượng được thực thi. Nhưng nó phải được gọi từ đầu, mà không xây dựng đối tượng trước, vì nó thường là nhiệm vụ của hàm main () (bootstrap), để phân tích các đối số và xây dựng đối tượng, thường bằng cách sử dụng các tham số chương trình / đối số này.


10

Hãy để tôi giải thích những điều này một cách đơn giản hơn nhiều:

public static void main(String args[])

Tất cả các ứng dụng Java, ngoại trừ các applet, bắt đầu thực thi chúng main().

Từ khóa publiclà một công cụ sửa đổi truy cập cho phép thành viên được gọi từ bên ngoài lớp.

staticđược sử dụng bởi vì nó cho phép main()được gọi mà không cần phải khởi tạo một thể hiện cụ thể của lớp đó.

voidchỉ ra rằng main()không trả lại bất kỳ giá trị.


9

Ý nghĩa của là public static void main(String args[])gì?

  1. public là một trình xác định truy cập có nghĩa là bất kỳ ai cũng có thể truy cập / gọi nó như JVM (Máy ảo Java.
  2. staticcho phép main()được gọi trước khi một đối tượng của lớp được tạo. Điều này là cần thiết bởi vì main()được gọi bởi JVM trước khi bất kỳ đối tượng nào được tạo ra. Vì nó là tĩnh nên nó có thể được gọi trực tiếp qua lớp.

    class demo {    
        private int length;
        private static int breadth;
        void output(){
            length=5;
            System.out.println(length);
        }
    
        static void staticOutput(){
            breadth=10; 
            System.out.println(breadth);
        }
    
        public static  void main(String args[]){
            demo d1=new demo();
            d1.output(); // Note here output() function is not static so here
            // we need to create object
            staticOutput(); // Note here staticOutput() function is  static so here
            // we needn't to create object Similar is the case with main
            /* Although:
            demo.staticOutput();  Works fine
            d1.staticOutput();  Works fine */
        }
    }

    Tương tự, đôi khi chúng ta sử dụng tĩnh cho các phương thức do người dùng xác định để chúng ta không cần tạo đối tượng.

  3. voidchỉ ra rằng main()phương thức được khai báo không trả về giá trị.

  4. String[] argschỉ định tham số duy nhất trong main()phương thức.

    args- một tham số có chứa một mảng các đối tượng thuộc loại lớp String.


6

Applet, midlets, servlets và bean các loại được xây dựng và sau đó có các phương pháp vòng đời được gọi trên chúng. Gọi main là tất cả những gì đã được thực hiện cho lớp chính, vì vậy không cần phải giữ trạng thái trong một đối tượng được gọi là nhiều lần. Việc xác định chính trên một lớp khác là điều khá bình thường (mặc dù không phải là một ý tưởng hay), điều này sẽ cản trở việc sử dụng lớp để tạo đối tượng chính.


5

Nó chỉ là một quy ước, nhưng có lẽ thuận tiện hơn so với thay thế. Với một hàm tĩnh, tất cả những gì bạn cần biết để gọi một chương trình Java là tên và vị trí của một lớp. Nếu nó không tĩnh, bạn cũng phải biết cách khởi tạo lớp đó hoặc yêu cầu lớp đó có một hàm tạo trống.


Đó không phải là một quy ước; đó là một phần của đặc tả ngôn ngữ; bộ thực thi sẽ không nhận ra một lớp không có phương thức chính tĩnh là điểm vào hợp lệ.
Cướp

2
Các thông số ngôn ngữ chính nó theo quy ước. Không có yêu cầu thực tế nào cho các nhà thiết kế Java đã chọn không yêu cầu một tĩnh chính. Tuy nhiên, như Logan giải thích, các lựa chọn thay thế phức tạp hơn.
David Arno

@DavidArno Sẽ hợp lý hơn khi nói rằng quy ước tuân theo đặc tả ngôn ngữ.
Hầu tước Lorne

5

Nếu phương thức chính không tĩnh, bạn sẽ cần tạo một đối tượng của lớp chính từ bên ngoài chương trình. Làm thế nào bạn muốn làm điều đó?


5

Khi bạn thực thi Máy ảo Java (JVM) bằng javalệnh,

java ClassName argument1 argument2 ...

Khi bạn thực thi ứng dụng của mình, bạn chỉ định tên lớp của nó làm đối số cho lệnh java, như trên

JVM cố gắng gọi phương thức chính của lớp mà bạn chỉ định

Thời điểm này, không có đối tượng nào của lớp được tạo ra.

Khai báo maindưới dạng tĩnh allowsJVM thành invokechính withouttạo một instancelớp.

chúng ta hãy quay lại với lệnh

ClassNamelà một command-line argumentJVM cho nó biết lớp nào sẽ thực thi. Theo ClassName, bạn cũng có thể chỉ định một list of Strings(được phân tách bằng dấu cách) làm đối số dòng lệnh mà JVM sẽ truyền cho ứng dụng của bạn. -Các đối số có thể được sử dụng để chỉ định các tùy chọn (ví dụ: tên tệp) để chạy ứng dụng - đây là lý do tại sao có một tham số được gọi String[] argstrong chính

Tài liệu tham khảo: Cách lập trình Java ™ (Đối tượng ban đầu), Phiên bản thứ mười


4

Gần đây, câu hỏi tương tự đã được đăng tại Lập trình viên.

TL; DR một phần của câu trả lời được chấp nhận là,

Trong Java, lý do public static void main(String[] args)

  1. Mong muốn
  2. mã được viết bởi một người có kinh nghiệm về C (không phải bằng Java)
  3. được thực thi bởi ai đó đã từng chạy PostScript trên NeWS

http://i.stack.imgur.com/qcmzP.png

 
Đối với C #, lý do tương tự quá mức để nói. Các nhà thiết kế ngôn ngữ giữ cho cú pháp điểm nhập chương trình quen thuộc với các lập trình viên đến từ Java. Như kiến ​​trúc sư C #, Anders Hejlsberg ,

... Cách tiếp cận của chúng tôi với C # đơn giản là cung cấp một giải pháp thay thế ... cho các lập trình viên Java ...

...


3

Tôi nghĩ rằng từ khóa 'static' làm cho phương thức chính trở thành một phương thức lớp và các phương thức lớp chỉ có một bản sao của nó và có thể được chia sẻ bởi tất cả, và đồng thời, nó không yêu cầu một đối tượng để tham khảo. Vì vậy, khi lớp trình điều khiển được biên dịch, phương thức chính có thể được gọi. (Tôi chỉ ở cấp độ bảng chữ cái của java, xin lỗi nếu tôi sai)


Tất cả các phương pháp 'chỉ có một bản sao của nó'.
Hầu tước Lorne

3

main () là tĩnh vì; tại thời điểm đó trong vòng đời của ứng dụng, ngăn xếp ứng dụng có tính chất thủ tục do không có đối tượng nào được khởi tạo.

Đó là một bảng đá sạch. Ứng dụng của bạn đang chạy vào thời điểm này, ngay cả khi không có bất kỳ đối tượng nào được khai báo (hãy nhớ rằng, có các mẫu mã hóa OO theo thủ tục). Bạn, với tư cách là nhà phát triển, biến ứng dụng thành một giải pháp hướng đối tượng bằng cách tạo các thể hiện của các đối tượng của bạn và tùy thuộc vào mã được biên dịch bên trong.

Hướng đối tượng là tuyệt vời cho hàng triệu lý do rõ ràng. Tuy nhiên, đã qua rồi những ngày mà hầu hết các nhà phát triển VB thường xuyên sử dụng các từ khóa như "goto" trong mã của họ. "goto" là một lệnh thủ tục trong VB được thay thế bằng đối tác OO của nó: gọi phương thức.

Bạn cũng có thể xem điểm nhập cảnh tĩnh (chính) là tự do thuần túy. Nếu Java đủ khác để khởi tạo một đối tượng và chỉ trình bày trường hợp đó cho bạn khi chạy, bạn sẽ không có lựa chọn nào NHƯNG để viết một ứng dụng thủ tục. Không thể tưởng tượng được như Java có thể nghe được, có thể có nhiều kịch bản đòi hỏi các cách tiếp cận theo thủ tục.

Đây có lẽ là một câu trả lời rất mơ hồ. Hãy nhớ rằng, "lớp" chỉ là một tập hợp các mã liên quan đến nhau. "Sơ thẩm" là một thế hệ tự trị cô lập, sống và thở của lớp đó.


7
Điều này là không chính xác. Rất nhiều đối tượng được khởi tạo trước khi mainđạt được. Và nếu bạn bao gồm một hàm tạo tĩnh trong lớp chứa hàm chính, thì nó sẽ được thực thi trước đó main.
Konrad Rudolph

2

Nó chỉ là một quy ước. JVM chắc chắn có thể xử lý các phương thức chính không tĩnh nếu đó là quy ước. Rốt cuộc, bạn có thể định nghĩa một trình khởi tạo tĩnh trên lớp của mình và khởi tạo một trăm đối tượng trước khi đến phương thức main () của bạn.


2

Protoype public static void main(String[])là một quy ước được định nghĩa trong JLS :

Phương thức chính phải được khai báo công khai, tĩnh và void. Nó phải chỉ định một tham số chính thức (§8.4.1) có kiểu khai báo là mảng Chuỗi.

Trong đặc tả JVM 5.2. Khởi động máy ảo chúng ta có thể đọc:

Máy ảo Java khởi động bằng cách tạo một lớp ban đầu, được chỉ định theo cách phụ thuộc vào việc triển khai, sử dụng trình nạp lớp bootstrap (§5.3.1). Sau đó, máy ảo Java liên kết lớp ban đầu, khởi tạo nó và gọi phương thức lớp công khai void main (String []) . Việc gọi phương thức này thúc đẩy tất cả thực hiện thêm. Việc thực thi các hướng dẫn máy ảo Java cấu thành phương thức chính có thể gây ra liên kết (và do đó tạo ra) các lớp và giao diện bổ sung, cũng như gọi các phương thức bổ sung.

Điều thú vị là, trong đặc tả JVM, không đề cập đến việc phương thức chính phải là tĩnh. Nhưng thông số kỹ thuật cũng nói rằng máy ảo Java thực hiện 2 bước trước:

Khởi tạo một lớp hoặc giao diện bao gồm thực thi phương thức khởi tạo lớp hoặc giao diện của nó.

Trong 2.9. Phương pháp đặc biệt :

Một phương thức khởi tạo lớp hoặc giao diện được định nghĩa:

Một lớp hoặc giao diện có nhiều nhất một phương thức khởi tạo lớp hoặc giao diện và được khởi tạo (§5.5) bằng cách gọi phương thức đó. Phương thức khởi tạo của một lớp hoặc giao diện có tên đặc biệt <clinit>, không có đối số và không có giá trị.

Và một phương thức khởi tạo lớp hoặc giao diện khác với một phương thức khởi tạo cá thể được định nghĩa như sau:

Ở cấp độ của máy ảo Java, mọi hàm tạo được viết bằng ngôn ngữ lập trình Java (JLS §8.8) xuất hiện dưới dạng một phương thức khởi tạo cá thể có tên đặc biệt <init>.

Vì vậy, JVM khởi tạo một phương thức khởi tạo lớp hoặc giao diện chứ không phải là một phương thức khởi tạo cá thể mà thực sự là một hàm tạo. Vì vậy, họ không cần đề cập rằng phương thức chính phải tĩnh trong đặc tả JVM vì nó ngụ ý bởi thực tế là không có cá thể nào được tạo trước khi gọi phương thức chính.


2

Các publictừ khóa là một modifier truy cập, cho phép các lập trình viên để kiểm soát khả năng hiển thị của các thành viên lớp. Khi một thành viên lớp được đi trước public, thì thành viên đó có thể được truy cập bằng mã bên ngoài lớp mà nó được khai báo.

Ngược lại publicprivate, ngăn không cho thành viên được sử dụng bởi mã được xác định bên ngoài lớp của nó.

Trong trường hợp này, main()phải được khai báo là public, vì nó phải được gọi bằng mã bên ngoài lớp khi chương trình được khởi động.

Từ khóa staticcho phép main()được gọi mà không cần phải khởi tạo một thể hiện cụ thể của lớp. Điều này là cần thiết vì main()được trình thông dịch Java gọi trước khi bất kỳ đối tượng nào được tạo.

Từ khóa voidchỉ đơn giản là cho trình biên dịch main()không trả về giá trị.


1

Điểm vào thực sự cho bất kỳ ứng dụng nào là một phương thức tĩnh. Nếu ngôn ngữ Java hỗ trợ một phương thức cá thể là "điểm vào", thì bộ thực thi sẽ cần triển khai nó bên trong như một phương thức tĩnh, xây dựng một thể hiện của đối tượng theo sau bằng cách gọi phương thức cá thể.

Ngoài ra, tôi sẽ kiểm tra lý do để chọn một trong ba tùy chọn sau:

  1. A static void main()như chúng ta thấy ngày hôm nay.
  2. Một phương thức thể hiện void main()được gọi trên một đối tượng được xây dựng mới.
  3. Sử dụng hàm tạo của một kiểu làm điểm vào (ví dụ: nếu lớp mục nhập được gọi Program, thì việc thực thi sẽ bao gồm một cách hiệu quả new Program()).

Phá vỡ:

static void main()

  1. Gọi hàm tạo tĩnh của lớp kèm theo.
  2. Gọi phương thức tĩnh main().

void main()

  1. Gọi hàm tạo tĩnh của lớp kèm theo.
  2. Xây dựng một thể hiện của lớp kèm theo bằng cách gọi hiệu quả new ClassName().
  3. Gọi phương thức cá thể main().

new ClassName()

  1. Gọi hàm tạo tĩnh của lớp kèm theo.
  2. Xây dựng một thể hiện của lớp (sau đó không làm gì với nó và chỉ cần trả về).

Cơ sở lý luận:

Tôi sẽ đi theo thứ tự ngược lại cho cái này.

Hãy nhớ rằng một trong những mục tiêu thiết kế của Java là nhấn mạnh (yêu cầu khi có thể) các thực hành lập trình hướng đối tượng tốt. Trong ngữ cảnh này, hàm tạo của một đối tượng khởi tạo đối tượng, nhưng không chịu trách nhiệm cho hành vi của đối tượng. Do đó, một đặc điểm kỹ thuật đã cho một điểm vàonew ClassName() sẽ gây nhầm lẫn tình huống cho các nhà phát triển Java mới bằng cách buộc một ngoại lệ đối với thiết kế của một hàm tạo "lý tưởng" trên mọi ứng dụng.

Bằng cách thực hiện main()một phương thức cá thể, vấn đề trên chắc chắn được giải quyết. Tuy nhiên, nó tạo ra sự phức tạp bằng cách yêu cầu đặc tả liệt kê chữ ký của hàm tạo của lớp nhập cũng như chữ ký của main()phương thức.

Tóm lại, việc chỉ định a static void main()tạo ra một đặc tả có độ phức tạp ít nhất trong khi tuân thủ nguyên tắc đặt hành vi vào các phương thức . Xem xét cách đơn giản để thực hiện một main()phương thức mà chính nó xây dựng một thể hiện của một lớp và gọi một phương thức cá thể, không có lợi thế thực sự để chỉ định main()như một phương thức cá thể.


1
Đây chỉ là cầu xin câu hỏi. Dù sao thì Java cũng cần một trình tải ứng dụng, điều này rất nặng nề trước khi gọi main. Lý do của bạn về mainviệc quá phức tạp cho người mới bắt đầu dường như không thể tin được. Trong thực tế, tĩnh mainrất khó hiểu cho người mới bắt đầu, tôi nghi ngờ một constructor sẽ được nhiều hơn như vậy. Bạn nói rằng một người xây dựng nên không chịu trách nhiệm về hành vi của đối tượng. Điều này nghe có vẻ thú vị nhưng tôi không chắc là mình đồng ý. Tại sao không? Điều gì ngăn cản điều này?
Konrad Rudolph

1

static - Khi JVM thực hiện một cuộc gọi đến phương thức chính, không có đối tượng nào tồn tại cho lớp được gọi do đó nó phải có phương thức tĩnh để cho phép gọi từ lớp.


1

Tôi không biết nếu JVM gọi phương thức chính trước khi các đối tượng được khởi tạo ... Nhưng có một lý do mạnh mẽ hơn nhiều tại sao phương thức main () là tĩnh ... Khi JVM gọi phương thức chính của lớp (nói , Người). nó gọi nó bằng " Person.main () ". Bạn thấy đấy, JVM gọi nó bằng tên lớp. Đó là lý do tại sao phương thức main () được coi là tĩnh và công khai để JVM có thể được truy cập.

Hy vọng nó sẽ giúp. Nếu có, hãy cho tôi biết bằng cách bình luận.


0

Các phương thức tĩnh không yêu cầu bất kỳ đối tượng nào. Nó chạy trực tiếp nên chính chạy trực tiếp.


0

Từ khóa tĩnh trong phương thức chính được sử dụng vì không có bất kỳ sự khởi tạo nào diễn ra trong phương thức chính. Nhưng đối tượng được xây dựng chứ không phải là kết quả, chúng ta sử dụng từ khóa tĩnh trong phương thức chính. Trong bộ nhớ ngữ cảnh jvm được tạo khi lớp tải vào nó. Và tất cả các thành viên tĩnh có mặt trong bộ nhớ đó. nếu bây giờ chúng ta tạo tĩnh chính thì nó sẽ nằm trong bộ nhớ và có thể truy cập được jvm (class.main (..)) để chúng ta có thể gọi phương thức chính mà không cần phải tạo heap.


0

Nó chỉ là một quy ước như chúng ta có thể thấy ở đây:

Phương thức phải được khai báo công khai và tĩnh , nó không được trả về bất kỳ giá trị nào và nó phải chấp nhận một mảng String làm tham số. Theo mặc định, đối số không tùy chọn đầu tiên là tên của lớp được gọi. Một tên lớp đủ điều kiện nên được sử dụng. Nếu tùy chọn -jar được chỉ định, đối số không phải tùy chọn đầu tiên là tên của kho lưu trữ JAR chứa các tệp tài nguyên và lớp cho ứng dụng, với lớp khởi động được chỉ định bởi tiêu đề tệp kê khai Lớp chính.

http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/java.html#description


Quy tắc của ngôn ngữ, ý bạn là.
Hầu tước Lorne

0

Các từ khóa void static static có nghĩa là trình thông dịch máy ảo Java (JVM) có thể gọi phương thức chính của chương trình để khởi động chương trình (công khai) mà không tạo một thể hiện của lớp (static) và chương trình không trả về dữ liệu cho trình thông dịch Java VM (void) khi nó kết thúc.

Nguồn: Essentials, Phần 1, Bài 2: Xây dựng ứng dụng


0

Về cơ bản, chúng tôi biến các THÀNH VIÊN DỮ LIỆU và CHỨC NĂNG THÀNH VIÊN đó thành STATIC không thực hiện bất kỳ nhiệm vụ nào liên quan đến một đối tượng. Và trong trường hợp của phương thức chính, chúng ta đang biến nó thành STATIC vì nó không liên quan gì đến đối tượng, vì phương thức chính luôn chạy dù chúng ta có tạo đối tượng hay không.


0

Bất kỳ phương thức nào được khai báo là tĩnh trong Java đều thuộc về chính lớp đó. Một lần nữa phương thức tĩnh của một lớp cụ thể chỉ có thể được truy cập bằng cách tham khảo lớp nhưClass_name.method_name();

Vì vậy, một lớp không cần phải được khởi tạo trước khi truy cập một phương thức tĩnh.

Vì vậy, phương thức main () được khai báo staticđể có thể truy cập mà không cần tạo đối tượng của lớp đó.

Vì chúng ta lưu chương trình với tên của lớp nơi có phương thức chính (hoặc từ nơi chương trình sẽ bắt đầu thực hiện, áp dụng cho các lớp không có main()phương thức () (Cấp độ nâng cao)). Vì vậy, theo cách đã đề cập ở trên:

Class_name.method_name();

phương pháp chính có thể được truy cập.

Tóm lại, khi chương trình được biên dịch, nó tìm kiếm main()phương thức có các Stringđối số như: main(String args[])trong lớp được đề cập (tức là theo tên của chương trình) và vì lúc đầu, nó không có phạm vi để khởi tạo lớp đó, vì vậy hàm main () phương thức được khai báo là tĩnh.


Nó xảy ra khi chương trình được thực thi, không được biên dịch.
Hầu tước Lorne

0

Từ java.sun.com (có thêm thông tin trên trang web):

Phương thức chính là tĩnh để cung cấp cho trình thông dịch Java VM một cách để bắt đầu lớp mà không cần tạo một thể hiện của lớp điều khiển trước. Thể hiện của lớp điều khiển được tạo trong phương thức chính sau khi chương trình bắt đầu.

Sự hiểu biết của tôi luôn luôn đơn giản là phương thức chính, giống như bất kỳ phương thức tĩnh nào, có thể được gọi mà không cần tạo một thể hiện của lớp liên kết, cho phép nó chạy trước mọi thứ khác trong chương trình. Nếu nó không tĩnh, bạn sẽ phải khởi tạo một đối tượng trước khi gọi nó - điều này tạo ra vấn đề 'gà và trứng', vì phương thức chính nói chung là những gì bạn sử dụng để khởi tạo các đối tượng khi bắt đầu chương trình.


Nhưng nó không chạy được trước bất cứ thứ gì khác trong chương trình. Toàn bộ tranh luận là một lời ngụy biện, và hơn thế nữa, đây không phải là câu trả lời đầu tiên đề cập đến nó, thậm chí không phải là thứ hai hay thứ ba.
Konrad Rudolph

Tôi xin lỗi vì câu trả lời của tôi lặp lại những gì người khác đã nói; Tôi chỉ trả lời theo sự hiểu biết tốt nhất của tôi và từ những gì tôi có thể tìm thấy trực tuyến. Từ kết quả mà tôi đã xem xét, không có lý do nào khác là tại sao phương thức chính là tĩnh; trừ khi có một ẩn sâu ở đâu đó có lẽ đó là câu trả lời duy nhất. Sự hiểu biết của tôi về Java khá cơ bản, nhưng tôi đã nghe lý do trên (từ các giáo sư, sách giáo khoa, v.v.) và không bao giờ khác.
Jesse M

@Jlie M Nhận xét của bạn chỉ có ý nghĩa nếu bạn thậm chí không cân nhắc đọc các câu trả lời khác trước. Mà nhân tiện không phải là một điều xa vời để làm. Như bạn đã đề cập, sự hiểu biết của bạn khá cơ bản nên rất có thể ai đó đã trả lời câu hỏi một cách thành thạo hơn. Và bình luận của bạn dường như là một sự hợp lý hóa để làm cho câu trả lời của bạn trông tốt hơn. Đó là một tuyên bố phi thường rằng bạn có sách giáo khoa và giáo sư Java nghĩ rằng những gì bạn tuyên bố và thẳng thắn tôi không tin họ làm. (Có tài liệu tham khảo nào không?)
LeoR

1
@KonradRudolph Những bình luận hàng đầu có vẻ khá hợp lý. main () được sử dụng làm điểm vào chương trình và có một số tài liệu tham khảo trên trang web Java nói rằng nó được cho là tương tự như cách C / C ++ có hàm main (). Vì Java là tất cả các Đối tượng, nên nó phải tĩnh để tránh việc khởi tạo đối tượng. Có nó tĩnh cũng cho phép nó được tải và thực thi vào JVM khi chạy. Tôi chỉ đang hồi tưởng lại những câu trả lời trước đó, nhưng tôi đang tự hỏi những gì bạn sẽ xem là một câu trả lời thỏa mãn. Tôi nghĩ điều tốt nhất bạn sẽ nhận được là "Đó là cách họ muốn". Hãy ghi nhớ ngày Java đã được thực hiện.
trevor-e

1
@Jlie Spot-on. Hoàn toàn có thể đó chỉ là vấn đề quy ước (mặc dù tôi hy vọng rằng nó không phải, đó sẽ là một câu trả lời nhàm chán). Sở thích ban đầu của tôi về câu hỏi này là vì tôi nghĩ rằng việc sử dụng một cá thể phù hợp để đại diện cho đối tượng chạy ứng dụng, và có điểm vào là một phương thức (hoặc hàm tạo) của lớp này sẽ là một thiết kế rõ ràng hơn, vì Java được thiết kế để hướng đối tượng từ get-go, và kể từ khi đối tượng dường như tương tự (chủ đề, thông qua Runnable) trong Java làm sử dụng thiết kế này. Tại sao ngoại lệ (rõ ràng) ở đây?
Konrad Rudolph
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.