OOPS

1. What is Encapsulation?

  • It’s a way of writing a class, such that you cannot work with the fields which represent data directly. You have to work with that indirectly by using the behavior of the instance.
  • It can be done by declaring field as private access mode, can access through getter(accessor)/setter(mutator).
  • And also calling directly fields and action on fields can’t be intercepted in java. But in the case of methods, we can do that. For example, frameworks are already doing it.
    • Smart Setter/Getter:
      • validation, computation etc inside setter/getter.
      • Not recommended as clean coding pov.
      • its breaking Single Responsibility also.

2. What is Inheritance?

  • Let’s say we have a class A, then whatever method or field its having we can say that it’s owned by class A.
  • class B extends A means B inherited from A;it inherits whatever is accessible depending on the access mode.
    • Whenever blueprint / class starts directly from something that already exists and then also able to change or add things that are inherited that is called as Particularisation and can be done in four ways:
      • add new fields
      • add new behaviors
      • change fields –> hiding fields (Not recommended in clean code)
      • change behaviour –> overriding
  • Particularisation: whenever a blueprint makes an object more particular type than one that is inherited.
  • Uses:
    • Avoid duplication code.
    • Example: A Car extends Vehicle, inheriting features like wheels and engine, but it adds specific characteristics like a sedan body style or a convertible top. Like create the general image and go on a more particular type.
    • This showcases how Java inheritance allows for building specialized objects by starting with a common base and adding unique properties.
  • We have two kinds of relationships between prototypes of Object.
    • class Circle extends Shape –> circle is-A shape
    • class Owner has-A field Dog –> An object contains another object as field
  • IS-A relationship happens in case of Inheritance. And Has-A relation happens in case of aggregation or composition.
  • Multilevel inheritance in case of class is not possible, i.e., we cannot extend more than one class if we do not extend any class by default its object class.

3. What is Overriding?

  • Inheritance allows us to override. If you want to change the behavior of inherited methods that called as overriding.
    • First rule: always ensure to have the same signature.
    • Second rule: if the return type is any Object Type, then we can override with co-variant types (subtypes/child) i.e. we can override Number with Integer, Integer extends Number
    • Third rule: while overriding, we can use more public modes rather than private modes, or we can use same and keep in mind if the method is private in parent it’s not inherited itself.
      • public > protected > package mode (default) > private
    • Fourth rule: we can avoid throwing exception while overriding, but we cannot through a wider range of exceptions than the method we override. Meaning **we can override the method throwing IOException with its child FileNotFoundException. But cannot override it by throwing Exception which will result in CE.
  • We can redefine static methods also, but being static it’s not called as overriding. Its method-hiding.

4. What is Polymorphism?

  • Java achieves polymorphism through inheritance.
  • One specific instance can take a shape, can have a form of all the more general types of the one that created instance. In below example class B extends A.
    B b = new B();
    A a = new B();
    

    we can say that B can have the shape of B as well as the shape of A.

  • We can refer to an instance through all more general types of that object i.e., Parent reference can hold child instance.
  • When you store the reference of an instance in the variable of a specific type, the instance is still the same in the memory, the reference to the instance, however, stored in a different type, and depending on the type in which you store the reference to the instance, you will have or not have the possibility to access some of the members instance.
  • The rule states that you can actually access only the members that are declared and visible based on the type of the reference in which the object’s reference was stored.

5. Explain the difference between method overloading and method overriding?

  • Method overloading occurs when multiple methods in the same class have the same name but different parameters. Method overriding occurs when a subclass provides a specific implementation of a method that is already defined in its superclass.
  • Overloading
    • Applies to methods as well as constructors in Java.
    • Multiple methods with the same identifier with in the same class, but the method should differ with parameter (by number, by type of at least one of them, order of types).
    • Only identifier and parameter are important when discussing overloading, so the return type of method is not important.
    • access type (public, private …) is not important.
    • And can over load static method with non-static method.
    • And it doesn’t matter the return type as well as the exceptions its throwing.
      void a() {
      } 
      
      int a() { 
       return 10; // CE
      }
      
    • For Java, priority always is to choose the methods for which no implicit conversion is needed.
      • Example:
      • if we have two methods on takes int another double, we call method with int value, the method takes int as parameter will be executed.
      • If we delete the method having int parameter and call the method with int value, the method having double parameter will handle it.
      • But suppose we delete the method with double parameter and call the method having int parameter with double value, we will get CE. One workaround will be call it like (int) 10.0.

