为什么Java主方法是静态的?

Why is the Java main method static?

Java主()方法的签名是:

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

这个方法是静态的吗?


这只是惯例。实际上,甚至名称main()和传入的参数都是纯约定的。

当您运行Java.EXE(或Windows上的JavaW.EXE)时,真正发生的是一对Java本地接口(JNI)调用。这些调用加载的dll实际上是JVM(对了-java.exe不是JVM)。JNI是当我们必须桥接虚拟机世界和C、C++等的世界时使用的工具。反过来也是正确的-不可能(至少据我所知)在不使用JNI的情况下实际运行JVM。

基本上,java.exe是一个超简单的C应用程序,它解析命令行,在jvm中创建一个新的字符串数组来保存这些参数,解析出指定为包含main()的类名,使用jni调用查找main()方法本身,然后调用main()方法,将新创建的字符串数组作为参数传入。这非常像使用Java中的反射时所做的那样,它只是使用了混淆的命名的本地函数调用。

编写自己版本的java.exe(源代码与JDK一起分发)并让它做完全不同的事情是完全合法的。事实上,这正是我们对所有基于Java的应用程序所做的。

我们的Java应用程序都有自己的启动程序。我们主要是这样做的,所以我们得到了自己的图标和进程名,但在其他情况下,除了常规的main()调用之外,我们还想做一些事情来让事情顺利进行(例如,在一个情况下,我们正在做COM互操作性,实际上我们将一个COM句柄传递到main()而不是字符串数组)。

所以,长和短:静态的原因是B/C很方便。之所以称为"main",是因为它必须是某种东西,而main()是C的旧时代(在那些日子里,函数的名称很重要)所做的。我想JavaEXE可以允许您只指定一个完全限定的主方法名称,而不只是一个类(Java COM.MyCopy.Fo.SoMeSimialMain)-但这只会使得IDE更难自动检测项目中的"可启动"类。


方法是静态的,因为否则会有歧义:应该调用哪个构造函数?尤其是如果你的班级看起来像这样:

1
2
3
4
5
public class JavaClass{
  protected JavaClass(int x){}
  public void main(String[] args){
  }
}

JVM是否应该调用new JavaClass(int)?对于x应该通过什么?

如果没有,JVM是否应该在不运行任何构造函数方法的情况下实例化JavaClass?我认为不应该这样做,因为这会对整个类产生特殊的影响-有时您有一个尚未初始化的实例,您必须在每个可以调用的方法中检查它。

对于JVM来说,有太多的边缘情况和模棱两可的地方,以至于在调用入口点之前必须实例化一个类。这就是为什么main是静态的。

我不知道为什么main总是被标记为public


C++C#Java中的main()方法是静态的。因为运行时引擎可以调用它们,而不必实例化任何对象,所以main()主体中的代码将完成其余的工作。


为什么是公共静态void main(string[]args)?

这就是Java语言的设计和Java虚拟机的设计和编写。

Oracle Java语言规范

查看第12章执行-第12.1.4节调用test.main:

Finally, after completion of the initialization for class Test (during which other consequential loading, linking, and initializing may have occurred), the method main of Test is invoked.

The method main must be declared public, static, and void. It must accept a single argument that is an array of strings. This method can be declared as either

1
public static void main(String[] args)

or

1
public static void main(String... args)

Oracle Java虚拟机规范

查看第2章Java编程语言概念-第2.17节执行:

The Java virtual machine starts execution by invoking the method main of some specified class and passing it a single argument, which is an array of strings. This causes the specified class to be loaded (§2.17.2), linked (§2.17.3) to other types that it uses, and initialized (§2.17.4). The method main must be declared public, static, and void.

Oracle OpenJDK源

下载并提取源JAR,查看如何编写JVM,查看../launcher/java.c,其中包含命令java [-options] class [args...]后面的本机C代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
 * 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);

... ...


让我们简单地假设,不需要static作为应用程序入口点。

然后,应用程序类将如下所示:

1
2
3
4
5
6
7
8
class MyApplication {
    public MyApplication(){
        // Some init code here
    }
    public void main(String[] args){
        // real application code here
    }
}

构造函数代码和main方法之间的区别是必要的,因为在OO语言中,构造函数只能确保实例正确初始化。初始化之后,实例可以用于预期的"服务"。将完整的应用程序代码放入构造函数会破坏这一点。

因此,这种方法将强制应用三种不同的合同:

  • 必须有一个默认的构造函数。否则,JVM将不知道要调用哪个构造函数以及应该提供哪些参数。
  • 必须有一个main方法1。好吧,这并不奇怪。
  • 类不能是abstract。否则,JVM无法实例化它。

另一方面,static方法只需要一份合同:

  • 必须有一个main方法1。

这里,abstract和多个构造函数都不重要。

