529

Javamain方法的方法签名是:

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

这种方法必须是静态的有什么原因吗?

4

37 回答 37

418

这只是惯例。事实上,即使是 main() 的名称,以及传入的参数也是纯粹的约定俗成的。

当您运行 java.exe(或 Windows 上的 javaw.exe)时,真正发生的是几个 Java 本地接口 (JNI) 调用。这些调用加载真正是 JVM 的 DLL(没错 - java.exe 不是 JVM)。当我们必须连接虚拟机世界和 C、C++ 等世界时,JNI 是我们使用的工具……反过来也是如此——实际上不可能(至少据我所知) JVM 在不使用 JNI 的情况下运行。

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

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

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

所以,总而言之:它是静态的原因是 b/c 很方便。它被称为“main”的原因是它必须是某种东西,而 main() 是他们在 C 的旧时代所做的(在那些日子里,函数的名称重要)。我想 java.exe 可以让你只指定一个完全限定的主方法名称,而不仅仅是类 (java com.mycompany.Foo.someSpecialMain) - 但这只会让 IDE 更难自动检测 '项目中的可启动类。

于 2008-09-30T03:32:18.703 回答
351

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

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

JVM应该调用new JavaClass(int)吗?它应该通过x什么?

如果不是,JVM 是否应该在JavaClass不运行任何构造方法的情况下进行实例化?我认为它不应该,因为这会使你的整个类成为特例——有时你有一个尚未初始化的实例,你必须在每个可以调用的方法中检查它。

对于 JVM 必须在调用入口点之前实例化一个类来说,有太多的边缘情况和歧义是有意义的。这就是为什么main是静态的。

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

于 2008-09-28T20:28:34.377 回答
192

mainC++、C#和Java中的方法是静态的。

这是因为它们可以被运行时引擎调用,而无需实例化任何对象,然后主体中的代码main将完成其余的工作。

于 2008-09-28T19:48:01.480 回答
40

为什么是 public static void main(String[] args) ?

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

Oracle Java 语言规范

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

最后,在完成类Test的初始化之后(在此期间可能发生了其他相应的加载、链接和初始化),调用Test的main方法。

方法 main 必须声明为 public、static 和 void。它必须接受一个作为字符串数组的参数。此方法可以声明为

public static void main(String[] args)

或者

public static void main(String... args)

Oracle Java 虚拟机规范

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

Java 虚拟机通过调用某个指定类的 main 方法并传递给它一个参数来开始执行,该参数是一个字符串数组。这会导致指定的类被加载(第 2.17.2 节)、链接(第 2.17.3 节)到它使用的其他类型,并被初始化(第 2.17.4 节)。方法 main 必须声明为 public、static 和 void。

Oracle OpenJDK 源代码

下载并解压源 jar 并查看 JVM 是如何编写的,检查一下../launcher/java.c,其中包含命令后面的本机 C 代码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);

... ...
于 2012-07-12T04:05:57.983 回答
40

让我们简单地假设,这static不是应用程序入口点所必需的。

应用程序类将如下所示:

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此处完整的签名仅计为一份合同。

于 2012-07-15T12:20:02.707 回答
14

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

Java 语言规范中提供了有关 Java 程序的初始化和执行的更多信息。

于 2008-09-28T19:51:03.953 回答
12

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

于 2008-09-28T19:48:09.733 回答
12

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

于 2008-09-28T19:48:30.090 回答
12

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

public static void main(String args[])

除小程序外,所有 Java 应用程序都从main().

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

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

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

