Do you’ve got what it takes to ace a Java Interview? We are right here that will help you consolidate your expertise and ideas in Java. Before we begin the important Interview questions for core java, let’s understand what Java is all about.
What is Java?
Java is an object-oriented programming language designed specially to permit developers a platform of continuity. Java is different from other programming paradigms. With functional and logical programming, developers can keep or replace what they have already completed, rather than starting from scratch. The objects hold the code smartly prepared and smooth to alter whilst necessary.
Enroll now with Java training in Electronic City and get your certification in java to start your career with top MNC companies. The following article will cover all the popular Core Java interview questions.
1. What’s the difference between Association, Aggregation, and Composition?
Association
Composition
- The composition is referred to as an association between two objects when one class owns the other, but the other class cannot exist when it has been destroyed.
- For example, Human class is a composition of several body parts including the Hand, Leg, and Heart.
- When a human object dies, all its body parts ceased to exist meaningfully, this is one example of Composition.
- Composition is also very much preferred in object-oriented design over the inheritance.
- The composition represents the strongest form of relationship and association is the most general form.
- We can use a final keyword to represent Composition.
- Since in Composition, the Owner object expects the part object to be available and function, by making it final, you provide a guarantee that, when the Owner will be created, this part object will exist.
- If one object is part-of another object e.g. Engine is part of a Car, then the association or relationship between them is Composition.
- According to UML notation composition is a Filled diamond.
Example Program:
public class Car { private String make; private int year; private final Engine engine; public Car(String make, int year, int engineCapacity, int engineSerialNumber) { this.make=make; this.year=year; // we create the engine using parameters passed in Car constructor // only the Car instance has access to the engine instance // when Car instance is garbage collected, the engine instance is garbage collected too engine = new Engine(engineCapacity, engineSerialNumber); } public String getMake() { return make; } public int getYear() { return year; } public int getEngineSerialNumber() { return engine.getEngineSerialNumber(); } public int getEngineCapacity() { return engine.getEngineCapacity(); } } public class Engine { private int engineCapacity; private int engineSerialNumber; public Engine(int engineCapacity, int engineSerialNumber) { this.engineCapacity = engineCapacity; this.engineSerialNumber = engineSerialNumber; } public int getEngineCapacity() { return engineCapacity; } public int getEngineSerialNumber() { return engineSerialNumber; } } |
Aggregation
- Aggregation allows objects to exist without being part of the main object.
- For example, a player who is part of a Team can exist without a team and can become part of other teams as well.
- It is a lighter form of Composition, where a sub-part object can meaningfully exist without the main objects.
- On the other hand, if one object just has another object e.g. Car has a driver then it’s Aggregation.
- According to UML notation aggregation is an Empty diamond.
Example Program:
public class Car { private String make; private int year; private Engine engine; public Car(String make, int year, Engine engine) { this.make = make; this.year = year; // the engine object is created outside and is passed as an argument to the Car constructor // When this Car object is destroyed, the engine is still available to objects other than the Car // If the instance of Car is garbage collected the associated instance of Engine may not be garbage collected (if it is still referenced by other objects) this.engine = engine; } public String getMake() { return make; } public int getYear() { return year; } public Engine getEngine() { return engine; } } |
Class Diagram:
2. What’s polymorphism in Java?
- Polymorphism is the ability to create a variable, a function, or an object that has more than one form and we can create functions or reference variables, which behave differently in a different programmatic context.
- It is one of the major building blocks of object-oriented programming along with inheritance, abstraction, and encapsulation.
- An example of polymorphism is referring to the instance of a subclass, with a reference variable of super-class.
Object o = new Object(); //o can hold the reference of any subtype Object o = new String(); Object o = new Integer(); |
- In this, String is a subclass of the Object class. This is a basic example of polymorphism.
- According to the java language, there are two versions of polymorphism.
- Compile-time polymorphism (static binding or method overloading)
- Runtime polymorphism (dynamic binding or method overriding)
- Compile-time polymorphism (static binding or method overloading)
- During compile time it decides the flow of control.
- And then is achieved through Method overloading.
- In method overloading, an object can have two or more methods with the same name, BUT, their method parameters are different.
- These parameters may be different on two bases:
- Parameter type
- Parameter count
3. What are the advantages of JMS?
- Asynchronous communications :
- An application needs to notify another that an event has occurred with no need to wait for a response.
- Reliability:
- Ensure once-and-only-once message delivery. With your DB approach, you have to “reinvent the wheel”, especially if you have several clients reading the messages.
- Loose coupling:
- Not all systems can communicate using a database but JMS is pretty good to be used in heterogeneous environments with decoupled systems that can communicate over system boundaries.
- It is an implementation to handle the producer-consumer problem.
4. What’s Abstraction?
- Abstraction is a way to segregate implementation from an interface
- Interface and abstract class in Java are very useful to achieve Abstraction in Java.
- An interface or abstract class is something that is not concrete, something which is incomplete.
- To use an interface or abstract class, we need to extend and implement an abstract method with concrete behavior.
- One example of Abstraction is creating an interface to denote common behavior without specifying any details about how that behavior works e.g. You create an interface called Server which has the start() and stop() method.
- In the abstraction of the Server, Every server should have a way to start and stop and details may differ.
Abstraction Using an abstract class
- Abstract Class
- Java has a concept of abstract classes, and abstract methods but a variable cannot be abstract in Java.
- An abstract method in Java doesn’t have a body, it’s just a declaration. To use an abstract method, you need to override that method in the sub-class.
- When you know something needs to be there but are not sure how exactly it should look like. E.g. By creating a class called Vehicle, there should be methods like start () and stop () but don’t know how that start and stop method should work, because every vehicle can have a different start and stop mechanism e.g. some can be started by kicking or some can be by pressing buttons
- So the implementation of those start() and stop() methods should be left to their concrete implementation e.g. Scooter, MotorBike, Car, etc
- In Java, you cannot create an instance of the abstract class using the new operator, it’s a compiler error. Though abstract class can have a constructor.
- In both class and method, the abstract is the keyword.
- A class automatically becomes an abstract class when any of its methods are declared as abstract.
- If a class extends an abstract class or interface it has to provide implementation to all its abstract methods to be a concrete class. Alternatively, this class can also be abstract.
Abstraction Using Interface
- Interface
- In Java Interface is another way of providing abstraction, Interfaces are by default abstract and only contain public, static, final constant, or abstract methods.
- It’s very common interview question is where should we use abstract class and where should we use Java Interfaces in my view this is important to understand to design better Java application, you can go for java interface if you only know the name of methods your class should have e.g. for Server it should have start () and stop () method but we don’t know how exactly these start and stop method will work.
- If you know some of the behavior while designing class and that would remain common across all subclasses add that into an abstract class.
- An interface like the Runnable interface is a good example of abstraction in Java which is used to abstract tasks executed by multiple threads. Callable is another good abstract of a task that can return value.
5. Difference between Abstract class and interface?
Abstract Class
- An abstract class can extend only one class or one abstract class at a time and another concrete (regular) class or abstract class.
- It has both abstract and concrete methods.
- In abstract class keyword, “abstract” is mandatory to declare a method as an abstract.
- It may have protected and public abstract methods and static, final, or static final variables with any access specifiers.
Interface
- An interface can extend any number of interfaces at a time and only extend another interface.
- It can have only abstract methods and public abstract methods.
- In an interface keyword, “abstract” is optional to declare a method as an abstract.
- It may only have public static final (constant) variable.
Example Program for abstract class and interface:
//Creating interface that has 4 methods interface A{ void a();//bydefault, public and abstract void b(); void c(); void d(); } //Creating abstract class that provides the implementation of one method of A interface abstract class B implements A{ public void c(){System.out.println(“I am C”);} } //Creating subclass of abstract class, now we need to provide the implementation of rest of the methods class M extends B{ public void a(){System.out.println(“I am a”);} public void b(){System.out.println(“I am b”);} public void d(){System.out.println(“I am d”);} } //Creating a test class that calls the methods of A interface class Test5{ public static void main(String args[]){ A a=new M(); a.a(); a.b(); a.c(); a.d(); }} |
6. What’s Encapsulation?
We can achieve encapsulation in Java by declaring all the class fields as private. Then provide the public getter and setter methods for them.
Example program for Encapsulation
package com.journaldev.oops.encapsulation; public class Data { private int id; private String value; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } |
7. Difference between Domain Model and Data model?
Domain Model
- A domain model is generally implemented as an object model within a layer that uses a lower-level layer for persistence and “publishes” an API to a higher-level layer to gain access to the data and behavior of the model.
- In the Unified Modeling Language (UML), a class diagram is used to represent the domain model.
Data Model
- Data models define how the logical structure of a database is modeled and they are fundamental entities to introduce abstraction in a DBMS. It defines how data is connected to each other and how they are processed and stored inside the system.
- In the very first data model, all the data used is to be kept in the same plane called flat data models. Earlier data models were not so scientific, hence they were prone to introduce lots of duplication and update anomalies.
8. What is Microservices Architecture?
- Microservices is a way of breaking large software projects into smaller, independent, and loosely coupled modules.
- Individual modules are responsible for highly defined and discrete tasks and communicate with other modules through simple, universally accessible APIs.
- A microservice architectural style is an approach to developing a single application as a suite of small services, each running in its process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and are independently deployable by fully automated deployment machinery.
- Microservices came about to help solve the frustrations developers were having with large applications that require change cycles to be tied together. In a monolithic application, any small change or update required building and deploying an entirely new application. This inevitably means any application development entails a certain amount of planning, preparation, time, and, potentially, money.
- Microservices, on the other hand, require little centralized management. Microservices applications are independently deployable and scalable. They enhance business capabilities with less planning and production than monoliths.
- In a microservices architecture, each service runs a unique process and usually manages its own database.
Advantages:
- Easily deployed.
- Require less production time.
- Can scale quickly.
- Reused in different projects.
- Work well with containers, such as Docker.
- Complement cloud activities.
Disadvantages:
- Potentially too granular.
- Latency during heavy use.
- Testing can be complex.
9. What are the methods in Object class?
- equals (Object obj)
- Checks whether the obj object is equal to the object on which the equals method is called.
- hashCode()
- hashCode() is used for the HashTable . It returns the hash value of the object.
- getClass()
- It returns the runtime class object.
- clone()
- It creates and returns a copy of the object.
- notify()
- It will wake up the thread waiting for the object’s monitor.
- notifyAll()
- It will wake up all the threads that are waiting for the object’s monitor.
- toString()
- It will return the string representation of the object.
- wait()
- This method causes the current thread to place itself in the wait set for this object and then relinquish any and all synchronization claims on this object.
10. How cloning method works in Java?
- The clone() is a tricky method from java. lang. To create a copy of an object in Java Object class is used.
- An object which is returned by the clone() method is known as a clone of the original instance.
- A clone object should follow basic characteristics e.g. a.clone() != a, which means original and clone are two separate object in Java heap, a.clone().getClass() == a.getClass() and clone.equals(a), which means clone is exact copy of original object.
- java.lang.Object provides default implementation of clone() method in Java. It’s declared as protected and native in Object class, so implemented in native code. Since its convention to return clone() of an object by calling super.clone() method, any cloning process eventually reaches to java.lang.Object clone() method.
- This method first checks if the corresponding object implements the Cloneable interface, which is a marker interface. If that instance doesn’t implement Cloneable then it throws CloneNotSupportedException in Java, a checked exception, which is always required to be handled while cloning an object.
- If an object passes this check, then java. lang. Object’s clone() method creates a shallow copy of the object and returned it to the caller.
- In that case, both the original object and copy of the object will point to the same object in the heap. You can prevent this by using the technique known as deep cloning, in which each mutable field is cloned separately. In short, here is how the clone method works in Java:
- Any class calls the clone() method on an instance, which implements Cloneable and overrides the protected clone() method from the Object class, to create a copy.
- The call to clone() method on Rectangle is delegated to super. clone(), which can be a custom superclass or by default java. lang.Object.
Example Program for Clonig Method
import org.apache.log4j.Logger; /** * Simple example of overriding clone() method in Java to understand How Cloning of * Object works in Java. * * @author */ public class JavaCloneTest { private static final Logger logger = Logger.getLogger(JavaCloneTest.class); public static void main(String args[]) { Rectangle rec = new Rectangle(30, 60); logger.info(rec); Rectangle copy = null; try { logger.info(“Creating Copy of this object using Clone method”); copy = rec.clone(); logger.info(“Copy ” + copy); } catch (CloneNotSupportedException ex) { logger.debug(“Cloning is not supported for this object”); } //testing properties of object returned by clone method in Java logger.info(“copy != rec : ” + (copy != rec)); logger.info(“copy.getClass() == rec.getClass() : ” + (copy.getClass() == rec.getClass())); logger.info(“copy.equals(rec) : ” + copy.equals(rec)); //Updating fields in original object rec.setHeight(100); rec.setWidth(45); logger.info(“Original object :” + rec); logger.info(“Clonned object :” + copy); } } public class Rectangle implements Cloneable{ private int width; private int height; public Rectangle(int w, int h){ width = w; height = h; } public void setHeight(int height) { this.height = height; } public void setWidth(int width) { this.width = width; } public int area(){ return widthheight; } @Override public String toString(){ return String.format(“Rectangle [width: %d, height: %d, area: %d]”, width, height, area()); } @Override protected Rectangle clone() throws CloneNotSupportedException { return (Rectangle) super.clone(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Rectangle other = (Rectangle) obj; if (this.width != other.width) { return false; } if (this.height != other.height) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 47 hash + this.width; hash = 47 hash + this.height; return hash; } } |
Output: 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – Rectangle [width: 30, height: 60, area: 1800] 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – Creating Copy of this object using Clone method 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – Copy Rectangle [width: 30, height: 60, area: 1800] 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – copy != rec : true 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – copy.getClass() == rec.getClass() : true 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – copy.equals(rec) : true 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – Original object :Rectangle [width: 45, height: 100, area: 4500] 2013–05–20 23:46:58,882 0 [main] INFO JavaCloneTest – Cloned object :Rectangle [width: 30, height: 60, area: 1800] |
Deep Cloning with Mutable field
- Since the default implementation of the clone() method only does a shallow copy of objects, it can create issues, if the original object contains mutable objects or Collection classes.
- In our example, we have a class called Programmer, with String name, int age, and List of Certifications.
- When we override the clone() method inside the Programmer class, we need to explicitly take care of this List, otherwise, both original and cloned objects will point to the same Collection in the Java heap, which means, any change e.g. adding a new Certification in the original object will also reflect in a cloned object or vice-versa.
- Since an object should be independent of its clone, we need to fix this issue by applying deep cloning techniques.
- To fix this, we reassign certification fields of the clone object by explicitly copying data, as shown in the following line :
clone.certifications = new ArrayList(certifications); //deep copying |
Example Program for Deep Cloning with Mutable fiel
import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /* * Java program to show how to override clone method for deep copying. * This example includes a mutable field in class to be cloned to show how you deal with * practical classes which contain both immutable and mutable fields. * */ public class CloneTest { private static final Logger logger = LoggerFactory.getLogger(Cloneclass); public static void main(String args[]) { Programmer javaguru = new Programmer(“John”, 31); javaguru.addCertificates(“OCPJP”); javaguru.addCertificates(“OCMJD”); javaguru.addCertificates(“PMP”); javaguru.addCertificates(“CISM”); logger.debug(“Real Java Guru : {}”, javaguru); Programmer clone = javaguru.clone(); logger.debug(“Clone of Java Guru : {}”, clone); //let’s add another certification to java guru javaguru.addCertificates(“Oracle DBA”); logger.debug(“Real Java Guru : {}”, javaguru); logger.debug(“Clone of Java Guru : {}”, clone); } } class Programmer implements Cloneable{ private static final Logger logger = LoggerFactory.getLogger(Programmer.class); private String name; private int age; private List certifications ; public Programmer(String name, int age) { this.name = name; this.age = age; this.certifications = new ArrayList(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void addCertificates(String certs){ certifications.add(certs); } @Override public String toString() { return String.format(“%s, %d, Certifications: %s”, name, age, certifications.toString()); } @Override protected Programmer clone() { Programmer clone = null; try{ clone = (Programmer) super.clone(); clone.certifications = new ArrayList(certifications); //deep copying }catch(CloneNotSupportedException cns){ logger.error(“Error while cloning programmer”, cns); } return clone; } } |
The output of Shallow copying : [main] DEBUG CloneTest – Real Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM] [main] DEBUG CloneTest – Clone of Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM] [main] DEBUG CloneTest – Real Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM, Oracle DBA] [main] DEBUG CloneTest – Clone of Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM, Oracle DBA] After deep copying collection: [main] DEBUG CloneTest – Real Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM] [main] DEBUG CloneTest – Clone of Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM] [main] DEBUG CloneTest – Real Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM, Oracle DBA] [main] DEBUG CloneTest – Clone of Java Guru: John, 31, Certifications: [OCPJP, OCMJD, PMP, CISM] |
Best Practices to follow while overriding clone method in Java
- Return Subclass from clone() method, instead of returning java.lang.Object
- Use deep copy, if your class contains any mutable field.
- Don’t throw CloneNotSupportedException.
11. Difference between Shallow and Deep Copy in Java?
- When we call Object. clone(), this method performs a shallow copy of the object, by copying data field by field, and if we override this method and by convention first call super.clone(), and then modify some fields to “deep” copy, then we get a deep copy of the object. This modification is done to ensure that the original and cloned objects are independent of each other.
- In shallow copy main or parent object is copied, but they share the same fields or children if fields are modified in one parent object other parent fields have automatic same changes occur, but in deep copy, this is not the case.
- If our parent object contains only primitive value then the shallow copy is good for making a clone of any object because in the new object value is copied but if the parent object contains any other object then only reference value is copied in new parent object and both will point to the same object so in that case according to our need we can go for a deep copy.
- Deep copy is expensive as compared to shallow copy in terms of object creation because it involves recursive copying of data from other mutable objects, which are part of the original object.
12. How garbage collection works internally in Java?
- Heap in java creates every Java object.
- What is GC (Garbage collection) process in java?
- GC (Garbage collection) is the process by which JVM cleans objects (unused objects) from the heap to reclaim heap space in java.
- What is Automatic Garbage Collection in JVM heap memory in java?
- Automatic garbage collection is the process of Identifying objects which are in use in java heap memory and which objects are not in use in java heap memory and deleting the unused objects in java heap memory.
- How do identify objects which are in use in java heap memory?
- Objects in use (or referenced objects) are those objects which are still needed by the java program, some part of the java program is still pointing to that object.
- Which objects are NOT in use in java heap memory?
- Objects not in use (or unreferenced objects or unused objects) are those objects which are not needed by the java program, no part of the java program is pointing to that object. So, these unused objects can be cleaned in the garbage collection process and memory used by an unreferenced object can be reclaimed.
- GC (Garbage collection) process automatically clears objects from the heap to reclaim heap space. You just need to specify the type of garbage collector type you want to use at the JVM startup.
- Gc (garbage collector) calls finalize method for garbage collection. Finalize method is called only once by the garbage collector for an object in java.
- Daemon threads are low-priority threads that run intermittently in the background for doing garbage collection (GC) in java.
- We can force early GC (garbage collection) in java by using the following methods
System.gc(); Runtime.getRuntime().gc(); System.runFinalization(); Runtime.getRuntime().runFinalization(); |
- By calling these methods JVM runs the finalize () methods of any objects pending finalization i.e. objects which have been discarded but their finalize method is yet to be run. After finalize method is executed JVM reclaims space from all the discarded objects in the java.
- Note: Calling these methods does not guarantee that it will immediately start performing garbage collection.
- Finalize method execution is not assured – We must not override finalize method to write some critical code of application because methods execution is not assured. Writing some critical code in finalize method and relying on it may make the application go wrong in java.
- Dealing with OutOfMemoryError in java.
- WeakhashMap in java – java.util.WeakHashMap is a hash table-based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will be automatically removed by the garbage collector when its key is no longer in ordinary use.
- An object which is set explicitly set to null becomes eligible for GC (garbage collection) in java.
Example 1 String s=”abc”; //s is currently not eligible for gc (garbage collection) in java. s = null; //Now, s is currently eligible for gc (garbage collection) in java. Example 2 List list =new ArrayList(); //list is currently not eligible for gc (garbage collection). list = null; //Now, list is currently eligible for gc (garbage collection). |
- The difference in garbage collection in C/C++ and Java (Hint: In terms of memory allocation and deallocation of objects)?
- In java garbage collection (memory allocation and deallocation of objects) is an automatic process.
- But, In C and C++ memory allocation and deallocation of objects) is a manual process.
- All the variables declared inside the block become eligible for GC (garbage collection) when we exit that block (As the scope of those variables is only that block) in java.
Example of garbage collection while using block in java :
class MyClass { public static void main(String[] args) { boolean var = false; if (var) { // begin block 1 int x = 1; // x is declared inside block //………. //code inside a block… //………. } // end block 1 //And now x is eligible for gc (garbage collection) else { // begin block 2 int y = 1; //………. //code inside a block… //………. } // end block 2 //And now y is eligible for gc (garbage collection) } } |
Terms frequently used in Garbage Collection (GC) in java
- What is Throughput in GC(garbage collection) in java?
- In short, Throughput is the time not spent in garbage collection (GC) ( in percent).
- Throughput focuses on maximizing the amount of work by an application in a specific period of time.
- Examples of how throughput might be measured include
- The number of transactions completed in a given time.
- The number of jobs that a batch program can complete in an hour.
- The number of database queries that can be completed in an hour.
- What are pauses in GC(garbage collection) in java?
- Pauses are application pauses i.e. when the application doesn’t give any response because of garbage collection (GC).
JVM Heap memory with diagram in java
- JVM Heap memory (Hotspot heap structure) in java consists of the following elements
- Young Generation
- Eden,
- S0 (Survivor space 0)
- S1 (Survivor space 1)
- Old Generation (Tenured)
- Permanent Generation.
- So, JVM Heap memory (Hotspot heap structure) is divided into three parts Young Generation, Old Generation (tenured), and Permanent Generation.
- The young Generation is further divided into Eden, S0 (Survivor space 0), and S1 (Survivor space 1).
- Young Generation
GARBAGE COLLECTION (Minor and major garbage collection) in JVM Heap memory (i.e. in young, old, and permanent generations)
Young Generation (Minor garbage collection occurs in YoungGeneration)
- New objects are allocated to the Young generation.
- Minor garbage collection occurs in Young Generation.
- When minor garbage collection?
- When the young generation fills up, this causes a minor garbage collection.
- All the unreferenced (dead) objects are cleaned up by the young generation.
- When objects are moved from the young to the old generation in the JVM heap?
- Some of the objects which aren’t cleaned up survive in the young generation and get aged. Eventually, such objects are moved from the young to the old generations.
- What is Stop the World Event?
- Minor garbage collections are called Stop the World events.
- All the non-daemon threads running in the application are stopped during minor garbage collections (i.e. the application stops for while).
- Daemon threads perform minor garbage collection. (Daemon threads are low-priority threads that run intermittently in the background for garbage collection).
Old Generation or (tenured generation) – (Major garbage collection occurs in Old Generation)
- The Old Generation is used for storing the long-surviving aged objects (Some of the objects which aren’t cleaned up survive in the young generation and gets aged. Eventually such objects are moved from young to old generation).
- Major garbage collection occurs in Old Generation.
- At what time (or what age) objects are moved from the young to the old generation in the JVM heap?
- There is some threshold set for young generation object and when that age is met, the object gets moved to the old generation during GC(garbage collection) in java.
- What is a major garbage collection in java?
- When the old generation fills up, this causes a major garbage collection. The old generation cleans the Objects.
- The major collection is much slower than the minor garbage collection in the JVM heap because it involves all live objects.
- Major GC(garbage collections) in responsive applications in java?
- Major garbage collections should be minimized for responsive applications because applications must not be stopped for long.
- Optimizing Major GC(garbage collections) in responsive applications in java?
- The selection of appropriate garbage collectors for the old generation space affects the length of the “Stop the World” event for a major garbage collection.
Permanent Generation or (Permgen) – (full garbage collection occurs in permanent generation in java).
- Permanent generation Space contains metadata required by JVM to describe the classes and methods used in the application.
- A full garbage collection in java includes permanent generation.
- The permanent generation space is populated at runtime by JVM based on classes in use in the application.
- The permanent generation space also contains Java SE library classes and methods in java.
JVM garbage collects those classes when classes are no longer required and space may be needed for other classes in Java.
Most important VM (JVM) PARAMETERS in JVM Heap memory
- -Xms: Xms is the minimum heap size that is allocated at the initialization of JVM.
- Example: java -Xms512m MyJavaProgram
- It will set the minimum heap size of JVM to 512 megabytes.
- -Xmx: Xmx is the maximum heap size that JVM can use.
- Example: java -Xmx512m MyJavaProgram
- It will set the maximum heap size of JVM to 512 megabytes.
- Young Generation(VM PARAMETERS for Young Generation)
- -Xmn: -Xmn sets the size of the young generation.
- java -Xmn512m MyJavaProgram
- -XX:NewRatio: NewRatio controls the size of the young generation.
- -XX: NewRatio=3 means that the ratio between the young and old/tenured generation is 1:3.
- In other words, the combined size of the Eden and survivor spaces will be one-fourth of the total heap size.
- -XX: NewSize – NewSize is the minimum size of the young generation which is allocated at initialization of JVM.
- Note: If you have specified -XX: NewRatio then the minimum size of the young generation is allocated automatically at initialization of JVM.
- -XX: MaxNewSize – MaxNewSize is the maximum size of a young generation that JVM can use.
- -XX: SurvivorRatio : (for survivor space)
- SurvivorRatio can be used to tune the size of the survivor spaces, but this is often not as important for performance
- -Xmn: -Xmn sets the size of the young generation.
- Old Generation (tenured) – (VM PARAMETERS for Old Generation)
- -XX:NewRatio: NewRatio controls the size of the young and old generations.
- Permanent Generation (VM PARAMETERS for Permanent Generation)
- -XX:PermSize: It’s is the initial value of Permanent Space that is allocated at the startup of JVM.
Different Garbage collectors in detail
Serial collector / Serial GC
- The serial collector is also called Serial GC (Garbage collector) in java.
- Serial GC (Garbage collector) is designed for single-threaded environments in java and is rarely used in java.
- In Serial GC (Garbage collector), both minor and major garbage collections are done serially by one thread (using a single virtual CPU) in java.
- Serial GC (Garbage collector) uses a mark-compact collection method. This method moves older memory to the beginning of the heap so that new memory allocations are made into a single continuous chunk of memory at the end of the heap. This compacting of memory makes it faster to allocate new chunks of memory to the heap in java.
- The serial garbage collector is the default for client-style machines in Java SE 5 and 6.
- When to Use the Serial GC (garbage Collector) in java?
- The Serial GC is the garbage collector of choice for most applications that do not have low pause time requirements and run on client-style machines. It takes advantage of only a single virtual processor for garbage collection work in java.
- Serial GC (garbage collector) is also popular in environments where a high number of JVMs are run on the same machine. In such environments when a JVM does a garbage collection it is better to use only one processor to minimize the interference on the remaining JVMs in java.
- VM (JVM) option for enabling serial GC (garbage Collector) in java?
- -XX:+UseSerialGC
- Example of Passing Serial GC in Command-Line for starting the jar
- java -Xms256m -Xmx512m -XX:+UseSerialGC -jar d:\MyJar.jar
Throughput GC (Garbage collector) or Parallel collector in java
- The throughput garbage collector is the default garbage collector for JVM in java.
- It may use multiple threads to execute a minor collection and so reduces the serial execution time of the application in java and also uses a parallel version of the young generation garbage collector in java.
- It is similar to the serial garbage collector but uses multiple threads to do the minor collection in java.
- The tenured generation collector is the same as the serial garbage collector in java.
- When to Use the Throughput GC (Garbage collector) in java?
- The Throughput garbage collector should be used when an application can afford low pauses in java.
- And the application is running on a host with multiple CPUs (to derive advantage of using multiple threads for garbage collection) in java.
- VM (JVM) option for enabling throughput GC (Garbage collector) in java?
- -XX:+UseParallelGC
- Example of using throughput collector in Command-Line for starting jar
- java -Xms256m -Xms512m -XX:+UseParallelGC -jar d:\MyJar.jar
- With this VM (JVM) option you get a
- The multi-threaded young generation garbage collector in java,
- the single-threaded old-generation garbage collector in java and
- Single-threaded compaction of the old generation in java.
- VM (JVM) option for enabling throughput collector with n number of threads in java?
- -XX:ParallelGCThreads=<numberOfThreads>
Incremental low pause garbage collector (train low pause garbage collector) in java
- The incremental low pause garbage collector is not used these days in java. An incremental low pause collector was used in Java 4.
Concurrent Mark Sweep (CMS) Collector / concurrent low pause collector in java
- Concurrent Mark Sweep (CMS) collector collects the old/tenured generation in java and minimizes the pauses by doing most of the garbage collection work concurrently with the application threads in java.
- Concurrent Mark Sweep (CMS) Collector on live objects?
- Concurrent Mark Sweep (CMS) Collector does not copy or compact the live objects. A garbage collection is done without moving the live objects. If fragmentation becomes a problem, allocate a larger heap in java.
- When to Use the Concurrent Low Pause Collector in java?
- It should be used if your applications that require low garbage collection pause times in java and when your application can afford to share processor resources with the garbage collector while the application is running in java.
- Concurrent Low Pause Collector is beneficial to applications that have a relatively large set of long-lived data (a large tenured generation) and run on machines with two or more processors in java
- Examples when to use Concurrent Mark Sweep (CMS) collector / concurrent low pause collector should be used for?
- Example 1 – Desktop UI application that responds to events,
- Example 2 – Web server responding to a request and
- Example 3 – Database responding to queries.
- VM (JVM) option for enabling Concurrent Mark Sweep (CMS) Collector in java?
- -XX:+UseConcMarkSweepGC
- Example of using Concurrent Mark Sweep (CMS) collector / concurrent low pause collector in Command-Line for starting jar
- java -Xms256m -Xms512m -XX:+UseConcMarkSweepGC -jar d:\MyJar.jar
- Heap Structure for CMS garbage Collector?
- CMS garbage collectors divide the heap into three sections: young generation, old generation, and permanent generation of fixed memory size.
- The young Generation is further divided into Eden, S0 (Survivor space 0), and S1 (Survivor space 1).
- Detailed Steps in GC (garbage collection) cycle in Concurrent Mark Sweep (CMS) Collector / concurrent low pause garbage collector in java?
- Young Generation GC (garbage Collection) in java
- Live objects are copied from the Eden space and survivor space to the other survivor space.
- Any older objects that have reached their aging threshold are promoted to the old generation
- After Young generation GC (garbage Collection) in java
- After a young GC, the Eden space and one of the survivor spaces are cleared.
- Promoted objects (older objects that have reached their aging threshold in young GC) are available in the old generation.
- Old Generation GC (garbage Collection) with CMS in java
- Initial mark phase – (First pause happens/ stop the world event ) – mark live/reachable objects (Example – objects on thread stack, static objects, etc.) and elsewhere in the heap (Example – the young generation).
- The concurrent marking phase – (No pause phase ) – finds live objects while the application continues to execute.
- Remark – (Second pause happens/ stop the world events) – It finds objects that were missed during the concurrent marking phase due to the concurrent execution of the application threads.
- Sweep phase – do the concurrent sweep, and memory is freed up.
- Objects that were not marked in the previous phase are deallocated in place.
- There is no compaction
- Unmarked objects are equal to Dead Objects.
- Young Generation GC (garbage Collection) in java
Reset phase – do the concurrent reset.
G1 Garbage Collector (or Garbage First) in java
- A G1 garbage collector was introduced in Java 7 and designed to replace the CMS collector (Concurrent Mark-Sweep garbage Collector).
- It is parallel, the G1 garbage collector is concurrent, and the G1 garbage collector is incrementally compacting the low-pause garbage collector in java.
- The G1 garbage collector has a much better layout than the other garbage collectors like serial, throughput, and CMS garbage collectors in java compacts sufficiently to completely avoid the use of fine-grained free lists for allocation and instead relies on regions.
- They allow customizations by allowing users to specify pause times and limit GC pause times and maximize throughput.
- VM (JVM) option for enabling G1 Garbage Collector (or Garbage First) in java?
- -XX:+UseG1GC
- Example of using G1 Garbage Collector in Command-Line for starting jar>
- java -Xms256m -Xms512m -XX:+UseG1GC -jar d:\MyJar.jar
- G1(Garbage First) collector functioning
- CMS garbage collectors divide the heap into three sections: young generation, old generation, and permanent generation of fixed memory size.
- All memory objects end up in one of these three sections.
- The G1 collector takes a different approach than the CMS garbage collector in partitioning java heap memory.
- The heap is split/partitioned into many fixed-sized regions (Eden, survivor, old generation regions), but there is not a fixed size for them. This provides greater flexibility in memory usage.
- When to use the G1 garbage collector?
- G1 must be used when applications that require large heaps with limited GC latency.
- Example – Application that requires
- heaps around 5-6GB or larger and
- pause time required below 0.5 seconds
- The G1(Garbage First) collector working Step by Step
- G1(Garbage First) garbage collector Heap Structure
- The heap is split/partitioned into many fixed-sized regions (Eden, survivor, old generation regions), but there is not a fixed size for them. This provides greater flexibility in memory usage.
- Each region’s size is chosen by JVM at startup.
- Generally, the heap is divided into 2000 regions by JVM varying in size from 1 to 32Mb.
- G1(Garbage First) garbage collector Heap Allocation
- As mentioned above there is the following region in heap Eden, survivor, and old generation region. Also, Humongous and unused regions are there in heaps.
- Young Generation in G1 garbage collector
- Generally, a heap is divided into 2000 regions by JVM.
- The minimum size of the region can be 1Mb and
- The maximum size of the region can be 32Mb.
- Regions are not required to be contiguous like CMS garbage collectors.
- Young GC in G1 garbage collector
- Live objects are copied or moved to survivor regions.
- If an object’s aging threshold is met it gets promoted to old generation regions.
- It is STW (stop the world) event. Eden size and survivor size are calculated for the next young GC.
- The young GC is done parallel using multiple threads.
- End of a Young GC with the G1 garbage collector
- At this stage, Live objects have been evacuated (copied or moved) to survivor regions or old generation regions.
- Old Generation Collection with G1 garbage collector
- The G1 collector is a low pause collector for old-generation objects.
- Initial Mark –
- It is STW (stop the world) event.
- With G1, it is piggybacked on a normal young GC. Mark survivor regions (root regions) which may have references to objects in the old generation.
- Root Region Scanning –
- Scan survivor regions for references to the old generation.
- This happens while the application continues to run. The phase must be completed before a young GC can occur.
- Concurrent Marking –
- Find live objects over the entire heap.
- This happens while the application is running.
- This phase can be interrupted by young generation garbage collections.
- Remark (Stop the World Event) –
- Completes the marking of live objects in the heap.
- Uses an algorithm called snapshot-at-the-beginning (SATB) which is much faster than the algorithm used in the CMS collector.
- Cleanup (Stop the World Event and Concurrent) –
- Performs accounting on live objects and completely free regions. (Stop the world)
- The young generation and old generation are reclaimed at the same time
- Old generation regions are selected based on their liveness.
- Scrubs the Remembered Sets. (Stop the world)
- Reset the empty regions and return them to the free list. (Concurrent)
- G1(Garbage First) garbage collector Heap Structure
13. JVM (java virtual machine) in detail in java?
- What is JVM (Java virtual machine) in java?
- JVM is the virtual machine on which java code executes.
- It is responsible for converting byte code into machine-specific code.
- JVM (Java Virtual Machine) Architecture?
- JVM (Java Virtual Machine) consists of a Class Loader Subsystem, Runtime Data Areas, and Execution Engine.
- Class Loader Subsystem
- Classloader is a subsystem of JVM and is used to load class files.
- It verifies the class file using the byte code verifier. Class file will only be loaded if it is valid.
- Runtime Data Areas
- Method Area
- The method area is also called a class area.
- Method area stores data for every class like fields, constant pool, method’s data, and information.
- Heap
- Heap is a place where all objects are stored in JVM (java virtual machine).
- It even contains arrays because arrays are objects.
- Java Threads (Java thread Stacks)
- You must know that every thread has its stack.
- How stack frames are created when the thread calls a new method?
- As we know every thread has its stack. Whenever Thread. start() method is called new stack frame is created and it is pushed on top of that thread’s stack.
- What does the thread stack contain?
- The stack contains
- All the local variables,
- All the parameters,
- All the return address
- Does stack store/contains object OR what stack doesn’t contain?
- Stack never stores objects, but it stores object references.
- Program counter registers (PC Registers)
- Program counter registers contain the address of instructions currently being executed and the address of the next instruction as well.
- Native internal Threads (Native thread stack )
- The native internal threads area contains all the information related to the native platform.
- Example – If we are running JVM (java application) on windows, it will contain all information related to the native platform i.e. windows.
- If we are running JVM (java application) on Linux, it will contain all information related to the native platform i.e. Linux.
- Method Area
- Execution Engine
- Execution Engine contains JIT (Just In Time) Compiler and Garbage collector compiler. Execution Engine also contains an Interpreter.
- JIT(Just In Time) compiler
- JIT compiler compiles bytecodes to machine code at run time and improves the performance of Java applications.
- JIT Compiler internal working
- JIT compilation does require processor time and memory usage. When the JVM first starts up, lots of methods are called. Compiling all of these methods might can affect startup time significantly, though the program ultimately may achieve good performance.
- Methods are not compiled when they are called the first time. For every method JVM maintains a call count, which is incremented every time the method is called. The methods are interpreted by JVM until the call count does not exceed the JIT compilation threshold (The JIT compilation threshold improves performance and helps the JVM to start quickly. The threshold has been selected carefully by java developers to obtain optimal performances. The balance between startup times and long-term performance is maintained).
- Therefore, very frequently used methods are compiled as soon as JVM has started, and less used methods are compiled later.
- How does JIT improve the performance of the Most frequently used methods?
- After a method is compiled, its call count is reset to zero, and subsequent calls to the method increment its call count. When the call count of a method reaches a JIT recompilation threshold, the JIT compiler compiles the method a second time, applying more optimizations as compared to optimizations applied in the previous compilation. This process is repeated until the maximum optimization level is reached. Most frequently used methods are always optimized to maximize the performance benefits of using the JIT compiler.
- Example –
- Let’s say the JIT recompilation threshold = 2
- After a method is compiled, its call count is reset to zero, and subsequent calls to the method increment its call count. When the call count of a method reaches a 2 (i.e. JIT recompilation threshold), the JIT compiler compiles the method a second time, applying more optimizations as compared to optimizations applied in the previous compilation.
- Garbage Collector
- Garbage Collector Garbage collection is the process by which JVM clears objects (unused objects) from the heap to reclaim heap space.
- Interpreter
- An interpreter is responsible for reading the bytecode and then executing the instructions.
- Native method libraries
- A native method interface is an interface that connects JVM with the native method libraries for executing native methods.
- Example of Native method libraries>
- If we are running JVM (java application) on windows, then the Native method interface(window method interface) will connect JVM with the window methods libraries(native method libraries) for executing window methods (native methods).
- You must know about JNI, What is Java Native Interface(JNI)?
- You may write your application purely in java but there are certain situations where java code might need to meet your requirement.
- Programmers use the JNI (Java Native Interface) to write the Java native methods when an application cannot be written purely in Java.
- JVM components related to performance
- Three components Heap, JIT (Just In Time) Compiler, and Garbage collector are related to JVM’s performance tuning.
- Heap and Garbage collector for tuning JVM’s performance >
- Heap stores every object. The garbage collector manages the heap at JVM initialization.
- There are many VM (JVM) options for Increasing and decreasing the heap size for managing objects for best performance.
- Select the different garbage collectors depending on your requirement.
- JIT (Just In Time) Compiler for tuning JVM’s performance >
- JIT compiler compiles bytecodes to machine code at run time and improves the performance of Java applications.
- The JIT (Just In Time) Compiler is rarely needed in newer versions of JVM tuning.
- How is the Java platform an independent language?
- Once source code (i.e. .java file) is compiled on one platform(bytecode is formed). That bytecode can be executed (interpreted) on any other platform running a JVM.
- Every platform has a different JVM implementation.
- JVM for the windows platform is different from JVM for the Linux platform.
14. How to override equals and hashCode in Java?
Object classes defines the methods HashCode() and equals(). For this reason, all java objects inherit a default implementation of these methods.
Usage of hashCode() and equals()
- hashCode() method is used to get a unique integer for a given object. This integer is used for determining the bucket location when this object needs to be stored in some HashTable-like data structure. By default, the Object’s hashCode() method returns an integer representation of the memory address where the object is stored.
- equals() method, as the name suggests, is used to simply verify the equality of two objects. The default implementation simply checks the object references of two objects to verify their equality.
Overriding the default behavior
- Everything works fine until you do not override any of these methods in your classes. But, sometimes application needs to change the default behavior of some objects.
- Let’s take an example where your application has an Employee object. Let us create a minimal possible structure for the Employee class::
public class Employee { private Integer id; private String firstname; private String lastName; private String department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getDepartment() { return department; } public void setD epartment(String department) { this.department = department; } } |
- Above Employee class has some very basic attributes and there accessor methods. Now consider a simple situation where you need to compare two employee objects.
public class EqualsTest { public static void main(String[] args) { Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Prints false in console System.out.println(e1.equals(e2)); } } |
- No prize for guessing. The above method will print “false“. But, is it correct after knowing that both objects represent the same employee. In a real-time application, this must return true.
- To achieve correct behavior, we need to override equals method as below:
public boolean equals(Object o) { if(o == null) { return false; } if (o == this) { return true; } if (getClass() != o.getClass()) { return false; } Employee e = (Employee) o; return (this.getId() == e.getId()); } |
- Add this method to your Employee class, and EqualsTest will start returning “true“.
- If we are not yet done, The above-modified Employee class needs to test again in a different way.
import java.util.HashSet; import java.util.Set; public class EqualsTest { public static void main(String[] args) { Employee e1 = new Employee(); Employee e2 = new Employee(); e1.setId(100); e2.setId(100); //Prints ‘true’ System.out.println(e1.equals(e2)); Set<Employee> employees = new HashSet<Employee>(); employees.add(e1); employees.add(e2); //Prints two objects System.out.println(employees); } } |
- The above class prints two objects in the second print statement. If both employee objects have been equal, in a Set that stores unique objects, there must be only one instance inside HashSet, after all both objects refer to the same employee. What is it we are missing??
- We are missing the second important method hashCode(). As java docs say if you override the equals() method then you must override the hashCode() method. So let us add another method to our Employee class.
@Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + getId(); return result; } |
- Once the above method is added to the Employee class, the second statement starts printing only a single object in the second statement, thus validating the true equality of e1 and e2.
Overriding hashCode() and equals() using Apache Commons Lang
- Apache commons provide two excellent utility classes HashCodeBuilder and EqualsBuilder for generating hash code and equals methods. Below is its usage:
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; public class Employee { private Integer id; private String firstname; private String lastName; private String department; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } @Override public int hashCode() { final int PRIME = 31; return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).toHashCode(); } @Override public boolean equals(Object o) { if (o == null) return false; if (o == this) return true; if (o.getClass() != getClass()) return false; Employee e = (Employee) o; return new EqualsBuilder(). append(getId(), e.getId()). isEquals(); } } |
- Alternatively, if you are using any code editor, they also must be capable of generating some good structure for you. For example, Eclipse IDE has option under right-click on class >> source > Generate hashCode() and equals() … will generate a very good implementation for you.
Important things to remember
- Always use the same attributes of an object to generate hashCode() and equals() both. As in our case, we have used employee id.
- equals() must be consistent (if the objects are not modified, then it must keep returning the same value).
- Whenever a.equals(b), then a.hashCode() must be same as b.hashCode().
- If you override one, then you should override the other.
Special Attention When Using ORM
- If you’re dealing with an ORM, make sure to always use getters, and never field references in hashCode() and equals(). In ORM, it’s possible that fields are lazily loaded and aren’t available until their getter method is called.
- For example, In our Employee class if we use e1.id == e2.id. so that id field gets lazy-loaded. So in this case, one might be zero or null, thus resulting in incorrect behavior.
- But if uses e1.getId() == e2.getId(), we can be sure even if field is lazy loaded; calling getter will populate the field first.
15. Difference between Loose Coupling and Tight Coupling in Java?
First to understand what is Loose coupling and Tight coupling and also to know the advantage of loose coupling.
In short Loose coupling means reducing the dependencies of a class that uses a different class directly. Tight coupling means classes and objects are dependent on one another. In general, tight coupling is usually not good because it reduces flexibility and re-usability of code and it makes changes much more difficult and not easy to test.
Tight Coupling
A tightly coupled object is an object that needs to know quite a bit about other objects and is usually highly dependent on each other’s interfaces. Changing one object in a tightly coupled application often requires changes to several other objects. In a small application, we can easily identify the changes and there is less chance to miss anything.
See below code is for tight coupling.
public class Journey { Car car = new Car(); public void startJourney() { car.travel(); } } public class Car { public void travel () { System.out.println(“Travel by Car”); } } |
In the above code, the Journey class is dependent on the Car class to provide service to the end-user(main class to call this Journey class).
In the above case, the class Journey is tightly coupled with the class Car so the Journey class should be changed if any changes are needed in the class Car class. For example, if the Car class travel() method changes to the journey() method then you have to change the start journey() method will call the journey() method instead of calling the travel() method.
See below code,
public class Journey { Car car = new Car(); public void startJourney() { car.journey(); } } public class Car { public void journey () { System.out.println(“Travel by Car”); } } |
The best example of tight coupling is RMI (Remote Method Invocation) (Nowadays everywhere uses web services and SOAP instead of using RMI, which has some disadvantages).
Loose Coupling
Loose coupling is a design goal that seeks to reduce the interdependencies between components of a system to reduce the risk that changes in one component will require changes in any other component. To increase the flexibility of the system, make it more maintainable, and makes the entire framework more stable we use Loose coupling.
The below code is an example of loose coupling.
public interface Vehicle { void start(); } public class Car implements Vehicle { @Override public void start() { System.out.println(“Travel by Car”); } } public class Bike implements Vehicle { @Override public void start() { System.out.println(“Travel by Bike”); } } // create main class Journey public class Journey { public static void main(String[] args) { Vehicle v = new Car(); v.start(); } } |
In the above example, The object’s Journey and Car are getting loosely coupled. It means Vehicle is an interface and we can inject any of the implemented classes at run time and we can provide service to the end-user.
Examples of Loose coupling are Interface, JMS, and Spring IOC(Dependency Injection, which can reduce the tight coupling).
Advantages of Loose coupling
A loosely coupled will help you when your application needs to change or grow. If you design with loosely coupled architecture, only a few parts of the application should be affected when requirements change. With too a tight coupled architecture, many parts will need to change and it will be difficult to identify exactly which parts will be affected. In short,
1) It improves testability.
2) It helps you follow the GOF principle of the program to interfaces, not implementations.
3) The benefit is that it’s much easier to swap other pieces of code/modules/objects/components when the pieces are not dependent on one another.
4) It’s highly changeable. One module doesn’t break other modules in unpredictable ways.