this, static members, final constants and toString
this when a parameter shadows a field, this(...) chaining constructors, static fields shared by all objects, final values that do not change and toString for printing an object readably. The favorite of 'what is printed' questions on the final exam.
You know how to write classes. This week there are four tools commonly used inside classes: this, static, final and toString. Each rests on a few rules, but in the exam these rules are set up as traps with small differences: a class where this was forgotten in the constructor, a static counter, a println that calls toString automatically.
Golden rules:
- If a parameter has the same name as a field, you cannot reach the field without
this.. - A static field is a single copy; all objects share it. An instance field is separate in each object.
- A static method cannot directly access instance fields.
- final is assigned once. Constants are
static finaland UPPER_CASE. - println(object) automatically calls
toString().
The this reference
Inside every instance method there is a hidden variable: this. It points to the object that called the method. In the call p.getName(), this is p.
Shadowing
Giving constructor parameters the same names as the fields is a common habit. But then, inside the method, that name refers to the parameter:
class BadPlayer {
private String name;
private int number;
public BadPlayer(String name, int number) {
name = name; // parameter = parameter
number = number;
}
...
}
class Player {
private String name;
private int number;
public Player(String name, int number) {
this.name = name; // field = parameter
this.number = number;
}
...
}
// in main:
BadPlayer bp = new BadPlayer("Deniz", 22);
System.out.println(bp.getName() + " " + bp.getNumber());
Player p = new Player("Deniz", 22);
System.out.println(p.getName() + " " + p.getNumber());
null 0
Deniz 22
BadPlayer compiles and runs, but the fields are never assigned. The compiler does not warn; a typical logic error.
Constructor chaining with this(…)
If there are several constructors, you can do the common work in one of them and call it from the others:
class Time {
private int hour, minute;
public Time() { this(0, 0); }
public Time(int hour) { this(hour, 0); }
public Time(int hour, int minute) {
this.hour = hour;
this.minute = minute;
}
@Override
public String toString() {
return String.format("%02d:%02d", hour, minute);
}
}
// main: System.out.println(new Time() + " " + new Time(9) + " " + new Time(14, 30));
00:00 09:00 14:30
Write the this(...) call on the first line of the constructor. This is the rule of the textbook and of Java versions up to 21; javac --release 21 rejected a constructor with a line written before it, saying flexible constructors is not supported in -source 21. (This rule was relaxed with Java 25, but in the exam apply the classic rule.) Rules such as validation are written in only one constructor and not repeated.
return this
If a method returns this, the calls can be chained. The Temp example in the question bank shows this: new Temp(20).add(5).add(3) adds to the same object one after another.
static members
An instance variable is separate in each object. A static field, on the other hand, belongs to the class and there is a single copy in memory; all objects share it. That is why it is also called a class variable.
class Ticket {
private static int count = 0; // how many tickets were created (shared)
private int id; // this ticket's number (separate in each object)
private String owner;
public Ticket(String owner) {
this.owner = owner;
count++;
id = count;
}
public static int getCount() { return count; }
public int getId() { return id; }
public int getCountViaInstance() { return count; }
}
// in main:
System.out.println(Ticket.getCount());
Ticket a = new Ticket("A");
Ticket b = new Ticket("B");
Ticket c = new Ticket("C");
System.out.println(Ticket.getCount() + " " + a.getId() + " " + b.getId() + " " + c.getId());
System.out.println(a.getCountViaInstance());
0
3 1 2 3
3
Ticket class: count = 3 (single copy)
a: id = 1 b: id = 2 c: id = 3 (separate in each object)
- Even when there were no objects,
Ticket.getCount()worked: static members do not need an object, they are used with the class name. - An instance method (
getCountViaInstance) can read a static field. - The opposite direction does not work: a static method does not have
this, so it cannot know which object’s field you mean.
public class Main {
private int score = 5;
public static void main(String[] args) {
System.out.println(score);
}
}
Main.java:5: error: non-static variable score cannot be referenced from a static context
You may have seen this error since the first weeks when writing helper methods for main: because main is static, the methods it called also had to be static.
Solve the Counter example in the question bank with a trace table: draw a single box next to the class for the static field, and a separate box next to each object for the instance field.
call x.mine y.mine Counter.total
start 0 0 0
x.hit() 1 0 1
y.hit() 1 1 2
y.hit() 1 2 3
Each hit increases two boxes at once: the calling object’s own box and the single box everyone shares.
When to use static? Ask yourself: “Does this information belong to a single object, or to the whole class?” A student’s grade belongs to that student: an instance field. The total number of students, the VAT rate or the next number to give out belongs to the whole class: static. The same question for methods: if the method uses an object’s fields it is an instance method; if it works only with its parameters (like Math.max) it can be static.
| static | not static | |
|---|---|---|
| Belongs to | the class | each object |
| How many copies | 1 | as many as objects |
| Access | Ticket.getCount() | a.getId() |
| Example | counter, constants, Math.sqrt | name, balance, grade |
final and constants
final says that a variable can be assigned a value once.
class Shop {
public static final double TAX_RATE = 0.20; // constant
public static double priceWithTax(double price) {
return price * (1 + TAX_RATE);
}
}
class Circle {
private final double radius; // assigned once in the constructor
public Circle(double radius) { this.radius = radius; }
public double area() { return Math.PI * radius * radius; }
}
// main: Shop.priceWithTax(100), Shop.TAX_RATE, new Circle(2).area() (%.2f)
120.0
0.2
12.57
- For a constant,
static finalare used together: a single copy that does not change. Its name is written in capital letters with underscores:TAX_RATE,MAX_SIZE.Math.PIis such a constant too. - A final instance field can be different in each object but does not change after the object is created. It is assigned in the constructor.
If you try to assign to it, it does not compile:
Main.java:6: error: cannot assign a value to static final variable MAX
Main2.java:4: error: cannot assign a value to final variable radius
toString
Every class gets a toString() method from Object, Java’s root class. The default version returns a useless text: the class name, @ and a hash code (like NoToString@1b6d3586). If you write your own version (override):
@Override
public String toString() {
return name + " #" + number;
}
// in main:
Player p2 = new Player("Can", 7);
System.out.println(p2); // println calls toString automatically
String msg = "Kaptan: " + p2; // concatenation calls it too
System.out.println(msg);
Can #7
Kaptan: Can #7
Rules:
- The signature is exactly
public String toString(). No parameters, return type String, public. - The method returns, it does not print. Putting a println inside is a wrong habit.
- The
@Overridetag is not required but is good: if you write the signature wrong, the compiler warns you. String.formatuses the same specifiers as printf but returns a String instead of printing to the screen; it is very useful inside toString.
Common mistakes
| Wrong idea | The truth |
|---|---|
name = name; assigns the field | It assigns the parameter to itself; this.name = name; |
this(...) anywhere in the constructor | Write it on the first line (required in Java 21 and earlier) |
this can be used in a static method | There is no this in static |
| A static field is separate in each object | A single copy, shared |
| An object is required for a static method | Class.method() is enough |
| An instance field is accessed directly from main | Through an object: obj.field |
| A final field can never be assigned | It is assigned once (in the declaration or the constructor) |
| A constant name is camelCase | UPPER_CASE: TAX_RATE |
| println inside toString | Return a String with return |
println(obj) prints the fields | If toString is not written, ClassName@... |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
What is printed?
class Box {
private int size;
public Box(int size) { size = size * 2; }
public int getSize() { return size; }
}
// in main:
Box b = new Box(5);
System.out.println(b.getSize());Show answer
Inside the constructor the name size refers to the parameter (shadowing). size = size * 2 only makes the parameter 10; the field never changes, it stays at the default 0. The correct version is this.size = size * 2;.
Same class, but the constructor body is this.size = size * 2;. What is printed for new Box2(5).getSize()?
Show answer
this.size is the object's field, the size on the right is the parameter. The field becomes 5 * 2 = 10.
What is printed?
class Counter {
static int total = 0;
private int mine = 0;
public void hit() { mine++; total++; }
public int getMine() { return mine; }
}
// in main:
Counter x = new Counter();
Counter y = new Counter();
x.hit();
y.hit();
y.hit();
System.out.println(x.getMine() + " " + y.getMine() + " " + Counter.total);Show answer
mine is an instance variable: separate in each object (1 in x, 2 in y). total is static: it belongs to the class, there is a single copy and all objects share it. Three hits, total 3.
Time has Time(int hour) which calls this(hour, 0), and toString returns String.format("%02d:%02d", hour, minute). What is printed?
Time t = new Time(7);
System.out.println(t);Show answer
this(hour, 0) calls the two-parameter constructor in the same class: hour = 7, minute = 0. When println receives an object, it automatically calls toString(). %02d pads the number to two digits with a zero: 07.
Player.toString() returns name + " #" + number. What is printed?
Player p = new Player("Ali", 10);
System.out.println("#" + p + "#");Show answer
When an object is used in String concatenation, Java calls its toString() method: "Ali #10". A # is added at the start and at the end.
Ticket has private static int count; each constructor call does count++; id = count;. Assume count starts at 0. What is printed?
Ticket t = new Ticket("X");
Ticket u = new Ticket("Y");
System.out.println(t.getId() + u.getId() + Ticket.getCount());Show answer
t's id is 1, u's is 2, count is 2. All three are ints and there is no String at the start: addition is done, 1 + 2 + 2 = 5. If you wanted concatenation, you would put "" + at the start: "122".
Shop has public static final double TAX_RATE = 0.20; and priceWithTax(p) returns p * (1 + TAX_RATE). What is printed?
System.out.println(Shop.priceWithTax(50) + " " + (int) Shop.priceWithTax(10));Show answer
A static method is called with the class name, no object is needed. 50 * 1.2 = 60.0 (double). 10 * 1.2 = 12.0, 12 with the cast.
What is printed?
class Temp {
private int value;
public Temp(int value) { this.value = value; }
public Temp add(int d) { value += d; return this; }
public String toString() { return value + "C"; }
}
// in main:
Temp tm = new Temp(20).add(5).add(3);
System.out.println(tm);Show answer
return this returns the object that called the method itself, so the calls can be chained (method chaining). First 5, then 3 is added to the same object: 28.
Which lines do not compile?
public class Main {
private int score = 5;
public static final int MAX = 10;
public static void main(String[] args) {
System.out.println(score); // line A
System.out.println(MAX); // line B
MAX = 20; // line C
}
}Show answer
A: main is static; it is not clear whose score it is → non-static variable score cannot be referenced from a static context. B: a static field can be read from a static method. C: a final variable cannot be assigned a second time → cannot assign a value to static final variable MAX.
A class has no toString method. What does System.out.println(obj) print?
- Nothing
- The values of all fields
- Class name, @ and a hexadecimal hash code (e.g. NoToString@1b6d3586)
- A compile-time error
Show answer
Every class gets a default toString from Object: ClassName@hashCode. The code may change on each run. If you want to see the fields, you have to write toString yourself (override).
Write a constructor for Player (fields private String name; private int number;) whose parameters have the same names as the fields.
Show answer
The parameter names shadow the fields; to reach the field, this. is a must.public Player(String name, int number) { this.name = name; this.number = number;}The version written without this (name = name;) printed null 0 in verification.
Write a toString method for Player that returns the name, a space, # and the number (e.g. Can #7).
Show answer
The signature must be exactly public String toString(): public, return type String, no parameters. The method does not print, it returns.@Overridepublic String toString() { return name + " #" + number;}System.out.println(new Player("Can", 7)); → Can #7.
Verification: All outputs were run with JDK 26 (verify/SEN1001/W12.java; the classes Player, BadPlayer, Time, Ticket, Shop, Circle, Box, Box2, Counter and Temp are in the same file). The static and final compile errors were obtained with javac under verify/SEN1001/err/st/; the this(...) rule was also tried separately with javac --release 21.