于 2015-05-09T13:31:27.843 回答
10

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

  1. public是访问说明符,意味着任何人都可以访问/调用它,例如 JVM(Java 虚拟机。
  2. static允许main()在创建类的对象之前调用。这是必需的,因为main()在创建任何对象之前由 JVM 调用。由于它是静态的,因此可以通过类直接调用。

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

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

  3. void表示main()被声明的方法没有返回值。

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

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

于 2013-07-08T00:11:05.057 回答
6

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

于 2008-09-28T19:47:36.130 回答
6

构建各种 Applet、midlets、servlet 和 bean,然后调用它们的生命周期方法。调用 main 是对主类所做的所有事情,因此不需要将状态保存在多次调用的对象中。将 main 固定在另一个类上是很正常的(尽管不是一个好主意),这会妨碍使用该类来创建主对象。

于 2008-09-28T20:04:12.050 回答
5

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

于 2008-09-28T22:09:02.360 回答
5

java当您使用该命令执行 Java 虚拟机 (JVM) 时,

java ClassName argument1 argument2 ...

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

JVM 尝试调用您指定的类的 main 方法

——此时,还没有创建类的对象。

将 JVM声明main为静态以主要创建一个类。allowsinvokewithoutinstance

让我们回到命令

ClassNamecommand-line argumentJVM 的一个,它告诉它要执行哪个类。在 ClassName 之后,您还可以指定一个list of Strings(由空格分隔)作为 JVM 将传递给您的应用程序的命令行参数。- 这样的参数可能用于指定选项(例如,文件名)来运行应用程序 - 这就是为什么String[] args在 main 中调用了一个参数

参考资料:Java™ 如何编程(早期对象),第十版

于 2017-02-21T09:56:54.910 回答
3

这只是一个约定。如果按照惯例,JVM 当然可以处理非静态主要方法。毕竟,你可以在你的类上定义一个静态初始化器,并在你的 main() 方法之前实例化无数个对象。

于 2008-09-28T20:15:22.570 回答
3

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

于 2012-06-12T12:48:33.353 回答
3

main() 是静态的,因为;在应用程序生命周期的那个时刻,应用程序堆栈本质上是程序化的,因为还没有实例化对象。

这是一张白纸。您的应用程序此时正在运行,即使没有声明任何对象(请记住,有过程和 OO 编码模式)。作为开发人员,您通过创建对象实例并依赖于其中编译的代码,将应用程序转变为面向对象的解决方案。

面向对象的伟大之处在于数百万个显而易见的原因。然而,大多数 VB 开发人员在其代码中经常使用诸如“goto”之类的关键字的日子已经一去不复返了。“goto”是 VB 中的一个过程命令,它被它的 OO 对应物所取代:方法调用。

您还可以将静态入口点(主要)视为纯粹的自由。如果 Java 足以实例化一个对象并在运行时只向您呈现该实例,那么您将别无选择,只能编写一个程序化应用程序。尽管对于 Java 来说这听起来难以想象,但可能有许多场景需要过程方法。

这可能是一个非常晦涩的回答。请记住,“类”只是相互关联的代码的集合。“实例”是该类的一个孤立的、活生生的、自主的一代。

于 2012-07-10T14:37:20.557 回答
3

最近,Programmers.SE 也发布了类似的问题

TL;DR部分接受的答案是,

在Java中,原因public static void main(String[] args)

  1. 高斯林通缉
  2. 由有 C(不是 Java)经验的人编写的代码
  3. 由习惯在新闻上运行PostScript的人执行

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

 
对于 C#,可以说推理是传递相似的。语言设计者使来自 Java 的程序员熟悉的程序入口点语法。正如 C# 架构师Anders Hejlsberg 所说

...我们使用 C# 的方法只是为 Java 程序员提供替代方案...

...

于 2012-07-25T05:58:45.577 回答
2

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

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

在这种情况下,main()必须声明为public,因为它必须在程序启动时由其类之外的代码调用。

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

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

于 2011-04-30T11:39:29.057 回答
2

任何应用程序的真正入口点是静态方法。如果 Java 语言支持将实例方法作为“入口点”,则运行时需要在内部将其实现为静态方法,该方法构造对象的实例,然后调用实例方法。

有了这个,我将研究选择以下三个选项中的一个特定选项的理由:

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

分解:

static void main()

  1. 调用封闭类的静态构造函数。
  2. 调用静态方法main()

void main()

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

new ClassName()

  1. 调用封闭类的静态构造函数。
  2. 构造一个类的实例(然后什么都不做,只是简单地返回)。

理由:

我将按相反的顺序进行此操作。

请记住,Java 的设计目标之一是强调(尽可能要求)良好的面向对象编程实践。在这种情况下,对象的构造函数初始化对象,但不应对对象的行为负责。因此,一个提供入口点的规范new ClassName()会通过强制在每个应用程序上设计一个“理想”构造函数的异常来混淆新 Java 开发人员的情况。

通过制作main()实例方法,上述问题肯定解决了。但是,它要求规范列出入口类的构造函数的签名以及main()方法的签名,从而增加了复杂性。

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

于 2012-07-10T22:43:24.320 回答
2

原型是JLSpublic static void main(String[])中定义的约定:

方法 main 必须声明为 public、static 和 void。它必须指定一个声明类型为字符串数组的形式参数(第 8.4.1 节)。

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

Java 虚拟机通过使用引导类加载器(第 5.3.1 节)创建一个初始类来启动,该类以依赖于实现的方式指定。Java 虚拟机然后链接初始类,对其进行初始化,并调用公共类方法 void main(String[])。调用此方法会驱动所有进一步的执行。执行构成主要方法的 Java 虚拟机指令可能会导致附加类和接口的链接(并因此创建),以及调用附加方法。

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

类或接口的初始化包括执行其类或接口的初始化方法。

2.9 中。特殊方法

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

一个类或接口最多有一个类或接口初始化方法,并通过调用该方法进行初始化(第 5.5 节)。类或接口的初始化方法具有特殊名称<clinit>,不带参数,并且是无效的。

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

在 Java 虚拟机级别,用 Java 编程语言 (JLS §8.8) 编写的每个构造函数都显示为具有特殊名称的实例初始化方法<init>

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

于 2012-07-13T15:15:47.560 回答
1

public static void 关键字表示Java虚拟机(JVM)解释器可以调用程序的main方法来启动程序(public),而不需要创建类的实例(static),并且程序不向Java VM解释器返回数据(无效)当它结束时。

资料来源: 要点,第 1 部分,第 2 课:构建应用程序

于 2012-07-11T20:03:48.343 回答
1

我不知道JVM是否在实例化对象之前调用了main方法......但是main()方法是静态的还有一个更强大的原因......当JVM调用类的main方法时(比如说, 人)。它通过“ Person.main() ”调用它。您会看到,JVM 通过类名调用它。这就是为什么 main() 方法应该是静态和公共的,以便 JVM 可以访问它。

希望它有所帮助。如果是这样,请通过评论告诉我。

于 2015-06-03T19:16:51.610 回答
1

static - 当 JVM 调用 main 方法时,被调用的类不存在任何对象,因此它必须具有静态方法才能允许从类调用。

于 2016-12-08T10:04:00.860 回答
0

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

于 2010-06-01T12:49:51.877 回答
0

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

于 2012-01-18T23:51:02.040 回答
0

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

该方法必须声明为 public 和 static,它不能返回任何值,并且它必须接受 String 数组作为参数。默认情况下,第一个非选项参数是要调用的类的名称。应使用完全限定的类名。如果指定了 -jar 选项,则第一个非选项参数是包含应用程序的类和资源文件的 JAR 归档的名称,启动类由 Main-Class 清单头指示。

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

于 2012-07-11T18:13:00.480 回答
0

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

main 方法是静态的,为 Java VM 解释器提供了一种启动类的方法,而无需先创建控制类的实例。程序启动后在main方法中创建控件类的实例。

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

于 2012-07-12T12:43:15.647 回答
0

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

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

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

由于我们使用 main 方法所在的类的名称保存程序(或者程序应该从哪里开始执行,适用于没有main()方法()的类(高级))。所以通过上面提到的方式:

Class_name.method_name();

可以访问main方法。

简而言之,当程序被编译时,它会搜索main()具有以下String参数的方法:main(String args[])在提到的类中(即通过程序的名称),并且由于一开始它没有实例化该类的范围,所以 main()方法被声明为静态。

于 2015-01-22T22:02:01.960 回答
0

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

于 2015-07-10T11:06:30.190 回答
0

背后的原因很简单,因为对象不需要调用静态方法,如果是非静态方法,java虚拟机先创建对象然后调用main()方法,这会导致额外内存分配的问题。

于 2016-04-29T02:18:00.090 回答
0

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

于 2016-08-18T17:12:08.397 回答
0

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

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

于 2018-10-14T16:27:52.317 回答
-1

static 表示该方法是类方法。调用时不需要任何类对象。

于 2009-11-05T07:00:20.687 回答
-1

由于从 main() 开始执行程序,并且 java 是纯面向对象的程序,其中在 main() 中声明了对象,这意味着在创建对象之前调用 main(),因此如果 main() 将是非静态的,则调用它将需要一个对象,因为静态意味着不需要对象............

于 2010-03-12T07:29:29.813 回答
-1

为什么 main() 在 Java 中是静态的,这是一个常见问题。

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

于 2011-07-01T19:51:28.417 回答
-5

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

于 2009-01-12T12:15:50.867 回答