6. Explain Abstract class in Java?

  • In Java, we use two kinds of structures when declaring abstract prototypes: abstract classes and interfaces. If a class is declared as abstract, we cannot instantiate it.
  • Rule:
    • We can create an abstract class with non-abstract methods, but we cannot create abstract methods without an abstract class.
    • We can also create an empty abstract class.
      • For example, the HttpServlet class.
      • Another example would be when creating an API; you always want to specify everything needed about your API.
  • By declaring a class as abstract, we specify that this class is an incomplete prototype and must be completed through inheritance by other classes.
  • If a method is final, we cannot override it anymore, though it can be inherited. Therefore, using final for abstract methods makes no sense and its illegal in Java. Similarly, if a class is final, it cannot be inherited anymore. Therefore, using final for abstract classes makes no sense.
  • Abstract classes can contain constructors because when another class inherits this class and implements its methods, it will still call the parent class constructor.

7. What is an Interface in Java?

  • In Java, an interface represents a contract used to decouple two implementations. These implementations have the privilege of knowing from the contract: one of them what should be expected to consume, and the second one what should be implemented.
  • An interface is still an abstract prototype.
    public abstract interface Playable {
    
    }
    

    Keep in mind the abstract keyword is optional.

  • Until Java 7, the only things we were able to declare inside an interface were:
    • Abstract methods (by default, any method is public and abstract)
    • Static final variables.
  • From Java 8 onwards, we can declare non-abstract and static methods in interfaces as well.
    • Default is the new keyword inside the interface. For default methods, still, the access modifier is public by default.
      public default void m() {
          System.out.println(":)");
      } 
      
  • From Java 9 onwards, we can declare private static methods and private methods inside interfaces. From a clean code point of view, it’s advised to avoid writing your own default methods.
  • When we implement an interface in a class, we use the implements keyword. The same rules for polymorphism also apply to interfaces. We can inherit from multiple interfaces, unlike classes where we can inherit from only one class.
      public class Monster implements Scary, StoryCharacter {}
      public interface Baz extends Foo, Bar {}
    
  • Interfaces serve as abstraction contracts between objects, defining the functionality of your application. For example, if you have a scenario where two objects rely on each other to implement the algorithm of your application, let’s say object A and object B, they utilize each other to fulfill certain functionality.
  • The issue with the above approach is that it directly couples these two objects, resulting in not only coupling their responsibilities but also their implementations.
  • The difference when implementing this using interfaces is that logically, A and B are not completely related to one another; only their responsibilities are related. This means that the implementation of B’s responsibility is not crucial for A. The advantage of this approach is that if we need to make changes to the application in the future, such as finding a better way of implementing certain functionality, we will have less code to modify, and the maintainability of the application increases.

8. What is Marker Interface?

  • If the interface does not inherit anything, it does not have any methods then its called as Marker Interface. It’s just a way of marking characteristics of an object. Then you can use instance of operator to check it’s of what type.
  • After annotations in Java 5 have been introduced nowadays, we are not using any more marker interfaces.
  • Using annotations, we can mark classes, methods, constructors, parameters, and so on. And it’s cleaner also So for Metadata, we are using annotations.

9. How do we consider the default method in Interface?

  • We can consider that the default method is a suggestion from Contract to how to implement a specific behavior. However, if we want to override it, we can do though. Generally, from clean code point of view avoid writing default methods. But in some rare scenarios for re-usability purpose, we use default methods.
  • In the below case in class Foo, we will get compilation error because it inherits both method m() and it has two default implementations.
  • However, if the class also inherits the methods from another class, then the method inherited from the class has priority not the interface. And we will not get above a compilation problem.

10. Where would be static methods used in interfaces?

  • Again, in some cases, if you want to avoid duplication of code, then you might use static methods.
  • And in the case of a factory design pattern also we can use static methods inside the interface. Earlier, we used to create by help of an abstract class and static methods inside. Or it was even a good idea to make it final class and make constructor private.

11. When do you know that you need to decouple two objects?

  • You will observe that in specific situations, if you only have one implementation and that will basically never change, then we can do the relationship directly with the objects.

12. What is Functional Interfaces?

  • As long as the interface consists of only one abstract method, it’s known as Functional interface. Keep in mind though it can contain multiple default and static methods.
  • For functional interfaces, we can use lambda expression. It adds some kind of functional syntax to java, just a way to write some shorter syntax for some interfaces.

13. What is the difference between final, finally, and finalize in Java?

  • final is a keyword used to apply restrictions on class, method, and variable.
  • finally is a block used to execute important code such as closing resources, regardless of whether an exception is thrown or not.
  • finalize() is a method called by the garbage collector before an object is destroyed.

