当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么Javamain方法是静态的?》经验,为你挑选了14个好方法。

Java main()方法的方法签名是:

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

这种方法是否有理由是静态的?



1> Kevin Day..:

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

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

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

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

我们的每个Java应用程序都有自己的启动器.我们主要这样做,所以我们得到了自己的图标和进程名称,但是在其他我们希望除了常规main()调用之外还要执行某些操作的情况下它已经派上用场了(例如,在一种情况下我们正在做COM互操作性,我们实际上将COM句柄传递给main()而不是字符串数组.

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


+1:非常迷人(特别是关于编写自定义`java.exe`的部分)
有趣的是,我不同意"这只是惯例".部分答案.OP的主要问题是声明中静态的原因.我不认为`main()`声明中的`static`只是为了约定.然而,它是'main()'而不是其他东西的事实是可行的.
@BenVoigt你调用LoadLibrary()来获取jvm dll.然后,您调用GetProcAddress( "JNI_CreateJavaVM"),那么你调用JNI_CreateJavaVM函数(http://docs.oracle.com/javase/1.4.2/docs/guide/jni/spec/invocation.html).加载VM后,使用标准JNI调用来查找正确的类,加载静态main方法并调用它.那里没有太大的误解空间.JNI绝对是您加载VM的方式.您可能习惯使用native关键字javah -jni等编写客户端JNI ...但这只是JNI的一半.
@David所以它确实如此.我实际上更喜欢原来参与其中一个人的答案 - 但这是一个非常远的镜头.不幸的是,大多数其他答案都是临时推理的练习.这个提供了非常有趣的细节,除了谦虚不发明错误的技术细节以推理(可能)非技术性原因.
@Jared - 他们可能需要一个公共的无参数构造函数,并使`main`非静态,并且仍然适合该语言的范围.没有设计师的回应,我们必须同意不同意.:)

2> Jacob Krall..:

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

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

JVM应该打电话new JavaClass(int)吗?应该通过x什么?

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

有很多边缘情况和含糊之处使得JVM必须在调用入口点之前实例化一个类.这就是为什么main是静态的.

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


我个人喜欢`public static void main`作为入口点的标记 - 公共无参数构造函数不会尖叫"这可能是一个切入点!" 以同样的方式.
"应该调用哪个构造函数?"甚至可以想象*这个问题是什么?"main"调用的决定存在同样的"问题".非常奇怪(对你而言),JVM管理这个很好.
main方法总是公开的,因为它必须由运行时引擎JVM访问.
@EdwinDalorzo - 通过强制实例化入口点类可以获得什么?调用静态方法会给类带来最小的负担.如果对您的设计更有意义,可以自由实例化.
实现接口不能解决实例化问题.
@Sudheesh:JVM是强制执行访问修饰符的东西。它可以使用私有方法做任何想要的事情。

3> Noah Goodric..:

main()在方法C++,C#Java是静态
因为他们然后可以由运行时引擎调用而不不必实例的任何对象,则代码在身体的main()将完成剩下的.


如果你的主类有重载的构造函数,JVM将如何知道要调用哪个构造函数?它会通过什么参数?
@Namratha:是的,你错过了什么."静态方法不能引用非静态方法"是不正确的.正确的陈述是:"每个静态方法在使用任何非静态方法时都必须提供一个对象".看看`main`这样的`static`方法经常使用`new`来创建这样的对象.
@Jacob JVM如何知道调用哪个重载`static void main`?根本不是问题.

4> yorkw..:

为什么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);

... ...


这里的问题是,对于原始形式的问题,这实际上是一个非常好的答案,有很多参考文献(+1).但是,我很想了解将静态方法作为入口点而不是构造函数或实例方法的设计决策的*基本原理*.
一般来说,当方法结果计算仅依赖于其参数时,因此它不依赖于对象实例内部状态,它可以是静态的.并且建议将其设置为静态以实现代码可维护性/可重用性.如果方法`main`不是静态的,则意味着必须知道类实例状态,并且定义要复杂得多,比如首先使用哪个构造函数.
@Yves它*可以*.如果另一种设计有意义,它就不必了.我在这里的评论中听到了一些好的论点,但我仍然认为一个过程实际上非常像一个线程(它*是*),而Java中的一个线程通常表示为`Runnable`的一个实例.以相同的方式表示整个过程(即将`Runnable.Run`作为入口点)在Java中确实有意义.当然,`Runnable`本身可以说是一个设计缺陷,因为Java没有匿名方法(还).但既然它已经存在......

5> A.H...:

让我们简单地假装,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此处完整签名仅为一份合同.


@KonradRudolph:我的观点不是JRE必须做的工作.我的观点是强制语言的_every user_在必要时遵循更多合同.从这个意义上说,`static public main(String [])`方法是_one_签名,因此是_one_ contract.否则必须遵循三个_independent_合同.
@KonradRudolph:没有矛盾:在一个案例中,系统会强制用户签订三份合同.可疑的合同,这些合同无法通过编译器检查,并且从用户的角度来看是独立的.在通常的"线程"和"可运行"情况下,用户不会隐藏任何内容,他可以清楚地看到发生了什么,并且他有改变实施_only_那些适合他的合同 - 他在控制中,而不是系统.
这是最好的答案.令人遗憾的是,许多用户只会阅读页面上的前2或3个答案; 这个人不太可能很快到达那里.它提到了构造函数仅用于初始化的重要性 - 因此在构造函数运行整个应用程序的样式中进行编码是没有意义的.

6> 小智..:

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

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



7> BlackWasp..:

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



8> PhiLho..:

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



9> 小智..:

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

    public 是一个访问说明符,意味着任何人都可以访问/调用它,例如JVM(Java虚拟机).

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

    类似地,我们在某些时候对用户定义的方法使用static,这样我们就不需要创建对象了.

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

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

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



10> 小智..:

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

public static void main(String args[])

除applet之外的所有Java应用程序都从它开始执行main().

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

static之所以使用是因为它允许main()在不必实例化该类的特定实例的情况下进行调用.

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



11> Tom Hawtin -..:

构建各种类型的小程序,midlet,servlet和bean,然后调用生命周期方法.调用main是对主类所做的所有操作,因此不需要在多次调用的对象中保存状态.将main放在另一个类上是很正常的(虽然不是一个好主意),这会妨碍使用类来创建主对象.



12> Logan..:

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


语言规范本身遵循惯例.Java设计者没有实际要求选择需要静态主服务器.但是,正如Logan所解释的那样,替代方案更为复杂.

13> micro..:

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



14> Basheer AL-M..:

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

java ClassName argument1 argument2 ...

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

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

- 在这一点上,没有创建类的对象.

声明main为静态allows的JVM到invokewithout创建一个instance类的.

让我们回到命令

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

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

推荐阅读
mobiledu2402851203
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有