Skip to content
Saket Khopkar edited this page Jan 11, 2022 · 4 revisions

Welcome to the The-Java-Programming-Repository wiki!

Why java is not 100% Object Oriented Language?

  • In Java, everything is not considered as an Object, Primitive data types are also available. For example: int data type is used for numbers which is not an object type. (Only Integer is object type).
  • All features of OOP language is not fully supported by Java. For example: Multiple Inheritance, Operator Overloading, etc is not supported by Java.
  • Pure OOP Language should satisfy following features:
  1. Encapsulation / Information Hiding
  2. Inheritance
  3. Polymorphism / Dynamic Binding
  4. All pre-defined types should be Objects.
  5. All operations performed by sending messages to Objects (Message Passing)
  6. All user-defined should be Objects.
  • As Java is not supporting some of the features as feature 4 and 5 and some other also, Java is not 100% pure OOP Language.

Why java is truly Object Oriented Language? Explain.

  • Almost everything in Java is an object. All program code and data reside inside classes and objects. Java comes in extensive set of classes, arranged in packages, that we can use in our programs using inheritance. The object model in Java is simple and easy to extend.
  • Java supports following features:
  1. Objects
  2. Classes
  3. Data Abstraction
  4. Encapsulation
  5. Inheritance
  6. Polymorphism
  7. Dynamic Binding
  8. Message Passing
  9. All user-defined data types are Objects.
  • As Java is supporting all these features java is truly OOP Language.

Mathematical Functions in Java:

  • For performing the various mathematical operations the java.lang package contains a math class. Math class contains most commonly used mathematical functions.
  • Syntax of using Math functions: Math.method_name(Parameters);
Method Description
static double abs(double a) Returns an absolute value of the double value
static float abs(float a) Returns an absolute value of the float value
static int abs(int a) Returns an absolute value of the integer value
static long abs(long a) Returns an absolute value of the long value
static double max(double a, double b) Returns an maximum value among two double values
static float max(float a, float b) Returns an maximum value among two float values
static int max(int a, int b) Returns an maximum value among two integer values
static long abs(long a, long b) Returns an maximum value among two long values
static double min(double a, double b) Returns an minimum value among two double values
static float min(float a, float b) Returns an minimum value among two float values
static int min(int a, int b) Returns an minimum value among two integer values
static long min(long a, long b) Returns an minimum value among two long values
static double exp(double a) Returns an Euler number e raised to the
static double pow(double a, double b) Returns an result of first argument raised to the second argument
static double sqrt(double a) Returns an correctly rounded positive square root of a double value
static long round(double a) Returns closest long to the argument
static int round(double a) Returns closest int to the argument

What are the important features of Java 8 release?

  • Java 8 has been released in March 2014, so it’s one of the hot topic in java interview questions. If you answer this question clearly, it will show that you like to keep yourself up-to-date with the latest technologies.
  • Java 8 has been one of the biggest release after Java 5 annotations and generics. Some of the important features of Java 8 are:
  1. forEach() method in Iterable interface

  2. default and static methods in Interfaces

  3. Functional Interfaces and Lambda Expressions

  4. Java Stream API for Bulk Data Operations on Collections

  5. Java Time API

  6. Collection API improvements

  7. Concurrency API improvements

  8. Java IO improvements

  9. forEach() method in Iterable interface

  • Whenever we need to traverse through a Collection, we need to create an Iterator whose whole purpose is to iterate over, and then we have business logic in a loop for each of the elements in the Collection. We might get ConcurrentModificationException if the iterator is not used properly.
  • Java 8 has introduced forEach method in java.lang.Iterable interface so that while writing code we focus on business logic. The forEach method takes java.util.function.Consumer object as an argument, so it helps in having our business logic at a separate location that we can reuse. Let’s see forEach usage with a simple example.
package com.journaldev.java8.foreach;
 
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.lang.Integer;
 
public class Java8ForEachExample {
 
    public static void main(String[] args) {
         
        //creating sample Collection
        List<Integer> myList = new ArrayList<Integer>();
        for(int i=0; i<10; i++) myList.add(i);
         
        //traversing using Iterator
        Iterator<Integer> it = myList.iterator();
        while(it.hasNext()){
            Integer i = it.next();
            System.out.println("Iterator Value::"+i);
        }
         
        //traversing through forEach method of Iterable with anonymous class
        myList.forEach(new Consumer<Integer>() {
 
            public void accept(Integer t) {
                System.out.println("forEach anonymous class Value::"+t);
            }
 
        });
         
        //traversing with Consumer interface implementation
        MyConsumer action = new MyConsumer();
        myList.forEach(action);
         
    }
 
}
 
//Consumer implementation that can be reused
class MyConsumer implements Consumer<Integer>{
 
