Win a copy of The Little Book of Impediments (e-book only) this week in the Agile and Other Processes forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

instance variable creation prior to object creation?

 
nick woodward
Ranch Hand
Posts: 370
11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
is it right to say that object constructors initialize instance variables, but do not declare (or create them)?

for example: say a class A, is extended by B. B overrides A's only method (print), which prints a '2' (where as A's prints a '1').

when B is created, it implicitly calls super, calling A's constructor, which is designed to call the print method (polymorphically calling B's version). This prints '0' because B's constructor has not run/initialized the variable, correct?

so when is the variable created? when the class is loaded? i would've previously assumed that the constructor 'creates' the members of the object, so any methods or ivars called prior to the constructor would cause an exception. i tried looking in the JLS, but as usual understood all of the words, but none of the meaning!

any help would be greatly appreciated!

thanks,

nick
 
Roel De Nijs
Sheriff
Posts: 10662
144
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote:for example: say a class A, is extended by B. B overrides A's only method (print), which prints a '2' (where as A's prints a '1').

when B is created, it implicitly calls super, calling A's constructor, which is designed to call the print method (polymorphically calling B's version). This prints '0' because B's constructor has not run/initialized the variable, correct?

Let's see if you are correct using a code snippetOutput:
iA = 1
iB = 0


So your statement was absolutely correct At the moment an object is created, all instance variables exist and are initialized with their default values. But iB will get its actual value (2) when the constructor of A has finished and the constructor of B is being executed. So if you add a call to the print method to B's constructor, you'll see iB = 2 in the output as well.Output:
iA = 1
iB = 0
iB = 2


nick woodward wrote:so when is the variable created? when the class is loaded? i would've previously assumed that the constructor 'creates' the members of the object, so any methods or ivars called prior to the constructor would cause an exception.

The instance variable is definitely not created when the class is loaded, because the class is only loaded once when the JVM needs the class for the very first time (at this point static variables and static initializer blocks are executed). But when you create an object, the instance variables exist and are initialized with their default value. You won't get an exception or compiler error, unless you try to use an instance variable before the parent constructor has finished its execution, like in this code snippetSo at line1 you'll get a compiler error because you try to access/use iB before its parent constructor has run.

Hope it helps!
Kind regards,
Roel

PS. Good question. Have a cow!
 
nick woodward
Ranch Hand
Posts: 370
11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

At the moment an object is created, all instance variables exist and are initialized with their default values. But iB will get its actual value (2) when the constructor of A has finished and the constructor of B is being executed.


i guess this is the crux of it... "at the moment an object is created". i always assumed that the object was created (or at least completed) at the end of its constructor. but from the way you're discussing this, it seems that maybe i was mistaken.

is the call to the constructor (eg new B()) when the object is created then? and the constructor just deals with the initialization? to answer my own question, i don't think it can be, because why then does your second example cause an exception? it would surely just pass a default value!

...wheres the confused smiley......!

thanks for the cow!
 
Tim Holloway
Saloon Keeper
Posts: 18367
56
Android Eclipse IDE Linux
  • Likes 3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm going to explain this in terms of a typical low-level implementation. Actual mechanisms may vary, but the net effect is the same.

Let's say I have class B, which extends class A, which, of course, extends java.lang.Object.

If you invoke the newInstance method (for example, via the "new" operator or via reflection), then the first thing that's required is enough memory to contain the instance of "B", which is (approximately) the size of a java.lang.Object plus the size of the instance variables in class A, plus the size of the instance variables unique to class B. Memory services are called to obtain a chunk of RAM of that size. That's the substrate that the object will be constructed upon. At the moment, it's (probably) all binary zeroes.

So now that we have the substrate memory, we build upon it. First, the JVM will intialize any general object management overhead items. Those are not visible to application code - in fact, undefined to application code, but the JVM needs them to manage the new object. Stuff to aid location of the object, the object's Class, garbage collection support, whatever.

Now the object's constructor is invoked. The first several lines of this method have special import because it's here - and ONLY here that the "self" and "super" constructors may be invoked. If they are not explicitly invoked, then they will be implicitly invoked. You would explicitly invoke super() if you wanted to invoke a superclass constructor with arguments - otherwise that object's default no-arguments constructor would be invoked. If there is no default constructor, the compiler will complain. The "self" constructor is usually used to invoke shared init code in the event that you have multiple constructors. Needless to say, circular/recursive constructors are not allowed.

