Java Constructors - Comprehensive Notes

Overview

  • A constructor is a special method that runs every time you create an instance of a class.
  • Constructors have no explicit return type.
  • The name of the constructor must be exactly the same as the class name.
  • The constructor is invoked when you use the new operator, e.g. new ClassName(...) creates an object and runs the corresponding constructor.
  • In the examples, the class is named Machine and a few constructors are demonstrated.

The Machine example: setup and fields

  • The class defines instance variables including:
    • a private String name;
    • a private int code;
  • You can initialize instance variables inside a constructor so every new object starts with a default state.
  • Example concept shown: initialize name to a default value like "Anie" inside the constructor.
  • The transcript emphasizes that you can see the constructor run simply by creating a new object with new Machine().

How constructors run and what triggers them

  • Creating an object triggers the constructor:
    • Example: Machine m = new Machine(); → this calls the no-parameter constructor and executes its body.
  • You can also just write new Machine(); (without assigning it to a variable) and the constructor still runs, which demonstrates that the new expression is what actually invokes the constructor.
  • Output examples shown:
    • “Constructor running” when the no-argument constructor executes.
    • If a constructor prints inside, you’ll see that output when the constructor runs.

Constructor overloading: multiple constructors with different parameters

  • Just like methods, constructors can be overloaded by having different parameter lists.

  • Java selects the appropriate constructor based on the argument list you provide when you call new Machine(...).

  • Demonstrated constructors in the transcript:

    • No-argument constructor: prints "Constructor running".
    • String-only constructor: prints "Second Constructor running" and sets the instance name from the parameter.
    • String + int constructor: prints "Third Constructor running" and initializes both name and code from the parameters.
  • Example invocations:

    • Machine machine1 = new Machine(); // no-arg constructor
    • Machine machine2 = new Machine("Berti"); // constructor with a String parameter
    • Machine machine3 = new Machine("Chalky", 7); // constructor with String and int parameters
  • Key explanation: the name parameter is differentiated from the instance variable name using this.name to refer to the instance variable, and name to refer to the parameter.

  • Code sketch (as described in the transcript):

    • No-arg constructor initializes defaults and prints a message:
    public class Machine {
        private String name;
        private int code;
    public Machine() {
        this.name = "Anie"; // default initialization example
        System.out.println("Constructor running");
    }
    
    public Machine(String name) {
        this.name = name;
        System.out.println("Second Constructor running");
    }
    
    public Machine(String name, int code) {
        this.name = name;
        this.code = code;
        System.out.println("Third Constructor running");
    }
    
    }
  • Note: In this transcript, the constructors are shown with print statements to indicate which one is running, reinforcing the idea that the correct constructor is chosen based on the arguments provided.

Distinguishing instance variables and parameters

  • The transcript emphasizes using this to refer to instance variables when a parameter has the same name:
    • Example: this.name = name; assigns the parameter name to the instance variable name.
    • Similarly, this.code = code; assigns the parameter code to the instance variable code.
  • This helps avoid confusion between local variables/parameters and fields of the object.

Practical example: calling one constructor from another (constructor chaining)

  • Concept described: you can have more complex constructors call simpler ones to reuse initialization logic.

  • A common approach (as described in the video) is to delegate from a simpler constructor to a more complex one using this(...) with the appropriate arguments.

  • Important details from the transcript:

    • The most complex constructor can be the one that actually performs initialization for all fields, while simpler ones delegate to it.
    • When delegating, the call to the other constructor using this(...) must be the first statement in the constructor.
    • Example scenario described: from a no-argument constructor, you can delegate to a two-parameter constructor with this("Anie", 0); and then perform any additional actions (like printing) after the delegation returns.
    • The transcript notes that you should not attempt to call a constructor by its name directly as a method (e.g., Machine("Anie", 0)) – instead, you should use this(...) to delegate.
    • It also mentions avoiding infinite recursion: a constructor should not end up calling itself in a way that leads to an endless loop.
  • Demonstrative pattern (as described):

    • A no-argument constructor delegates to a two-argument constructor:
    public Machine() {
        this("Anie", 0); // first line: delegate to another constructor
        System.out.println("Constructor running"); // additional behavior after delegation
    }
    
    public Machine(String name, int code) {
        this.name = name;
        this.code = code;
        System.out.println("Third Constructor running");
    }
    
    • In this pattern, invoking new Machine() first runs the two-parameter constructor (printing "Third Constructor running"), then returns to the no-argument constructor and prints "Constructor running".
  • Important caveat (as noted in transcript): while the example shows delegation, the key takeaway is that the first line must be the delegation call, and you can chain constructors to avoid duplicate initialization.

Summary of key takeaways

  • Constructors are special methods with no return type and must share the class name.
  • You create objects with new ClassName(...), which invokes the matching constructor.
  • You can overload constructors with different parameter lists; Java selects the appropriate one based on the call.
  • You can initialize instance variables inside constructors to ensure a consistent initial state for every object.
  • The this keyword is used to distinguish instance variables from parameters and can be used to delegate to other constructors via this(...).
  • When using constructor delegation, the this(...) call must be the first statement in the constructor, and subsequent code will run after the delegated constructor completes.
  • It’s possible to call a simpler constructor from a more complex one, or vice versa, to centralize initialization logic and avoid duplication.
  • You can observe the effect of constructor selection and delegation via simple print statements like:
    • "Constructor running"
    • "Second Constructor running"
    • "Third Constructor running"
  • The transcript hints at future topics: next up is static variables and static methods.
  • Connections to broader concepts:
    • This behavior reinforces foundational OOP principles: object initialization, encapsulation (private fields), and constructor overloading as a form of polymorphism.
    • The use of this connects to previous explanations of distinguishing between instance-level and local scope.