在类本身内部创建类的实例是如何工作的? [英] How does creating a instance of class inside of the class itself works?

查看:24
本文介绍了在类本身内部创建类的实例是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

什么使在类本身内部创建类的实例成为可能?

What makes it possible to create a instance of class inside of the class itself?

public class My_Class
 {

      My_Class new_class= new My_Class();
 }

我知道这是可能的,并且我自己已经做到了,但我仍然无法让自己相信这不是谁是第一个——先有鸡还是先有蛋?"这样的事情.问题的类型.我很高兴收到一个答案,从编程的角度以及从 JVM/编译器的角度澄清这一点.我认为理解这一点将有助于我清除面向对象编程的一些非常重要的瓶颈概念.

I know it is possible and have done it myself but I cannot still make myself believe that this is not something like " who was first--Chicken or egg ?" type of problem. I could be glad to receive an answer that will clarify this from programming perspective as well as from JVM/ compiler perspective. I think understanding this will help me clear some very important bottleneck concepts of OO programming.

我收到了一些答案,但都没有达到我预期的程度.

I have received some answers but none are clear to the degree I expected.

推荐答案

在类本身中创建类的实例绝对没有问题.在程序编译和运行时,明显的先有鸡还是先有蛋的问题以不同的方式解决.

There is absolutely no problem in creating instances of a class in the class itself. The apparent chicken-or-egg problem is solved in different ways while the program is being compiled and when it is being run.

编译时

当一个创建自身实例的类被编译时,编译器发现该类有一个循环依赖在自己身上.这种依赖很容易解决:编译器知道该类已经被编译,所以它不会再次尝试编译它.相反,它假装该类已经存在相应地生成代码.

When a class that creates an instance of itself is being compiled, the compiler finds that the class has a circular dependency on itself. This dependency is easy to solve: the compiler knows that the class is already being compiled so it won't try to compile it again. Instead, it pretends that the class already exists generates code accordingly.

运行时

创建自身对象的类最大的先有鸡还是先有蛋的问题是当该类甚至不存在时;也就是说,当类被加载时.通过将类加载分为两步来解决这个问题:首先,类被定义,然后它被初始化.

The biggest chicken-or-egg problem with a class creating an object of itself is when the class does not even exist yet; that is, when the class is being loaded. This problem is resolved by breaking class loading into two steps: first the class is defined and then it is initialized.

定义意味着将类注册到运行时系统(JVM或CLR),以便它知道类的对象具有的结构,以及调用其构造函数和方法时应该运行哪些代码.

Defining means registering the class with the runtime system (JVM or CLR), so that it knows the structure that objects of the class have, and what code should be run when its constructors and methods are called.

一旦定义了类,它就会被初始化.这是通过初始化静态成员和运行静态初始化程序块和其他在特定语言中定义的东西来完成的.回想一下,此时已经定义了类,因此运行时知道类的对象是什么样的,以及应该运行哪些代码来创建它们.这意味着在初始化时创建类的对象没有任何问题.

Once the class has been defined it is initialized. This is done by initializing static members and running static initializer blocks and other things defined in the particular language. Recall that the class is already defined at this point, so the runtime knows what objects of the class look like and what code should be run to create them. This means there's no problem whatsoever to create objects of the class when initializing it.

以下示例说明了 Java 中的类初始化和实例化如何交互:

Here's an example that illustrates how class initialization and instantiation interact in Java:

class Test {
    static Test instance = new Test();
    static int x = 1;

