Java Learning – Constructors and Attributes

Spread the love

The main role of a constructor is to initialize an object, not to create the object.
When an object is created, by using the new keyword, Java knows that memory should be allocated to have at least space to address all the attributes defined in the class.
For instance, the following class, Point2D, needs, at minimum, 8 bytes for the int x and int y.
After the memory is allocated, it is time to initialize the class. static initializations are already done at class loading time, and anonymous initializers/constructors will be addressed just before the named constructors (in another post). The attributes are initialized with their default values by their type; for instance, for int x, x will receive a 0.

class Point2D {
	private int x;
	private int y;
	
	public Point2D() {
		this(0,0);
		this.x = 2;
		this.y = 2;
		System.out.println(String.format("Changed Default: (%d,%d)", this.x, this.y));
	}
	
	public Point2D(int x, int y) {
		System.out.println(String.format("Default: (%d,%d)", this.x, this.y));
		this.x = x;
		this.y = y;
		System.out.println(String.format("Changed: (%d,%d)", this.x, this.y));
	}
}

public class Constructors {
	public static void main(String[] args) {
		var p0 = new Point2D(1,1);
	}
}

When the main code above is executed, the output is:

Default: (0,0)
Changed: (1,1)

The (0,0) are the values assigned by Java to the attributes by default.
The (1,1) are the values passed to the constructor.

If we change the call of the main to use the default constructor:

public class Constructors {
	public static void main(String[] args) {
		var p0 = new Point2D();
	}
}

Now the output is:

Default: (0,0)
Changed: (0,0)
Changed Default: (2,2)

Since we are using constructor chaining , the default constructor will delegate the initialization to another constructor and later change the attribute values, again. The (0,0) is displayed twice because the default constructor uses these values for the initialization. (For a point, it makes sense to use origin as the default coordinate!)

There are, however, some subtleties, when the final word is added to the attribute declaration.

But, what happens if we add final to the private final int x attribute?

class Point2D {
	private final int x;
	private int y;
	
	public Point2D() {
		this(0,0);
		this.x = 2; // #1 PROBLEM HERE!
		this.y = 2;
		System.out.println(String.format("Changed Default: (%d,%d)", this.x, this.y));
	}
	
	public Point2D(int x, int y) {
	    // #2 PROBLEM HERE! on the `this.x`. Use before initialization.
		System.out.println(String.format("Default: (%d,%d)", this.x, this.y));
		this.x = x; // #3 OK here!
		this.y = y;
		System.out.println(String.format("Changed: (%d,%d)", this.x, this.y));
	}
}

The statement this(0,0), before the comment #1 in the constructor, delegated (this()) the initialization to the other constructor by using chaining.

public Point2D() {
		this(0,0); // chaining
		this.x = 2; // #1 PROBLEM HERE!

Meaning, it should be this second constructor to initialize the final int x, or delegate it to a further constructor in the chain.

public Point2D(int x, int y) {
	    // #2 PROBLEM HERE! on the `this.x`. Use before initialization.
		System.out.println(String.format("Default: (%d,%d)", this.x, this.y));
		this.x = x; // #3 OK here!
		this.y = y;
		System.out.println(String.format("Changed: (%d,%d)", this.x, this.y));
	}

So, the reader can think the error in comment #1 happens because the final int x is already initialized in comment #3 .
It is not the case! Try to remove it. The error will remain, and another error will appear, indicating that Point2D(int x, int y) should initialize the final int x!

  • The blank final field x may not have been initialized

Java will assume that all final attributes should be initialized at the end of the constructor chaining.
Remember, a final attribute can only be initialized once!

The this.x = 2 in the first constructor has to be removed.

public Point2D() {
		this(0,0);
		// this.x = 2; // #1 PROBLEM HERE!
		this.y = 2;
		System.out.println(String.format("Changed Default: (%d,%d)", this.x, this.y));
	}

The this.y = 2 is correct to use since it is not final.

Leave a Reply

Your email address will not be published. Required fields are marked *