In addition to explicit constructors, there's also an implicit constructor. You can see its name in some debuggers, where it's listed as _init(). You cannot invoke this method explicitly. It contains all of the code for the initializer expressions of the class declaration. For example:


has an implicit constructor:


So. First, the appropriate super-constructor is invoked. If there are intermediate classes, such as class A, then each of those classes bounces up the hierarchy until the java.lang.Object's _init() and constructor methods have been invoked. The java.lang.Object constructor returns to the class A constructors - its _init() method and the applicanble class A constructor (if any). Once we have a fully-populated Object fully-extended with class A properties, then class A's constructor returns to class B's constructor. Class B's _init() method is invoked, then the rest of the class B constructor code is executed. All done in a way to ensure that dependent properties have all been allocated and initialized before subclasses might need them. Since a superclass cannot reference members of a derived subclass and no subclass can remove a superclass property, only hide it, everybody's happy.
 
Roel De Nijs
Sheriff
Posts: 10662
144
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote:is the call to the constructor (eg new B()) when the object is created then? and the constructor just deals with the initialization? to answer my own question, i don't think it can be, because why then does your second example cause an exception? it would surely just pass a default value!

What happens whn you create a new instance (using e.g. new B()) is very well explained in Tim's post. Let me focus on the 2nd part of your doubts. From the JLS section about Explicit Constructor Invocations:
JLS, section 8.8.7.1. Explicit Constructor Invocations wrote:An explicit constructor invocation statement in a constructor body may not refer to any instance variables or instance methods or inner classes declared in this class or any superclass, or use this or super in any expression; otherwise, a compile-time error occurs.

So I still want to emphasize that you can not use instance variables nor instance methods! I already posted a code example of the first one; here's a code example of the latter oneSo just remember it's not only instance variables, but also instance methods which will cause a compiler error if you use them in an explicit constructor invocation statement. It doesn't matter if you try to invoke a super or this constructor.

nick woodward wrote:...wheres the confused smiley......!

Here it is: !

Hope it helps!
Kind regards,
Roel
 
nick woodward
Ranch Hand
Posts: 370
11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You're very right - I read Tim's explanation on object creation and forgot what my problem was completely! Excellent stuff, thanks!

However.......A little more thought on my part and I've ruined everything again! It's like I understand both of your points in isolation but can't quite put them together...... I've got to be missing something here. A few brain cells perhaps!

Roel De Nijs wrote:So just remember it's not only instance variables, but also instance methods which will cause a compiler error if you use them in an explicit constructor invocation statement. It doesn't matter if you try to invoke a super or this constructor.


I'll write it out as I now understand it, for the sake of my own clarity as much as anyone elses understanding of it:

1. The memory is allocated for the object, derived objects, and members of both, when the new operator is called.

2. B calls this() or super() on the way up the object hierarchy. It cannot call this(instance member) or super(instance member) because ivars and methods are currently undefined.

3 This occurs up to java.lang.Object. We go back down the hierarchy invoking _init(), followed by the constructor. This initializes the objects variables, explaining why a polymorphic call from the super class returns a default or uninitialized variable.

What I still am failing to grasp is where the change occurs in the object variables. On the way up the hierarchy the members of the current object seem to not exist at all (point 2). On the way back down they return default values prior to the _init() method running (point 3).

(and there it is!)

Thanks again, this has definitely got my brain working!

Nick
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
It's not that the variables don't exist on the way up the hierarchy of constructor calls. They clearly do exist because you can access them from the super class by calling methods overridden by the sublclass.

However the writers of the JLS have decided to explicitly disallow references to instance members of classes in calls to this() or super() because they know that at this point the variables have not been initialised.

However when it comes to a superclass calling non-final non-private methods it can't be known whether or not a field will be accessed before being initialised. At the point the superclass code is compiled the subclass (where the uninitialised variable lives) may not even have been written yet.

The JLS authors could have chosen to ban this just on the off chance, but I imagine they decided this was too restrictive. Perhaps they simply didn't even consider the possibility. Either way, it is best practice never to call a method that could possibly be overridden from a constructor.
 
Roel De Nijs
Sheriff
Posts: 10662
144
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote:I'll write it out as I now understand it, for the sake of my own clarity as much as anyone elses understanding of it:

