为什么java中的构造函数没有返回类型?

发布于 2024-11-26 01:24:27 字数 212 浏览 1 评论 0 原文

可能的重复:
为什么构造函数不返回值

为什么构造函数没有返回类型,甚至没有返回类型?这是什么原因呢?

Possible Duplicate:
Why constructor not returns value

Why don't constructors have a return type, not even void? What's the reason for that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浪菊怪哟 2024-12-03 01:24:27

构造函数在内部是一个名为 void 返回类型的非静态方法。它不返回任何内容。在内部分配第一个对象,然后调用其构造函数。对象本身没有分配构造函数。
换句话说,语法 new Object() 不仅调用构造函数,还创建新对象,并在调用构造函数后返回它。 Suns 的 Java 教程 表示“遵循 new 运算符通过调用构造函数来初始化新对象。”初始化并不意味着创建。

回答问题。缺少返回类型声明是区分构造函数和方法的一种方式。但是您可以从构造函数返回,就像从 void 方法返回一样。例如,这段代码可以正确编译和运行:

public class TheClass {
    public TheClass(){
        return;
    }
    public void TheClass(){ //confusing, but this is void method not constructor
        return;
    }

    public static void main(String[]a){
        TheClass n = new TheClass();
        n.TheClass();//void method invocation
    }
}

此类有一个 void 方法(不要在家尝试 - 大写方法是一种不好的风格)和一个构造函数。区别在于声明的返回类型。

看一下这个 JNI 代码片段,它演示了构造函数是一个非静态 void 方法:

 jstring
 MyNewString(JNIEnv *env, jchar *chars, jint len)
 {
     jclass stringClass;
     jmethodID cid;
     jcharArray elemArr;
     jstring result;

     stringClass = (*env)->FindClass(env, "java/lang/String");
     if (stringClass == NULL) {
         return NULL; /* exception thrown */
     }
 /* Get the method ID for the String(char[]) constructor */
     cid = (*env)->GetMethodID(env, stringClass,
                               "<init>", "([C)V");
     if (cid == NULL) {
         return NULL; /* exception thrown */
     }

     /* Create a char[] that holds the string characters */
     elemArr = (*env)->NewCharArray(env, len);
     if (elemArr == NULL) {
         return NULL; /* exception thrown */
     }
     (*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);

     result = (*env)->AllocObject(env, stringClass);
     if (result) {
         (*env)->CallNonvirtualVoidMethod(env, result, stringClass,
                                          cid, elemArr);
         /* we need to check for possible exceptions */
         if ((*env)->ExceptionCheck(env)) {
             (*env)->DeleteLocalRef(env, result);
             result = NULL;
         }
     }
     /* Free local references */
     (*env)->DeleteLocalRef(env, elemArr);
     (*env)->DeleteLocalRef(env, stringClass);
     return result;
 }

尤其是这些片段:

 /* Get the method ID for the String(char[]) constructor */
 cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V");