14. What is the difference between static and final keywords?

  • static is used to create class-level variables and methods, while final is used to create constants or to prevent inheritance, method overriding, or variable reassignment.

15. What is this(), super() and this, super in java?

  • If there is an inheritance between two classes, then when we create an object of second class, the first thing will happen is like it will call the constructor of first class in the default constructor.
  • Because if you remember Particularisation, lets say class B extends A then Java has to know what A is before creating an object for B.
  • When a developer does not provide a constructor, class provides a default one. Inside that constructor first statement will be super().
  • Let’s say we make the class A constructor as private, then class B won’t be able to call class A constructor, and we will get compilation issue.
  • super() and this() can’t be used together, and both are first instruction in the constructor.
  • Super and this without parenthesis:
    • Super: represent the parent instance
    • this represents the current instance
    • These two keywords can be used inside the constructor as well as the behavior of the class.

16. Explain the concept of method chaining in Java?

  • Method chaining, also known as cascading, is a technique where multiple method calls are chained together in a single line. Each method returns an object of the same class, allowing the next method to be called on it.

17. What is the purpose of ENUM in Java?

  • The Enum class in Java is used to define a fixed set of constants. Enums provide type safety and can be used in switch statements.
  • We can create an ENUM by enum keyword. We can imagine this as a class that defines from the beginning that the number of instances we can use. And we cannot create another instance of that class.
  • With enum, we always start with enumerating the instances of these types. In below SMALL, MEDIUM, BIG are not some kind of primitive not Strings, they are an instance of the Coffee object.
  • The equivalent of above if you want to write in Java is like below.

18. Explain the difference between == and .equals() methods when comparing objects in Java?

  • == compares the references of two objects to check if they refer to the same memory location, while .equals() compares the content or values of two objects to check if they are logically equal. The .equals() method should be overridden in classes where logical equality is meaningful.

Collection Framework

1. What is the Java Collection Framework?

  • Collection<E> represents a group of elements, including various types such as List<E>, Set<E>, Deque<E>, Queue<E>. All of these are collection of single type of element but are differ from each other in someways because they are following different kind of rules.
    • List<E> -> ArrayList<E>, LinkedList<E>, Vector<E>
    • Set<E> -> HashSet<E>[not ordered, not sorted], LinkedHashSet<E>[ordered], TreeSet<E>[sorted]
    • Deque<E> -> ArrayDeque<E>
    • Map<K,V> –> Collection of associations(pairs), Key-Value Even if the Map does not extend Collection, it is part of the Collection framework. You can check the documentation also.

2. What are the core interfaces of the Collection Framework?

  • The core interfaces of the Collection Framework are Collection, List, Set, Queue and Map.

3. Explain the difference between List, Set, and Map.

  • List(I)
    • ordered
    • indexed
    • do allow duplicates
  • Set(I) / SortedSet(I) / NavigableSet(I)
    • not (always) ordered
    • not indexed
    • doesn’t allow duplicate
  • Map is a collection of key-value pairs where each key is unique.

4. What is the difference between ArrayList and LinkedList?

  • ArrayList uses a dynamic array to store elements and provides fast random access, while LinkedList uses a doubly linked list and provides fast insertion and deletion.

5. Explain the Iterator interface in Java.

  • The Iterator interface provides a way to iterate over a collection sequentially. It allows traversal of elements in a collection and supports operations like next(), hasNext(), and remove().

6. What is the purpose of the hashCode() and equals() methods in Java?

  • Contract between hashCode() and equals():
    • If two elements have the same hashCode, they are not necessarily equal, but if the two elements are equal, they always have the same hashCode.
  • HashSet uses hashCode(), So it’s the fastest collection in java in terms of finding an element.
  • Whenever you are using HashSet with an own object, implement both hashCode() and equals() of object class.
  • The hashCode() method returns a hash code value for an object, which is used by hash-based collections like HashMap, HashSet. The equals() method checks if two objects are logically equal and is used by collections like HashSet and HashMap to determine equality.

7. Explain the Comparable and Comparator interfaces.

  • The Comparable interface is used to define the natural ordering of objects. It contains a single method compareTo() which compares the current object with another object.
  • The Comparator interface is used to define custom ordering of objects and contains methods like compare().
  • Comparable(I) -> Natural sorting order, override public int compareTo(T o);
  • let’s say we have a class from another library, we cannot change to sort that kind of object, we use Comparator<I>.
    • Comparator -> int compare(Dog d1, Dog d2)

8. What is the purpose of the Collections class in Java?

  • The Collections class in Java provides utility methods for working with collections. It includes methods for sorting, searching, shuffling, and synchronizing collections.