    public void accept(Integer t) {
        System.out.println("Consumer impl Value::"+t);
    }
}
  • The number of lines might increase but forEach method helps in having the logic for iteration and business logic at separate place resulting in higher separation of concern and cleaner code.
  1. default and static methods in Interfaces
  • If you read forEach method details carefully, you will notice that it’s defined in Iterable interface but we know that interfaces can’t have a method body. From Java 8, interfaces are enhanced to have a method with implementation.
  • We can use default and static keyword to create interfaces with method implementation. forEach method implementation in Iterable interface is:
default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
  • We know that Java doesn’t provide multiple inheritance in Classes because it leads to Diamond Problem. So how it will be handled with interfaces now since interfaces are now similar to abstract classes?
  • The solution is that compiler will throw an exception in this scenario and we will have to provide implementation logic in the class implementing the interfaces.
package com.journaldev.java8.defaultmethod;
 
@FunctionalInterface
public interface Interface1 {
 
    void method1(String str);
     
    default void log(String str){
        System.out.println("I1 logging::"+str);
    }
     
    static void print(String str){
        System.out.println("Printing "+str);
    }
     
    //trying to override Object method gives compile-time error as
    //"A default method cannot override a method from java.lang.Object"
     
//  default String toString(){
//      return "i1";
//  }
     
}
package com.journaldev.java8.defaultmethod;
 
@FunctionalInterface
public interface Interface2 {
 
    void method2();
     
    default void log(String str){
        System.out.println("I2 logging::"+str);
    }
 
}
  • Notice that both the interfaces have a common method log() with implementation logic.
package com.journaldev.java8.defaultmethod;
 
public class MyClass implements Interface1, Interface2 {
 
    @Override
    public void method2() {
    }
 
    @Override
    public void method1(String str) {
    }
 
    //MyClass won't compile without having it's own log() implementation
    @Override
    public void log(String str){
        System.out.println("MyClass logging::"+str);
        Interface1.print("abc");
    }
     
}
  • As you can see that Interface1 has static method implementation that is used in MyClass.log() method implementation. Java 8 uses default and static methods heavily in Collection API and default methods are added so that our code remains backward compatible.

  • If any class in the hierarchy has a method with the same signature, then default methods become irrelevant. The Object is the base class, so if we have equals(), hashCode() default methods in the interface, it will become irrelevant. That’s why for better clarity, interfaces are not allowed to have Object default methods.

  1. Functional Interfaces and Lambda Expressions
  • If you notice the above interface code, you will notice @FunctionalInterface annotation. Functional interfaces are a new concept introduced in Java 8. An interface with exactly one abstract method becomes a Functional Interface. We don’t need to use @FunctionalInterface annotation to mark an interface as a Functional Interface.
  • @FunctionalInterface annotation is a facility to avoid the accidental addition of abstract methods in the functional interfaces. You can think of it like @Override annotation and it’s best practice to use it. java.lang.Runnable with a single abstract method run() is a great example of a functional interface.
  • One of the major benefits of the functional interface is the possibility to use lambda expressions to instantiate them. We can instantiate an interface with an anonymous class but the code looks bulky.
Runnable r = new Runnable(){
            @Override
            public void run() {
                System.out.println("My Runnable");
            }};
  • Since functional interfaces have only one method, lambda expressions can easily provide the method implementation. We just need to provide method arguments and business logic. For example, we can write above implementation using lambda expression as:
Runnable r1 = () -> {
            System.out.println("My Runnable");
        };
  • If you have single statement in method implementation, we don’t need curly braces also. For example above Interface1 anonymous class can be instantiated using lambda as follows:
Interface1 i1 = (s) -> System.out.println(s);
i1.method1("abc");
  • So lambda expressions are a means to create anonymous classes of functional interfaces easily. There are no runtime benefits of using lambda expressions, so I will use it cautiously because I don’t mind writing a few extra lines of code.
  • A new package java.util.function has been added with bunch of functional interfaces to provide target types for lambda expressions and method references. Lambda expressions are a huge topic, I will write a separate article on that in the future.
  1. Java Stream API for Bulk Data Operations on Collections
  • A new java.util.stream has been added in Java 8 to perform filter/map/reduce like operations with the collection. Stream API will allow sequential as well as parallel execution. This is one of the best features for me because I work a lot with Collections and usually with Big Data, we need to filter out them based on some conditions.

  • Collection interface has been extended with stream() and parallelStream() default methods to get the Stream for sequential and parallel execution. Let’s see their usage with a simple example.

package com.journaldev.java8.stream;
 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
 
public class StreamExample {
 
    public static void main(String[] args) {
         
        List<Integer> myList = new ArrayList<>();
        for(int i=0; i<100; i++) myList.add(i);
         
        //sequential stream
        Stream<Integer> sequentialStream = myList.stream();
         
        //parallel stream
        Stream<Integer> parallelStream = myList.parallelStream();
         
        //using lambda with Stream API, filter example
        Stream<Integer> highNums = parallelStream.filter(p -> p > 90);
        //using lambda in forEach
        highNums.forEach(p -> System.out.println("High Nums parallel="+p));
         
        Stream<Integer> highNumsSeq = sequentialStream.filter(p -> p > 90);
        highNumsSeq.forEach(p -> System.out.println("High Nums sequential="+p));
 
    }
 
}
  • If you will run above example code, you will get output like this:
