SEN1001 · Week 11 · Classes and Objects II

Constructors, UML class diagrams, arrays of objects

Constructors that prepare the object the moment it is born, the default constructor rule, constructor overloading, reading and drawing a UML class diagram, keeping objects in an array and in an ArrayList. The skeleton of the final writing questions.

constructordefault constructorno-argument constructorconstructor overloadinginitializationUML class diagramattributeoperationvisibility (+ / -)array of objectsArrayList of objectsnull element

Last week you first created an empty object with new and then filled it with set methods. That means a few lines for every object, and if you forget to set one field the object is left half-done. A constructor does this job at the moment the object is born. This week you will also learn the standard way of drawing classes on paper (the UML class diagram) and keeping many objects in an array or an ArrayList.

Golden rules:

  1. The name of a constructor is the name of the class, and it has no return type.
  2. If you write no constructor, Java adds a no-argument one; if you write one, it does not.
  3. Constructors can be overloaded too. The arguments decide which one runs.
  4. In UML, - is private, + is public; the type comes after the name.
  5. Creating an array of objects does not create objects. A separate new for each element.

Constructor

class Student {
    private String name;
    private double gpa;

    public Student(String n, double g) {      // no return type
        name = n;
        setGpa(g);                            // the validation is in the setter
    }

    public Student(String n) {                // second constructor (overloading)
        name = n;
    }

    public String getName() { return name; }
    public double getGpa() { return gpa; }
    public void setGpa(double g) {
        if (g >= 0.0 && g <= 4.0) gpa = g;
    }
}
Student s1 = new Student("Deniz", 3.2);
Student s2 = new Student("Ece");
System.out.println(s1.getName() + " " + s1.getGpa());
System.out.println(s2.getName() + " " + s2.getGpa());
Student s3 = new Student("Can", 5.7);
System.out.println(s3.getName() + " " + s3.getGpa());
Deniz 3.2
Ece 0.0
Can 0.0
  • new Student("Deniz", 3.2) first makes room for the object, puts the fields at their defaults, then runs the two-parameter constructor.
  • s2 was created with the one-parameter constructor; since gpa was not touched, it is 0.0.
  • In s3, 5.7 is out of range; it was rejected because the constructor calls the setter. If you had written gpa = g; directly in the constructor, an invalid value would have gotten in.

Order of execution

class Book {
    private String title;
    private int pages = 100;            // field initial value

    public Book(String t) {
        System.out.println("constructor: pages=" + pages);
        title = t;
        pages = 250;
    }
    public String describe() { return title + " " + pages; }
}
// main: Book bk = new Book("Java"); System.out.println(bk.describe());
constructor: pages=100
Java 250

The order: fields get their default value → the initial values in the field declarations are assigned (pages = 100) → the body of the constructor runs.

The default constructor rule

If you wrote no constructor in the class, Java adds an invisible, empty, no-argument default constructor. That is why new Account() worked last week. But the moment you write a single constructor, this addition is not made:

class Car {
    private String model;
    public Car(String m) { model = m; }
}
// main: Car c = new Car();
Main.java:7: error: constructor Car in class Car cannot be applied to given types;
        Car c = new Car();
                ^
  required: String

If you want both new Car() and new Car("Egea"), you write both constructors yourself.

PITFALL · writing void on a constructor. public void Pen(String c) is not a constructor, it is an ordinary method whose name is the same as the class. Since the class has no real constructor left, new Pen("red") does not compile (required: no arguments), while new Pen() compiles but color stays null.

UML class diagram

UML (Unified Modeling Language) is the standard way to show a class on paper before turning it into code. In the exam it is asked in two directions: “Write the Java code of this diagram” or “Draw the diagram of this class”.

+--------------------------------------------+
|                 Student                    |   ← class name
+--------------------------------------------+
| - name : String                            |   ← fields (attributes)
| - gpa : double                             |
+--------------------------------------------+
| + Student(n : String, g : double)          |   ← constructors
| + Student(n : String)                      |
| + getName() : String                       |   ← methods (operations)
| + getGpa() : double                        |
| + setGpa(g : double) : void                |
+--------------------------------------------+
UMLJavaMeaning
- name : Stringprivate String name;private field
+ getName() : Stringpublic String getName()public method, returns String
+ setGpa(g : double) : voidpublic void setGpa(double g)parameter name : type
+ Student(n : String)public Student(String n)constructor, no return type written

When writing code from a diagram, go in order: first the class header, then a private field for each - line, then a public method header and body for each + line. When turning code into a diagram, go in the opposite direction: put the fields in the top compartment, the constructors and methods in the bottom one. Do not skip the getters and setters; they need to appear in the diagram.

Watch two things when converting: in UML the type comes after the name (name : String), in Java before (String name). The return type of a method is at the very end in UML, and before the name in Java.

Arrays of objects

