Wednesday, March 20, 2019

OOPS Questions in depth




Abstraction
:
 Data Abstraction is the property by virtue of which only the essential details are displayed to the user.The trivial or the non-essentials units are not displayed to the user. Ex: A car is viewed as a car rather than its individual components.
Data Abstraction may also be defined as the process of identifying only the required characteristics of an object ignoring the irrelevant details. The properties and behaviours of an object differentiate it from other objects of similar type and also help in classifying/grouping the objects.
Consider a real-life example of a man driving a car. The man only knows that pressing the accelerators will increase the speed of car or applying brakes will stop the car but he does not know about how on pressing the accelerator the speed is actually increasing, he does not know about the inner mechanism of the car or the implementation of accelerator, brakes etc in the car. This is what abstraction is.
In java, abstraction is achieved by interfaces and abstract classes. We can achieve 100% abstraction using interfaces.

Encapsulation: Encapsulation is defined as the wrapping up of data under a single unit. It is the mechanism that binds together code and the data it manipulates.Other way to think about encapsulation is, it is a protective shield that prevents the data from being accessed by the code outside this shield.
  • Technically in encapsulation, the variables or data of a class is hidden from any other class and can be accessed only through any member function of own class in which they are declared.
  • As in encapsulation, the data in a class is hidden from other classes, so it is also known as data-hiding.
  • Encapsulation can be achieved by: Declaring all the variables in the class as private and writing public methods in the class to set and get the values of variables.
Inheritence: Inheritance is an important pillar of OOP(Object Oriented Programming). It is the mechanism in java by which one class is allow to inherit the features(fields and methods) of another class.
Important terminology:
  • Super Class: The class whose features are inherited is known as superclass(or a base class or a parent class).
  • Sub Class: The class that inherits the other class is known as subclass(or a derived class, extended class, or child class). The subclass can add its own fields and methods in addition to the superclass fields and methods.
  • Reusability: Inheritance supports the concept of “reusability”, i.e. when we want to create a new class and there is already a class that includes some of the code that we want, we can derive our new class from the existing class. By doing this, we are reusing the fields and methods of the existing class.
The keyword used for inheritance is extends.
Syntax:
class derived-class extends base-class 
   //methods and fields 
}
Polymorphism: Polymorphism refers to the ability of OOPs programming languages to differentiate between entities with the same name efficiently. This is done by Java with the help of the signature and declaration of these entities.
What is association
Answer:
Association is relationship between two objects. It defines multiplicities between two objects such as one to one, one to many, many to many.
For example: 
Student and Professor
What is aggregation
Answer:
Aggregation is the special form of association. It is also called as “has-a” relationship. If One object contains another object, it is considered as aggregation.
For example:
Car has a tyre.
What is composition
Answer:
Composition is special type of aggregation.You may consider it as “restricted aggregation” If object contains another object and contained object can not exist without container object then this relationship is known as composition.This is one of the most asked Oops interview questions.
For example:
Car has a engine. Engine can not exist without a car.