Nothing wrong with your understanding Also note that this topic and explanation is far beyond what you need to know for the OCA exam (and even the OCP exam). The only thing you really need to know is the second point of your explanation (calling this(instance member) or super(instance member) will result in a compiler error).

nick woodward wrote:What I still am failing to grasp is where the change occurs in the object variables. On the way up the hierarchy the members of the current object seem to not exist at all (point 2). On the way back down they return default values prior to the _init() method running (point 3).

What you are probably missing with the polymorphic call is that this call happens in the super constructor and a subclass method is executed. During the execution of the super constructor, the parent part of the object is being "constructed" and initialized, so at that time instance variables of the parent class are getting their initial value (assigned instantly, in an instance initializer block or in a constructor) and the instance variables of the subclass already exist and are initialized with their default value (illustrated with the output of the overridden print method). But once the parent class constructor finishes, the instance variables are initialized to their appropriate value and the execution of the subclass constructor begins. And at this point the instance variables will also get initialized with their initial value (assigned instantly, in an instance initializer block or in a constructor).
Let's try with this code example (I have added a few instance initializer blocks, just for fun )Output:
iA (init1) = 0
iA (init2) = 1
iA (ctr) = 10
iB (print) = 0
iB (init2) = 2
iB (init1) = 2
iB (ctr) = 2


So when the constructor of A is running, iB can be accessed through the polymorphic print method. But although iB is initialized instantly (on the same line as the declaration), this value is still not assigned when the polymorphic print method is executed. But once the constructor of A has finished, iB is finally initialized with 2 (and printed in the instance initializer blocks and the constructor)

Hope it helps!
Kind regards,
Roel
 
nick woodward
Ranch Hand
Posts: 370
11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
thanks alot to all of you! very helpful.

i went through your example Roel and got the exact same output - so i don't think my confusion lies there (although i was going to ask for a similar question to work through anyway - there's a particularly difficult one in K&B7 that I always got wrong - so looks like I've got it sorted now! thanks!)

i think my problem probably lies with what Mike has said - the design decision to not allow access to member variables on the way up the hierarchy, even though (as far as I can tell / comprehend) there doesn't seem to be much of a reason for this. i think it will probably become more clear when i have more experience!

again, really helpful. this will undoubtedly compound my problem of getting all the 'tough' enthuware questions right, and all the 'very easy' ones wrong.....

Nick


*edit - that link!

" The Alot is an imaginary creature that I made up to help me deal with my compulsive need to correct other people's grammar. It kind of looks like a cross between a bear, a yak and a pug, and it has provided hours of entertainment for me in a situation where I'd normally be left feeling angry and disillusioned with the world."

lol!
 
Roel De Nijs
Sheriff
Posts: 10662
144
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
nick woodward wrote:i think my problem probably lies with what Mike has said - the design decision to not allow access to member variables on the way up the hierarchy, even though (as far as I can tell / comprehend) there doesn't seem to be much of a reason for this. i think it will probably become more clear when i have more experience!

