What Happens if main() is Not Static
When you type java MyApp, the Heap is empty — no objects exist yet. The JVM needs an entry point to start your programme.
If main() were non-static — JVM would need to create an object first. But which constructor? What arguments? What if it throws an exception? JVM has no information to decide.
With static — JVM calls MyApp.main(args) directly on the class level. No object needed, no guessing.
Without static — JVM doesn't even try. You get:
Error: Main method not found, please define as public static void main(String[] args)
Java 21 introduced a preview feature — you can write just void main() without static, without String[] args, without public. JVM handles object creation internally.
#Java #JVM #Static #Main
When you type java MyApp, the Heap is empty — no objects exist yet. The JVM needs an entry point to start your programme.
If main() were non-static — JVM would need to create an object first. But which constructor? What arguments? What if it throws an exception? JVM has no information to decide.
With static — JVM calls MyApp.main(args) directly on the class level. No object needed, no guessing.
Without static — JVM doesn't even try. You get:
Error: Main method not found, please define as public static void main(String[] args)
Java 21 introduced a preview feature — you can write just void main() without static, without String[] args, without public. JVM handles object creation internally.
#Java #JVM #Static #Main
Memory Leaks in Java — yes, even with GC
A lot of devs think "Java has GC, so no memory leaks". I thought the same. Then I spent 3 hours debugging why our app crashed every night at 3am.
Turned out — one unclosed DB connection. That's it.
GC is smart but it's not magic. It removes objects nobody references anymore. The problem is you — you keep referencing objects you forgot about.
Classic examples from real life:
You register a listener and never unregister. You put something in ThreadLocal and move on. You cache objects in a static list and never clean it. You open a connection and "handle it later".
The app doesn't crash immediately. It slows down. Memory grows. GC works harder. Until one night — boom.
The fix isn't a library or a pattern. It's just discipline — close what you open.
#Java #Backend #RealTalk
A lot of devs think "Java has GC, so no memory leaks". I thought the same. Then I spent 3 hours debugging why our app crashed every night at 3am.
Turned out — one unclosed DB connection. That's it.
GC is smart but it's not magic. It removes objects nobody references anymore. The problem is you — you keep referencing objects you forgot about.
Classic examples from real life:
You register a listener and never unregister. You put something in ThreadLocal and move on. You cache objects in a static list and never clean it. You open a connection and "handle it later".
The app doesn't crash immediately. It slows down. Memory grows. GC works harder. Until one night — boom.
The fix isn't a library or a pattern. It's just discipline — close what you open.
#Java #Backend #RealTalk
GC Pauses Increased After a Small Code Change
A small code change caused GC pauses to spike. How?
Most developers add a log line, forget to close a connection, or switch from primitive to wrapper type. Looks innocent. But the chain reaction is brutal.
Too many objects created → Eden fills fast → Minor GC runs constantly → objects survive and get promoted to Old Generation → Old Gen fills up → Major GC kicks in → long Stop The World pause.
Minor GC pauses are short — milliseconds. Major GC pauses are long — hundreds of milliseconds or even seconds. That's the difference between a smooth app and one that freezes randomly.
The culprit is almost always objects living longer than they should — unclosed connections, unbounded caches, forgotten listeners.
#Java #JVM #GC #Performance
A small code change caused GC pauses to spike. How?
Most developers add a log line, forget to close a connection, or switch from primitive to wrapper type. Looks innocent. But the chain reaction is brutal.
Too many objects created → Eden fills fast → Minor GC runs constantly → objects survive and get promoted to Old Generation → Old Gen fills up → Major GC kicks in → long Stop The World pause.
Minor GC pauses are short — milliseconds. Major GC pauses are long — hundreds of milliseconds or even seconds. That's the difference between a smooth app and one that freezes randomly.
The culprit is almost always objects living longer than they should — unclosed connections, unbounded caches, forgotten listeners.
#Java #JVM #GC #Performance
Increasing Heap Size Made Performance Worse
Counterintuitive but real. Here's why.
Bigger heap means more objects can live in memory. GC runs less frequently — sounds good. But when Major GC finally kicks in it has to scan the entire heap.
512MB heap → Major GC → 200ms pause
16GB heap → Major GC → 10 second pause 💀
App doesn't crash. It just freezes for 10 seconds randomly. Users think it's dead.
More heap = less frequent but more painful GC pauses.
The real fix is not throwing more memory at the problem. Fix memory leaks first. Then if you still need large heap — switch to ZGC or Shenandoah which keep pauses under 1ms regardless of heap size.
#Java #JVM #GC #Performance #Heap
Counterintuitive but real. Here's why.
Bigger heap means more objects can live in memory. GC runs less frequently — sounds good. But when Major GC finally kicks in it has to scan the entire heap.
512MB heap → Major GC → 200ms pause
16GB heap → Major GC → 10 second pause 💀
App doesn't crash. It just freezes for 10 seconds randomly. Users think it's dead.
More heap = less frequent but more painful GC pauses.
The real fix is not throwing more memory at the problem. Fix memory leaks first. Then if you still need large heap — switch to ZGC or Shenandoah which keep pauses under 1ms regardless of heap size.
#Java #JVM #GC #Performance #Heap
== vs equals() in Java
== compares memory addresses — are these two variables pointing to the same object in heap?
equals() compares content — are these two objects meaningfully the same?
Default equals() from Object class just does ==. The magic happens when you override it — like String does, comparing character by character.
Two Dogs with the same name are two different objects at different heap addresses. == returns false. Override equals() and it returns true.
Important rule — whenever you override equals() always override hashCode() too. Otherwise HashMap and HashSet break completely.
#Java #CoreJava #Equals #Hashcode
Shallow Copy vs Deep Copy
Shallow copy — copies the object but nested objects are still shared. Change a nested object in the copy and the original changes too.
Deep copy — copies everything completely. Original and copy are fully independent. Change anything in one — the other doesn't care.
Most common approach for deep copy in real projects — copy constructor, or serialize then deserialize, or libraries like ModelMapper.
#Java #CoreJava #DeepCopy #ShallowCopy
Serialization in Java
Serialization converts an object into a byte stream so it can be saved to a file, sent over a network, or stored in cache.
Deserialization is the reverse — byte stream back to object.
Serializable is a marker interface — no methods, just tells JVM this class can be serialized.
Used for — saving object state to file, sending objects between services, storing in Redis, deep copying objects.
The output is binary — not human readable. If you need human readable use JSON instead.
#Java #Serialization #CoreJava
transient vs volatile
Two completely different problems — just both are field modifiers.
volatile — always read fresh value from main memory, bypassing CPU cache. Solves visibility problem between threads.
transient — skip this field during serialization. Field won't be written to file or sent over network. Most common use — sensitive data like passwords and tokens you never want leaving the application.
#Java #CoreJava #Volatile #Transient #Threads
Reflection in Java
Reflection API gives access to private fields, methods, constructors and annotations at runtime — bypassing normal access rules.
Spring uses it heavily — creating beans without you calling new, injecting dependencies into private fields with @Autowired, scanning annotations like @Service and @Transactional.
JUnit finds @Test methods via reflection. Jackson reads object fields to convert to JSON. Hibernate maps fields to database columns.
Downside — slow, breaks encapsulation, and Java 9+ restricts access to internal JDK classes via module system. One of the biggest reasons companies stayed on Java 8 for so long.
#Java #Reflection #CoreJava #Spring
Abstract Class vs Interface
Abstract class — use when classes share common state and behavior. Supports only one extends. Can have instance fields, concrete methods, abstract methods.
Interface — use when unrelated classes share a contract. A class can implement many interfaces. Since Java 8 supports default and static methods too.
Real use case difference — abstract class is what you ARE, interface is what you CAN DO.
Vehicle is an abstract class — Car and Truck share speed, acceleration, fuel logic.
Flyable is an interface — Bird, Airplane, Superman are completely unrelated but all can fly.
#Java #CoreJava #AbstractClass #Interface #OOP
Design Patterns in Java
Patterns are proven solutions to common software design problems. Three categories:
Creational — how objects are created.
Singleton — one instance across entire app. DB connections, Logger, Spring beans by default.
Factory — delegate object creation to a method. Spring BeanFactory, JDBC DriverManager.
Builder — construct complex objects step by step. Lombok @Builder, HTTP request builders.
Structural — how objects are composed.
Proxy, Adapter, Decorator — Spring AOP uses Proxy pattern heavily.
Behavioral — how objects communicate.
Observer, Strategy, Template Method — used everywhere in frameworks.
#Java #DesignPatterns #CoreJava #Spring
== compares memory addresses — are these two variables pointing to the same object in heap?
equals() compares content — are these two objects meaningfully the same?
Default equals() from Object class just does ==. The magic happens when you override it — like String does, comparing character by character.
Two Dogs with the same name are two different objects at different heap addresses. == returns false. Override equals() and it returns true.
Important rule — whenever you override equals() always override hashCode() too. Otherwise HashMap and HashSet break completely.
#Java #CoreJava #Equals #Hashcode
Shallow Copy vs Deep Copy
Shallow copy — copies the object but nested objects are still shared. Change a nested object in the copy and the original changes too.
Deep copy — copies everything completely. Original and copy are fully independent. Change anything in one — the other doesn't care.
Most common approach for deep copy in real projects — copy constructor, or serialize then deserialize, or libraries like ModelMapper.
#Java #CoreJava #DeepCopy #ShallowCopy
Serialization in Java
Serialization converts an object into a byte stream so it can be saved to a file, sent over a network, or stored in cache.
Deserialization is the reverse — byte stream back to object.
Serializable is a marker interface — no methods, just tells JVM this class can be serialized.
Used for — saving object state to file, sending objects between services, storing in Redis, deep copying objects.
The output is binary — not human readable. If you need human readable use JSON instead.
#Java #Serialization #CoreJava
transient vs volatile
Two completely different problems — just both are field modifiers.
volatile — always read fresh value from main memory, bypassing CPU cache. Solves visibility problem between threads.
transient — skip this field during serialization. Field won't be written to file or sent over network. Most common use — sensitive data like passwords and tokens you never want leaving the application.
#Java #CoreJava #Volatile #Transient #Threads
Reflection in Java
Reflection API gives access to private fields, methods, constructors and annotations at runtime — bypassing normal access rules.
Spring uses it heavily — creating beans without you calling new, injecting dependencies into private fields with @Autowired, scanning annotations like @Service and @Transactional.
JUnit finds @Test methods via reflection. Jackson reads object fields to convert to JSON. Hibernate maps fields to database columns.
Downside — slow, breaks encapsulation, and Java 9+ restricts access to internal JDK classes via module system. One of the biggest reasons companies stayed on Java 8 for so long.
#Java #Reflection #CoreJava #Spring
Abstract Class vs Interface
Abstract class — use when classes share common state and behavior. Supports only one extends. Can have instance fields, concrete methods, abstract methods.
Interface — use when unrelated classes share a contract. A class can implement many interfaces. Since Java 8 supports default and static methods too.
Real use case difference — abstract class is what you ARE, interface is what you CAN DO.
Vehicle is an abstract class — Car and Truck share speed, acceleration, fuel logic.
Flyable is an interface — Bird, Airplane, Superman are completely unrelated but all can fly.
#Java #CoreJava #AbstractClass #Interface #OOP
Design Patterns in Java
Patterns are proven solutions to common software design problems. Three categories:
Creational — how objects are created.
Singleton — one instance across entire app. DB connections, Logger, Spring beans by default.
Factory — delegate object creation to a method. Spring BeanFactory, JDBC DriverManager.
Builder — construct complex objects step by step. Lombok @Builder, HTTP request builders.
Structural — how objects are composed.
Proxy, Adapter, Decorator — Spring AOP uses Proxy pattern heavily.
Behavioral — how objects communicate.
Observer, Strategy, Template Method — used everywhere in frameworks.
#Java #DesignPatterns #CoreJava #Spring
🔒 JPA — @Transactional
"The session stays open until the job finishes."
• Guarantees ACID — all or nothing
• Keeps Hibernate session alive for the entire method
• Without it → session closes after first query → LazyInitializationException
• readOnly=true → skips dirty checking → faster reads, no accidental writes
• spring.jpa.open-in-view=true (default) keeps session open till HTTP response — hides real problems, disable it
• Dirty checking: Hibernate tracks changes and auto-flushes at transaction end
💡 Reminder: No @Transactional = no session = no lazy loading.
#Java #JPA #Hibernate #Transactional #ACID #SpringBoot #EPAM
"The session stays open until the job finishes."
• Guarantees ACID — all or nothing
• Keeps Hibernate session alive for the entire method
• Without it → session closes after first query → LazyInitializationException
• readOnly=true → skips dirty checking → faster reads, no accidental writes
• spring.jpa.open-in-view=true (default) keeps session open till HTTP response — hides real problems, disable it
• Dirty checking: Hibernate tracks changes and auto-flushes at transaction end
💡 Reminder: No @Transactional = no session = no lazy loading.
#Java #JPA #Hibernate #Transactional #ACID #SpringBoot #EPAM
🔧 JPA — FETCH JOIN & @EntityGraph
"Tell Hibernate exactly what to load, exactly when you need it."
• FETCH JOIN → collapses N+1 into 1 single SQL query
• @EntityGraph → same idea, but without writing JPQL
• Both keep FetchType LAZY but load eagerly on demand
• Loop after FETCH JOIN = iterating memory, not hitting DB
• Use FETCH JOIN when you KNOW you'll need the children
• Don't use it blindly — fetching unnecessary data is still waste
💡 Reminder: LAZY is the default. FETCH JOIN is your controlled override.
#Java #JPA #Hibernate #FetchJoin #EntityGraph #SpringBoot #EPAM
"Tell Hibernate exactly what to load, exactly when you need it."
• FETCH JOIN → collapses N+1 into 1 single SQL query
• @EntityGraph → same idea, but without writing JPQL
• Both keep FetchType LAZY but load eagerly on demand
• Loop after FETCH JOIN = iterating memory, not hitting DB
• Use FETCH JOIN when you KNOW you'll need the children
• Don't use it blindly — fetching unnecessary data is still waste
💡 Reminder: LAZY is the default. FETCH JOIN is your controlled override.
#Java #JPA #Hibernate #FetchJoin #EntityGraph #SpringBoot #EPAM
🚨 JPA — The N+1 Problem
"You asked for 1 list. Hibernate made 1001 queries."
• 1 query fetches all parents
• N queries fire — one per parent — to fetch their children
• With 1000 users → 1001 DB hits
• Root cause: LAZY loading triggered inside a loop
• Grows silently — works fine in dev, crashes in production
• How to spot it: same query shape repeating in logs with different IDs
• Enable spring.jpa.show-sql=true to see it happening live
💡 Reminder: The loop is not the problem. DB roundtrips inside the loop are.
#Java #JPA #Hibernate #NPlus1 #Performance #SpringBoot #EPAM
"You asked for 1 list. Hibernate made 1001 queries."
• 1 query fetches all parents
• N queries fire — one per parent — to fetch their children
• With 1000 users → 1001 DB hits
• Root cause: LAZY loading triggered inside a loop
• Grows silently — works fine in dev, crashes in production
• How to spot it: same query shape repeating in logs with different IDs
• Enable spring.jpa.show-sql=true to see it happening live
💡 Reminder: The loop is not the problem. DB roundtrips inside the loop are.
#Java #JPA #Hibernate #NPlus1 #Performance #SpringBoot #EPAM
⚡ JPA — FetchType: LAZY vs EAGER
"EAGER loads children when parent is fetched. LAZY loads children only when you actually call them."
• LAZY = load on demand ✅
• EAGER = load everything immediately ⚠️
• Default: @OneToMany → LAZY, @ManyToOne → EAGER, @OneToOne → EAGER
• EAGER with large data = fetching millions of rows you didn't ask for
• Always prefer LAZY, load eagerly only when you explicitly need it
• Use FETCH JOIN or @EntityGraph to control loading, not EAGER
💡 Golden rule: Always LAZY. Load eagerly only when you choose to.
#Java #JPA #Hibernate #SpringBoot #Backend #EPAM
"EAGER loads children when parent is fetched. LAZY loads children only when you actually call them."
• LAZY = load on demand ✅
• EAGER = load everything immediately ⚠️
• Default: @OneToMany → LAZY, @ManyToOne → EAGER, @OneToOne → EAGER
• EAGER with large data = fetching millions of rows you didn't ask for
• Always prefer LAZY, load eagerly only when you explicitly need it
• Use FETCH JOIN or @EntityGraph to control loading, not EAGER
💡 Golden rule: Always LAZY. Load eagerly only when you choose to.
#Java #JPA #Hibernate #SpringBoot #Backend #EPAM
🔗 JPA — Cascade Types
"When something happens to the parent, the same happens to the child."
• PERSIST → save parent = save children
• REMOVE → delete parent = delete children
• MERGE → update parent = update children
• ALL → all of the above
• Cascade is NOT a DB feature — it's pure Hibernate logic
• DB has its own version: ON DELETE CASCADE (they're independent)
• Without cascade → you must save/delete every child manually
💡 Reminder: Cascade flows Parent → Child, not the other way around.
#Java #JPA #Hibernate #SpringBoot #Backend #EPAM
"When something happens to the parent, the same happens to the child."
• PERSIST → save parent = save children
• REMOVE → delete parent = delete children
• MERGE → update parent = update children
• ALL → all of the above
• Cascade is NOT a DB feature — it's pure Hibernate logic
• DB has its own version: ON DELETE CASCADE (they're independent)
• Without cascade → you must save/delete every child manually
💡 Reminder: Cascade flows Parent → Child, not the other way around.
#Java #JPA #Hibernate #SpringBoot #Backend #EPAM