Inheritance
Inheritance is a basic object oriented feature in which one class acquires and extends upon the properties of another class, using the keyword extends . For Interfaces and the keyword implements , see interfaces.
With the use of the extends keyword among classes, all the properties of the superclass (also known as the Parent
Class or Base Class) are present in the subclass (also known as the Child Class or Derived Class)
public class BaseClass {
public void baseMethod(){
System.out.println("Doing base class stuff");
}
}
public class SubClass extends BaseClass {
}
Instances of SubClass have inherited the method baseMethod() :
SubClass s = new SubClass();
s.baseMethod(); //Valid, prints "Doing base class stuff"
Additional content can be added to a subclass. Doing so allows for additional functionality in the subclass without any change to the base class or any other subclasses from that same base class:
public class Subclass2 extends BaseClass {
public void anotherMethod() {
System.out.println("Doing subclass2 stuff");
}
}
Subclass2 s2 = new Subclass2();
s2.baseMethod(); //Still valid , prints "Doing base class stuff"
s2.anotherMethod(); //Also valid, prints "Doing subclass2 stuff"
Fields are also inherited:
public class BaseClassWithField {
public int x;
}
public class SubClassWithField extends BaseClassWithField {
public SubClassWithField(int x) {
this.x = x; //Can access fields
}
}
private fields and methods still exist within the subclass, but are not accessible:
public class BaseClassWithPrivateField {
private int x = 5;
public int getX() {
return x;
}
}
public class SubClassInheritsPrivateField extends BaseClassWithPrivateField {
public void printX() {
System.out.println(x); //Illegal, can't access private field x
System.out.println(getX()); //Legal, prints 5
}
}
SubClassInheritsPrivateField s = new SubClassInheritsPrivateField();
int x = s.getX(); //x will have a value of 5.
In Java, each class may extend at most one other class.
public class A{}
public class B{}
public class ExtendsTwoClasses extends A, B {} //Illegal
This is known as multiple inheritance, and while it is legal in some languages, Java does not permit it with classes. As a result of this, every class has an unbranching ancestral chain of classes leading to Object , from which all classes descend.
Abstract Classes
An abstract class is a class marked with the abstract keyword. It, contrary to non-abstract class, may contain abstract - implementation-less - methods. It is, however, valid to create an abstract class without abstract methods. An abstract class cannot be instantiated. It can be sub-classed (extended) as long as the sub-class is either also
abstract, or implements all methods marked as abstract by super classes.
An example of an abstract class:
public abstract class Component {
private int x, y;
public setPosition(int x, int y) {
this.x = x;
this.y = y;
}
public abstract void render();
}
The class must be marked abstract, when it has at least one abstract method. An abstract method is a method that has no implementation. Other methods can be declared within an abstract class that have implementation in order to provide common code for any sub-classes. Attempting to instantiate this class will provide a compile error:
//error: Component is abstract; cannot be instantiated
Component myComponent = new Component();
However a class that extends Component , and provides an implementation for all of its abstract methods and can be instantiated.
public class Button extends Component {
@Override
public void render() {
//render a button
}
}
public class TextBox extends Component {
@Override
public void render() {
//render a textbox
}
}
Instances of inheriting classes also can be cast as the parent class (normal inheritance) and they provide a polymorphic effect when the abstract method is called.
Component myButton = new Button();
Component myTextBox = new TextBox();
myButton.render(); //renders a button
myTextBox.render(); //renders a text box
Abstract classes vs Interfaces
Abstract classes and interfaces both provide a way to define method signatures while requiring the extending/implementing class to provide the implementation.
There are two key differences between abstract classes and interfaces:
A class may only extend a single class, but may implement many interfaces.
An abstract class can contain instance (non- static ) fields, but interfaces may only contain static fields.
Version < Java SE 8
Methods declared in interfaces could not contain implementations, so abstract classes were used when it was useful to provide additional methods which implementations called the abstract methods.
Version ≥ Java SE 8
Java 8 allows interfaces to contain default methods, usually implemented using the other methods of the interface, making interfaces and abstract classes equally powerful in this regard.
Anonymous subclasses of Abstract Classes
As a convenience java allows for instantiation of anonymous instances of subclasses of abstract classes, which provide implementations for the abstract methods upon creating the new object. Using the above example this could look like this:
Component myAnonymousComponent = new Component() {
@Override
public void render() {
// render a quick 1-time use component
}
}
Using 'final' to restrict inheritance and overriding Final classes
When used in a class declaration, the final modifier prevents other classes from being declared that extend the
class. A final class is a "leaf" class in the inheritance class hierarchy.
// This declares a final class
final class MyFinalClass {
/* some code */
}
// Compilation error: cannot inherit from final MyFinalClass
class MySubClass extends MyFinalClass {
/* more code */
}
Use-cases for final classes
Final classes can be combined with a private constructor to control or prevent the instantiation of a class. This can be used to create a so-called "utility class" that only defines static members; i.e. constants and static methods.
public final class UtilityClass {
// Private constructor to replace the default visible constructor
private UtilityClass() {}
// Static members can still be used as usual
public static int doSomethingCool() {
return 123;
}
}
Immutable classes should also be declared as final . (An immutable class is one whose instances cannot be changed after they have been created; see the Immutable Objects topic. ) By doing this, you make it impossible to create a mutable subclass of an immutable class. That would violate the Liskov Substitution Principle which
requires that a subtype should obey the "behavioral contract" of its supertypes.
From a practical perspective, declaring an immutable class to be final makes it easier to reason about program behavior. It also addresses security concerns in the scenario where untrusted code is executed in a security sandbox. (For instance, since String is declared as final , a trusted class does not need to worry that it might be tricked into accepting mutable subclass, which the untrusted caller could then surreptitiously change.)
One disadvantage of final classes is that they do not work with some mocking frameworks such as Mockito.
Update: Mockito version 2 now support mocking of final classes.

Final methods
The final modifier can also be applied to methods to prevent them being overridden in sub-classes:
public class MyClassWithFinalMethod {
public final void someMethod() {
}
}
public class MySubClass extends MyClassWithFinalMethod {
@Override
public void someMethod() { // Compiler error (overridden method is final)
}
}
Final methods are typically used when you want to restrict what a subclass can change in a class without forbidding subclasses entirely. The final modifier can also be applied to variables, but the meaning of final for variables is unrelated to
inheritance.
The Liskov Substitution Principle
Substitutability is a principle in object-oriented programming introduced by Barbara Liskov in a 1987 conference keynote stating that, if class B is a subclass of class A , then wherever A is expected, B can be used instead:
class A {...}
class B extends A {...}
public void method(A obj) {...}
A a = new B(); // Assignment OK
method(new B()); // Passing as parameter OK
This also applies when the type is an interface, where there doesn't need to any hierarchical relationship between the objects:
interface Foo {
void bar();
}
class A implements Foo {
void bar() {...}
}
class B implements Foo {
void bar() {...}
}
List<Foo> foos = new ArrayList<>();
foos.add(new A()); // OK
foos.add(new B()); // OK
Now the list contains objects that are not from the same class hierarchy.
Abstract class and Interface usage: "Is-a"
relation vs "Has-a" capability
When to use abstract classes: To implement the same or different behaviour among multiple related objects
When to use interfaces: to implement a contract by multiple unrelated objects
Abstract classes create "is a" relations while interfaces provide "has a" capability.
This can be seen in the code below:
public class InterfaceAndAbstractClassDemo{
public static void main(String args[]){
Dog dog = new Dog("Jack",16);
Cat cat = new Cat("Joe",20);
System.out.println("Dog:"+dog);
System.out.println("Cat:"+cat);
dog.remember();
dog.protectOwner();
Learn dl = dog;
dl.learn();
cat.remember();
cat.protectOwner();
Climb c = cat;
c.climb();
Man man = new Man("Ravindra",40);
System.out.println(man);
Climb cm = man;
cm.climb();
Think t = man;
t.think();
Learn l = man;
l.learn();
Apply a = man;
a.apply();
}
}
abstract class Animal{
String name;
int lifeExpentency;
public Animal(String name,int lifeExpentency ){
this.name = name;
this.lifeExpentency=lifeExpentency;
}
public abstract void remember();
public abstract void protectOwner();
public String toString(){
return this.getClass().getSimpleName()+":"+name+":"+lifeExpentency;
}
}
class Dog extends Animal implements Learn{
public Dog(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName()+" can remember for 5 minutes");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " will protect owner");
}
public void learn(){
System.out.println(this.getClass().getSimpleName()+ " can learn:");
}
}
class Cat extends Animal implements Climb {
public Cat(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " won't protect owner");
}
public void climb(){
System.out.println(this.getClass().getSimpleName()+ " can climb");
}
}
interface Climb{
void climb();
}
interface Think {
void think();
}
interface Learn {
void learn();
}
interface Apply{
void apply();
}
class Man implements Think,Learn,Apply,Climb{
String name;
int age;
public Man(String name,int age){
this.name = name;
this.age = age;
}
public void think(){
System.out.println("I can think:"+this.getClass().getSimpleName());
}
public void learn(){
System.out.println("I can learn:"+this.getClass().getSimpleName());
}
public void apply(){
System.out.println("I can apply:"+this.getClass().getSimpleName());
}
public void climb(){
System.out.println("I can climb:"+this.getClass().getSimpleName());
}
public String toString(){
return "Man :"+name+":Age:"+age;
}
}
output:
Dog:Dog:Jack:16
Cat:Cat:Joe:20
Dog can remember for 5 minutes
Dog will protect owner
Dog can learn:
Cat can remember for 16 hours
Cat won't protect owner
Cat can climb
Man :Ravindra:Age:40
I can climb:Man
I can think:Man
I can learn:Man
I can apply:Man
Key notes:
Animal is an abstract class with shared attributes: name and lifeExpectancy and abstract methods: 1.
remember() and protectOwner() . Dog and Cat are Animals that have implemented the remember() and protectOwner() methods. Cat can climb() but Dog cannot. Dog can think() but Cat cannot. These specific capabilities are added to Cat 2.
and Dog by implementation.
Man is not an Animal but he can Think , Learn , Apply , and Climb . 3.
Cat is not a Man but it can Climb . 4.
Dog is not a Man but it can Learn 5.
Man is neither a Cat nor a Dog but can have some of the capabilities of the latter two without extending 6.
Animal , Cat , or Dog . This is done with Interfaces.
Even though Animal is an abstract class, it has a constructor, unlike an interface. 7.
TL;DR: Unrelated classes can have capabilities through interfaces, but related classes change the behaviour through extension of base classes. Refer to the Java documentation page to understand which one to use in a specific use case.
Consider using abstract classes if...You want to share code among several closely related classes. 1. You expect that classes that extend your abstract class have many common methods or fields, or require 2. access modifiers other than public (such as protected and private). You want to declare non-static or non-final fields. 3.
Consider using interfaces if...You expect that unrelated classes would implement your interface. For example, many unrelated objects can 1.implement the Serializable interface.You want to specify the behaviour of a particular data type but are not concerned about who implements its 2.behaviour.You want to take advantage of multiple inheritance of type. 3.
Static Inheritance
Static method can be inherited similar to normal methods, however unlike normal methods it is impossible to create "abstract" methods in order to force static method overriding. Writing a method with the same signature as a static method in a super class appears to be a form of overriding, but really this simply creates a new function
hides the other.
public class BaseClass {
public static int num = 5;
public static void sayHello() {
System.out.println("Hello");
}
public static void main(String[] args) {
BaseClass.sayHello();
System.out.println("BaseClass's num: " + BaseClass.num);
SubClass.sayHello();
//This will be different than the above statement's output, since it runs
//A different method
SubClass.sayHello(true);
StaticOverride.sayHello();
System.out.println("StaticOverride's num: " + StaticOverride.num);
}
}
public class SubClass extends BaseClass {
//Inherits the sayHello function, but does not override it
public static void sayHello(boolean test) {
System.out.println("Hey");
}
}
public static class StaticOverride extends BaseClass {
//Hides the num field from BaseClass
//You can even change the type, since this doesn't affect the signature
public static String num = "test";
//Cannot use @Override annotation, since this is static
//This overrides the sayHello method from BaseClass
public static void sayHello() {
System.out.println("Static says Hi");
}
}
Running any of these classes produces the output:
Hello
BaseClass's num: 5
Hello
Hey
Static says Hi
StaticOverride's num: test
Note that unlike normal inheritance, in static inheritance methods are not hidden. You can always call the base sayHello method by using BaseClass.sayHello() . But classes do inherit static methods if no methods with the same signature are found in the subclass. If two method's signatures vary, both methods can be run from the
subclass, even if the name is the same. Static fields hide each other in a similar way.
Programming to an interface
The idea behind programming to an interface is to base the code primarily on interfaces and only use concrete classes at the time of instantiation. In this context, good code dealing with e.g. Java collections will look something
like this (not that the method itself is of any use at all, just illustration):
public <T> Set<T> toSet(Collection<T> collection) {
return Sets.newHashSet(collection);
}
while bad code might look like this:
public <T> HashSet<T> toSet(ArrayList<T> collection) {
return Sets.newHashSet(collection);
}
Not only the former can be applied to a wider choice of arguments, its results will be more compatible with code provided by other developers that generally adhere to the concept of programming to an interface. However, the most important reasons to use the former are: most of the time the context, in which the result is used, does not and should not need that many details as the concrete implementation provides;
adhering to an interface forces cleaner code and less hacks such as yet another public method gets added to a class serving some specific scenario; the code is more testable as interfaces are easily mockable; finally, the concept helps even if only one implementation is expected (at least for testability). So how can one easily apply the concept of programming to an interface when writing new code having in mind
one particular implementation? One option that we commonly use is a combination of the following patterns: programming to an interface
factory
builder
The following example based on these principles is a simplified and truncated version of an RPC implementation written for a number of different protocols:
public interface RemoteInvoker {
<RQ, RS> CompletableFuture<RS> invoke(RQ request, Class<RS> responseClass);
}
The above interface is not supposed to be instantiated directly via a factory, instead we derive further more
concrete interfaces, one for HTTP invocation and one for AMQP, each then having a factory and a builder to construct instances, which in turn are also instances of the above interface:
public interface AmqpInvoker extends RemoteInvoker {
static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
return new AmqpInvokerBuilder(instanceId, factory);
}
}
Instances of RemoteInvoker for the use with AMQP can now be constructed as easy as (or more involved depending on the builder):
RemoteInvoker invoker = AmqpInvoker.with(instanceId, factory)
.requestRouter(router)
.build();
And an invocation of a request is as easy as:
Response res = invoker.invoke(new Request(data), Response.class).get();
Due to Java 8 permitting placing of static methods directly into interfaces, the intermediate factory has become
implicit in the above code replaced with AmqpInvoker.with() . In Java prior to version 8, the same effect can be achieved with an inner Factory class:
public interface AmqpInvoker extends RemoteInvoker {
class Factory {
public static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
return new AmqpInvokerBuilder(instanceId, factory);
}
}
}
The corresponding instantiation would then turn into:
RemoteInvoker invoker = AmqpInvoker.Factory.with(instanceId, factory)
.requestRouter(router)
.build();
The builder used above could look like this (although this is a simplification as the actual one permits defining of up to 15 parameters deviating from defaults). Note that the construct is not public, so it is specifically usable only from the above AmqpInvoker interface:
public class AmqpInvokerBuilder {
...
AmqpInvokerBuilder(String instanceId, ConnectionFactory factory) {
this.instanceId = instanceId;
this.factory = factory;
}
public AmqpInvokerBuilder requestRouter(RequestRouter requestRouter) {
this.requestRouter = requestRouter;
return this;
}
public AmqpInvoker build() throws TimeoutException, IOException {
return new AmqpInvokerImpl(instanceId, factory, requestRouter);
}
}
Generally, a builder can also be generated using a tool like FreeBuilder.
Finally, the standard (and the only expected) implementation of this interface is defined as a package-local class to enforce the use of the interface, the factory and the builder:
class AmqpInvokerImpl implements AmqpInvoker {
AmqpInvokerImpl(String instanceId, ConnectionFactory factory, RequestRouter requestRouter) {
...
}
@Override
public <RQ, RS> CompletableFuture<RS> invoke(final RQ request, final Class<RS> respClass) {
...
}
}
Meanwhile, this pattern proved to be very efficient in developing all our new code not matter how simple or complex the functionality is.
Overriding in Inheritance
Overriding in Inheritance is used when you use a already defined method from a super class in a sub class, but in a different way than how the method was originally designed in the super class. Overriding allows the user to reuse code by using existing material and modifying it to suit the user's needs better. The following example demonstrates how ClassB overrides the functionality of ClassA by changing what gets sent out through the printing method:
Example:
public static void main(String[] args) {
ClassA a = new ClassA();
ClassA b = new ClassB();
a.printing();
b.printing();
}
class ClassA {
public void printing() {
System.out.println("A");
}
}
class ClassB extends ClassA {
public void printing() {
System.out.println("B");
}
}
Output:
A
Variable shadowing
Variables are SHADOWED and methods are OVERRIDDEN. Which variable will be used depends on the class that the variable is declared of. Which method will be used depends on the actual class of the object that is referenced by the variable.
class Car {
public int gearRatio = 8;
public String accelerate() {
return "Accelerate : Car";
}
}
class SportsCar extends Car {
public int gearRatio = 9;
public String accelerate() {
return "Accelerate : SportsCar";
}
public void test() {
}
public static void main(String[] args) {
Car car = new SportsCar();
System.out.println(car.gearRatio + " " + car.accelerate());
// will print out 8 Accelerate : SportsCar
}
}
Narrowing and Widening of object references
Casting an instance of a base class to a subclass as in : b = (B) a; is called narrowing (as you are trying to narrow the base class object to a more specific class object) and needs an explicit type-cast. Casting an instance of a subclass to a base class as in: A a = b; is called widening and does not need a type-cast. To illustrate, consider the following class declarations, and test code:
class Vehicle {
}
class Car extends Vehicle {
}
class Truck extends Vehicle {
}
class MotorCycle extends Vehicle {
}
class Test {
public static void main(String[] args) {
Vehicle vehicle = new Car();
Car car = new Car();
vehicle = car; // is valid, no cast needed
Car c = vehicle // not valid
Car c = (Car) vehicle; //valid
}
}
The statement Vehicle vehicle = new Car(); is a valid Java statement. Every instance of Car is also a Vehicle .
Therefore, the assignment is legal without the need for an explicit type-cast.
On the other hand, Car c = vehicle; is not valid. The static type of the vehicle variable is Vehicle which means that it could refer to an instance of Car , Truck , MotorCycle , or any other current or future subclass of Vehicle . (Or indeed, an instance of Vehicle itself, since we did not declare it as an abstract class.) The assignment cannot be allowed, since that might lead to car referring to a Truck` instance.
To prevent this situation, we need to add an explicit type-cast:
Car c = (Car) vehicle;
The type-cast tells the compiler that we expect the value of vehicle to be a Car or a subclass of Car . If necessary, compiler will insert code to perform a run-time type check. If the check fails, then a ClassCastException will be thrown when the code is executed.
Note that not all type-casts are valid. For example:
String s = (String) vehicle; // not valid
The Java compiler knows that an instance that is type compatible with Vehicle cannot ever be type compatible with String . The type-cast could never succeed, and the JLS mandates that this gives in a compilation error.
Inheritance and Static Methods
In Java, parent and child class both can have static methods with the same name. But in such cases implementation of static method in child is hiding parent class' implementation, it's not method overriding. For example:
class StaticMethodTest {
// static method and inheritance
public static void main(String[] args) {
Parent p = new Child();
p.staticMethod(); // prints Inside Parent
((Child) p).staticMethod(); // prints Inside Child
}
static class Parent {
public static void staticMethod() {
System.out.println("Inside Parent");
}
}
static class Child extends Parent {
public static void staticMethod() {
System.out.println("Inside Child");
}
}
}
Static methods are bind to a class not to an instance and this method binding happens at compile time. Since in the
first call to staticMethod() , parent class reference p was used, Parent 's version of staticMethod() is invoked. In
second case, we did cast p into Child class, Child 's staticMethod() executed.




<script data-ad-client="ca-pub-5946381419757134" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>

1 comment:

  1. Thanks dhinesh. I will add java 8 functionalities also and framework from understanding point of view

    ReplyDelete

Top DataStructures Problem from Medium-2

  Array: Find a pair with the given sum in an array Maximum Sum Subarray Problem (Kadane’s Algorithm) Longest Increasing Subsequence Problem...