Maybe that design decision is made to prevent object creation mayhem like this What will be the value of iA when you create a B instance? It could be 0 or 6: 0 is not what you would expect and 6 requires to create/initialize the subclass before its parent classes (and that's like building a house and starting with the attic )
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
There is a reason for them to disallow references to member variables in calls to super, as I mentioned above.

The JLS explicitly defines the order that code gets run when initialising a new instance of a class. The first thing that runs (assuming the class is loaded) is the call to this/super in the constructor. After that returns all of the initialisers will run, and then the rest of the constructor body will run.

This means that the member variables have not yet been initialised when super runs. This means that it is never meaningful to allow super to be passed a member field as a parameter, so it makes sense to disallow it.

They could have allowed it if they had wanted to (in the same way that calling overridable methods from constructors is allowed), but what benefit would that give? It would be confusing, and lead to strange errors at runtime.
 
nick woodward
Ranch Hand
Posts: 370
11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Roel De Nijs wrote:
Maybe that design decision is made to prevent object creation mayhem like this What will be the value of iA when you create a B instance? It could be 0 or 6: 0 is not what you would expect and 6 requires to create/initialize the subclass before its parent classes (and that's like building a house and starting with the attic )


Well, it's definitely not 6

It just seems odd to have a compiler error there, but not with the polymorphic call - which is why I asked where exactly the variables were declared rather than initialized - but i understand now that its a design thing, just not quite why!

Mike - yeah, I did see you give an explanation, thanks, but I didnt' really understand it tbh. "it can't be known whether or not a field will be accessed before being initialised". do you mean because it can't be known if the call in the superclass is polymorphic or not? so the non-overridden call would access initialized variables of the current class, but the overridden one would attempt to access subclass variables?

I dunno, from a beginner's perspective having derived variables suddenly accessible (although uninitialized) when java.lang.Object is constructed seems a bit odd. It seems like they should've either been accessible from the moment the variables were assigned memory space, returning a zero until initialization, or should've returned an error for any polymorphic call to an uninitialized variable on the way down the hierarchy. I'm sure there is a very valid reason why this isn't the case. That's what I meant by 'becoming clear when I had more experience'. My brain is overloaded. The answer could be obvious, or maybe there doesn't even need to be one!

Interesting stuff anyway. For the time being, is it safe for me to assume that accessing anything pre-java.lang.Object creation throws an error, and anything after could be polymorphic and therefore 0? Plus that in general I should stay the hell away from methods in constructors? :P
 
Mike. J. Thompson
Bartender
Posts: 689
17
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
With the case of accessing instance fields in the call to super, are you happy that that will always result in accessing an uninitialised variable and so will always be wrong?

In the case of calling overridden methods from the constructor, consider the following class:



So, should the compiler allow that to compile? Would allowing that class to compile result in access of an uninitialised variable?

It is impossible for the compiler to answer that question. It will not be possible to know until runtime whether the code above will print out "What should the compiler do here?", or do something else entirely.

You might think that the compiler should just look at all the other classes, and see if any other class has overriden that method, but that would not work. What happens if the class that overrides that method has not been written yet? In that case the compiler that is compiling MyClass could not possibly know if that code would result in accessing an uninitialised variable yet.

So the possibly-polymorphic method call differs from the call to super. Where the call to super() will always result in uninitialised variable access, the method call from the constructor only has the possibility to result in uninitialised variable access. The designers of Java could have just disallowed constructor calls from calling any method that is not private or static if the class is not final, but that would have meant a lot of perfectly safe code would not compile.
 
nick woodward
Ranch Hand
Posts: 370
11
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike. J. Thompson wrote:With the case of accessing instance fields in the call to super, are you happy that that will always result in accessing an uninitialised variable and so will always be wrong?

yes. but this feels like it is a trick question i also don't really see the issue with returning an uninitialised result for at least consistancy. I understand why they don't though - because access is never possible. But then there should be more clarity on when exactly the variable is declared and accessible. Maybe I'm making this more complicated than it needs to be.

Mike. J. Thompson wrote:
In the case of calling overridden methods from the constructor, consider the following class:



So, should the compiler allow that to compile? Would allowing that class to compile result in access of an uninitialised variable?

It is impossible for the compiler to answer that question. It will not be possible to know until runtime whether the code above will print out "What should the compiler do here?", or do something else entirely.

You might think that the compiler should just look at all the other classes, and see if any other class has overriden that method, but that would not work. What happens if the class that overrides that method has not been written yet? In that case the compiler that is compiling MyClass could not possibly know if that code would result in accessing an uninitialised variable yet.

So the possibly-polymorphic method call differs from the call to super. Where the call to super() will always result in uninitialised variable access, the method call from the constructor only has the possibility to result in uninitialised variable access. The designers of Java could have just disallowed constructor calls from calling any method that is not private or static if the class is not final, but that would have meant a lot of perfectly safe code would not compile.


ok, i see why the second suggestion of mine would not of worked. I dunno, it just feels like members should be created, and either be uninitialized or not, rather than created, unaccessible, uninitialized or initialized.

as i mentioned before though, this is from the naive perspective of someone learning. it definitely feels above my pay grade!

thanks for your help though!
 
Simon Roberts
Author
Ranch Hand
Posts: 170
7
Java Linux Netbeans IDE
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If it helps, know that Java's "constructors" are _not_ constructors. They're initializers. The construction is handled by the JVM when you invoke "new". The object space is allocated, and all the bytes are set to zero. That's indivisible; it either works completely, or you never get to see any object at all. After that, the initialization happens, through several means that you've discussed.

BTW, if you look at the classfile, you'll see that all constructors have a name. And that name is not the name of the class. It's "<init>" complete with the angle brackets
 
Sachin Tripathi
Ranch Hand
Posts: 368
3
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well Roel you said that:
At moment object is created all instance variable existsand initialized with their default values. That I find somehow misleading

Consider a case
Where class C extends B
And we create object of C in its main method

Now when constructor of C is invoked
It implicitly calls constructor of B(but don't create its (B)object) (and also marked its(B) field as private)

Now it implicitly invokes constructor of A having print()
Which will run B's version
But B's instance field shouldn't exist as its object is not created


But it still prints :0(Why?)



Don't you think I too deserve a cow now?
 
Roel De Nijs
Sheriff
Posts: 10662
144
AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sachin Tripathi wrote:But it still prints :0(Why?)

Because your understanding of how the object of C is created, is wrong!

Assume this example codeline1 creates only one (1) object! And to create this object, the following steps are executed (in this order), between brackets I added why this step is executed (if applicable):
  • main() invokes new C() => C constructor is invoked
  • C() invokes super() => B constructor is invoked
  • B() invokes super() => A constructor is invoked
  • A() invokes super() => Object constructor is invoked
  • Object instance variables are given their explicit values (if any)
  • Object constructor completes
  • A instance variables are given their explicit values (if any) => iA is set to 1
  • print method is invoked => print method in class B is executed => iB = 0 is printed
  • A constructor completes
  • B instance variables are given their explicit values (if any) => iB is set to 2
  • B constructor completes
  • C instance variables are given their explicit values (if any)
  • C constructor completes


  • Although 4 constructors have been executed, only one (1) object is created when invoking new C(). So the reason why 0 is printed, is the same as already explained a few times in this topic. When the new C object is created, all instance variables (iA and iB exist and are initialized with their default values. So when the print method in the A constructor is invoked, the (overridden) print method in class B is executed, which prints the value of iB which is the default value as iB has not yet got its explicit value.

    Hope it helps!
    Kind regards,
    Roel
     
    nick woodward
    Ranch Hand
    Posts: 370
    11
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Sachin Tripathi wrote:Well Roel you said that:
    At moment object is created all instance variable existsand initialized with their default values. That I find somehow misleading

    Consider a case
    Where class C extends B
    And we create object of C in its main method

    Now when constructor of C is invoked
    It implicitly calls constructor of B(but don't create its (B)object) (and also marked its(B) field as private)

    Now it implicitly invokes constructor of A having print()
    Which will run B's version
    But B's instance field shouldn't exist as its object is not created


    But it still prints :0(Why?)



    Don't you think I too deserve a cow now?


    the bolded part is where you've gone wrong - the instance variable does exist, it just hasn't been initialized.

    i think its a simplification of the process that confuses things. as simon mentions, the constructors aren't really constructing, they're initialising. Tim's post mentions it too.

    PS: my cow :P
     
    Sachin Tripathi
    Ranch Hand
    Posts: 368
    3
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Creating object of subclass Y
    Will create instance variable of its along with all its super class's with its default value

    Getting it right?
     
    Mike. J. Thompson
    Bartender
    Posts: 689
    17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Creating an object will create all of its fields and all of its superclass's fields and will assign them whatever values the class is written to give them, not necessarily the default value.

    This thread is about the specific order that the fields are initialised, and what the compiler does when trying to access the fields before they are initialised.
     
    Roel De Nijs
    Sheriff
    Posts: 10662
    144
    AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    nick woodward wrote:It just seems odd to have a compiler error there, but not with the polymorphic call - which is why I asked where exactly the variables were declared rather than initialized - but i understand now that its a design thing, just not quite why!

    It's not odd at all!

    The compiler simply has different information. When you use this codethe compiler definitely knows that iB is used in the super() call, because that's the actual code so a quick glance of the compiler at the code is enough to know this.

    But with overridden methods and polymorphic calls, that's not the case at all. Let's say I create an Animal class for the (soon to be) very popular zoo-animals library So the compiler is very happy with this class (no compiler errors) and thus I get my highly wanted .class file. I create my zoo-animals library and upload this library to the Maven Central Repository so everybody can use my awesome library

    You are a real animal lover and you decide to use this zoo-animals library to create your own zoo application. You'll start with the Lion classAnd the compiler is impressed with your coding skills and compiles this class without complaining (no compiler errors).

    Now it's time to test your code and you start populating your crazy zooAgain valid Java code, so this class compiles successfully as well. And then you run your application and you'll expect to see "I am Simba, but I prefer THE LION KING!". But instead you get this runtime exception
    Exception in thread "main" java.lang.NullPointerException
    at com.crazyzoo.animals.carnivore.Lion.toString(Lion.java:15)
    at be.zoo.animals.Animal.<init>(Animal.java:8)
    at com.crazyzoo.animals.carnivore.Lion.<init>(Lion.java:10)
    at com.crazyzoo.CrazyZoo.main(CrazyZoo.java:8)


    And that's why the compiler can't complain about these polymorphic calls. But if you use a code quality analyzer (like SonarQube), you'll get a warning if a constructor calls an overridable method (as it's a bad practice with potential risk to introduce hard to spot bugs).

    Hope it helps!
    Kind regards,
    Roel

    PS. Do you know why you got the NullPointerException at runtime?
     
    nick woodward
    Ranch Hand
    Posts: 370
    11
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Roel De Nijs wrote:
    nick woodward wrote:It just seems odd to have a compiler error there, but not with the polymorphic call - which is why I asked where exactly the variables were declared rather than initialized - but i understand now that its a design thing, just not quite why!

    It's not odd at all!

    The compiler simply has different information. When you use this codethe compiler definitely knows that iB is used in the super() call, because that's the actual code so a quick glance of the compiler at the code is enough to know this.

    ...

    PS. Do you know why you got the NullPointerException at runtime?



    was just using this thread as a reference (it's great by the way, thanks) and came across this reply - sorry Roel, not sure how I originally missed it!

    interesting question though, and one that I thought I'd be able to spot straight away..... took me a second or two though (and a little help using the exception), and i'm not 100% on the reasoning again

    as far as i can see it's because the Animal class calls toString() in it's constructor, which is polymorphic in that the JVM invokes the Lion toString() method - which wouldn't be a problem if that method didn't try to access the 'nickname' field (which hasn't yet been ..... i want to say assigned a value, but if that was the case it would be null, which would be fine)

    it's been a month, i need to re-read this thread (i've been rereading the K&B7 book). but my initial answer would be that

    this is ok,


    but this is what is causing the problem:


    Nick

    *fake edit: ahh! calling .toUpperCase() on null?
     
    Roel De Nijs
    Sheriff
    Posts: 10662
    144
    AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    nick woodward wrote:*fake edit: ahh! calling .toUpperCase() on null?

    Exactly! That's the reason When toString() is invoked in the parent's constructor, the toString() method of class Lion is executed, but nickname isn't initialized yet and thus is null (default value for a reference variable). And then the toUpperCase() method is invoked on a null reference. Auwch!

    And in this code snippet it's pretty obvious that toString() method is invoked. This line invokes the toString() method as well, but in disguise

    Hope it helps!
    Kind regards,
    Roel
     
    Mike. J. Thompson
    Bartender
    Posts: 689
    17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    And that is why it is best practice that a constructor never calls any method in its class that could possibly ever be overridden in a subclass.
     
    Roel De Nijs
    Sheriff
    Posts: 10662
    144
    AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Mike. J. Thompson wrote:And that is why it is best practice that a constructor never calls any method in its class that could possibly ever be overridden in a subclass.

    Exactly! But then exam questions would be no fun at all anymore
     
    nick woodward
    Ranch Hand
    Posts: 370
    11
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    'anymore'

    i almost forgot statics in all of this..... they, and static blocks, must run before all of this then, right? before the first constructor call to super or this? which is why they can be used in either call?


     
    Mike. J. Thompson
    Bartender
    Posts: 689
    17
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Static initializers run once straight after the class is loaded by the class loader, so yes they run before any constructor runs.

    Also the direct superclass of a class must be loaded and initialized before the class is initialized.
     
    Roel De Nijs
    Sheriff
    Posts: 10662
    144
    AngularJS Chrome Eclipse IDE Hibernate Java jQuery MySQL Database Spring Tomcat Server
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    nick woodward wrote:i almost forgot statics in all of this..... they, and static blocks, must run before all of this then, right? before the first constructor call to super or this? which is why they can be used in either call?

    Static initializer blocks and static methods are related to the class, not to an instance. So that means: static initializer blocks will run (only once) when the class is loaded (could be hours before a constructor of this class is invoked). Static methods can be invoked without any instance of this class. So don't get fooled by this codeIt simply prints 1979 (and no NullPointerException is thrown).
     
    • Post Reply
    • Bookmark Topic Watch Topic
    • New Topic