C# 在类本身内部创建类的实例是如何工作的?
是什么使得在类本身内部创建类的实例成为可能C# 在类本身内部创建类的实例是如何工作的?,c#,java,class,compiler-construction,C#,Java,Class,Compiler Construction,是什么使得在类本身内部创建类的实例成为可能 public class My_Class { My_Class new_class= new My_Class(); } 我知道这是可能的,我自己也做过,但我仍然无法让自己相信这不是“谁是第一个——鸡还是蛋?”之类的问题。我很高兴收到一个答案,它将从编程的角度以及JVM/编译器的角度阐明这一点。我认为理解这一点将有助于我澄清OO编程中一些非常重要的瓶颈概念 我收到了一些答案,但没有一个能达到我预期的程度。在类本身中创建类实例绝对没
public class My_Class
{
My_Class new_class= new My_Class();
}
我知道这是可能的,我自己也做过,但我仍然无法让自己相信这不是“谁是第一个——鸡还是蛋?”之类的问题。我很高兴收到一个答案,它将从编程的角度以及JVM/编译器的角度阐明这一点。我认为理解这一点将有助于我澄清OO编程中一些非常重要的瓶颈概念
我收到了一些答案,但没有一个能达到我预期的程度。在类本身中创建类实例绝对没有问题。在编译程序和运行程序时,以不同的方式解决明显的鸡或蛋问题 编译时 当编译一个创建自身实例的类时,编译器发现该类本身有一个实例。这种依赖关系很容易解决:编译器知道该类已经被编译,因此不会再次尝试编译它。相反,它假装类已经存在,并相应地生成代码 运行时 类创建自己的对象的最大问题是当类还不存在时;也就是说,当类被加载时。通过将类加载分为两个步骤来解决此问题:首先定义类,然后初始化类 定义意味着向运行时系统(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知道这一点
存在一个名为Test
的类,它有一个main
方法和一个构造函数
Test
类有两个静态变量,一个称为x
,另一个称为instance
,以及
测试
类的对象布局是什么。换句话说:一个物体看起来像什么;它有什么属性。在这种情况下,Test
没有任何实例属性
既然定义了类,就初始化它。首先,将默认值0
或null
分配给每个静态属性。这将x
设置为0
。然后JVM按照源代码顺序执行静态字段初始值设定项。有两种:
创建测试
类的实例,并将其分配给实例
。创建实例有两个步骤:
为对象分配第一个内存。JVM可以做到这一点,因为它已经从类定义阶段知道了对象布局
调用Test()
构造函数初始化对象。JVM可以这样做,因为它已经从类定义阶段获得了构造函数的代码。构造函数打印出x
的当前值,即0
将静态变量x
设置为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);
}
}
在对象内部创建对象实例可能会导致StackOverflower错误,因为每次从该“Test
”类创建实例时,您都将创建另一个insta
public class Test {
public Test() {
Test ob = new Test();
}
public static void main(String[] args) {
Test alpha = new Test();
}
}
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
}
}
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);
}
}
public class MyClass {
private static MyClass instance;
static {
instance = new MyClass();
}
// some methods
}