Student[] group = new Student[3];
System.out.println(group[0]);
group[0] = new Student("Ali", 2.8);
group[1] = new Student("Bora", 3.6);
group[2] = new Student("Cem", 3.1);
double sum = 0;
for (Student s : group) sum += s.getGpa();
System.out.printf("%.2f%n", sum / group.length);
System.out.println(best(group).getName());
null
3.17
Bora
group ──► [ ref | ref | ref ]
             │     │     │
             ▼     ▼     ▼
           Ali   Bora   Cem      (each one is a separate new)

new Student[3] creates an array but puts no Student in it: three null references. A separate new Student(...) is needed for each element. If you call a method on an element that is not filled:

Student[] partial = new Student[2];
partial[0] = new Student("Z", 1.0);
for (Student s : partial) System.out.println(s.getName());
Z
NullPointerException

It crashed in the second iteration because the second element is null. If the array is not completely full, do an if (s != null) check in the loop.

The best method is the object version of the max pattern; its full code is in the question bank. The difference is this: we keep not the largest value but the object that has that value, and we return it.

Objects with ArrayList

If you do not know how many objects there will be, ArrayList is more convenient:

class Product {
    private String name;
    private double price;
    private int quantity;
    public Product(String n, double p, int q) { name = n; price = p; quantity = q; }
    public String getName() { return name; }
    public double getPrice() { return price; }
    public int getQuantity() { return quantity; }
    public void setQuantity(int q) { if (q >= 0) quantity = q; }
    public double subtotal() { return price * quantity; }
}

public static Product findByName(ArrayList<Product> items, String name) {
    for (Product p : items) {
        if (p.getName().equals(name)) return p;
    }
    return null;                      // not found
}
// in main:
ArrayList<Product> cart = new ArrayList<>();
cart.add(new Product("Kalem", 12.5, 4));
cart.add(new Product("Defter", 40.0, 2));
cart.add(new Product("Silgi", 7.5, 3));
double total = 0;
for (Product p : cart) total += p.subtotal();
System.out.println(total);
System.out.println(findByName(cart, "Defter").getPrice());
System.out.println(findByName(cart, "Cetvel"));
cart.remove(0);
System.out.println(cart.size() + " " + cart.get(0).getName());
152.5
40.0
null
2 Defter

When searching for objects, names are compared with equals. When nothing is found, the counterpart of -1 in int arrays is returning null. The calling side must know that null may come back and check for it.

Common mistakes

Wrong ideaThe truth
The return type of a constructor is voidNo return type is written
Even if I write a constructor, new X() always worksIt does not work if you did not write the no-argument constructor
A constructor is called as obj.Student(...)Only with new Student(...)
Validation in a constructor is unnecessaryValidate by calling the setter
UML name : String → Java name StringString name
In UML + is private+ is public, - is private
new Student[3] creates three studentsThree null references
In an array of objects every element is filledCheck: if (s != null)
Search with p.getName() == nameequals
If a search finds nothing, return -1 (with objects)Return null

Test yourself

Exam-style questions. Solve them on paper first, then reveal the answer.

Question 1easy

Point has two constructors: Point() sets x and y to 0, Point(int x0, int y0) stores the given values. What is printed?

Point pt = new Point(3, 4);
Point pt2 = new Point();
System.out.println(pt.getX() + pt.getY() + " " + pt2.getX());
Show answer
7 0

The constructor is chosen by the number of arguments (overloading). pt.getX() + pt.getY() is two ints: 3 + 4 = 7, then String concatenation. pt2 uses the no-argument constructor, so 0.

Question 2medium

The Student(String n, double g) constructor calls setGpa(g), which accepts only values from 0.0 to 4.0. What is printed?

Student s = new Student("Mert", -1.0);
System.out.println(s.getGpa());
Show answer
0.0

The constructor leaves the validation to the setter: -1.0 is rejected, gpa stays at the default 0.0. Writing the validation in one place (the setter) and calling it from the constructor is a good habit.

Question 3easy

What is printed?

class Tracer {
    private int value;
    public Tracer(int v) {
        value = v * 2;
        System.out.println("built " + value);
        value++;
    }
    public int getValue() { return value; }
}
// in main:
Tracer tr = new Tracer(5);
System.out.println(tr.getValue());
Show answer
built 10
11

new Tracer(5) runs the constructor: value = 10, built 10 is printed, value becomes 11. Then getValue returns 11. A constructor runs line by line like an ordinary method.

Question 4medium

What is printed?

Student[] arr = new Student[4];
arr[1] = new Student("A", 2.0);
arr[3] = new Student("B", 3.0);
int created = 0;
for (Student s : arr) if (s != null) created++;
System.out.println(created + " " + arr.length);
Show answer
2 4

new Student[4] opens four reference slots, all null; it creates no Student object. Only two of them were given objects with new. The length of the array is still 4.

Question 5easy

What is printed?