9. Explain the difference between fail-fast and fail-safe iterators.

  • Fail-fast iterators throw a ConcurrentModificationException if the underlying collection is modified structurally while iterating. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behaviour at an undetermined time in the future.
  • Fail-safe iterators do not throw exceptions and allow modification of the underlying collection during iteration.

10. What is the difference between HashSet and TreeSet?

  • HashSet is an unordered collection that does not allow duplicate elements, while TreeSet is a sorted set that stores elements in sorted order and does not allow duplicates.
  • TreeSet -> sorting is happened based on Comparator you provide or by using the natural sorting defined by the object.(Comparable)
  • TreeSet extends NavigableSet, NavigableSet extends SortedSet
  • TreeSet under the hood uses a Black-Red tree.
  • And HashSet internally uses HashMap.

11. Explain the purpose of the Queue interface in Java.

  • The Queue interface in Java represents a collection designed for holding elements before processing. It follows the FIFO (First-In-First-Out) order.

12. What is the difference between ArrayList and Vector?

  • ArrayList is not synchronized and is not thread-safe, while Vector is synchronized and is thread-safe.
  • Vector is slower than ArrayList due to synchronization.

13. Explain the purpose of the Map interface in Java.

  • The Map interface in Java represents a collection of key-value pairs where each key is unique. It provides methods for storing, retrieving, and manipulating elements based on keys.

14. What is the difference between HashMap and Hashtable?

  • HashMap
    • non synchronized
    • allow one null key and multiple null values
    • fast compare to Hashtable
    • traversed by Iterator
    • fail - fast
  • Hashtable
    • is synchronized
    • does not allow null keys or values.
    • slow compared to HashMap
    • traversed by Iterator and Enumerator
    • fail - safe

15. What is the purpose of the LinkedHashMap class in Java?

  • The LinkedHashMap class in Java maintains the insertion order of its elements. It is a subclass of HashMap and also implements the Map interface.

16. What is the purpose of the java.util.concurrent.atomic package in Java?

  • The java.util.concurrent.atomic package in Java provides classes that support atomic operations on variables. These classes ensure that operations are performed atomically without the need for explicit synchronization, making them useful for concurrent programming.
    • Example: AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference

17. Explain Concurrent Collections in java?

  • Concurrent collections in Java are specialized data structures designed for concurrent access by multiple threads, and they are synchronized internally.
    • ConcurrentMap<Integer, String> m1 = new ConcurrentHashMap<>(); -> Simple implementation of synchronized hashmap

      NOTE: whenever there is a SkipList in the name, that kind of implementation sorts the value.

    • ConcurrentMap<Integer, String> m1 = new ConcurrentSkipListMap<>(); -> equivalent to TreeMap
    • ConcurrentSkipListSet<Integer> s1 = new ConcurrentSkipListSet<>(); -> equivalent to TreeSet
    • NavigableSet<Integer> s1 = new ConcurrentSkipListSet<>(); -> equivalent to TreeSet
    • Queue<Integer> q = new ConcurrentLinkedQueue<>();
    • Deque<Integer> d = new ConcurrentLinkedDeque<>();
    • CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
    • CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();
    • BlockingDeque<Integer> d1 = new LinkedBlockingDeque<>();
    • BlockingQueue<Object> q1 = new LinkedBlockingQueue<>();

18. When to use Collections.synchronizedMap() and when to use ConcurrentHashMap?

  • If from the beginning, you don’t know your collection will be used by multithreading architecture, for that specific scenario use below one for that specific use case itself.
      Map<Integer, Integer> m1 = new HashMap<>();
      Map<Integer, Integer> sm1 = Collections.synchronizedMap(m1);
    
  • If from the beginning you know your collection will be used on multiple threads, then go for ConcurrentHashMap.
    ConcurrentHashMap<Integer, Integer> m2 = new ConcurrentHashMap<>();
    

19. How CopyOnWriteArrayList or CopyOnWriteArraySet works?

  • These implementations do not apply concurrency directly to the operations themselves; they do not synchronize the operations. Instead, they work by creating a new instance for any operation that changes the collection. This ensures that the creation of the new instance is synchronized. When you perform operations such as adding values, removing values, or changing references inside the collection, you are actually working on a copy of the collection.
  • Therefore, any mutating operation on this type of collection generates a new instance of the collection, copying all the values again.
  • However, it’s essential to use caution with this kind of collection, as excessive mutating operations may quickly fill up memory and result in an Out of Memory error. This approach effectively addresses concurrency concerns.
  • It’s recommended to use it only when there are significantly more read operations than write operations. This is because, in scenarios where read operations predominate, the collection behaves similar to a non-synchronized collection.