What properties are guaranteed by constructors in Java?
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?
@Dog — can you create a factory in java which doesn’t use an object’s constructor? (using sun.misc.Unsafe doesn’t count).
@jtahlborn sorry mate, this is grey area, you can sort of avoid constructor. reflection and cloning are a few examples
@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).
4 Answers 4
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:
- Allocates space for the object.
- Sets all the instance variables in the object to their default values. This includes the instance variables in the object’s superclasses.
- Assigns the parameter variables for the object.
- Processes any explicit or implicit constructor invocation (a call to this() or super() in the constructor).
- Initializes variables in the class.
- 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.
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 .
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.
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.
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.
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.
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.
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.
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.
Properties of Constructors in Java
A constructors in Java is a special method that is used to initialize objects. The constructor is called when an object of a class is created. It can be used to set initial values for object attributes. In Java, a constructor is a block of codes similar to the method. It is called when an instance of the class is created. At the time of calling the constructor, memory for the object is allocated in the memory. It is a special type of method that is used to initialize the object. Every time an object is created using the new() keyword, at least one constructor is called. There are certain properties of constructors in java.
Properties of Constructors
1. Constructor Should not have any return type
If we declare a return type for a constructor, JVM considers it a normal method. The basic aim of the constructor is to assign a value to the object. It implicitly returns the address of the object.
2. The access specifier of the constructor can be private, default, protected, public
If the access specifier of the constructor is private, then an object of the corresponding class can be created in the context of the same class but not in the context of another class. If the access specifier of the constructor is the default, then an object of the corresponding class can be created in the context of classes that are in the same package. Similarly, if the constructor is declared as protected, the object of the corresponding class can be created in the context of classes of the same package as well as inherited classes of other packages. And, if the constructor is declared as public, the object of the corresponding class can be created in the context of any class.
3. A constructor cannot be declared as final and synchronized
The main purpose of using the final keyword is to restrict the overriding of a method (basically overriding is done in the context of inherited classes). As there is no concept of overriding constructors, declaring the final is useless. Using synchronized for a constructor raises a syntax error. Because only the thread that creates an object should have access to it while it is being constructed.