ArrayList<Student> list = new ArrayList<>();
list.add(new Student("X", 2.5));
list.add(new Student("Y", 3.5));
list.add(new Student("Z", 3.0));
int count = 0;
for (Student s : list) if (s.getGpa() >= 3.0) count++;
System.out.println(count + " " + list.get(1).getName());
Show answer
2 Y

Those with a GPA of 3.0 and above are Y (3.5) and Z (3.0): 2. list.get(1) returns a Student; you can call .getName() directly on it: Y.

Question 6easy

Product(String n, double p, int q) stores name, price and quantity; subtotal() returns price * quantity. What is printed?

Product pr = new Product("Su", 5.0, 3);
pr.setQuantity(pr.getQuantity() + 2);
System.out.println(pr.subtotal());
Show answer
25.0

The quantity becomes 3 + 2 = 5. 5.0 * 5 = 25.0 (the result is a double because price is a double).

Question 7hard

What is printed?

Student a = new Student("Tan", 2.0);
Student[] two = {a, a};
two[0].setGpa(3.9);
System.out.println(two[1].getGpa() + " " + a.getGpa());
Show answer
3.9 3.9

Both elements of the array are references to the same object. However you change it, there is a single object: all three see 3.9.

Question 8medium

Account2(String o, double b) sets the balance; withdraw subtracts only if amount > 0 && amount <= balance, otherwise prints Insufficient funds. What is printed?

Account2 acc = new Account2("Deniz", 100);
acc.withdraw(-50);
acc.withdraw(30);
acc.withdraw(500);
System.out.println(acc.getBalance());
Show answer
Insufficient funds
Insufficient funds
70.0

-50 is not positive → message. 30 is valid → 70. 500 > 70 → message. The int 100 was given to the constructor but widened because the parameter is a double; the balance is printed as 70.0.

Question 9medium

Class Car has only the constructor public Car(String m). Does Car c = new Car(); compile?

Show answer
No: constructor Car cannot be applied to given types (required: String)

Java adds the no-argument default constructor itself only if the class has no constructor at all. The moment you write a constructor, this gift is gone. For new Car() you must write the no-argument constructor yourself.

Question 10hard

What is wrong with this class? What happens with new Pen("red")?

class Pen {
    private String color;
    public void Pen(String c) { color = c; }
}
Show answer
void makes it a method, not a constructor; new Pen("red") does not compile

A constructor has no return type, not even void. void Pen(...) becomes an ordinary method with the same name as the class. Since the class has no real constructor left, Java adds the no-argument default constructor; the error for new Pen("red") is: required: no arguments.

Question 11medium

Read the UML class diagram and write the class header, the fields and the constructor in Java.

+----------------------------------------+
|               Course                   |
+----------------------------------------+
| - code : String                        |
| - credits : int                        |
+----------------------------------------+
| + Course(code : String, credits : int) |
| + getCode() : String                   |
| + getCredits() : int                   |
+----------------------------------------+
Show answer
class Course { private String code; private int credits; public Course(String c, int cr) { code = c; credits = cr; } ... }

- is private, + is public. In UML the type is written after the name with a colon; in Java it is written first. The return type comes after the parentheses.public class Course { private String code; private int credits; public Course(String c, int cr) { code = c; credits = cr; } public String getCode() { return code; } public int getCredits() { return credits; }}

Question 12hard

Write a static method best(Student[] list) that returns the student with the highest GPA (assume the array is full and not empty).

Show answer
Student top = list[0]; for (Student s : list) if (s.getGpa() > top.getGpa()) top = s; return top;

The object version of the max pattern: we keep not the largest value but the object that has the largest value. The return type is Student.public static Student best(Student[] list) { Student top = list[0]; for (Student s : list) { if (s.getGpa() > top.getGpa()) top = s; } return top;}Test: Ali 2.8, Bora 3.6, Cem 3.1 → best(group).getName() = Bora.

Verification: All outputs were run with JDK 26 (verify/SEN1001/W11.java and W11b.java; the classes Student, Book, Product, Point, Tracer, Account2, Course and Pen). The constructor errors were obtained with javac under verify/SEN1001/err/ctor/.

Review card

Constructor: SAME name as the class, NO return type (not even void), called automatically by new public Student(String n, double g) { name = n; setGpa(g); } If you write no constructor, Java adds an empty no-argument one; if you write one, it does NOT add it Constructor overloading: Point() and Point(int, int) chosen by the arguments A field initial value (pages = 100) is assigned BEFORE the constructor void Pen(...) → becomes a method, NOT a constructor Write the validation in the setter, call the setter from the constructor UML: class name / fields / methods (3 compartments) - private + public UML field: - name : String method: + getName() : String + setAge(a : int) : void Student[] g = new Student[3]; → 3 null references, NO objects; a new for each A method through a null element → NullPointerException ArrayList<Student> list; list.add(new Student(...)); list.get(i).getName() Searching objects: if (p.getName().equals(name)) return p; if not found return null;