我听到有人说“如果 main 不是静态的,那么 JVM 可以创建一个包含 main 的类的对象并通过对象调用该 main。
但问题是 JVM 如何知道在构造函数重载的情况下调用哪个构造函数,或者即使存在只有一个参数化构造函数,然后传递什么。”
这是正确的理由吗?
因为如何在不进入主函数的情况下创建类的对象?
请就此发表您的看法。如果这不是正确的原因,那么正确的原因是什么?
这只是一个约定。Java 语言设计者可以很容易地决定您必须指定一个要实例化的类,使其构造函数成为主要方法。但是调用静态方法同样有效,并且不需要先实例化一个类。
此外,如果该类具有超类,您可以通过更改超类来改变程序启动的行为(因为必须在子类之前调用超类构造函数),这可能是无意的。静态方法没有这个问题。
主要方法是静态的,因为它使事情变得更简单,但如果他们想让它更复杂,他们可以做到。
“这个理由对吗?”
在某种程度上是这样,尽管从未像那样具体解释过。
我们可以约定用 调用构造函数String...args
,这意味着您至少需要一个对象来运行,但(可能)设计者认为不需要。
例如,有这样的东西没有技术障碍:
class Hello {
public void main(String...args){
System.out.println("Hello, world");
}
}
事实上,如果你不指定一个,Java 会为你创建一个无参数构造函数,如果类包含一个 main 方法,那么包含一个 var args 构造函数也很容易,并在引擎盖下创建以下代码:
class Hello {
// created by the compiler
public Hello(){}
public Hello( String ... args ) {
this();
main( args );
}
// end of code created by the compiler
public void main( String ... args ) {
System.out.println("Hello, world!");
}
}
但这会创建不需要的代码,以及额外分配的对象,在这种情况下不会做任何事情;两个构造函数(而不是一个)等等。最后它看起来太神奇了。
取决于语言设计者。在这种情况下,他们可能认为不这样做会更简单,只需验证调用的类是否具有称为方法的特殊签名public static void main( String [] args )
顺便说一句,您可以在没有 main 方法Hello! world
的情况下使用 Java 程序,但它会抛出java.lang.NoSuchMethodError: main
public class WithoutMain {
static {
System.out.println("Look ma, no main!!");
System.exit(0);
}
}
$ java WithoutMain
Look ma, no main!!
我知道这不是另一种选择,但是,这很有趣不是吗?
我听到有人说“如果 main 不是静态的,那么 JVM 可以创建一个包含 main 的类的对象并通过对象调用该 main。
这不是真的。至少,这在JLS中没有指定。
但问题是 JVM 如何知道在构造函数重载的情况下调用哪个构造函数,或者即使只有一个参数化构造函数,然后传递什么。”
如果这是真的,我只希望它调用(隐式)默认的无参数构造函数。
static 是一个关键字,当它在 main 方法之前应用时,JVM 会假定这是执行的起点。所以 JVM 是这样想的吗?java soft 交给 JVM 的人负责通过 main() 方法进入指定的类 EX:假设有两个类 A 和 B,其中 B 扩展了 A,这里按照 java,对于每个类都应该创建一个对象来访问变量和该类中的方法,在 B 类中编写了静态 main() 方法,静态是单词,如所说的,与程序执行开始无关..它将在程序执行之前为该关键字分配内存。
因为它可能有主要方法。并且因为主要对象不需要是对象。如果是,则需要实例化一个。
如果您自己使用 jvm.dll,则不需要 main 函数,只需创建一个对象并调用它。
但是,通过这种方式,可以进行非面向对象的编程,只是为了那些出于某种原因需要这样做的人。:)
main 是静态的,因此您的代码无需先实例化类即可执行。也许您甚至不想创建一个类,或者创建类很慢并且您想先打印出“正在加载...”文本,或者您有多个构造函数等...有很多原因不强制用户在命令执行开始之前创建一个类。
如果您静态创建对象,您仍然可以在执行 main() 之前创建对象。
是的,在 JVM 上运行的其他语言会创建对象或模块(也是对象)并运行它们。例如,堡垒语言“Hello world”看起来像
Component HelloWorld
Export Executable
run(args) = print "Hello, world!"
end
或者,没有参数:
Component HelloWorld
Export Executable
run() = print "Hello, world!"
end
Java 比纯 OO 语言更实用一些,具有静态方法和字段以及原始类型。它的静态 main 方法更接近于 C 的 main 函数。你得问戈斯林他为什么选择那个大会。
启动 JVM 的代码相当简单——这个例子创建了一个 JVM,创建了一个对象并run
使用命令行参数调用它的方法——创建启动函数(new main.HelloWorld()).run(args)
而不是main.HelloWorld.main(args)
:
#include <stdio.h>
#include <jni.h>
JNIEnv* create_vm() {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_2;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=C:\\java_main\\classes";
args.options = options;
args.ignoreUnrecognized = JNI_TRUE;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
return env;
}
int invoke_class(JNIEnv* env, int argc, char **argv) {
jclass helloWorldClass;
helloWorldClass = env->FindClass("main/HelloWorld");
if (helloWorldClass == 0)
return 1;
jmethodID constructorMethod = env->GetMethodID(helloWorldClass, "<init>", "()V");
jobject object = env->NewObject(helloWorldClass, constructorMethod);
if (object == 0)
return 1;
jobjectArray applicationArgs = env->NewObjectArray(argc, env->FindClass("java/lang/String"), NULL);
for (int index = 0; index < argc; ++index) {
jstring arg = env->NewStringUTF(argv[index]);
env->SetObjectArrayElement(applicationArgs, index, arg);
}
jmethodID runMethod = env->GetMethodID(helloWorldClass, "run", "([Ljava/lang/String;)V");
env->CallVoidMethod(object, runMethod, applicationArgs);
return 0;
}
int main(int argc, char **argv) {
JNIEnv* env = create_vm();
return invoke_class( env, argc, argv );
}
无需创建对象来调用静态方法。所以jvm不需要分配额外的内存来创建main的obj然后调用它。
但问题是 JVM 如何知道在构造函数重载的情况下调用哪个构造函数,或者即使只有一个参数化构造函数,然后传递什么。”
我也这么认为。我不使用Java。我使用 C++。如果您不自己编写构造函数,则默认为您提供无参数构造函数和复制构造函数。但是当您自己编写构造函数时,编译器不会提供构造函数。我认为 Java 也遵循了这个理论。
所以在一个类中它不能保证它不会有一个构造函数。还限制类具有用户定义的构造函数是一个坏主意。但是如果系统允许您编写自己的构造函数,那么即使不能保证它将是无参数构造函数。所以如果是带参数的构造函数就不知道要送什么参数了。
所以我认为这就是静态 Main 函数背后的实际原因。