How to Use Modern Java Like a Pro

Java has been around since 1995, and yet, it’s still going strong. While many developers complain about Java being too verbose or outdated, the truth is — Java has evolved. If you’re still writing Java like it’s 2005, you’re missing out on the features that make modern Java cleaner, smarter, and way more productive.

So, if you’re ready to stop writing boilerplate code and start coding like a modern Java pro, this article is your guide.

Step 1: Use the Latest LTS Version

Before diving into features, make sure you’re using a modern Java version.

✅ Recommendation: Use Java 17 or Java 21 (both are Long-Term Support versions).

Why? Because these versions include:

  • Lambdas and Streams
  • var for local type inference
  • Records, Sealed Classes, Pattern Matching
  • Virtual Threads (from Java 21)
  • Better performance and garbage collection

Step 2: Embrace the Power of var

Introduced in Java 10, the var keyword allows local variable type inference. It reduces boilerplate and makes code more readable when the type is obvious.

Example:

// Before
Map<String, List<String>> map = new HashMap<>();

// Modern Java
var map = new HashMap<String, List<String>>();

It keeps your code cleaner and less repetitive, especially with generics.

⚠️ Use it wisely: Don’t use var when the type is unclear or makes the code harder to read.

Step 3: Use Records for Immutable Data Classes

If you’re still writing POJOs with getters, setters, equals, hashCode, and toString — stop.

Modern Java gives you records, introduced in Java 14.

Example:

record User(String name, int age) {}

This one-liner automatically generates:

  • Constructor
  • Getters
  • equals()
  • hashCode()
  • toString()

And the best part? Records are immutable by default. Clean, concise, and ideal for data models, DTOs, etc.

Step 4: Write Declarative Code with Streams and Lambdas

If you’re still using for loops to filter and transform lists, you’re missing out on the expressive power of Streams and Lambdas (Java 8+).

Before (Imperative):

List<String> result = new ArrayList<>();
for (String name : names) {
if (name.startsWith("A")) {
result.add(name.toUpperCase());
}
}

After (Modern Java):

var result = names.stream()
.filter(n -> n.startsWith("A"))
.map(String::toUpperCase)
.toList();
  • More readable
  • Easier to parallelize
  • Encourages functional thinking

Learn to master map(), filter(), reduce(), collect(), and you’ll write cleaner logic with fewer bugs.

Step 5: Use Optional to Avoid NullPointerException

Instead of checking for null everywhere, use Optional (Java 8+) to express optional values clearly.

Traditional:

if (user != null && user.getEmail() != null) {
send(user.getEmail());
}

Modern:

Optional.ofNullable(user)
.map(User::getEmail)
.ifPresent(this::send);

Optional makes the presence or absence of a value explicit, reducing null checks and improving readability.

⚠️ Avoid overusing Optional as method parameters or fields. Use it primarily for return values.

Step 6: Replace if-else Chains with Modern Alternatives

Nested if-else blocks are a sign your code needs refactoring.

✅ Alternatives:

  • Use Map for simple key-value logic
  • Use switch expressions (Java 14+)
  • Use polymorphism for behavior

Modern switch example:

String result = switch (status) {
case "NEW" -> "Create Order";
case "PAID" -> "Ship Order";
case "CANCEL" -> "Cancel Order";
default -> "Unknown Status";
};

Much cleaner than multiple if-else or classic switch-case.

Step 7: Master Modern Spring Boot (If You’re Using Spring)

If you’re building web apps or APIs, you’re likely using Spring Boot. Make sure you’re using its modern capabilities:

  • ✅ Use Spring Boot 3+ with Java 17+
  • ✅ Embrace record-based DTOs
  • ✅ Use @RestController + @RequestMapping wisely
  • ✅ Enable lombok where appropriate to reduce boilerplate
  • ✅ Use @Component, @Service, @Repository for clear layer separation
  • ✅ Use constructor injection (no field injection)
  • ✅ Profile-based configs with application-dev.yml, application-prod.yml

And don’t forget to explore:

  • Spring Data JPA
  • Spring Security 6
  • Spring WebFlux for reactive apps

Step 8: Use Sealed Classes for Controlled Inheritance

Introduced in Java 17, sealed classes let you restrict which classes can extend a parent class. Perfect for domain models with controlled variations.

Example:

sealed interface Shape permits Circle, Rectangle {}

final class Circle implements Shape {}

final class Rectangle implements Shape {}

Great for exhaustive switch statements and better type safety in complex domain logic.

Step 9: Use Pattern Matching (Preview in Java 21)

Modern Java supports pattern matching for instanceof — and more is coming (like switch pattern matching).

Example:

if (obj instanceof String s) {
System.out.println("String length: " + s.length());
}

Cleaner, type-safe, and avoids unnecessary casting.

Step 10: Use the Right Tools Like a Pro

Mastering modern Java isn’t just about the language — it’s also about using modern tools:

  • IntelliJ IDEA (with live templates, refactoring tools)
  • Lombok (for concise POJOs)
  • Maven / Gradle (dependency and build management)
  • JUnit 5 + Mockito (modern testing frameworks)
  • Docker + Spring Boot (for cloud-native development)
  • Java Records, Streams, and Optionals — core of functional programming in Java

Summary: How to Use Modern Java Like a Pro


Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare