Skip to main content

πŸ” Is final Really Final in Java? The Truth May Surprise You 😲

πŸ’¬ “When I was exploring what to do and what not to do in Java, one small keyword caught my eye — final. I thought it meant: locked, sealed, frozen — like my fridge when I forget to defrost it.” 

But guess what? Java has its own meaning of final… and it’s not always what you expect! πŸ˜…

Let’s break it down together — with code, questions, confusion, jokes, and everything in between.


🎯 The Confusing Case: You Said It's Final... Then It Changed?! 🫠


final List<String> names = new ArrayList<>(); names.add("Anand"); names.add("Rahul"); System.out.println(names); // [Anand, Rahul] 🀯

Hold on... that’s final, right?!


So how on earth is it still changing?

Time to dive deeper...


🧠 Why Is It Designed Like This?

Here’s the key secret:

In Java, final applies to the reference, not the object it points to.

Let’s decode this like a spy mission πŸ•΅️‍♂️:

Imagine This:


final List<String> names = new ArrayList<>();

Behind the scenes:

  • names is a reference variable → Think of it like a pointer (πŸ“Œ) to some object.

  • new ArrayList<>() is the actual object → like the storage box where the strings live.

So when you say final, you're telling Java:

"Hey, this variable called names must always point to the same box — but it’s okay if the contents inside the box are moved around."

But What If You Try This?


names = new LinkedList<>(); // ❌ Compiler screams: “NOT ALLOWED!”

That’s because you’re trying to change the reference.
Final says: “Nuh-uh, no swapping the box!” πŸ™…‍♂️


πŸ”₯ Real-Life Example: Why Do People Even Use final Then?

Here’s the question you asked — and it’s brilliant πŸ’‘:

"If we can still change the contents… why do developers bother marking collections final? Isn’t that just fake safety?"

Let’s break this down with real-world project cases:


✅ Case 1: Mutable List with final — Best of Both Worlds?

Used In: Spring beans, service classes, DTOs


public class UserGroup { private final List<String> members = new ArrayList<>(); public void addMember(String name) { members.add(name); // ✅ You can mutate the list } // But... public void setMembers(List<String> newList) { this.members = newList; // ❌ Compile error – can't reassign final } }

Why it’s useful:

  • You lock the list itself to prevent someone accidentally overwriting it.

  • But you allow controlled modification through methods like addMember().

πŸ‘‰ So you're telling the team:

"This list will always exist. You can update it. But don’t even think about replacing it!" 😎


🚫 Case 2: You Want Full Immutability — Final Isn't Enough!

Let’s say you’re building:

  • A config class,

  • A cache key,

  • Or a user role list that should never ever change once created.

If you only use final, someone can still sneak in a .add() or .clear() and ruin everything! 😱

So instead, use:


List<String> roles = List.of("ADMIN", "USER"); // Java 9+

Or:

List<String> safeList = Collections.unmodifiableList(Arrays.asList("x", "y"));

Try modifying that?


safeList.add("Z"); // πŸ’₯ Boom! UnsupportedOperationException

That’s Java saying:

“Touch that list, and I’ll blow up your stacktrace!” 😀


πŸ€ͺ Silly Q&A Time: What I Asked (And Answered)


❓ Can I do this?

List<String> names = null; names = new ArrayList<>();

✅ Yes! Because names isn’t final. You can point it anywhere.


❓ Can I do this?

final List<String> names = null; names = new ArrayList<>(); // ❌ NOPE! You already assigned null once.

Java:

“You gave it a value, even if it’s null. That’s it. Final means FINAL.” πŸ˜’


❓ So… final = useless?

NO! It’s like a seatbelt — doesn’t stop the car, but helps prevent unnecessary accidents.

Use it when:

  • You want to make sure the reference stays safe from being reassigned.

