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

🐱 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...

🎭 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...

🌟 My Journey – From Zero to Senior Java Tech Lead 🌟

 There’s one thing I truly believe… If I can become a Java developer, then anyone in the world can. πŸ’― Sounds crazy? Let me take you back. πŸ•“ Back in 2015… I had zero coding knowledge . Not just that — I had no interest in coding either. But life has its own plans. In 2016, I got a chance to move to Bangalore and joined a Java course at a training center. That’s where it all started — Every day, every session made me feel like: "Ohhh! Even I can be a developer!" That course didn’t just teach Java — it gave me confidence . πŸ§ͺ Two Life-Changing Incidents 1️⃣ The Interview That Wasn't Planned Halfway through my course, I had to urgently travel to Chennai to donate blood to a family member. After that emotional rollercoaster, I found myself reflecting on my skills and the future. The next day, as I was preparing for my move to Bangalore to complete the remaining four months of my course, I randomly thought — "Let me test my skills... let me just see...