-1


I'm trying to understand recursive process in given example.

public class Test{

   Test t = new Test();

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

Since there is no any default Test constructor created how this method is having stackoverflow eroor?

Doesn't it suppose to have something like that to have stackoverflow error?

public class Test {

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

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

I understand that in second example the stackoverflow error occurs because of the infinity object creation. No problem in that.
My problem is in example on doesn't we suppose to call the
Test t = new Test();
statement is a constructor body? Otherwise how it called?
Please help me to figure this out?
Thank you.
EDIT.
Given below is the error output for both executions.

Exception in thread "main" java.lang.StackOverflowError
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
    at interview.Test.<init>(Test.java:11)
Dil.
  • 1,996
  • 7
  • 41
  • 68
  • 1
    Have you tried running it? – Jacob G. Feb 04 '18 at 04:03
  • @JacobG yes. Both returns stackoverflow error.. – Dil. Feb 04 '18 at 04:04
  • Exceptions usually come with a stack trace. Show it. – Mad Physicist Feb 04 '18 at 04:04
  • 2
    It's not clear what you're confused about. Both versions call the constructor when instantiated. Hence the stack overflow. – shmosel Feb 04 '18 at 04:04
  • 2
    In your first snippet you create infinitely many objects, since every instance creates an additional instance `t` via the field initialization. – Henry Feb 04 '18 at 04:04
  • 2
    "Since there is no any default Test constructor created" - The default constructor is created automatically. – Jacob G. Feb 04 '18 at 04:05
  • [Default constructor vs. inline field initialization](https://stackoverflow.com/q/4916735) – Pshemo Feb 04 '18 at 04:05
  • @Henry. Good catch. – Mad Physicist Feb 04 '18 at 04:05
  • @ MadPhysicist posted the error? – Dil. Feb 04 '18 at 04:07
  • @Henry I understand the infinity object creation in example two. What I'm trying to understand if there is a default constructor is created in compile time in example one doesn't it supposed to include the object creation inside it's body. – Dil. Feb 04 '18 at 04:10
  • @JacobG I understand the infinity object creation in example two. What I'm trying to understand if there is a default constructor is created in compile time in example one doesn't it supposed to include the object creation inside it's body. – Dil. Feb 04 '18 at 04:11
  • Initialization of class fields is executed at start of each constructor (right after its `super(...)` call). That also includes default constructor. So `public class Test{ Test t = new Test(); ...}` is compiled like `public class Test{ Test t; Test(){ t = new Test();}...}`. – Pshemo Feb 04 '18 at 04:12
  • @pippilongstocking I added an answer with more explanation. – Henry Feb 04 '18 at 04:14

4 Answers4

1

The first snippet of code is equivalent to this:

public class Test {

    Test t;

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

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

because the compiler generates a default constructor. In the body of the constructor a new instance is created, which gives recursively rise to more instances being created.

Henry
  • 42,982
  • 7
  • 68
  • 84
  • Since the object creation in main method new constructor is created. But can you please explain to me how t = new Test(); statement moved in that constructor – Dil. Feb 04 '18 at 04:22
  • What's the difference? The initialization of the field is executed in any case when the object is created. It does not matter if it is inside the constructor or a direct field initialization. The version of the code I showed is closer to the actual byte code generated. – Henry Feb 04 '18 at 04:31
1

Compiler implicitly add default constructor to your java class. And, constructor job is to initialize all member variable with its default value or specified value.

In your case, when java started running first code, Then,

Test t = new Test();

converts into following instructions

  Test t;
    descriptor: LTest;
    flags:

  Test();
    descriptor: ()V
    flags:
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: new           #2                  // class Test
         8: dup
         9: invokespecial #3                  // Method "<init>":()V
        12: putfield      #4                  // Field t:LTest;
        15: return
      LineNumberTable:
        line 1: 0
        line 3: 4

where you could see, declaration and initialization has split and initialization has moved inside constructor, which creates infinite loop.

Ravi
  • 30,829
  • 42
  • 119
  • 173
1

You are creating a member variable. Like a class Human that has a member variable Pet.

public class Human {
    // A human with a pet, initially 'null'
    Pet p = null;
}

But in your example every Test has a member variable Test. So if you create an instance of a Test it will execute the code to instantiate the member variable (like a human creating his pet Pet p = new Pet();).

public class Test {
    // A test with a test, initially 'new Test()'
    Test t = new Test();
}

But this Test has a member variable Test too (like the pet having a pet again), which then needs to be created again.

So at first you want to create a Test. But this has a member variable that yields a creation of a second Test. This second Test has again a member variable Test, yielding a third one and so on.

There you have your infinite loop. There will never be a Test object without a Test member, nor some final value like null. Every Test will again try to create a member.

You could stop the recursion by saying "the member should not get allocated yet" by putting null:

public class Test {
    // A test with a test, initially 'null', no recursion
    Test t = null;
}

But by putting new Test() you start the recursion.


Note on constructor

Other answers compared the statement with your second example, with the call inside the constructor.

Don't get confused by that. The statement does not move into the constructor. It is just that statements in this member scope get executed right at the beginning of object creation. So it basically is comparable to executing it from the constructor, but it is different code.

Technically, the compiler might realize that by moving it to the start of the constructor and by that generate equal bytecode for both versions though.

Zabuzard
  • 25,064
  • 8
  • 58
  • 82
  • 1
    OMG that is well detailed. Thank you for your examples with the answer? So to my latest understanding this stackoverflow error is happening because Test class has a member variable of Test. Am I right? – Dil. Feb 04 '18 at 04:40
  • Yes, but more precisely due to every `Test` wanting to allocate that member not with something final like `null` but another `Test` object. And by creating another `Test` object that other `Test` object does the same again, wanting and creating another `Test` member and so on. – Zabuzard Feb 04 '18 at 04:43
  • Well, technically the field initializations move into the method as you can see in the generated bytecode. – Henry Feb 04 '18 at 04:46
  • Yes, in the bytecode it might actually be equal. But I think that confused OP. But after the discussion it should be clear I think. – Zabuzard Feb 04 '18 at 04:47
  • 1
    @Zabuza yes it is detailed. Thanks again. – Dil. Feb 04 '18 at 04:51
  • public class Test {Test () {new Test (); }} It gives the same effect. – Kamil Tomasz Jarmusik Feb 04 '18 at 14:50
0

If you're serious. In your case, the attempt to create a Test initiates the creation of a Test that initiates the creation of Test and so to stackoverflow. :D Recursion must have its beginning and end. For example:

public static long factorial(int n) {

    if(n < 2)
        return 1;

    return n*factorial(n-1);
}

we start with n and end with 1.

  • I understand the recursion part. Thanks for the humor. What I'm trying to understand is how Test t = new Test(); generates inside the default constructor? – Dil. Feb 04 '18 at 04:27
  • new Test () calls the default constructor in which there is another new Test () and so on. – Kamil Tomasz Jarmusik Feb 04 '18 at 14:46