  • You want to signal to other developers: "This thing shouldn’t be swapped out!"


🀯 Plot Twist: final + static = Even More Interesting


public class Constants { public static final List<String> list = new ArrayList<>(); }

Yes, this compiles.
But now you have a globally accessible mutable list.
If someone calls:

Constants.list.add("HACKED");

You're done. πŸ”₯

Solution?
Make it unmodifiable during initialization:


public static final List<String> list = List.of("x", "y", "z");


🧩 Is List.of() Same as Collections.unmodifiableList()?

Almost — but not exactly.


FeatureList.of(...)Collections.unmodifiableList(...)
Java versionJava 9+Java 8+
Nulls allowed?❌ No✅ Yes
Backed by modifiable list?❌ No✅ Yes (original list can still change!)
Truly immutable?✅ Yes❌ Not fully (underlying list might be modifiable)

So yeah — List.of() is stronger. More like a real lock πŸ”’

Even trying to null something in it throws a tantrum 😀


🎨 Wrapping Up

πŸ”’ final doesn't mean "unchangeable" — it means "this reference won’t be reassigned"
πŸ”¨ The object can still be changed (unless you make it unmodifiable)

 

**So next time someone says “It’s final, bro” —
Just smile and ask:

"Yeah, but can I still add to it?" 😏**

Comments

Popular posts from this blog

🎭 Spring’s Secret: Why @Transactional & Friends Betray You Silently

πŸ’‘ Lesson Learned — Not a Prod Bug, But a Real Pain No, this wasn’t a production outage. Nobody screamed at me. But I sat for 3 hours wondering: “Why the heck is my @Transactional not rolling back!?” 😡‍πŸ’« “Why is Redis cache not working?” 🀯 Turned out, the issue was one silent villain: 🧱 Self-invocation 🀷 What Is @Transactional ? If you're new: @Transactional = Tells Spring to start a DB transaction when a method is called. It’ll commit if everything’s okay. It’ll rollback if something fails. 🧠 Think of it like wrapping your code in: try { beginTransaction(); // your logic commit(); } catch(Exception e) { rollback(); } πŸ•΅️ Real-Life Analogy — The Gateway Community 🏘️ Let me tell you about my society — it has a strict watchman at the gate. Here’s how it works: πŸ›‚ Watchman = Spring Proxy 🏠 Your apartment = Your service class πŸšͺ Your room = A method inside that class πŸƒ Scenario 1: Outsider Visits Your friend from outside...

🧡 Virtual Threads in Java — The Ultimate Guide with Diagrams, Code & Interview Qs!

πŸš€ “How are Virtual Threads different from Thread Pools?” 😡 “Are they OS threads or JVM threads?” πŸ™ƒ “Should I still use CompletableFuture?” 🀯 “How do I even use them in real-time microservices?” 🧠 What are Virtual Threads? Virtual Threads (introduced in Java 21 as stable πŸŽ‰) are lightweight threads managed by the JVM instead of the OS kernel. πŸ‘‰ They look like normal threads, but don’t hog OS resources like traditional threads. 🧠 What is the OS Kernel? πŸ›️ OS Kernel = The Brain of the Operating System It’s the core part of your OS (Windows, Linux, Mac) that: Manages memory 🧠 Schedules threads πŸ•’ Talks to hardware πŸ’» Handles I/O operations πŸ“¨ When you create a traditional thread in Java, the JVM asks the OS Kernel to create a real OS-level thread. πŸ–Ό️ Imagine This... ┌───────────────────────────┐ │ Your Java Application │ └────────────┬──────────────┘ │ ...

🐱 Tomcat vs ⚡ Netty – Which One Should You Use?

🐱 Tomcat vs ⚡ Netty – Which One Should You Use? So recently I got curious about this too πŸ€”. Everywhere in Spring Boot tutorials we see Tomcat . Then suddenly while exploring Spring WebFlux , the name Netty pops up. And I was like – “Wait, who’s this Netty guy trying to replace Tomcat?” πŸ˜… Let’s break it down with real-time examples , icons , and fun comparisons . 🐱 Tomcat – The Traditional Web Server Type: Servlet Container (blocking I/O) World: Used with Spring MVC Style: Thread-per-request model πŸ‘©‍πŸ’» Pros: Stable, widely used, battle-tested Cons: Struggles with huge concurrent connections πŸ‘‰ Example in real life: Tomcat is like a restaurant with fixed waiters 🍴. - Each customer = one thread/waiter - If too many customers come in at once → waiters run out → customers wait outside πŸšͺ ⚡ Netty – The Reactive Rockstar Type: Asynchronous Event-Driven Network Framework World: Default for Spring WebFlux Style: Event-lo...