Java Best Practices and Common Mistakes
A closing checklist of habits that separate solid, maintainable Java from code that merely compiles.
2 min read
Java code that compiles and runs isn't automatically Java code that's safe to maintain. A few habits, mostly small, are what separate the two in practice.
Favor immutability where you can
// Avoid: mutable, anyone can change it after construction
public class Point {
public int x;
public int y;
}
// Prefer: immutable, or a record (Java 16+)
public record Point(int x, int y) {}An object that can't change after it's constructed can't end up in an unexpected state later, and can be shared freely between threads without synchronization. Reach for final fields and, where the object is a simple data holder, Java's record type, which generates the constructor, getters, equals(), hashCode(), and toString() for you.
Common mistakes to avoid
Comparing objects with == instead of .equals().
// Avoid
if (userInput == "yes") { }
// Prefer
if (userInput.equals("yes")) { }Catching Exception broadly, or swallowing it silently.
// Avoid: hides real bugs, catches things you never intended to catch
try {
doWork();
} catch (Exception e) { }
// Prefer: catch specific exceptions, and at least log what happened
try {
doWork();
} catch (IOException e) {
logger.error("Failed to read input file", e);
}Ignoring null until it throws a NullPointerException.
// Avoid
String name = getUser().getName(); // NPE if getUser() returns null
// Prefer
Optional<User> user = getUserOptional();
String name = user.map(User::getName).orElse("Unknown");Using raw types instead of generics.
// Avoid
List items = new ArrayList();
// Prefer
List<String> items = new ArrayList<>();Making everything public.
// Avoid: no encapsulation, anything can mutate internal state
public class Account {
public double balance;
}
// Prefer: private fields, controlled access
public class Account {
private double balance;
public double getBalance() { return balance; }
}A final checklist
- [ ] Fields are
privateby default; only expose what callers actually need. - [ ]
.equals()is used for object comparison,==only for primitives and reference identity checks. - [ ] Exceptions are caught at the appropriate specificity, never silently swallowed.
- [ ] Collections and generics are used with type parameters, never raw types.
- [ ] Resources (files, connections) are managed with try-with-resources.
- [ ]
@Overrideis used on every intentional method override. - [ ] Classes and methods have a single, clear responsibility rather than doing several unrelated things.
- [ ] Naming follows convention:
camelCasefor methods and variables,PascalCasefor classes,ALL_CAPSfor constants.
None of these are exotic techniques — they're the same language features covered throughout this course, applied with a bit more discipline. That discipline is most of what separates Java that merely runs from Java a team can maintain for years.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.