由于Java是为用户设计的一种简单语言,所以应用程序入口点的设计并不简单,而是使用一个合同,而不是用一个复杂的方式使用三个独立的和脆弱的合同来设计。

请注意:这个参数不是关于JVM内部或JRE内部的简单性。这个论点是关于用户的简单性。

1.完整的签字仅算作一份合同。


如果不是,如果有多个构造函数,应该使用哪个构造函数?

关于Java语言规范中可用的Java程序的初始化和执行有更多的信息。


否则,它将需要执行对象的一个实例。但是必须从头开始调用它,而不首先构造对象,因为它通常是main()函数(bootstrap)的任务,通过使用这些参数/程序参数来解析参数和构造对象。


在调用主方法之前,不会实例化任何对象。拥有static关键字意味着可以在不首先创建任何对象的情况下调用该方法。


让我用更简单的方式来解释这些事情:

1
public static void main(String args[])

除了applet之外,所有Java应用程序都从EDCOX1 2开始执行它们。

关键字public是一个访问修饰符,允许从类外部调用成员。

使用static是因为它允许调用main()而不必实例化该类的特定实例。

void表示main()不返回任何值。


public static void main(String args[])的意思是什么?

  • EDCOX1 9是一个访问标识符,这意味着任何人都可以访问/调用它,例如JVM(Java虚拟机)。
  • static允许在创建类的对象之前调用main()。这是必要的,因为在生成任何对象之前,JVM调用main()。因为它是静态的,所以可以通过类直接调用它。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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 */

        }
    }

    类似地,我们有时对用户定义的方法使用静态的,这样我们就不需要创建对象。

  • void表示声明的main()方法不返回值。

  • String[] args指定main()方法中的唯一参数。

    args—包含类类型String的对象数组的参数。


  • 构建各种小程序、MIDlet、servlet和bean,然后调用它们的生命周期方法。调用main是对主类所做的全部工作,因此不需要在多次调用的对象中保留状态。将main固定在另一个类上是很正常的(尽管不是一个好主意),这会妨碍使用该类创建主对象。


    如果主方法不是静态的,则需要从程序外部创建主类的对象。你想怎么做?


    这只是一个惯例,但可能比其他选择更方便。使用静态main,调用Java程序所需知道的是类的名称和位置。如果不是静态的,您还必须知道如何实例化该类,或者要求该类具有空的构造函数。


    当使用EDCOX1 OR 12命令执行Java虚拟机(JVM)时,

    1
    java ClassName argument1 argument2 ...

    执行应用程序时,将其类名指定为Java命令的参数,如上所述

    JVM试图调用您指定的类的主方法

    —at this point, no objects of the class have been created.

    Declaring main as static allows the JVM to invoke main without creating
    an instance of the class.

    让我们回到指挥部

    ClassName是jvm的command-line argument,jvm告诉它要执行哪个类。在类名后面,您还可以指定一个list of Strings(用空格分隔)作为命令行参数,JVM将传递给您的应用程序。-这些参数可以用来指定运行应用程序的选项(例如文件名),这就是为什么在主文件中有一个名为String[] args的参数的原因。

    参考文献:Java?如何编程(早期对象),第十版


    我认为关键字"static"使主方法成为类方法,类方法只有一个副本,可以被所有人共享,而且不需要对象作为引用。因此,在编译驱动程序类时,可以调用主方法。(我只是Java的字母级别,如果我错了,抱歉)


    main()是静态的,因为在应用程序的生命周期中,应用程序堆栈在本质上是过程性的,因为还没有实例化任何对象。

    这是一张干净的石板。您的应用程序此时正在运行,即使没有声明任何对象(记住,有过程和OO编码模式)。作为开发人员,您可以通过创建对象的实例并根据内部编译的代码,将应用程序转换为面向对象的解决方案。

    面向对象是伟大的,有数百万明显的原因。然而,大多数VB开发人员在代码中经常使用诸如"goto"这样的关键字的日子已经一去不复返了。goto"是vb中的一个过程命令,由它的OO对应项:方法调用替换。

    您也可以将静态入口点(主)视为纯自由。如果Java不同于实例化一个对象,并且只在运行时向您呈现该实例,那么您将别无选择,只能编写过程性应用程序。对于Java来说,这是不可想象的,可能有很多方案需要程序化的方法。

    这可能是一个非常模糊的回答。记住,"class"只是一组相互关联的代码。实例"是那个阶级中一个孤立的、活的、呼吸自主的一代。


    最近,类似的问题也在programmers.se上发布。

    • 为什么爪哇中的静态main方法,而不是C构造函数,而不是构造函数?

      Looking for a definitive answer from a primary or secondary source for why did (notably) Java and C# decide to have a static method as their entry point – rather than representing an application instance by an instance of an Application class, with the entry point being an appropriate constructor?

    接受答案的一部分是:

    In Java, the reason of public static void main(String[] args) is that

  • Gosling wanted
  • the code written by someone experienced in C (not in Java)
  • to be executed by someone used to running PostScript on NeWS
  • http://i.stack.imgur.com/qcmzP.png

    nbsp;对于C,可以说,推理是可传递的相似性。语言设计者保持程序入口点语法对于来自Java的程序员来说是熟悉的。正如C建筑师Anders Hejlsberg所说,

    ...our approach with C# has simply been to offer an alternative... to Java programmers...

    < /块引用>


    原型public static void main(String[])是jls中定义的一种约定:

    The method main must be declared public, static, and void. It must specify a formal parameter (§8.4.1) whose declared type is array of String.

    在JVM规范5.2中。虚拟机启动我们可以阅读:

    The Java virtual machine starts up by creating an initial class, which is specified in an implementation-dependent manner, using the bootstrap class loader (§5.3.1). The Java virtual machine then links the initial class, initializes it, and invokes the public class method void main(String[]). The invocation of this method drives all further execution. Execution of the Java virtual machine instructions constituting the main method may cause linking (and consequently creation) of additional classes and interfaces, as well as invocation of additional methods.

    有趣的是,在JVM规范中,没有提到主方法必须是静态的。但是规范还说Java虚拟机之前执行2个步骤:

    • 链接初始类(5.4.链接)
    • 初始化(5.5.初始化)

    Initialization of a class or interface consists of executing its class or interface initialization method.

    2.9。特殊方法:

    定义了类或接口初始化方法:

    A class or interface has at most one class or interface initialization method and is initialized (§5.5) by invoking that method. The initialization method of a class or interface has the special name , takes no arguments, and is void.

    类或接口初始化方法不同于定义如下的实例初始化方法:

    At the level of the Java virtual machine, every constructor written in the Java programming language (JLS §8.8) appears as an instance initialization method that has the special name .

    因此,JVM初始化一个类或接口初始化方法,而不是一个实际是构造函数的实例初始化方法。因此,他们不需要提到主方法在JVM规范中必须是静态的,因为在调用主方法之前没有创建实例这一事实暗示了这一点。


    这只是一个惯例。如果这是惯例的话,JVM当然可以处理非静态的主要方法。毕竟,您可以在类上定义一个静态初始值设定项,并在使用main()方法之前实例化无数个对象。


    我不知道在对象被实例化之前,JVM是否调用了主方法…但main()方法是静态的还有一个更强有力的原因…当jvm调用类的主方法(比如person)时。它通过"person.main()"调用它。你看,JVM用类名调用它。这就是为什么main()方法应该是静态的和公共的,以便JVM可以访问它。

    希望能有所帮助。如果有,请通过评论让我知道。


    静态-当JVM调用主方法时,没有对象存在于被调用的类中,因此它必须有静态方法才能允许从类中调用。


    public关键字是一个访问修饰符,它允许程序员控制类成员的可见性。当类成员前面是public时,那么成员可以由声明它的类之外的代码访问。

    public相反的是private,它防止成员被其类之外定义的代码使用。

    在这种情况下,main()必须声明为public,因为它必须被调用当程序启动时,通过类外部的代码。

    关键字static允许调用main(),而不必实例化类的特定实例。这是必要的,因为在任何对象被生成之前,Java解释器调用EDCOX1 4。

    关键字void只是告诉编译器main()不返回值。


    任何应用程序的真正入口点都是静态方法。如果Java语言支持一个实例方法作为"入口点",那么运行时就需要在内部实现它作为一个静态方法,它构建对象的实例,然后调用实例方法。

    在这种情况下,我将研究选择以下三个选项之一的原因:

  • 像我们今天看到的那样。
  • 对新构造的对象调用的实例方法void main()
  • 使用类型的构造函数作为入口点(例如,如果入口类被称为Program,那么执行将有效地由new Program()组成)。
  • 击穿:

    static void main()

  • 调用封闭类的静态构造函数。
  • 调用静态方法main()
  • void main()

  • 调用封闭类的静态构造函数。
  • 通过有效地调用new ClassName()构造封闭类的实例。
  • 调用实例方法main()
  • new ClassName()

  • 调用封闭类的静态构造函数。
  • 构造类的实例(然后不处理该实例,只返回)。
  • 理论基础:

    我要按相反的顺序去买这个。

    请记住,Java的设计目标之一是强调(需要时尽可能)良好的面向对象编程实践。在此上下文中,对象的构造函数初始化对象,但不应对对象的行为负责。因此,通过给每个应用程序设计一个"理想"构造函数的异常,给EDCOX1×7的入口点的规范会混淆新Java开发人员的情况。

    通过将main()作为实例方法,一定解决了上述问题。但是,它通过要求规范列出入口类的构造函数的签名以及main()方法的签名来创建复杂性。

    总之,在遵循将行为放入方法的原则的同时,指定一个static void main()创建一个最不复杂的规范。考虑到实现一个main()方法是多么简单,该方法本身构造一个类的实例并调用一个实例方法,因此将main()指定为实例方法并没有真正的优势。


    静态方法不需要任何对象。它直接运行,所以主运行直接。


    在Java中声明为static的任何方法都属于类本身。同样,只能通过引用类似于Class_name.method_name();的类来访问特定类的静态方法。

    因此,在访问静态方法之前,不需要实例化类。

    因此,main()方法被声明为static,这样就可以在不创建该类的对象的情况下访问它。

    因为我们用主方法所在的类的名称保存程序(或者从程序开始执行的位置保存程序,适用于没有main()method()(高级级别)的类)。因此,通过上述方式:

    1
    Class_name.method_name();

    可以访问主方法。

    简言之,当程序编译时,它在所述类(即通过程序名)中搜索具有String参数的main()方法,如:main(String args[]),因为在开始时它没有实例化该类的范围,所以main()方法被声明为静态方法。


    有一个简单的原因,那就是因为对象不需要调用静态方法,如果它是非静态方法,Java虚拟机首先创建对象,然后调用主()方法,这将导致额外的内存分配问题。


    基本上,我们将这些数据成员和成员函数设置为静态的,不执行任何与对象相关的任务。在主方法的情况下,我们将它设置为静态的,因为它与对象无关,因为无论我们是否创建对象,主方法总是运行的。


    程序的主要方法有保留字static,这意味着它可以在静态上下文中使用。上下文与程序运行期间计算机内存的使用有关。当虚拟机加载程序时,为其创建静态上下文,分配计算机内存以存储程序及其数据等。动态上下文是在程序运行过程中稍后进行的某种内存分配。如果不允许主方法在静态上下文中运行,则程序将无法启动。


    使用主方法中的静态关键字是因为主方法中没有发生任何实例化。但是对象是构造的,而不是调用的,因此我们在主方法中使用静态关键字。在JVM中,当类加载到内存中时会创建上下文内存,并且所有静态成员都存在于该内存中。如果我们现在使主静态化,它将在内存中,并且可以被JVM(class.main(..)访问,这样我们就可以调用主方法,而不需要创建堆。


    从java.sun.com(网站上有更多信息):

    The main method is static to give the Java VM interpreter a way to start the class without creating an instance of the control class first. Instances of the control class are created in the main method after the program starts.

    我的理解一直很简单,主方法和任何静态方法一样,可以在不创建关联类的实例的情况下调用,从而允许它在程序中的任何其他方法之前运行。如果它不是静态的,那么在调用它之前必须先实例化一个对象——这会产生一个"鸡和蛋"问题,因为主方法通常是在程序开始时用来实例化对象的。


    The public static void keywords mean the Java virtual machine (JVM) interpreter can call the program's main method to start the program (public) without creating an instance of the class (static), and the program does not return data to the Java VM interpreter (void) when it ends.

    来源:要点,第1部分,第2课:建筑应用


    主方法总是需要静态的,因为在运行时JVM不创建任何对象来调用main方法,正如我们在Java中所知道的,静态方法是可以使用类名调用的唯一方法,所以main方法总是需要静态的。

    有关更多信息,请访问此视频:https://www.youtube.com/watch?v=z7rpnwg bfk&feature=youtu.be


    正如我们在这里看到的,这只是一个惯例:

    The method must be declared public and static, it must not return any
    value, and it must accept a String array as a parameter. By default,
    the first non-option argument is the name of the class to be invoked.
    A fully-qualified class name should be used. If the -jar option is
    specified, the first non-option argument is the name of a JAR archive
    containing class and resource files for the application, with the
    startup class indicated by the Main-Class manifest header.

    http://DOCS.Oracle .COM/JavaSe/1.4.2/DOCS/TooDOCs/Windows /Java.html描述


    static指示此方法是类方法。并且在不需要类的任何对象的情况下调用。


    由于程序是从主程序()开始执行的,而Java是纯对象的程序,其中对象被声明在主体()中,这意味着主体()在对象创建之前被调用,因此如果主体()将非静态,然后调用它,则需要一个对象,因为静态意味着不需要对象。


    这是一个经常被问及的问题,为什么在Java中主()是静态的。

    答:我们知道在Java中,JVM从()开始执行。当jvm在那时执行main()时,包含main()的类不会被实例化,因此我们不能在没有引用它的对象的情况下调用非静态方法。因此,为了调用它,我们将其设置为静态的,因为类加载器将在JVM上下文内存空间中加载所有静态方法,而JVM可以从中直接调用这些方法。


    因为,静态成员不是任何特定类和该主方法的一部分,不需要创建其对象,但仍然可以引用所有其他类。