Java is a high-level programming language that is famous for its robustness, object-oriented nature, enhanced security, and easy-to-learn features also known as Java Buzzwords. Java can be considered both a platform and a programming language. The founder of Java, James Gosling, is recognised as the “Father of Java.” It was known as Oak before Java. Since Oak was already a recognised business, James Gosling and his team changed the programming language’s name to Java. Java allows programmers to write, compile, and debug code easily. It is widely used in developing reusable code and modular programs. Java is an object-oriented programming language and it focuses on reducing dependencies. A java program can be written once and executed anywhere (WORA). Java programs are first compiled into bytecode and the byte code generated can be run on any Java Virtual Machine. Java is similar to C / C++ in terms of syntax. Java is also the name of an Indonesian island. It is here that the first coffee (also known as java coffee) was grown. It is while sipping coffee near his office that James Gosling thought of this name.
Polymorphism is a Java feature that allows us to do a single operation in multiple ways. Polymorphism is made up of two Greek words: poly and morphism. The words "poly" and "morphs" denote "many" and "forms," respectively. As a result, polymorphism denotes the presence of several forms. Polymorphism in Java is divided into two types: compile-time polymorphism and runtime polymorphism. Compile-time Polymorphism: Compile-time polymorphism is also known as static polymorphism. In Java, this is achieved by function overloading. Method Overloading: When there are numerous functions with the same name but distinct parameters, this is referred to as overloading. Changes in the number of arguments or the kind of arguments can cause functions to become overloaded. Example -
class Sample < // Method with 2 integer parameters static int Multiply(int a, int b) // method overloading < return a * b; > // Method with same name but with 2 double parameters static double Multiply(double a, double b) // method overloading < return a * b; > > class Test < public static void main(String[] args) < // Calling method by passing // input as in arguments System.out.println(Sample.Multiply(1, 4)); System.out.println(Sample.Multiply(5.5, 4.2)); > >
4 23.1
Explanation: In the above code, the class Sample has two functions with the same name ‘multiply’ but they have different function signatures which implement method overloading. So, we have the same name for a function that returns the multiplication of two integers and a function that returns the multiplication of two doubles. Runtime Polymorphism: Runtime polymorphism, often known as Dynamic Method Dispatch, is a technique for resolving calls to overridden methods at runtime rather than at compile time. A superclass's reference variable is used to call an overridden method in this process. The object referred to by the reference variable is used to determine which method should be called. Example -
class Sample< void fun()< System.out.println("Inside Sample's fun method."); > > class Test extends Sample< void fun()< System.out.println("Inside Test's fun method."); > > class Main< public static void main(String args[])< Sample s = new Test() s.fun(); > >
Inside Test's fun method.
Explanation - In the above code, the Test class inherits from the Sample class and both the Test class and the Sample class have a method named ‘fun’ defined. This leads to method overriding. Now in the main class, we create a ‘Sample’ reference variable and allot an instance of the ‘Test’ class to it. Now, we invoke the fun() method. Since, the reference variable stores an object of the ‘Test’ class, the fun() method of the ‘Test’ class gets invoked.
Create a free personalised study plan Create a FREE custom study plan Get into your dream companies with expert guidance Get into your dream companies with expert.. Real-Life Problems Prep for Target Roles Custom Plan Duration Flexible PlansWhat is Multithreading? Multithreading is a Java feature that permits the execution of two or more portions of a program at the same time to maximise CPU efficiency. Each such portion of the program is referred to as a thread.
Threads are lightweight processes within processes. Multitasking is accomplished through the use of multiprocessing and multithreading. Because threads share a memory, we employ multithreading rather than multiprocessing. They conserve memory by not allocating separate memory space, and context-switching between threads takes less time than processing. The above image shows 3 processes wherein process 1 consists of 3 threads, process 2 consists of 2 threads and process 3 consists of 1 thread. Java Runnable Interface: In Java, java.lang.Runnable is an interface that a class must implement if its instances are to be executed by a thread. Java Thread Class: Thread programming is possible with Java’s Thread class. The Thread class contains constructors and methods for creating and operating on threads. Thread is a subclass of Object that implements the Runnable interface. Methods of the Java Thread Class: The following table shows the methods of the Java Thread class and its use cases.
Method | Modifier and return type | Use Case |
---|---|---|
start() | void | start() is used to begin the thread’s execution. |
run() | void | It specifies the code to be executed by each thread. |
sleep() | static void | This function sleeps a thread for the period of time provided. |
currentThread() | static Thread | It returns a reference to the thread object that is presently running. |
join() | void | This function waits for a thread to terminate. |
getPriority() | int | This function returns the thread’s priority. |
getName() | String | getName() returns the thread’s name. |
setName() | void | setName() modifies the thread’s name. |
isAlive() | boolean | isAlive() is a boolean function that determines whether or not the thread is alive. |
getId() | long | This function returns the thread’s id. |
yield() | static void | This function causes the presently running thread object to pause and allow other threads to run for a short period of time. |
interrupt() | void | It interrupts the thread. |
suspend() | void | It is used to suspend the thread. |
destroy() | void | This function is used to destroy the thread group as well as all of its subgroups. |
stop() | void | This function is used to terminate the thread. |
resume() | void | resume() is a void function that is used to restart a suspended thread. |
isInterrupted() | boolean | isInterrupted() is a boolean function that determines whether or not the thread has been interrupted. |
setDaemon() | void | This function marks whether the thread is a daemon or a user thread. |
isDaemon() | boolean | This function determines whether the thread is a daemon thread. |
interrupted() | static boolean | This function checks if the current thread is interrupted. |
activeCount() | static int | This function returns the number of active threads in the thread group of the current thread. |
dumpStack() | static void | This function prints the current thread’s stack trace to the standard error stream. |
enumerate() | static int | This function copies the thread group and subgroups of all current threads into the provided array. |
holdLock() | static boolean | holdLock() is a static boolean function that returns true if and only if the current thread has the monitor lock on the provided object. |
checkAccess() | void | This function checks whether the currently running thread has the ability to change the thread. |
getStackTrace() | StackTraceElement[] | This method returns an array of stack trace items that describe the thread’s stack dump. |
getState() | Thread.State | getState() is used to return the thread’s current state. |
getThreadGroup() | ThreadGroup | getThreadGroup() returns the thread group that this thread belongs to. |
notifyAll() | void | This method is used to send a notification to all of an object’s waiting threads. |
notify() | void | This function is used to send a notification to only one thread that is waiting for an item. |
setContextClassLoader() | void | This method sets the Thread’s context ClassLoader. |
getContextClassLoader() | ClassLoader | getContextClassLoader() returns the thread’s context ClassLoader. |
toString() | String | Returns a string representation of this thread, containing its name, priority, and thread group. |
How to implement multithreading in Java?
Multithreading can be performed in Java using two different mechanisms:
By extending the Thread class:
We’ll make a class that extends the java.lang.Thread class. The run() method of the Thread class is overridden by this class. The run() procedure is where a thread starts its life. To begin thread execution, we construct an object of our new class and use the start() method. Start() calls the Thread object’s run() function.
class Sample extends Thread < public void run() < try < // Displaying the thread that is running System.out.println( "Thread " + Thread.currentThread().getId() + " is running"); > catch (Exception e) < // Throwing an exception System.out.println("An exception is caught"); > > > class Multithread < public static void main(String[] args) < int n = 5; // Number of threads for (int i = 0; i < n; i++) < Sample obj = new Sample(); obj.start(); > > >
Thread 20 is running Thread 18 is running Thread 21 is running Thread 19 is running Thread 17 is running
Explanation - In the above code, the Sample class extends the Thread class present in java.lang package. In the Multithread class, we create 5 threads by creating an instance of the Sample class. We then invoke the run() method of the instance created by calling the start() method of the instance.
By implementing the Runnable Interface:
We make a new class that implements the java.lang.Runnable interface and overrides the run() method. After that, we create a Thread object and call its start() method.
class Sample implements Runnable < public void run() < try < // Displaying the thread that is running System.out.println( "Thread " + Thread.currentThread().getId() + " is running"); > catch (Exception e) < // Throwing an exception System.out.println("An exception is caught"); > > > class Multithread < public static void main(String[] args) < int n = 5; // Number of threads for (int i = 0; i < n; i++) < Thread obj = new Thread(new Sample()); obj.start(); > > >
Thread 20 is running Thread 18 is running Thread 21 is running Thread 19 is running Thread 17 is running
Explanation: In the above code, the class Sample implements the Runnable interface. In the sample class, we override the run() method. In the Multithread class, we create 5 threads by creating an instance of the Thread class.
Runnable Interface vs. Thread Class:
The following are the key differences between using the Runnable interface and the Thread class:
Generics refer to types that have been parameterized. The goal is to make type (Integer, String, etc., as well as user-defined types) a parameter for methods, classes, and interfaces. Generics can be used to design classes that function with a variety of data types. A generic entity is a type that works on a parameterized type, such as a class, interface, or method. In Java, generics are equivalent to templates in C++. Generics are used extensively in classes such as HashSet, ArrayList, HashMap, and others.
Generic class:
In the same way that C++ specifies parameter types, we use <> to define parameter types in generic class formation. The following syntax is used to construct generic class objects.
ClassName obj = new ClassName ()
Here, ‘ClassName’ denotes the name of the class whose instance is to be created. specifies the data type to be used while instantiating the object. ‘obj’ is the name of the object to be created.
class SampleT> < // Declaring an object of type T T obj; Test(T obj) // constructor < this.obj = obj; > public T getObject() < return this.obj; > > class Test < public static void main (String[] args) < // instance of Integer type Sample obj1 = new Sample(10); System.out.println(obj1.getObject()); // instance of String type Test obj2 = new Test("Interview Bit"); System.out.println(obj2.getObject()); > >
10 Interview Bit
In the above example, a generic class Sample has been created which accepts a parameter T to determine the type of the class. We create 2 instances of the Sample class. One with an Integer type and the other with a String type.
The following are the advantages of using generics in Java:
Type-safety: In generics, we can only hold one type of object. It does not allow for the storage of objects of different types. Example -
List list = new ArrayList(); list.add(1); list.add("Interview Bit");
The above code snippet runs fine. This is because we have not specified any type while creating an instance of the List class. However, if we run the below code snippet, we get a compile-time error.
List list = new ArrayList(); list.add(1); list.add("Interview Bit");// compile-time error
This is because we have specified the instance of the List class to be of type Integer.
Typecasting isn’t necessary: When we use generics, we do not need to typecast the object when we access it.
List list = new ArrayList(); list.add("Interview Bit"); String s = (String) list.get(0);//typecasting
In the above code, we can see that before accessing the element of the list, we need to typecast it to String type explicitly. This is because we have not declared the list instance to be of a specific type. However, if we run the below code snippet, we do not need to typecast it explicitly.
List list = new ArrayList(); list.add("Interview bit"); String s = list.get(0);
This is because we have specified the instance of the List class to be of String type.
Checking at Compile Time: It is checked at compile time to ensure that an issue does not arise at runtime. It is considerably better to handle the problem at compile time than at runtime, according to an excellent programming approach.
List list = new ArrayList(); list.add("Interview Bit"); list.add(100);//Compile Time Error
The above code snippet gives a compile-time error. This verifies that the code is checked at compile-time.