当前位置: 首页 > 知识库问答 >
问题:

在类本身内部创建类的实例是如何工作的?

谷星文
2023-03-14

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

public class My_Class
 {

      My_Class new_class= new My_Class();
 }

我知道这是可能的,我自己也做过,但我仍然无法让自己相信这不是“谁是第一个——鸡还是蛋?”问题的类型。我很高兴收到一个答案,它将从编程角度以及JVM/编译器角度阐明这一点。我认为理解这一点将有助于我澄清OO编程中一些非常重要的瓶颈概念。

我收到了一些答案,但没有一个能达到我预期的程度。

共有3个答案

萧凡
2023-03-14

我经常看到自己从类中创建实例的主要原因是,当我试图在静态上下文中引用非静态项时,例如当我为游戏或其他东西制作框架时,我使用main方法来实际设置框架。当构造函数中有需要设置的内容时,也可以使用它(如下面所示,我将JFrame设置为不等于null):

public class Main {
    private JFrame frame;

    public Main() {
        frame = new JFrame("Test");
    }

    public static void main(String[] args) {
        Main m = new Main();

        m.frame.setResizable(false);
        m.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        m.frame.setLocationRelativeTo(null);
        m.frame.setVisible(true);
    }
}
伍嘉
2023-03-14

其他的回答大多涵盖了这个问题。如果它有助于大脑的运转,举个例子怎么样?

鸡和蛋的问题就像任何递归问题一样得到了解决:基本情况是不会产生更多的工作/实例/任何东西。

想象一下,您已经创建了一个类,以便在必要时自动处理跨线程事件调用。与线程WinForms密切相关。然后,您希望该类公开一个事件,每当有东西向处理程序注册或注销时,就会发生该事件,当然,它也应该处理跨线程调用。

您可以编写两次处理它的代码,一次用于事件本身,一次用于状态事件,或者编写一次并重复使用。

这门课的大部分内容都被删减了,因为它与讨论并不真正相关。

public sealed class AutoInvokingEvent
{
    private AutoInvokingEvent _statuschanged;

    public event EventHandler StatusChanged
    {
        add
        {
            _statuschanged.Register(value);
        }
        remove
        {
            _statuschanged.Unregister(value);
        }
    }

    private void OnStatusChanged()
    {
        if (_statuschanged == null) return;

        _statuschanged.OnEvent(this, EventArgs.Empty);
    }


    private AutoInvokingEvent()
    {
        //basis case what doesn't allocate the event
    }

    /// <summary>
    /// Creates a new instance of the AutoInvokingEvent.
    /// </summary>
    /// <param name="statusevent">If true, the AutoInvokingEvent will generate events which can be used to inform components of its status.</param>
    public AutoInvokingEvent(bool statusevent)
    {
        if (statusevent) _statuschanged = new AutoInvokingEvent();
    }


    public void Register(Delegate value)
    {
        //mess what registers event

        OnStatusChanged();
    }

    public void Unregister(Delegate value)
    {
        //mess what unregisters event

        OnStatusChanged();
    }

    public void OnEvent(params object[] args)
    {
        //mess what calls event handlers
    }

}
郭志泽
2023-03-14

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

编译时间

当编译创建自身实例的类时,编译器发现该类对自身具有循环依赖关系。这种依赖关系很容易解决:编译器知道该类已经被编译,因此不会再次尝试编译它。相反,它假装类已经存在,并相应地生成代码。

运行时

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

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

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

下面是一个例子,说明类初始化和实例化如何在Java中交互:

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

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

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

让我们逐步了解JVM将如何运行这个程序。首先,JVM加载测试类。这意味着首先定义类,以便JVM知道这一点

  1. 一个名为Test的类存在,它有一个main方法和一个构造函数,并且
  2. Test类有两个静态变量,一个名为x,另一个名为实例,并且
  3. Test类的对象布局是什么。换句话说:对象看起来像什么;它有什么属性。在这种情况下,Test没有任何实例属性。

现在类已经定义好了,它就被初始化了。首先,默认值0null被分配给每个静态属性。这将x设置为0。然后JVM按照源代码顺序执行静态字段初始化器。有两个:

  1. 创建测试类的实例,并将其分配给实例。创建实例有两个步骤:

直到现在,类才完成加载。请注意,JVM创建了该类的一个实例,尽管它尚未完全加载。因为构造函数为x打印了初始默认值0,所以您就有了这一事实的证据。

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

x=0
x=1

正如您所见,没有鸡或蛋的问题:将类加载分离到定义和初始化阶段完全避免了这个问题。

当对象的一个实例想要创建另一个实例时,如下面的代码所示,该怎么办?

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

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

这里没有概念上的问题,请注意:这只是在一个写得不好的程序中无限递归的常见情况。递归可以被控制,例如使用计数器;这个类的构造函数使用递归来制作一系列对象:

class Chain {
    Chain link = null;
    public Chain(int length) {
        if (length > 1) link = new Chain(length-1);
    }
}
 类似资料:
  • 问题内容: 什么使在类本身内部创建类的实例成为可能? 我知道这是有可能的,而且我自己也做过,但是我仍然不能使自己相信这不像是“谁先是鸡还是蛋?” 问题类型。我很高兴收到一个可以从编程角度以及从JVM /编译器角度阐明这一问题的答案。我认为理解这一点将帮助我清除OO编程中一些非常重要的瓶颈概念。 我已经收到了一些答案,但是还没有达到我期望的程度。 问题答案: 在类本身中创建类的实例绝对没有问题。在编

  • 问题内容: 我是Java新手。 我的文件如下所示: 在另一个Java文件中,我试图创建A对象调用 但是由于某种原因我得到了错误: 有人可以解释我该怎么做吗?我的意思是,我真的需要创建的实例,然后设置实例,然后将实例提供给方法,还是有另一种方法呢? 问题答案: 在您的示例中,您有一个内部类,该内部类始终与外部类的实例绑定。 如果您想要的只是嵌套类以提高可读性而不是实例关​​联的一种方式,那么您需要一

  • 我正在阅读关于从内部类继承的内容,我一直在努力理解下面的代码是如何工作的: 据我所知,关键字Super指的是超类,Super()指的是它没有参数构造函数。在外层类的情况下,超类是Object类。 我想了解一下,这段代码是如何工作的。 为什么Object类的实例化使扩展内部类成为可能? 创建Object类实例与创建外层类对象有什么关系? 删除包含"o.super();"的行会导致错误: java:包

  • 我是Java新手。 我的文件看起来像这样: 在另一个java文件中,我试图创建一个调用 但由于某种原因,我得到了一个错误: 有人能解释一下我怎么做我想做的事吗?我的意思是,我真的需要创建的实例,然后将其设置为,然后将的实例赋给该方法吗,还是有其他方法可以做到这一点?

  • 问题内容: 我想创建一个内部类的Spring Bean。如果我有以下内部类: 我想在我的XML配置文件中创建bean实例。 问题答案: 您不能使用点()表示法访问公共静态内部类,而应使用货币()。一个例子: 这将起作用。

  • 问题内容: 我正在创建一个类及其子类,需要在其中调用父级的静态方法以返回子级实例。 问题答案: 该静态方法被调用,其值是类对象,子类,你把它称为后的构造。因此,您可以使用实例化它: