6

I used to think that, intuitively speaking, a constructor in Java is the thing that makes an object, and that nothing can touch that object until its constructor returns. However, I have been proven wrong about this over and over again:

  1. uninitialized objects can be leaked by sharing this
  2. uninitialized objects can be leaked by a subclass accessing it from the finalizer
  3. uninitialized objects can be leaked to another thread before they're fully constructed

All of these facts violate my intuition of what I thought a constructor is.

I can no longer with confidence say what a constructor actually does in Java, or what it's meant to be used for. If I'm making a simple DTO with all final fields, then I can understand what the use of the constructor is, because this is exactly the same as a struct in C except it can't be modified. Other than that, I have no clue what constructors can be reliably used for in Java. Are they just a convention/syntactic sugar? (i.e If there were only factories that initialize objects for you, you would only have X x = new X(), then modify each field in x to make them have non default values - given the 3 facts above, this would be almost equivalent to how Java actually is)

I can name two properties that are actually guaranteed by constructors: If I do X x = new X(), then I know that x is an instance of X but not a subclass of X, and its final fields are fully initialized. You might be tempted to say that you know that constructor of X finished and you have a valid object, but this is untrue if you pass X to another thread - the other thread may see the uninitialized version (i.e what you just said is no different than the guarantees of calling a factory). What other properties do constructors actually guarantee?

Community
  • 1
  • 1
Dog
  • 7,707
  • 8
  • 40
  • 74
  • so your question is what properties do constructors guarantee in Java yes? – Woot4Moo May 20 '13 at 15:33
  • @Woot4Moo: Yes, that and/or what is the point in them (e.g if they're just for convention, like comments/names)? – Dog May 20 '13 at 15:33
  • I have changed the title, because I think it will get more viewership. – Woot4Moo May 20 '13 at 15:34
  • As long as your object is safely published, you have the guarantee that the object is properly initialised once the call to the constructor returns (i.e. all the operations in the constructor have completed). – assylias May 20 '13 at 15:35
  • @assylias: then what is the constructor doing that I can't do with a factory? – Dog May 20 '13 at 15:37
  • 2
    @Dog - can you create a factory in java which _doesn't_ use an object's constructor? (using sun.misc.Unsafe doesn't count). – jtahlborn May 20 '13 at 15:38
  • 1
    @jtahlborn well that is unfair. – Woot4Moo May 20 '13 at 15:41
  • @jtahlborn, I can make the factory be in the same package and set the fields to package-private, or make the factory be nested within the class. Any other use case will need all fields to be **public anyways**. – Dog May 20 '13 at 15:42
  • 1
    What you're observing is that a constructor can be constructed incorrectly. – Hot Licks May 20 '13 at 15:42
  • @HotLicks correctly constructed comment. :) – Raza May 20 '13 at 15:43
  • @Dog - you aren't avoiding using a constructor. _all_ java objects have a constructor. if you don't add one, an implicit, no-args version is created for you (which is what you would be using in the case you described). – jtahlborn May 20 '13 at 15:44
  • 1
    @jtahlborn sorry mate, this is grey area, you can sort of avoid constructor. reflection and cloning are a few examples – Raza May 20 '13 at 15:48
  • @jtahlborn: So you're saying that the point in the constructor is that you need to use it to get an instance of an object? That is a valid answer, although I already stated 2 more properties that it provides aside from that. Like I said, if Java only had default constructors, I think it would have been pretty much equivalent to how Java actually is, except without the syntactic sugar. – Dog May 20 '13 at 15:49
  • 1
    @Raza - reflection doesn't avoid constructors. cloning and serialization avoid constructors, but that doesn't make it a "greay area". you can only clone and serialize an existing instance, so, you _still_ need a constructor (for the existing instance). – jtahlborn May 20 '13 at 15:52
  • @Dog The point of a constructor is to initialize the object. The default constructor is there only to initialize the object when you don't want to do it explicitly. It is not the answer to your Question though and I really like the question. – Raza May 20 '13 at 15:54
  • @jtahlborn apologies and thanks for correcting me. – Raza May 20 '13 at 16:05

4 Answers4

6

All of these facts violate my intuition of what I thought a constructor is.

They shouldn't. A constructor does exactly what you think it does.

1: uninitialized objects can be leaked by sharing this

3: uninitialized objects can be leaked to another thread before they're fully constructed

The problem with the leaking of this, starting threads in the constructor, and storing a newly constructed object where multiple threads access it without synchronization are all problems around the reordering of the initialization of non-final (and non-volatile) fields. But the initialization code is still done by the constructor. The thread that constructed the object sees the object fully. This is about when those changes are visible in other threads which is not guaranteed by the language definition.

You might be tempted to say that you know that constructor of X finished and you have a valid object, but this is untrue if you pass X to another thread - the other thread may see the uninitialized version (i.e what you just said is no different than the guarantees of calling a factory).

This is correct. It is also correct that if you have an unsynchronized object and you mutate it in one thread, other threads may or may not see the mutation. That's the nature of threaded programming. Even constructors are not safe from the need to synchronize objects properly.

2: uninitialized objects can be leaked by a subclass accessing it from the finalizer

This document is talking about finalizers and improperly being able to access an object after it has been garbage collected. By hacking subclasses and finalizers you can generate an object that is not properly constructed but it is a major hack to do so. For me this does not somehow challenge what a constructor does. Instead it demonstrates the complexity of the modern, mature, JVM. The document also shows how you can write your code to work around this hack.

What properties are guaranteed by constructors in Java?

According to the definition, a constructor:

  1. Allocates space for the object.
  2. Sets all the instance variables in the object to their default values. This includes the instance variables in the object's superclasses.
  3. Assigns the parameter variables for the object.
  4. Processes any explicit or implicit constructor invocation (a call to this() or super() in the constructor).
  5. Initializes variables in the class.
  6. Executes the rest of the constructor.

In terms of your 3 issues, #1 and #3 are, again, about when the initialization of non-final and non-volatile fields are seen by threads other than the one that constructed the object. This visibility without synchronization is not guaranteed.

The #2 issue shows a mechanism where if an exception is thrown while executing the constructor, you can override the finalize method to obtain and improperly constructed object. Constructor points 1-5 have occurred. With the hack you can bypass a portion of 6. I guess it is in the eye of the beholder if this challenges the identity of the constructor.

Community
  • 1
  • 1
Gray
  • 115,027
  • 24
  • 293
  • 354
  • What? These optimizations make the code behave the way I expected it not to, also the first 2 bullets are not about this, for example number 2 means I can't rely on constructors to do what I expect under a `SecurityManager`. – Dog May 20 '13 at 15:36
  • 1
    There are tons of complexities in a multithreaded program that make it "behave the way [we] expected it not to". Reordering of instructions is one of them. You can have `x=1;` `y=2;` and another thread would see the `y` assignment but not the `x`. I don't see how this threatens the definition of the constructor @Dog. – Gray May 20 '13 at 15:43
  • the `SecurityManager` example has nothing to do with concurrency. Also it would be common sense to assume if you get an instance of `x`, the constructor's completion `happens-before` publishing the reference of `x`, but this is not the case in Java. The problem is I don't know what the "definition of a constructor" (purpose and properties of a constructor) is, and that's why I'm asking this question. – Dog May 20 '13 at 15:46
  • The finalizer leaks **are** an issue, because if you are using a `SecurityManager`, then you can no longer rely on only *fully constructed objects* being in the wild, because malicious code can use the technique described in point 2 in my question to obtain an uninitialized version. – Dog May 20 '13 at 15:53
  • 1
    You are correct that the finalizer hacks mentioned can result in an improperly initialized object unless you use their private initializer methods at the bottom. But to me that doesn't threaten my definition of the constructor. It demonstrates (yet again) the complexities of a modern, mature multithreaded language but that's about it for me. – Gray May 20 '13 at 15:57
  • The finalizer issue has nothing to do with concurrency. – Dog May 22 '13 at 18:00
1

From the JLS section 12.5:

12.5. Creation of New Class Instances

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

**

Unlike C++, the Java programming language does not specify altered rules for method >dispatch during the creation of a new class instance. If methods are invoked that are >overridden in subclasses in the object being initialized, then these overriding methods >are used, even before the new object is completely initialized.

And from JLS 16.9:

Note that there are no rules that would allow us to conclude that V is definitely unassigned before an instance variable initializer. We can informally conclude that V is not definitely unassigned before any instance variable initializer of C, but there is no need for such a rule to be stated explicitly.

Happens before 17.4.5:

Threading 17.5.2:

A read of a final field of an object within the thread that constructs that object is ordered with respect to the initialization of that field within the constructor by the usual happens-before rules. If the read occurs after the field is set in the constructor, it sees the value the final field is assigned, otherwise it sees the default value.

Gray
  • 115,027
  • 24
  • 293
  • 354
Woot4Moo
  • 23,987
  • 16
  • 94
  • 151
0

A class contains constructors that are invoked to create objects from the class blueprint.

This is what Oracle says about constructors.

Now to your point.

intuitively speaking, a constructor in Java is the thing that makes an object, and that nothing can touch that object until its constructor returns.

So according to the official documentation, your assumption is not right. And the point 1 and 2 are the abuse of the rules and behaviors of Java, unless you consciously want to leak your objects! As also being irrelevant to Constructor, I will skip discussing these points.

Now if we talk about your 3rd point, in multi-threaded environment there is nothing that can guarantee you about the consistency of your code, unless "properly synchronized blocks" or "the atomic instructions". As object creation is not a synchronized nor an atomic instruction, there is no guarantee of being consistent! There is nothing the Constructor can do with it. In other words its not the responsibility of the Constructor to make your object creation atomic.

Now, the answer to your question, "What other properties do constructors actually guarantee?" is somewhat easy. Constructors are merely nothing but special type of methods, that are invoked during object creation from the blue print of the class. So it can guarantee nothing, unless you give it a chance to be executed consistently like any other methods. It after being consistently executed it can guarantee you that, your object is created and initialized as you wanted and instructed in it.

Sazzadur Rahaman
  • 6,938
  • 1
  • 30
  • 52
-1

constructors is java are just used to initialize the state of the object created..nothing more.

Dory Zidon
  • 10,497
  • 2
  • 25
  • 39