Classes, Objects & static
Until now, your data has lived in separate variables: a name here, a balance there. Real programs deal with things: bank accounts, books, train tickets, students. Each thing has data (an account has an owner and a balance) and behaviour (you can deposit and withdraw). A class describes one kind of thing, and an object is one actual thing made from that class.
This is the core of object-oriented programming (OOP), and Java is built around it: every line of Java code lives inside a class. In this lesson you will write classes, create objects, and learn the difference between members that belong to each object and static members that belong to the class itself.
Classes and objects
Think of a class as the printed form for a library card and objects as the filled-in cards. The class lists the fields (the data each object holds). The keyword new creates an object, and you reach its fields with a dot.
public class Library {
public static void main(String[] args) {
Book first = new Book();
first.title = "Wings of Fire";
first.author = "A. P. J. Abdul Kalam";
first.pages = 180;
Book second = new Book();
second.title = "The Guide";
second.author = "R. K. Narayan";
System.out.println(first.title + " by " + first.author + ", " + first.pages + " pages");
System.out.println(second.title + " has " + second.pages + " pages recorded");
System.out.println(second.issuedTo);
}
}
class Book {
String title;
String author;
int pages;
String issuedTo;
}Wings of Fire by A. P. J. Abdul Kalam, 180 pages The Guide has 0 pages recorded null
This file contains two classes. Only one class in a file can be public, and the file is named after it. When you run a file directly with java Library.java, put the class with main first. Each Book object has its own copy of every field. Fields you do not set get default values, just like array elements: 0 for numbers, false for booleans and null for references.
Methods and this
Methods inside a class (without static) are instance methods: you call them on an object, and they work with that object's fields. Inside a method, the keyword this means "the object this method was called on". You can usually leave it out, as in balance += amount, but it helps when a parameter has the same name as a field.
public class Wallet {
public static void main(String[] args) {
Account priya = new Account();
priya.owner = "Priya";
priya.deposit(5000);
priya.withdraw(1200);
priya.withdraw(9000);
System.out.println(priya.summary());
Account rohan = new Account();
rohan.owner = "Rohan";
rohan.deposit(800);
System.out.println(rohan.summary());
}
}
class Account {
String owner;
double balance;
void deposit(double amount) {
balance += amount;
}
boolean withdraw(double amount) {
if (amount > balance) {
System.out.println("Sorry " + owner + ", only Rs " + balance + " available");
return false;
}
balance -= amount;
return true;
}
String summary() {
return owner + ": Rs " + this.balance;
}
}Sorry Priya, only Rs 3800.0 available Priya: Rs 3800.0 Rohan: Rs 800.0
priya.deposit(5000) changes only Priya's balance; Rohan's account is a separate object. Data and the code that changes it now live together, which is the main idea of OOP. Right now anyone can still write priya.balance = 1000000;, though. You will lock that down in Encapsulation & Packages.
Objects live behind references
As with arrays, a variable of a class type holds a reference to an object, not the object itself. Two variables can refer to the same object, == compares references, and a reference can be null.
public class Refs {
public static void main(String[] args) {
Phone a = new Phone();
a.model = "Galaxy";
Phone b = a; // same object, second name
b.model = "Pixel";
System.out.println(a.model);
Phone c = new Phone();
c.model = "Pixel";
System.out.println(a == b);
System.out.println(a == c);
System.out.println(c);
Phone lost = null;
System.out.println(lost.model.length());
}
}
class Phone {
String model;
}Pixel true false Phone@525b461a Exception in thread "main" java.lang.NullPointerException: Cannot read field "model" because "lost" is null at Refs.main(Refs.java:16)
a and b are one phone with two names, so changing b.model changed what a sees. a and c hold the same text but are different objects, so a == c is false. Printing an object shows its class name and an internal code, unless the class defines toString (you will do that next). Using a null reference throws a NullPointerException. Since Java 14 the message tells you exactly which variable was null; if your program was compiled without debug information (as java File.java does), the name appears as something like "<local4>" instead of "lost".
static: members that belong to the class
Sometimes a value or method belongs to the class as a whole, not to any single object: the ticket price that all tickets share, or a counter of how many tickets were sold. Mark such members static. There is only one copy of a static field, and you use it through the class name: Ticket.sold.
public class StaticDemo {
public static void main(String[] args) {
Ticket t1 = new Ticket("Aarav", 3);
Ticket t2 = new Ticket("Meera", 1);
Ticket t3 = new Ticket("Kabir", 5);
System.out.println(t1);
System.out.println(t3);
System.out.println("Tickets sold: " + Ticket.sold);
System.out.println("Price for 4 adults: Rs " + Ticket.priceFor(4));
}
}
class Ticket {
static final int PRICE = 250; // one value for the whole class
static int sold = 0; // shared counter
final int number; // each ticket has its own
String name;
int seats;
Ticket(String name, int seats) { // a constructor: runs on "new"
sold++;
this.number = sold;
this.name = name;
this.seats = seats;
}
static int priceFor(int seats) {
return seats * PRICE;
}
@Override
public String toString() {
return "Ticket #" + number + " for " + name + " (" + seats + " seats, Rs " + priceFor(seats) + ")";
}
}Ticket #1 for Aarav (3 seats, Rs 750) Ticket #3 for Kabir (5 seats, Rs 1250) Tickets sold: 3 Price for 4 adults: Rs 1000
Two new things appear here. Ticket(String name, int seats) is a constructor: it runs automatically when you write new Ticket(...) and sets up the new object. this.name = name copies the parameter into the field of the same name. Constructors get their own lesson next: Constructors. And toString tells Java how to print a Ticket; println(t1) calls it automatically. @Override marks that it replaces a method every class inherits, which you will understand in Inheritance & Polymorphism.
The rule to remember: static code cannot use instance fields directly, because there is no particular object to read them from. Writing count++ for an instance field inside static void main fails with error: non-static variable count cannot be referenced from a static context. Instance methods, on the other hand, can use static members freely, as toString does with priceFor.
Classes such as Math contain only static methods, which is why you call Math.max(...) without creating a Math object.
Common mistakes
- Forgetting
new.Account a; a.deposit(100);does not compile, becauseawas never given an object.Account a = null; a.deposit(100);compiles but throwsNullPointerException. - Making everything static. Beginners add
staticto silence the "static context" error. Instead, create an object and call methods on it. - Comparing objects with
==. It checks whether two references point to the same object, not whether the contents match. - Expecting a useful
println(obj)withouttoString. Add atoStringmethod to your classes.
Key points
- A class defines fields (data) and methods (behaviour);
newcreates objects from it. - Each object has its own copy of instance fields; unset fields get default values.
thisrefers to the current object; instance methods work on the object they are called on.- Variables of class types hold references;
==compares references, andnullmeans no object. staticfields and methods belong to the class, are shared, and are used through the class name.- Static code cannot access instance fields or methods without an object.
Exercise
Create a class Student with fields name, marks (an int[]) and a static field count that counts how many students were created (increase it in a constructor). Add an instance method average() returning a double, an instance method topMark(), and a toString method. In main, create Ananya with marks 88, 92, 79 and Farhan with marks 65, 71, 80, print both, and print the count.
Show solution
public class School {
public static void main(String[] args) {
Student ananya = new Student("Ananya", new int[]{88, 92, 79});
Student farhan = new Student("Farhan", new int[]{65, 71, 80});
System.out.println(ananya);
System.out.println(farhan);
System.out.println("Students created: " + Student.count);
}
}
class Student {
static int count = 0;
String name;
int[] marks;
Student(String name, int[] marks) {
this.name = name;
this.marks = marks;
count++;
}
double average() {
int total = 0;
for (int m : marks) {
total += m;
}
return (double) total / marks.length;
}
int topMark() {
int best = marks[0];
for (int m : marks) {
best = Math.max(best, m);
}
return best;
}
@Override
public String toString() {
return "%s: average %.1f, top %d".formatted(name, average(), topMark());
}
}Ananya: average 86.3, top 92 Farhan: average 72.0, top 80 Students created: 2
count is static, so both constructors increase the same counter. average() and topMark() are instance methods because each student has different marks.
Quick Quiz
1. What does new Account() do?
new creates an object (an instance) from a class.
2. A class has static int sold;. How many copies of sold exist when there are 50 objects?
A static field belongs to the class, so there is exactly one copy.
3. Why does count++ inside public static void main fail when count is an instance field?
Instance fields belong to objects; a static method is not called on any object.
4. What does this mean inside an instance method?
this refers to the current object.
Learn Java with a mentor
Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