High Nums parallel=91
High Nums parallel=96
High Nums parallel=93
High Nums parallel=98
High Nums parallel=94
High Nums parallel=95
High Nums parallel=97
High Nums parallel=92
High Nums parallel=99
High Nums sequential=91
High Nums sequential=92
High Nums sequential=93
High Nums sequential=94
High Nums sequential=95
High Nums sequential=96
High Nums sequential=97
High Nums sequential=98
High Nums sequential=99
  • Notice that parallel processing values are not in order, so parallel processing will be very helpful while working with huge collections.
  1. Java Time API
  • It has always been hard to work with Date, Time, and Time Zones in java. There was no standard approach or API in java for date and time in Java. One of the nice addition in Java 8 is the java.time package that will streamline the process of working with time in java.
  • Just by looking at Java Time API packages, I can sense that they will be very easy to use. It has some sub-packages java.time.format that provides classes to print and parse dates and times and java.time.zone provides support for time zones and their rules.
  • The new Time API prefers enums over integer constants for months and days of the week. One of the useful classes is DateTimeFormatter for converting DateTime objects to strings.
  1. Collection API improvements We have already seen forEach() method and Stream API for collections. Some new methods added in Collection API are:

  2. Iterator default method forEachRemaining(Consumer action) to perform the given action for each remaining element until all elements have been processed or the action throws an exception.

  3. Collection default method removeIf(Predicate filter) to remove all of the elements of this collection that satisfy the given predicate.

  4. Collection spliterator() method returning Spliterator instance that can be used to traverse elements sequentially or parallel.

  5. Map replaceAll(), compute(), merge() methods.

  6. Performance Improvement for HashMap class with Key Collisions

  7. Concurrency API improvements Some important concurrent API enhancements are:

  8. ConcurrentHashMap compute(), forEach(), forEachEntry(), forEachKey(), forEachValue(), merge(), reduce() and search() methods.

  9. CompletableFuture that may be explicitly completed (setting its value and status).

  10. Executors newWorkStealingPool() method to create a work-stealing thread pool using all available processors as its target parallelism level.

  11. Java IO improvements Some IO improvements known to me are:

  12. Files.list(Path dir) that returns a lazily populated Stream, the elements of which are the entries in the directory.

  13. Files.lines(Path path) that reads all lines from a file as a Stream.

  14. Files.find() that returns a Stream that is lazily populated with Path by searching for files in a file tree rooted at a given starting file.

  15. BufferedReader.lines() that return a Stream, the elements of which are lines read from this BufferedReader.

Miscellaneous Java 8 Core API improvements Some miscellaneous API improvements that might come handy are:

  1. ThreadLocal static method withInitial(Supplier supplier) to create instances easily.
  2. The Comparator interface has been extended with a lot of default and static methods for natural ordering, reverse order, etc.
  3. min(), max() and sum() methods in Integer, Long and Double wrapper classes.
  4. logicalAnd(), logicalOr() and logicalXor() methods in Boolean class.
  5. ZipFile.stream() method to get an ordered Stream over the ZIP file entries. Entries appear in the Stream in the order they appear in the central directory of the ZIP file.
  6. Several utility methods in Math class.
  7. jjs command is added to invoke Nashorn Engine.
  8. jdeps command is added to analyze class files
  9. JDBC-ODBC Bridge has been removed.
  10. PermGen memory space has been removed

Diamond Problem in Java

  • To understand diamond problem easily, let’s assume that multiple inheritances were supported in java.
  • Let’s say SuperClass is an abstract class declaring some method and ClassA, ClassB are concrete classes.
// SuperClass.java
package com.journaldev.inheritance;

public abstract class SuperClass {

	public abstract void doSomething();
}
// Class A
package com.journaldev.inheritance;
public class ClassA extends SuperClass{
	
	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of A");
	}
	
	//ClassA own method
	public void methodA(){
		
	}
}
// Class B
package com.journaldev.inheritance;
public class ClassB extends SuperClass{

	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of B");
	}
	
	//ClassB specific method
	public void methodB(){
		
	}
}
  • Now let’s say ClassC implementation would be something like below and it’s extending both ClassA and ClassB.
// Class C
package com.journaldev.inheritance;
// this is just an assumption to explain the diamond problem
//this code won't compile
public class ClassC extends ClassA, ClassB{

	public void test(){
		//calling super class method
		doSomething();
	}

}
  • Notice that test() method is making a call to superclass doSomething() method. This leads to the ambiguity as the compiler doesn’t know which superclass method to execute. Because of the diamond-shaped class diagram, it’s referred to as Diamond Problem in java. The diamond problem in Java is the main reason java doesn’t support multiple inheritances in classes.
  • Notice that the above problem with multiple class inheritance can also come with only three classes where all of them has at least one common method.