然后

 /* Allocate new object. */
 result = (*env)->AllocObject(env, stringClass);
 if (result) {
      /* Call uninitialized objects' constuctor. */
      (*env)->CallNonvirtualVoidMethod(env, result, stringClass, cid, elemArr);

分配第一个对象,然后调用非静态 方法。有关详细信息,请参阅 AllocObject 函数文档 表示“分配一个新的 Java 对象,而不调用该对象的任何构造函数。返回对 目的。”所以在JVM中对象不是由构造函数分配的,而是仅由构造函数初始化的。查看构造函数的字节码,我们发现没有返回任何对象(与 void 方法完全相同)。

另一种方式是,当您分解示例类时,您将看到从其构造函数调用父(对象)构造函数:

#javap -c NewClass
Compiled from "NewClass.java"
public class NewClass extends java.lang.Object{
public NewClass();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

}

请注意, 方法实际上并不是 Java 语言的一部分。相反,它是 Java 虚拟机期望在 Java 类文件中看到的内容。这种区别很重要,因为 Java 语言不依赖于类文件。 Java 源代码可以编译成其他二进制格式,包括本机可执行文件。将 Java 语言源代码转换为其他二进制格式的 Java 编译器不需要生成名为 的方法,只要在适当的时间以适当的方式初始化对象即可。 Java 语言规范 (JLS) 详细说明了初始化的顺序以及初始化的时间,但没有说明它实际上是如何完成的。

但我发现我们在这里讨论的是 JVM。

对于一些不相信的人来说,这是一个示例(thx biziclop),它表明对象存在并在从构造函数返回之前被分配:

   class AnotherClass {

        private String field;
        public static AnotherClass ref;

        public AnotherClass() {
            this.field = "value";
            AnotherClass.ref = this;
            throw new RuntimeException();
        }

        @Override
        public String toString() {
            return field;
        }
    }

    public class MainClass {
        public static void main(String[] a) {
            try {
                new AnotherClass();
                return;
            } catch (RuntimeException ex) {
                System.out.println("exception");
            }
            System.out.println("instance: " + AnotherClass.ref);
        }
    }

Constructor is internally a nonstatic method with name <init> and void return type. It does not return anything. Internally first object is allocated and then its constructor is called. Object is not allocated with constructor itself.
In other words the syntax new Object() not only calls the constructor but also creates new object and after calling the constructor returns it. The Suns' Java tutorial stands that "The new operator is followed by a call to a constructor, which initializes the new object." Initialize does not mean create.

Answering the question. Missing return type declaration is a way in which you distinguish the constructor from a method. But you can return from constructor as from void method. For example this code compiles and runs correctly:

public class TheClass {
    public TheClass(){
        return;
    }
    public void TheClass(){ //confusing, but this is void method not constructor
        return;
    }

    public static void main(String[]a){
        TheClass n = new TheClass();
        n.TheClass();//void method invocation
    }
}

This class has one void method (don't try it at home - uppercase method is a bad style) and one constructor. The difference is in declared return type.

Look at this JNI code snippet which demonstrates that constructor is a nonstatic void method:

 jstring
 MyNewString(JNIEnv *env, jchar *chars, jint len)
 {
     jclass stringClass;
     jmethodID cid;
     jcharArray elemArr;
     jstring result;

     stringClass = (*env)->FindClass(env, "java/lang/String");
     if (stringClass == NULL) {
         return NULL; /* exception thrown */
     }
 /* Get the method ID for the String(char[]) constructor */
     cid = (*env)->GetMethodID(env, stringClass,
                               "<init>", "([C)V");
     if (cid == NULL) {
         return NULL; /* exception thrown */
     }

     /* Create a char[] that holds the string characters */
     elemArr = (*env)->NewCharArray(env, len);
     if (elemArr == NULL) {
         return NULL; /* exception thrown */
     }
     (*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);

     result = (*env)->AllocObject(env, stringClass);
     if (result) {
         (*env)->CallNonvirtualVoidMethod(env, result, stringClass,
                                          cid, elemArr);
         /* we need to check for possible exceptions */
         if ((*env)->ExceptionCheck(env)) {
             (*env)->DeleteLocalRef(env, result);
             result = NULL;
         }
     }
     /* Free local references */
     (*env)->DeleteLocalRef(env, elemArr);
     (*env)->DeleteLocalRef(env, stringClass);
     return result;
 }

especially these fragments:

 /* Get the method ID for the String(char[]) constructor */
 cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V");

and then

 /* Allocate new object. */
 result = (*env)->AllocObject(env, stringClass);
 if (result) {
      /* Call uninitialized objects' constuctor. */
      (*env)->CallNonvirtualVoidMethod(env, result, stringClass, cid, elemArr);

first object is allocated and then nonstatic <init> method is called. For details look here. The AllocObject function documentation stands that "Allocates a new Java object without invoking any of the constructors for the object. Returns a reference to the object." So in JVM object is not allocated by constructor, but only initialized by it. Looking in constructors' bytecode we are seeing that no object is returned (exactly like in void methods).

Another way, when you dissasemble sample class, you will see invocation of parent (Object) constructor from its constructor:

#javap -c NewClass
Compiled from "NewClass.java"
public class NewClass extends java.lang.Object{
public NewClass();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

}

Note that the <init> method is not actually part of the Java language. Rather, it is something the Java virtual machine expects to see in a Java class file. This distinction is significant because the Java language does not depend on the class file. Java source can be compiled into other binary formats, including native executables. A Java compiler that translates Java language source into some other binary format need not generate a method named <init>, so long as objects are initialized in the proper way at the proper time. The Java Language Specification (JLS) details the order of initialization and when it occurs, but doesn't say how it is actually accomplished.

But I see that we're talking about JVM here.

For some of nonbelievers this is example (thx biziclop) which shows that object exists and is allocated before returning from constructor:

   class AnotherClass {

        private String field;
        public static AnotherClass ref;

        public AnotherClass() {
            this.field = "value";
            AnotherClass.ref = this;
            throw new RuntimeException();
        }

        @Override
        public String toString() {
            return field;
        }
    }

    public class MainClass {
        public static void main(String[] a) {
            try {
                new AnotherClass();
                return;
            } catch (RuntimeException ex) {
                System.out.println("exception");
            }
            System.out.println("instance: " + AnotherClass.ref);
        }
    }
若相惜即相离 2024-12-03 01:24:27

你将如何获得返回值?您对返回的哪种价值感兴趣?您将如何声明返回类型?

 X x = new X ();

将 X 引用分配给 x。现在,如果 new X 会返回一些东西,你应该如何获取它?

 class X { 
     public int X () { 
          return 42;
     }
 }

从ctor返回一些东西的逻辑是什么?错误信息?一些登录信息?将其写入文件或属性,稍后轮询。

由于每个对象只能访问该构造函数一次,因此我能想到使用另一个返回值的唯一原因是通知创建过程本身。

 class X { 
     private Y y;
     public int X () { 
          y = new Y ();
     }
     public Y getY () { return y; }
 }

How would you get the returned value? What kind of value are you interested in, being returned? How would you declare the return type?

 X x = new X ();

assigns an X-reference to x. Now, if new X would return something, how should you get it?

 class X { 
     public int X () { 
          return 42;
     }
 }

What's the logic of returning something from the ctor? An error message? Some loginfo? Write it to a file, or to an attribute, which you poll later.

Since the ctor is only accessed once per object, the only reason I can think of, to use another return value, would be, to inform about the process of creation itself.

 class X { 
     private Y y;
     public int X () { 
          y = new Y ();
     }
     public Y getY () { return y; }
 }
忘年祭陌 2024-12-03 01:24:27

尽管构造函数的 VM 实现不会返回任何值,但实际上它会返回任何值 - 新对象的引用。如果能够在一个语句中存储新对象的引用和附加返回值之一或两者,那么在语法上会很奇怪和/或令人困惑。

Even though the VM implementation of a constructor isn't to return any value, in practice it kind of does - the new object's reference. It would then be syntactically weird and / or confusing to be able to store one or both of the new object's reference and an additional return value in one statement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文