    public Test() {
        System.out.printf("x=%d
", x);
    }

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

让我们逐步了解 JVM 将如何运行此程序.首先JVM 加载Test 类.这意味着该类首先被定义,以便JVM知道

Let's step through how the JVM would run this program. First the JVM loads the Test class. This means that the class is first defined, so that the JVM knows that

  1. 存在一个名为 Test 的类,它有一个 main 方法和一个构造函数,并且
  2. Test 类有两个静态变量,一个叫做 x,另一个叫做 instance,以及
  3. Test 类的对象布局是什么.换句话说:一个对象是什么样子;它有什么属性.在这种情况下,Test 没有任何实例属性.
  1. a class called Test exists and that it has a main method and a constructor, and that
  2. the Test class has two static variables, one called x and another called instance, and
  3. what is the object layout of the Test class. In other words: what an object looks like; what attributes it has. In this case Test doesn't have any instance attributes.

既然定义了类,就初始化.首先,默认值 0null 被分配给每个静态属性.这会将 x 设置为 0.然后 JVM 按源代码顺序执行静态字段初始值设定项.有两个:

Now that the class is defined, it is initialized. First of all, the default value 0 or null is assigned to every static attribute. This sets x to 0. Then the JVM executes the static field initializers in the source code order. There are two:

  1. 创建Test 类的实例并将其分配给instance.创建实例有两个步骤:
  1. Create an instance of the Test class and assign it to instance. There are two steps to instance creation:
  1. 为对象分配第一个内存.JVM 可以这样做,因为它已经从类定义阶段就知道对象布局.
  2. 调用Test() 构造函数来初始化对象.JVM 可以做到这一点,因为它已经拥有类定义阶段的构造函数代码.构造函数打印出x的当前值,即0.
  1. First memory is allocated for the object. The JVM can do this because it already knows the object layout from the class definition phase.
  2. The Test() constructor is called to initialize the object. The JVM can do this because it already has the code for the constructor from the class definition phase. The constructor prints out the current value of x, which is 0.

  • 设置静态变量x1.
  • 现在这个类才完成加载.请注意,JVM 创建了该类的一个实例,即使它尚未完全加载.您已经证明了这一事实,因为构造函数打印出了 x 的初始默认值 0.

    Only now the class has finished loading. Notice that the JVM created an instance of the class, even though it was not fully loaded yet. You have proof of this fact because the constructor printed out the initial default value 0 for x.

    现在JVM已经加载了这个类,它调用main方法来运行程序.main 方法创建了 Test 类的另一个对象 - 程序执行中的第二个对象.构造函数再次打印出x 的当前值,现在是1.程序的完整输出是:

    Now that the JVM has loaded this class, it calls the main method to run the program. The main method creates another object of class Test - the second in the execution of the program. Again the constructor prints out the current value of x, which is now 1. The full output of the program is:

    x=0
    x=1
    

    正如您所看到的,不存在先有鸡还是先有蛋的问题:将类加载到定义和初始化阶段的分离完全避免了这个问题.

    As you can see there is no chicken-or-egg problem: the separation of class loading into definition and initialization phases avoids the problem completely.

    当对象的一个​​实例想要创建另一个实例时怎么办,就像下面的代码一样?

    What about when an instance of the object wants to create another instance, like in the code below?

    class Test {
        Test buggy = new Test();
    }
    

    当您创建此类的对象时,同样没有固有问题.JVM 知道应该如何在内存中布置对象,以便为它分配内存.它将所有属性设置为其默认值,因此 buggy 设置为 null.然后 JVM 开始初始化对象.为了做到这一点,它必须创建另一个 Test 类的对象.像以前一样,JVM 已经知道如何做到这一点:它分配内存,将属性设置为 null,然后开始初始化新对象……这意味着它必须创建相同的第三个对象类,然后是第四个、第五个,依此类推,直到耗尽堆栈空间或堆内存.

    When you create an object of this class, again there is no inherent problem. The JVM knows how the object should be laid out in memory so it can allocate memory for it. It sets all the attributes to their default values, so buggy is set to null. Then the JVM starts initializing the object. In order to do this it must create another object of class Test. Like before, the JVM already knows how to do that: it allocates the memory, sets the attribute to null, and starts initializing the new object... which means it must create a third object of the same class, and then a fourth, a fifth, and so on, until it either runs out of stack space or heap memory.

    请注意,这里没有概念问题:这只是编写糟糕的程序中无限递归的常见情况.例如可以使用计数器来控制递归;这个类的构造函数使用递归来创建一个对象链:

    There is no conceptual problem here mind you: this is just a common case of an infinite recursion in a badly written program. The recursion can be controlled for example using a counter; the constructor of this class uses recursion to make a chain of objects:

    class Chain {
        Chain link = null;
        public Chain(int length) {
            if (length > 1) link = new Chain(length-1);
        }
    }
    

    这篇关于在类本身内部创建类的实例是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