Sep 14, 2026
4 min read
Early Java code often compiles but still feels confusing because object-oriented programming is not just syntax. Classes, objects, constructors, access modifiers, and inheritance are design tools. When they are used without a clear model, small programs quickly become tangled.
The mistakes below are common because they are easy to write and hard to notice until the code grows.
static belongs to the class itself, not to one object. It is useful for constants, utility methods, and shared behavior. It is not a shortcut for avoiding object creation.
This is usually a warning sign:
public class Student {
static String name;
static double gpa;
}
If name and gpa are static, every Student shares the same values. Creating three students does not give each one a separate name. Most object fields should be instance fields:
public class Student {
private String name;
private double gpa;
public Student(String name, double gpa) {
this.name = name;
this.gpa = gpa;
}
}
Ask this before using static: should this value be shared by all objects, or should each object have its own copy?
A class is the blueprint. An object is the actual thing created from that blueprint.
Student ada = new Student("Ada", 3.8);
Student linus = new Student("Linus", 3.5);
Both variables use the same Student class, but they refer to different objects. If changing one student's value changes another student's value, check for accidental static fields or shared mutable objects.
Inheritance is powerful, but students often use it too early. Not every relationship is an extends relationship.
Use inheritance when one thing truly is a specialized version of another:
class GraduateStudent extends Student {}
Use composition when one thing has another thing:
class Course {
private Instructor instructor;
}
A Course is not an Instructor, so it should not extend Instructor. This sounds obvious in plain English, but it is a common source of awkward class hierarchies.
Encapsulation means protecting an object's internal state and exposing controlled behavior. It does not mean adding getters and setters for everything without thinking.
Weak encapsulation:
public double balance;
Better:
private double balance;
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
balance += amount;
}
The second version protects the rule that deposits must be positive. That rule belongs inside the object, not scattered across every caller.
Constructors should create valid objects. If an object cannot work without a required value, ask for that value in the constructor.
public Book(String title, String author) {
if (title == null || title.isBlank()) {
throw new IllegalArgumentException("Title is required");
}
this.title = title;
this.author = author;
}
This prevents half-built objects from moving through the program and failing later in a less obvious place.
NullPointerException is not random. It means your code tried to use a reference that points to nothing.
When you see it, check:
new?null?Fix the source of the missing object. Do not add null checks everywhere without understanding which value is missing and why.
If your class handles accounts, test invalid deposits. If your class handles grades, test empty lists. If your class handles search, test missing results.
Good OOP tests check object behavior, not just method calls:
Account account = new Account();
account.deposit(100);
assertEquals(100, account.getBalance());
Then add a failure case:
assertThrows(IllegalArgumentException.class, () -> account.deposit(-10));
The failure case often reveals whether your class owns its rules properly.
If your Java code compiles but the class design feels messy, EduSupport's Java tutoring can help you reason through objects, constructors, inheritance, and debugging. For student-written code that already exists, student code review can help you identify where the design or logic is drifting from the intended model.
Discuss tutoring, code review, project mentoring, or research-method guidance.