java 为什么可以在定义中实例化一个类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5186214/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 09:52:47  来源:igfitidea点击:

Why Can You Instantiate a Class within its Definition?

java

提问by McGlone

A coworker (who is very new to Java) stopped in today and asked what seemed like a very simple question. Unfortunately, I did an absolutely horrible job of trying to explain it to him. He had a book that had a little code that looked like this:

一位同事(对 Java 非常陌生)今天停下来问了一个看起来很简单的问题。不幸的是,我在试图向他解释这件事方面做得非常糟糕。他有一本书,里面有一些像这样的小代码:

class XCopy {

    public static void main(String[] args) {
        XCopy x = new XCopy(); // 1
        x.doIt();
    }

    public void doIt() {
        // Some code...
    }
}

He was confused on line 1. What he wanted to know was why a new instance of XCopy could be created within the definition of the class XCopy. He thought this would have given some sort of a forward referencing error. After all, we hadn't yet finished declaring what the class XCopy was, so how could we create one?

他在第 1 行感到困惑。他想知道为什么可以在类 XCopy 的定义内创建 XCopy 的新实例。他认为这会导致某种前向引用错误。毕竟,我们还没有完成 XCopy 类的声明,那么我们如何创建一个呢?

I certainly know that this is valid code but, when I tried to explain it to him, I found myself stumbling over the answer and I'm afraid I left him more confused than when he started. I'd like to hear some other explanations of why this works.

我当然知道这是有效的代码,但是当我试图向他解释它时,我发现自己在答案上磕磕绊绊,恐怕我让他比开始时更加困惑。我想听听其他关于为什么这样做的解释。

Any thoughts? Why can you instantiate an instance of a class within the definition of the class, itself?

有什么想法吗?为什么可以在类本身的定义中实例化类的实例?

回答by Matthew Gilliard

You are defining the class, all its fields and methods etc at compile time. The instance is not created until runtime. So there is no contradiction, the class is fully defined by the time you reach line #1.

您在编译时定义类、其所有字段和方法等。直到运行时才会创建实例。所以没有矛盾,当您到达第 1 行时,该类已完全定义。

As others point out, because the mainmethod is staticyou will reach line #1 without having instantiated an object, but you can do so without issue. I use this pattern all the time for one-class experiments.

正如其他人指出的那样,因为该main方法是static您将在没有实例化对象的情况下到达第 1 行,但您可以毫无问题地这样做。我一直使用这种模式进行一类实验。

回答by Dave Costa

Because code is compiled first, and executed later. All the compiler needs to know to validate that line is that a class named XCopy exists, and that it has a no-argument constructor. It doesn't need to know everything about the class.

因为代码是先编译后执行的。编译器需要知道来验证该行的所有内容是名为 XCopy 的类存在,并且它具有无参数构造函数。它不需要了解有关该类的所有信息。

回答by duffymo

It's not a forward reference in the C/C++ sense. Your main method is referring to the class as a type inside its own context. You aren't "ahead" of anything.

它不是 C/C++ 意义上的前向引用。您的主要方法将类称为其自身上下文中的类型。你并不“领先”任何东西。

The fact that main is static isn't germane, because it'll still work even for non-static methods:

main 是静态的这一事实并不是密切相关的,因为即使对于非静态方法它仍然可以工作:

public class Foo
{
   private String x;

   public Foo(String x) { this.x = x; }
   public Foo(Foo f) { this.x = f.x; }  // copy constructor; still compiles fine, even without static
}

One difference is the compilation and linking. C/C++ have separate compilation and linking steps. Java has a class loader. I think compiling to byte code and loading as needed at runtime using the class loader is a subtle difference between Java and C/C++ that explains why the forward reference idea isn't needed, but I'm not certain.

一个区别是编译和链接。C/C++ 有单独的编译和链接步骤。Java 有一个类加载器。我认为在运行时使用类加载器编译为字节码并根据需要加载是 Java 和 C/C++ 之间的细微差别,这解释了为什么不需要前向引用的想法,但我不确定。

回答by Kim Burgaard

A Class is just a blue print that describeswhat each and every instance of the class will look like and behave like. Depending on the visibility of the class and its constructors, code in the same class, in the same package, or complete strangers may create instances.

类只是一个蓝图,描述了类的每个实例的外观和行为。根据类及其构造函数的可见性,同一类、同一包中的代码或完全陌生的代码可能会创建实例。

It is for example common to provide a factory method in classes where the constructor should not be public:

例如,在构造函数不应公开的类中提供工厂方法是很常见的:

public class Foo {
    // only I get to create new instances
    private Foo() {
    }

    // but you can get instances through this factory method
    public static Foo createFoo() {
        return new Foo();
    }
}

回答by J?rn Horstmann

If your coworker is coming from a C or pascal programming background this question is abolutely logical. In a C program methods have to be declared above the line where they are first used. As its not always practical to order functions in this order, there are forward declarationsthat just give the methods name, return type and parameters, without defining the function body:

如果您的同事来自 C 或 pascal 编程背景,那么这个问题绝对是合乎逻辑的。在 C 程序中,方法必须在它们第一次使用的行上方声明。由于按此顺序对函数进行排序并不总是可行的,因此存在仅给出方法名称、返回类型和参数的前向声明,而不定义函数体:

// forward declaration
void doSomething(void);

void doSomethingElse(void) {
    doSomething();
}

// function definition
void doSomething(void) {
    ...
}

This was done to simplify the creation of a parser and to allow faster parsing since fewer passes over the source are needed. In Java however, identifiers are allowed to be used before their point of definition. Therefor parsing has to happen in several phases. After a syntax tree corresponding to the source code is build, this tree is traversed to determine all definitions of classes or methods. The method bodies are processed at a later stage, when all information about the names in scope are known.

这样做是为了简化解析器的创建并允许更快的解析,因为需要更少的源代码传递。但是,在 Java 中,允许在定义点之前使用标识符。因此,解析必须分几个阶段进行。在构建与源代码对应的语法树后,遍历该树以确定类或方法的所有定义。方法主体在稍后阶段进行处理,当所有关于范围内名称的信息都已知时。

So by the point that the method body of your main method is processed the compiler knows of the default constructor of your class and its doIt method and can generate the correct bytecode to call exactly this method.

因此,在处理 main 方法的方法体时,编译器知道类的默认构造函数及其 doIt 方法,并且可以生成正确的字节码以准确调用此方法。

回答by Isaac Truett

The same reason you can call a method on line 42 that isn't defined until line 78? Java isn't a scripting language, so things don't have to be declared before they are used (this is even true of some scripting languages, actually). Class definitions are considered as a whole at compile time.

您可以在第 42 行调用直到第 78 行才定义的方法的相同原因?Java 不是脚本语言,所以在使用之前不必声明(实际上对于某些脚本语言也是如此)。类定义在编译时被视为一个整体。

You can even instantiate an object of a class in its own constructor:

您甚至可以在其自己的构造函数中实例化一个类的对象:

public class Test {
    Test a;

    Test() {
        a = new Test();
    }

    public static void main(String[] args) {
        System.out.println(new Test());
    }
}

This produces... wait for it... a java.lang.StackOverflowError.

这会产生......等待它......一个java.lang.StackOverflowError

回答by adarshr

Because the main method is static. And by static, it means that the method doesn't belong to any particular instance of the class. That is, it can be accessed without creating an instance.

因为主要方法是静态的。对于静态,这意味着该方法不属于该类的任何特定实例。也就是说,它可以在不创建实例的情况下访问。

Thus, in order to invoke the doItmethod which is non-static, one must create an instance of the class which holds it.

因此,为了调用doIt非静态方法,必须创建一个包含它的类的实例。

回答by Bozho

The jvm loads the class whenever it is first "mentioned". Then there is nothing to prevent instantiation - it is already loaded

每当第一次“提及”该类时,jvm 就会加载该类。那么没有什么可以阻止实例化 - 它已经加载

回答by Varun

A class is loaded into memory by classloader and is initialized if any of the following happens.

类由类加载器加载到内存中,并在发生以下任一情况时进行初始化。

1) an Instance of class is created using either new() keyword or using reflection using class.forName(), which may throw ClassNotFoundException in Java.

1) 类的实例是使用 new() 关键字或使用 class.forName() 使用反射创建的,这可能会在 Java 中抛出 ClassNotFoundException。

2) an static method of Class is invoked.

2) Class 的静态方法被调用。

3) an static field of Class is assigned.

3) 分配了一个类的静态字段。

4) an static field of class is used which is not a constant variable.

4) 使用类的静态字段,它不是常量变量。

5) if Class is a top level class and an assert statement lexically nested within class is executed.

5) 如果 Class 是顶级类并且执行在词法上嵌套在类中的 assert 语句。

so by line 1, class is both loaded and initialized and hence there is no problem in instantiating an instance of class in itself.

所以在第 1 行,类被加载和初始化,因此在实例化类的实例本身时没有问题。

But if your code is like this,

但是如果你的代码是这样的

class Test {
    Test test2 = new Test();
    public static void main(String[] args) {
        Test test1 = new Test();     
    }
}

the above code will result in stackoverflow exception.



class Test {
    public static void main(String[] args) {
        Test test = new Test();     
    }
}

In the above code creating an instance won't call main again. 
Remember, main is a static method, not tied to any particular instance.



class Test {
    static Test test2 = new Test();
    public static void main(String[] args) {
        Test test1 = new Test();     
    }
}

This code will also run fine.

Read more from https://javarevisited.blogspot.in/2012/07/when-class-loading-initialization-java-example.html

https://javarevisited.blogspot.in/2012/07/when-class-loading-initialization-java-example.html阅读更多信息