0% found this document useful (0 votes)
198 views

Java

The document provides an overview of Java language features including lambda expressions, method references, default methods, type annotations, and repeating annotations introduced in Java 8. It also discusses modules, try-with-resources, and private interface methods introduced in Java 9.

Uploaded by

Mohamed Haydar
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
198 views

Java

The document provides an overview of Java language features including lambda expressions, method references, default methods, type annotations, and repeating annotations introduced in Java 8. It also discusses modules, try-with-resources, and private interface methods introduced in Java 9.

Uploaded by

Mohamed Haydar
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 125

Java Cheat Sheet

I. Language Basics:
1. Java Releases and Evolution:
Java 8
The main changes of the Java 8 release were these:

 Lambda Expression and Stream API


 Method Reference
 Default Methods
 Type Annotations
 Repeating Annotations
 Method Parameter Reflection

Lambda Expressions and Stream API


Java was always known for having a lot of boilerplate code. With the release
of Java 8, this statement became a little less valid. The stream API and
lambda expressions are the new features that move us closer to functional
programming.

In our examples, we will see how we use lambdas and streams in the different
scenarios.

The World Before Lambda Expressions


We own a car dealership business. To discard all the paperwork, we want to
create a piece of software that finds all currently available cars that have run
less than 50,000 km.

Let us take a look at how we would implement a function for something like
this in a naive way:

public class LambdaExpressions {


public static List<Car> findCarsOldWay(List<Car> cars) {
List<Car> selectedCars = new ArrayList<>();
for (Car car : cars) {
if (car.kilometers < 50000) {
selectedCars.add(car);
}
}
return selectedCars;
}
}
To implement this, we are creating a static function that accepts a List of cars.
It should return a filtered list according to a specified condition.

Using a Stream and a Lambda Expression


We have the same problem as in the previous example.

Our client wants to find all cars with the same criteria.

Let us see a solution where we used the stream API and lambda expression:
public class LambdaExpressions {
public static List<Car> findCarsUsingLambda(List<Car> cars) {
return cars.stream().filter(car -> car.kilometers < 50000)
.collect(Collectors.toList());
}
}
We need to transfer the list of cars into a stream by calling
the stream() method. Inside the filter() method we are setting our condition. We
are evaluating every entry against the desired condition. We are keeping only
those entries that have less than 50,000 kilometers. The last thing that we
need to do is to wrap it up into a list.

More about lambda expressions can be found in the docs.

Method Reference
Without Method Reference
We still own a car dealership shop, and we want to print out all the cars in the
shop. For that, we will use a method reference.

A method reference allows us to call functions in classes using a special kind


of syntax ::. There are four kinds of method references:
 Reference to a static method
 Reference to an instance method on a object
 Reference to an instance method on a type
 Reference to a constructor

Let us see how to do it using the standard method call:


public class MethodReference {
List<String> withoutMethodReference =
cars.stream().map(car -> car.toString())
.collect(Collectors.toList());
}
We are using a lambda expression to call the toString() method on each car.

Using a Method Reference


Now, let us see how to use a method reference in the same situation:
public class MethodReference {
List<String> methodReference = cars.stream().map(Car::toString)
.collect(Collectors.toList());
}
We are, again, using a lambda expression, but now we call
the toString() method by method reference. We can see how it is more concise
and easier to read.

To read more about method reference please look at the docs.

Default Methods
Let us imagine that we have a simple method log(String message) that prints log
messages on invocation. We realized that we want to provide timestamps to
messages so that logs are easily searchable. We don’t want our clients to
break after we introduce this change. We will do this using a default method
implementation on an interface.

Default method implementation is the feature that allows us to create a


fallback implementation of an interface method.

Use Case
Let us see how our contract looks:
public class DefaultMethods {

public interface Logging {


void log(String message);
}

public class LoggingImplementation implements Logging {


@Override
public void log(String message) {
System.out.println(message);
}
}
}
We are creating a simple interface with just one method and implementing it
in LoggingImplementation class.

Adding New Method


We will add new method inside the interface. The method accepts the second
argument called date that represents timestamp.
public class DefaultMethods {

public interface Logging {


void log(String message);

void log(String message, Date date);


}
}
We are adding a new method but not implementing it inside all client classes.
The compiler will fail with exception:
Class 'LoggingImplementation' must either be declared abstract
or implement abstract method 'log(String, Date)' in 'Logging'`.

Using Default Methods


After adding a new method inside the interface, our compiler threw
exceptions. We are going to solve this using default method implementation
for the new method.

Let us look at how to create a default method implementation:


public class DefaultMethods {

public interface Logging {


void log(String message);
default void log(String message, Date date) {
System.out.println(date.toString() + ": " + message);
}
}
}
Putting the default keyword allows us to add the implementation of the
method inside the interface. Now, our LoggingImplementation class does not fail
with a compiler error even though we didn’t implement this new method
inside of it.

To read more about default methods please refer to the docs.

Type Annotations
Type annotations are one more feature introduced in Java 8. Even though we
had annotations available before, now we can use them wherever we use a
type. This means that we can use them on:

 a local variable definition


 constructor calls
 type casting
 generics
 throw clauses and more

Tools like IDEs can then read these annotations and show warnings or errors
based on the annotations.

Local Variable Definition


Let us see how to ensure that our local variable doesn’t end up as a null value:
public class TypeAnnotations {

public static void main(String[] args) {


@NotNull String userName = args[0];
}}
We are using annotation on the local variable definition here. A compile-time
annotation processor could now read the @NotNull annotation and throw an
error when the string is null.

Constructor Call
We want to make sure that we cannot create an empty ArrayList:
public class TypeAnnotations {

public static void main(String[] args) {


List<String> request =
new @NotEmpty ArrayList<>(Arrays.stream(args).collect(
Collectors.toList()));
}
}
This is the perfect example of how to use type annotations on a constructor.
Again, an annotation processor can evaluate the annotation and check if the
array list is not empty.

Generic Type
One of our requirements is that each email has to be in a
format <name>@<company>.com. If we use type annotations, we can do it
easily:
public class TypeAnnotations {

public static void main(String[] args) {


List<@Email String> emails;
}
}
This is a definition of a list of email addresses. We use @Email annotation
that ensures that every record inside this list is in the desired format.

A tool could use reflection to evaluate the annotation and check that each of
the elements in the list is a valid email address.

For more information about type annotations please refer to the docs.

Repeating Annotations
Let us imagine we have an application with fully implemented security. It has
different levels of authorization. Even though we implemented everything
carefully, we want to make sure that we log every unauthorized action. On
each unauthorized action, we are sending an email to the owner of the
company and our security admin group email. Repeating annotations are our
way to go on this example.

Repeating annotations allows us to place multiple annotations on the same


class.

Creating a Repeating Annotation


For the example, we are going to create a repeating annotation called @Notify:
public class RepeatingAnnotations {

@Repeatable(Notifications.class)
public @interface Notify {
String email();
}

public @interface Notifications {


Notify[] value();
}
}
We create @Notify as a regular annotation, but we add
the @Repeatable (meta-)annotation to it. Additionally, we have to create a
“container” annotation Notifications that contains an array of Notify objects. An
annotation processor can now get access to all repeating Notify annotations
through the container annotation Noifications.

Please note that this is a mock annotation just for demonstration purposes.
This annotation will not send emails without an annotation processor that
reads it and then sends emails.

Using Repeating Annotations


We can add a repating annotation multiple times to the same construct:
@Notify(email = "[email protected]")
@Notify(email = "[email protected]")
public class UserNotAllowedForThisActionException
extends RuntimeException {
final String user;
public UserNotAllowedForThisActionException(String user) {
this.user = user;

}
}
We have our custom exception class that we will throw whenever a user tries
to do something that the user is not allowed. Our annotations to this class say
that we want to notify two emails when code throws this exception.

To read more about repeating annotations please refer to the docs.

Java 9
Java 9 introduced these main features:

 Java Module System


 Try-with-resources
 Diamond Syntax with Inner Anonymous Classes
 Private Interface Methods

Java Module System


A module is a group of packages, their dependencies, and resources. It
provides a broader set of functionalities than packages.

When creating the new module, we need to provide several attributes:

 Name
 Dependencies
 Public Packages - by default, all packages are module private
 Services Offered
 Services Consumed
 Reflection Permissions

Without going into many details, let us create our first module. Inside our
example, we will show several options and keywords that one can use when
creating a module.

Creating Modules Inside IntelliJ


First, we will go with a simple example. We will build a Hello World
application where we print “Hello” from one module, and we call the second
module to print “World!”.

Since I am working in the IntelliJ IDEA there is something that we need to


understand first. IntelliJ IDEA has the concept of modules. For it to work,
each Java module has to correspond to one IntelliJ module.

We have two modules: hello.module and world.module. They correspond


to hello and world IntelliJ modules, respectively. Inside each of them, we have
created the module-info.java file. This file defines our Java module. Inside, we
declare which packages we need to export and on which modules we are
dependent.

Defining our First Module


We are using the hello module to print the word: “Hello”. Inside, we call the
method inside the world module, which will print “World !”. The first thing
that we need to do is to declare export of the package containing
our World.class inside module-info.java:
module world.module {
exports com.reflectoring.io.app.world;
}
We use the keyword module with the module name to reference the module.

The next keyword that we use is exports. It tells the module system that we are
making our com.reflectoring.io.app.world package visible outside of our module.

There are several other keywords can be used:

 requires
 requires transitive
 exports to
 uses
 provides with
 open
 opens
 opens to

Out of these we will show only requires declaration. Others can be found in


the docs.

Defining our Second Module


After we created and exported the world module, we can proceed with
creating the hello module:
module hello.module {
requires world.module;}
We define dependencies using requires keyword. We are referencing our
newly created, hello.module. Packages that are not exported are, by default,
module private and cannot be seen from outside of the module.

To read more about the Java module system please refer to the docs

Try-with-resources
Try-with-resources is a feature that enables us to declare new autoclosable
resources on a try-catch block. Declaring them inside a try-catch block tells the
JVM to release them after the code has run. The only condition is that the
declared resource implements an Autoclosable interface.
Closing a Resource Manually
We want to read text using BufferedReader. BufferedReader is a closable
resource, so we need to make sure that it is properly closed after use. Before
Java 8 we would do it like this:
public class TryWithResources {
public static void main(String[] args) {
BufferedReader br = new BufferedReader(
new StringReader("Hello world example!"));
try {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
In finally block, we would call close(). The finally block ensures that the reader
is always properly closed.

Closing a Resource with  try-with-resources


Java 8 introduced the try-with-resource feature that enables us to declare our
resource inside try definition. This will ensure that our closable is closed
without using finally. Let us take a look at some example of using
the BufferedReader to read string:
public class TryWithResources {
public static void main(String[] args) {
final BufferedReader br3 = new BufferedReader(
new StringReader("Hello world example3!"));
try (BufferedReader reader = br3) {
System.out.println(reader.readLine());
} catch (IOException e) {
System.out.println("Error happened!");
}
}
}
Inside the try definition, we are assigning our previously created reader to the
new variable. Now we know that our reader will get closed every time.

To read more about the try-with-resources feature please refer to the docs.


Diamond Syntax with Inner Anonymous
Classes
Before Java 9 we couldn’t use a diamond operator inside the inner
anonymous class.

For our example, we will create the abstract class, StringAppender. The class
has only one method that appends two strings with - between them. We will
use the anonymous class for providing the implementation for
the append() method:
public class DiamondOperator {

StringAppender<String> appending = new StringAppender<>() {


@Override
public String append(String a, String b) {
return new StringBuilder(a).append("-").append(b).toString();
}
};

public abstract static class StringAppender<T> {


public abstract T append(String a, String b);
}
}
We use the diamond operator to omit type on the constructor call new
StringAppender<>(). Since we are using Java 8, in this example we will get a
compiler error:
java: cannot infer type arguments for
com.reflectoring.io.java9.DiamondOperator.StringAppender<T>

reason: '<>' with anonymous inner classes is not supported in -source 8


(use -source 9 or higher to enable '<>' with anonymous inner classes)
In Java 9, this compiler error is no longer happening.

Private Interface Methods


We already mentioned how we use default methods in interfaces.
How do we split the implementation into several methods? When working
with classes, we can achieve it using private methods. Could that be the
solution in our case?

As of Java 9, yes. We can create private methods inside our interfaces.

Usage of Private Interface Methods


For our example, we want to print out a set of names.

Interface containing this functionality had default method defined. We


decided that we should if the client doesn’t provide the implementation,
provide a set of predefined names that we read from the resource folder:
public class PrivateInterfaceMethods {

public static void main(String[] args) {


TestingNames names = new TestingNames();
System.out.println(names.fetchInitialData());
}

public static class TestingNames implements NamesInterface {


public TestingNames() {
}
}

public interface NamesInterface {


default List<String> fetchInitialData() {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(this.getClass()
.getResourceAsStream("/names.txt")))) {
return readNames(br);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

private List<String> readNames(BufferedReader br)


throws IOException {
ArrayList<String> names = new ArrayList<>();
String name;
while ((name = br.readLine()) != null) {
names.add(name);
}
return names;
}
}
}
We are using BufferedReader to read the file containing default names that we
share with the client. To encapsulate our code and, possibly, make it reusable
in other methods, we decided to move code for reading and saving names
into a List to the separate method. This method is private and, now, we can
use it anywhere inside our interface.

As mentioned, the main benefit of this feature inside Java 9 is better


encapsulation and reusability of the code.

Java 10
Local Variable Type Inference
Java always needed explicit types on local variables.

When writing and reading code, we always know which type we expect. On
the other hand, a lot of the code is just types with no usability.

The var type allows us to omit type from the left-hand side of our statements.

Old Way
Let us look into the example here. We want to create small a set of people,
put everything in one list and then go through that list in the for loop to print
out their name and last name:
public class LocalTypeVar {

public void explicitTypes() {


Person Roland = new Person("Roland", "Deschain");
Person Susan = new Person("Susan", "Delgado");
Person Eddie = new Person("Eddie", "Dean");
Person Detta = new Person("Detta", "Walker");
Person Jake = new Person("Jake", "Chambers");

List<Person> persons =
List.of(Roland, Susan, Eddie, Detta, Jake);

for (Person person : persons) {


System.out.println(person.name + " - " + person.lastname);
}
}
}
This is the type of code that we can see in most cases in Java. We use explicit
types to make sure that we know what the method expects.

Implicit Typing with var


Now, we will look into the same example, but using the var keyword that
Java 10 introduced. We still want to create several person objects and put
them in a list. After that, we will go through that list and print out the name of
each person:
public class LocalTypeVar {

public void varTypes() {


var Roland = new Person("Roland", "Deschain");
var Susan = new Person("Susan", "Delgado");
var Eddie = new Person("Eddie", "Dean");
var Detta = new Person("Detta", "Walker");
var Jake = new Person("Jake", "Chambers");

var persons = List.of(Roland, Susan, Eddie, Detta, Jake);

for (var person : persons) {


System.out.println(person.name + " - " + person.lastname);
}
}
}
We can see some of the most typical examples of using var type on local
variables. First, we use them for defining local variables. It can be a
standalone object or even a list with the diamond operator.

For more details about local type inference please visit the docs.

Java 11
Local Variable Type in Lambda Expressions
Java 11 introduced an improvement to the previously mentioned local type
inference. This allows us to use var inside lambda expressions.

We will, again, create several persons, collect them into the list and filter out
entries that don’t have an ‘a’ inside their name:
public class LocalTypeVarLambda {

public void explicitTypes() {


var Roland = new Person("Roland", "Deschain");
var Susan = new Person("Susan", "Delgado");
var Eddie = new Person("Eddie", "Dean");
var Detta = new Person("Detta", "Walker");
var Jake = new Person("Jake", "Chambers");

var filteredPersons =
List.of(Roland, Susan, Eddie, Detta, Jake)
.stream()
.filter((var x) -> x.name.contains("a"))
.collect(Collectors.toList());
System.out.println(filteredPersons);
}
}
Inside the filter() method we are using var to infer the type instead of explicitly
mentioning the type.

Please note that it doesn’t make a difference if we use var or type inference


without it. It will work the same for both.

Java 14

Switch Expressions
Switch expressions allowed us to omit break calls inside every case block. It
helps with the readability of the code and better understanding.

In this section, we will see several ways of how to use switch expressions.

Old Way of Switch Statements


We have a method where a client provides the desired month, and we return
the number of days inside that month.

The first thing that comes to our mind is to build it with switch-


case statements:

public class SwitchExpression {

public static void main(String[] args) {


int days = 0;
Month month = Month.APRIL;

switch (month) {
case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER :
days = 31;
break;
case FEBRUARY :
days = 28;
break;
case APRIL, JUNE, SEPTEMBER, NOVEMBER :
days = 30;
break;
default:
throw new IllegalStateException();
}
}
}
We need to make sure that we put a break statement inside our case code
block. Failing it will result in checking on other conditions after we match the
first one.

Using Switch Expressions


We will look into the same method as before. The user wants to send the
month and get the number of days in that month:
public class SwitchExpression {

public static void main(String[] args) {


int days = 0;
Month month = Month.APRIL;

days = switch (month) {


case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> 31;
case FEBRUARY -> 28;
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30;
default -> throw new IllegalStateException();
};
}
}
We are using a bit different notation in the case block. We are using -> instead
of the colon. Even though we are not invoking the break statement, we will
still jump out of the switch statement on the first valid condition.

This will do the same thing as the code shown in the previous example.

The  yield  Keyword


The logic inside the case block can be a bit more complicated than just
returning a value. For example, we want to log which month the user sent us:
public class SwitchExpression {

public static void main(String[] args) {


int days = 0;
Month month = Month.APRIL;

days = switch (month) {


case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> {
System.out.println(month);
yield 31;
}
case FEBRUARY -> {
System.out.println(month);
yield 28;
}
case APRIL, JUNE, SEPTEMBER, NOVEMBER -> {
System.out.println(month);
yield 30;
}
default -> throw new IllegalStateException();
};
}
}
In a multi-line code block, we have to use the yield keyword to return a value
from a case block.

To read more about using switch expressions please refer to the docs.

Java 15

Text Blocks
Text block is an improvement on formatting String variables. From Java 15,
we can write a String that spans through several lines as regular text.

Example Without Using Text Blocks


We want to send an HTML document via email. We are storing the email
template into a variable:
public class TextBlocks {

public static void main(String[] args) {


System.out.println(
"<!DOCTYPE html>\n" +
"<html>\n" +
" <head>\n" +
" <title>Example</title>\n" +
" </head>\n" +
" <body>\n" +
" <p>This is an example of a simple HTML " +
"page with one paragraph.</p>\n" +
" </body>\n" +
"</html>\n");
}
}
We are formatting our string like in the example above. We need to take care
of new lines and append all the lines to a single string.

Example of Using Text Blocks


Let us look into the same example of an HTML template for email. We want
to send an example email with some straightforward HTML formatting. This
time we will use a text block:
public class TextBlocks {

public static void main(String[] args) {


System.out.println(
"""
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<p>This is an example of a simple HTML
page with one paragraph.</p>
</body>
</html>
"""
);
}}
We used special syntax for opening and closing quotes: """. This allows us to
treat our string as if we are writing it in a .txt file.

There are some rules that we need to abide by when using a text block. We
need to make sure that we put a new line after our opening quotes, or our
compiler will throw an error:
Illegal text block start: missing new line after opening quotes.
If we want to end our string with \n we can do it by putting new line before
closing """ like in the example above.

To read more about text blocks please refer to the docs.

Java 16

Pattern Matching of instanceof


Pattern matching on the instanceof allows us to cast our variable inline and use
it inside the desired if-else block without explicitly casting it.

Example Without Pattern Matching


We have a base class called Vehicle and two classes that extends
it: Car and Bicycle. We omitted the code for this, and you can look it up in
the GitHub repo.

Our algorithm for calculating prices is depending on the instance of the


vehicle that is sent to it:
public class PatternMatching {
public static double priceOld(Vehicle v) {
if (v instanceof Car) {
Car c = (Car) v;
return 10000 - c.kilomenters * 0.01 -
(Calendar.getInstance().get(Calendar.YEAR) -
c.year) * 100;
} else if (v instanceof Bicycle) {
Bicycle b = (Bicycle) v;
return 1000 + b.wheelSize * 10;
} else throw new IllegalArgumentException(); }}
Since we are not using pattern matching, we need to cast the vehicle into the
correct type inside each if-else block. As we can see, it is a typical example of
boilerplate code for which Java is famous.

Using Pattern Matching


Let’s see how can we can discard the boilerplate part from the
example above:
public class PatternMatching {
public static double price(Vehicle v) {
if (v instanceof Car c) {
return 10000 - c.kilomenters * 0.01 -
(Calendar.getInstance().get(Calendar.YEAR) -
c.year) * 100;
} else if (v instanceof Bicycle b) {
return 1000 + b.wheelSize * 10;
} else throw new IllegalArgumentException();
}
}
One thing to note is the scope of the casted variable. It’s visible only within
the if statement.

For more information about pattern matching in instanceof method please refer


to the docs.

Records
How many POJOs (Plain Old Java Objects) have you written?

Well, I can answer for myself: “Too many!”.

Java has had a bad reputation for boilerplate code. Lombok allowed us to
stop worrying about getters, setters, etc. Java 16 finally introduced records to
remove a lot of boilerplate code.

A record class is nothing more than regular POJO, for which most of the code
is generated from the definition.

Plain Old Java Object definition


Let us look into the example of the POJO class before Java 16 introduced
records:
public class Vehicle {
String code;
String engineType;

public String getCode() {


return code;
}

public void setCode(String code) {


this.code = code;
}

public String getEngineType() {


return engineType;
}

public void setEngineType(String engineType) {


this.engineType = engineType;
}

public Vehicle(String code, String engineType) {


this.code = code;
this.engineType = engineType;
}

@Override
public boolean equals(Object o) ...

@Override
public int hashCode() ...

@Override
public String toString() ...
}
There are almost 50 lines of code for object that contains only two properties.
The IDE generated this code, but still, it is there and has to be maintained.

Record Definition
Definition of a vehicle record, with the same two properties, can be done in
just one line:
public record VehicleRecord(String code, String engineType) {}
This one line has all the same getters, setters, constructors, etc. as from the
example above. One thing to note is that the record class is, by default, final,
and we need to comply with that. That means we cannot extend a record
class, but most other things are available for us.
To read more about record classes please refer to the docs.

Java 17
Sealed Classes
The final modifier on a class doesn’t allow anyone to extend it. What about
when we want to extend a class but only allow it for some classes?

We are back at our car dealership business. We are so proud of our algorithm
for calculating prices that we want to expose it. We don’t want anyone using
our Vehicle representation, though. It is valid just for our business. We can
see a bit of a problem here. We need to expose class but constrain it also.

This is where Java 17 comes into play with sealed classes. The sealed class
allows us to make class effectively final for everyone except explicitly
mentioned classes.
public sealed class Vehicle permits Bicycle, Car {...}
We added a sealed modifier to our Vehicle class, and we had to add
the permits keyword with a list of classes that we allow to extend it. After this
change, we are still getting errors from the compiler.

There is one more thing that we need to do here.

We need to add final, sealed, or non-sealed modifiers to classes that will extend


our class.
public final class Bicycle extends Vehicle {...}

Constraints
Several constraints have to be met for the sealed class to work:

 Permitted subclasses must be accessible by the sealed class at compile


time
 Permitted subclasses must directly extend the sealed class
 Permitted subclasses must have one of the following modifiers:
o final
o sealed
o non-sealed
 Permitted subclasses must be in the same Java module

2. Compilation:
Java Is Compiled
Java is a compiled language rather than an interpreted language like Python.
While interpreted languages are translated at runtime, compiled languages are
compiled before reaching the virtual machine. This means that a program
called a compiler translates the Java written by the programmer into
Java bytecode which can then be interpreted by the Java virtual machine.
Compilers are quite helpful for several reasons. A few are listed below:
1. Compilers can check for errors prior to runtime. The Java compiler will
catch and report errors like TypeErrors, which can be produced by
giving a function the wrong type of object as a parameter, and
SyntaxErrors, which can be caused by forgetting syntax arguments like
parentheses or braces. Catching these and many other types of errors
prior to runtime help catch a large majority of the possible bugs caused
by programmer error and make Java programs more stable and bug-free
before they are run.
2. Compilers can help speed up programs. Interpreted languages can be
slow because they must parse text that is understandable to humans and
translate it into bits that can be understood by the machine. The
compiler does this translation work so that it happens before running
the program and does not slow down the running time of programs.
3. Compilers can verify access rights to classes and methods. Java has a
lot of built-in security because access rights can be assigned to each
class and method that limit what other classes or methods have access
to. The compiler checks that every program that uses a class or calls a
method has the correct access rights to use or call it.
3. Running Java Code:
Java Is Compiled
Java is a compiled language rather than an interpreted language like Python.
While interpreted languages are translated at runtime, compiled languages are
compiled before reaching the virtual machine. This means that a program
called a compiler translates the Java written by the programmer into
Java bytecode which can then be interpreted by the Java virtual machine.
Compilers are quite helpful for several reasons. A few are listed below:
4. Compilers can check for errors prior to runtime. The Java compiler will
catch and report errors like TypeErrors, which can be produced by
giving a function the wrong type of object as a parameter, and
SyntaxErrors, which can be caused by forgetting syntax arguments like
parentheses or braces. Catching these and many other types of errors
prior to runtime help catch a large majority of the possible bugs caused
by programmer error and make Java programs more stable and bug-free
before they are run.
5. Compilers can help speed up programs. Interpreted languages can be
slow because they must parse text that is understandable to humans and
translate it into bits that can be understood by the machine. The
compiler does this translation work so that it happens before running
the program and does not slow down the running time of programs.
6. Compilers can verify access rights to classes and methods. Java has a
lot of built-in security because access rights can be assigned to each
class and method that limit what other classes or methods have access
to. The compiler checks that every program that uses a class or calls a
method has the correct access rights to use or call it.

4. Variables:
1. Declaration and Initializing:
Assignment is whenever you’re storing data in the variable.
int a; // not an assignment
int b = 5; // assignment
a = 6; // assignment
b = 7; // assignment
Initialization is when you assign data to a variable for the first time. With all
other assignments, you’re overwriting data. With initialization, there was no
previous data to overwrite.
int a; // not an initialization
int b = 5; // initialization
a = 6; // initialization
b = 7; // not an initialization
Declaration is the point at which you create a variable. At this point, Java
knows nothing about the variable, so it’s necessary to specify the type. This is
the only time you need to specify the type since for all future time, Java can
refer to this declaration to determine what the type is.
2. Memory Location and Lifecycle:
Instance Variables
A variable which is declared inside a class and outside all the methods and
blocks is an instance variable. The general scope of an instance variable is
throughout the class except in static methods. The lifetime of an instance
variable is until the object stays in memory.
Class Variables
A variable which is declared inside a class, outside all the blocks and is
marked static is known as a class variable. The general scope of a class
variable is throughout the class and the lifetime of a class variable is until the
end of the program or as long as the class is loaded in memory.
Local Variables
All other variables which are not instance and class variables are treated as
local variables including the parameters in a method. Scope of a local
variable is within the block in which it is declared and the lifetime of a local
variable is until the control leaves the block in which it is declared.

3. Garbage Collections:
Garbage collection in java is about removing unused variables from the
memory so that there is sufficient space for the new variables. Garbage
collection works automatically in java making memory management more
efficient.
4. Primitive Types:
In Java, the primitive data types are the predefined data types of Java. They
specify the size and type of any standard values. Java has 8 primitive data
types namely byte, short, int, long, float, double, char and boolean. When a
primitive data type is stored, it is the stack that the values will be assigned.
When a variable is copied then another copy of the variable is created and
changes made to the copied variable will not reflect changes in the original
variable. Here is a Java program to demonstrate all the primitive data types in
Java.
5. Non-Primitive Types:
They are so-called because they refer to any particular object. Unlike the
primitive data types, the non-primitive ones are created by the users in Java.
Examples include arrays, strings, classes, interfaces etc. When the reference
variables will be stored, the variable will be stored in the stack and the
original object will be stored in the heap. In Object data type although two
copies will be created they both will point to the same variable in the heap,
hence changes made to any variable will reflect the change in both the
variables. Here is a Java program to demonstrate arrays(an object data type)
in Java.

Properties Primitive data types Objects

Origin Pre-defined data types User-defined data types

Stored structure Stored in a stack Reference variable is


Properties Primitive data types Objects

stored in stack and the


original object is stored in
heap

Two different variables is Two reference variable is


created along with different created but both are
assignment(only values are pointing to the same
When copied same) object on the heap

When changes
are made in the Change does not reflect in the Changes reflected in the
copied variable original ones. original ones.

Primitive datatypes do not The default value for the


Default value have null as default value reference variable is null

byte, short, int, long, float, array, string class,


Example double, char, boolean interface etc.

6. Casting:
The process of converting the value of one data type (int, float, double, etc.)
to another data type is known as typecasting.
In Java, there are 13 types of type conversion. However, in this tutorial, we
will only focus on the major 2 types:
1. Widening Type Casting: Java automatically converts one data type to
another data type.
2. Narrowing Type Casting: we manually convert one data type into another
using the parenthesis.
7. Final Variables:
In Java, the final keyword is used to denote constants. It can be used with
variables, methods, and classes.
Once any entity (variable, method or class) is declared final, it can be
assigned only once. That is,
 the final variable cannot be reinitialized with another value
 the final method cannot be overridden
 the final class cannot be extended

5. Methods:
1. Signatures:
The method signature in java is defined as the structure of the method that is
designed by the programmer. The method signature is the combination of the
method name and the parameter list. The method signature depicts the
behavior of the method i.e types of values of the method, return type of the
method, etc.
2. Static Methods:
The static keyword is used to construct methods that will exist regardless of
whether or not any instances of the class are generated. Any method that
uses the static keyword is referred to as a static method.
Features of static method:
 A static method in Java is a method that is part of a class rather
than an instance of that class.
 Every instance of a class has access to the method.
 Static methods have access to class variables (static variables)
without using the class’s object (instance).
 Only static data may be accessed by a static method. It is unable to
access data that is not static (instance variables).
 In both static and non-static methods, static methods can be
accessed directly.

Why use Static Methods?


1. To access and change static variables and other non-object-based
static methods.
2. Utility and assist classes frequently employ static methods.

Restrictions in Static Methods:


1. Non-static data members or non-static methods cannot be used by
static methods, and static methods cannot call non-static methods
directly.
2. In a static environment, this and super aren’t allowed to be used.

Why is the main method in Java static?


It’s because calling a static method isn’t needed of the object. If it were a
non-static function, JVM would first build an object before calling the
main() method, resulting in an extra memory allocation difficulty.

3. Instance Methods:
Instance Methods are the group of codes that performs a particular task.
Sometimes the program grows in size, and we want to separate the logic of
the main method from other methods. A  method is a function written inside
the class. Since java is an object-oriented programming language, we need
to write a method inside some classes. 
The important points regarding instance variables are:
1. Instance methods can access instance variables and instance methods
directly and undeviatingly.
2. Instance methods can access static variables and static methods
directly.

There are two types of Instance methods in Java:


1. Accessor Method   (Getters)
2. Mutator Method    (Setters)
The accessor method is used to make the code more secure and increase its
protection level, accessor is also known as a getter. Getter returns the value
(accessors), it returns the value of data type int, String, double, float, etc.
For the convenience of the program, getter starts with the word “get”
followed by the variable name.
 The mutator method is also known as the setter. It sets the value for any
variable which is used in the programs of a class. and starts with the word
“set” followed by the variable name. Getter and Setter make the
programmer convenient in setting and getting the value for a particular data
type. In both getter and setter, the first letter of the variable should be
capital.
Accessor and mutator are mainly used to access or set the value for the
private member of the class in the main method.

Difference Between the static method and instance method


Instance Methods Static Methods

It doesn’t require an object of the


It requires an object of the class. class.

It can access only the static attribute


It can access all attributes of a class. of a class.

The methods can be accessed only The method is only accessed by


using object reference. class name.

Syntax: Objref.methodname() Syntax: className.methodname()

It’s an example of pass-by-value It is an example of pass-by-


programming. reference programming.

4. Pass by Value or Pass by Reference:


One of the biggest confusion in Java programming language is whether java
is Pass by Value or Pass by Reference. I ask this question a lot in interviews
and still see the interviewee confused with it. So I thought to write a post
about it to clear all the confusion around it. First of all, we should understand
what is meant by pass by value or pass by reference.

 Pass by Value: The method parameter values are copied to another


variable and then the copied object is passed, that’s why it’s called pass
by value.
 Pass by Reference: An alias or reference to the actual parameter is
passed to the method, that’s why it’s called pass by reference.

6. Operator Precedence:

7. Math Operations:
8.  String Manipulation:

What are Strings?


A string in literal terms is a series of characters. Hey, did you say
characters, isn’t it a primitive data type in Java. Yes, so in technical terms,
the basic Java String is basically an array of characters.

The String Class Java extends the Object class.


String Concatenation:
Concatenation is joining of two or more strings.
String “Length” Method:
How will you determine the length of given String? I have provided a
method called as “length”. Use it against the String you need to find the
length.
String “indexOf” Method:
If I know the length, how would I find which character is in which position?
In short, how will I find the index of a character?
You answered yourself, buddy, there is an “indexOf” method that will help
you determine the location of a specific character that you specify.
String “charAt” Method:
Similar to the above question, given the index, how do I know the character
at that location?
Simple one again!! Use the “charAt” method and provide the index whose
character you need to find.
String “CompareTo” Method:
Do I want to check if the String that was generated by some method is equal
to something that I want to verify with? How do I compare two Strings?
Use the method “compareTo” and specify the String that you would like to
compare.
Use “compareToIgnoreCase” in case you don’t want the result to be case
sensitive.
The result will have the value 0 if the argument string is equal to this string;
a value less than 0 if this string is lexicographically less than the string
argument; and a value greater than 0 if this string is lexicographically
greater than the string argument.
String “Contain” Method:
I partially know what the string should have contained, how do I confirm if
the String contains a sequence of characters I specify?
Use the method “contains” and specify the characters you need to check.
Returns true if and only if this string contains the specified sequence of
char values.
String “endsWith” Method:
How do I confirm if a String ends with a particular suffix? Again you
answered it. Use the “endsWith” method and specify the suffix in the
arguments.
Returns true if the character sequence represented by the argument is a
suffix of the character sequence represented by this object.
String “replaceAll” & “replaceFirst” Method:
I want to modify my String at several places and replace several parts of the
String?
Java String Replace, replaceAll and replaceFirst methods. You can specify
the part of the String you want to replace and the replacement String in the
arguments.
String Java “tolowercase” & Java “touppercase” Method:
I want my entire String to be shown in lower case or Uppercase?
Just use the “toLowercase()” or “ToUpperCase()” methods against the
Strings that need to be converted.
9. Conditionals:
1. If Statement:
The Java if statement is the most simple decision-making statement. It is
used to decide whether a certain statement or block of statements will be
executed or not i.e if a certain condition is true then a block of statement is
executed otherwise not.

2. Switch Statement:
The switch statement is a multi-way branch statement. In simple words, the
Java switch statement executes one statement from multiple conditions. It is
like an if-else-if ladder statement. It provides an easy way to dispatch
execution to different parts of code based on the value of the expression.
Basically, the expression can be a byte, short, char, and int primitive data
types. It basically tests the equality of variables against multiple values.
10. Loops:
1. For Loop:
The Java for loop is used to iterate a part of the program several times. If the
number of iteration is fixed, it is recommended to use for loop.
It consists of four parts:
1. Initialization: It is the initial condition which is executed once when
the loop starts. Here, we can initialize the variable, or we can use an
already initialized variable. It is an optional condition.
2. Condition: It is the second condition which is executed each time to
test the condition of the loop. It continues execution until the
condition is false. It must return boolean value either true or false. It is
an optional condition.
3. Increment/Decrement: It increments or decrements the variable value.
It is an optional condition.
4. Statement: The statement of the loop is executed each time until the
second condition is false.

2. While Loop:
Java while loop is a control flow statement that allows code to be executed
repeatedly based on a given Boolean condition. The while loop can be
thought of as a repeating if statement. While loop in Java comes into use
when we need to repeatedly execute a block of statements. The while loop is
considered as a repeating if statement. If the number of iterations is not
fixed, it is recommended to use the while loop.
3. Do-While Loop:
The Java do-while loop is used to iterate a part of the program repeatedly, until the specified condition
is true. If the number of iteration is not fixed and you must have to execute the loop at least once, it is
recommended to use a do-while loop.

Java do-while loop is called an exit control loop. Therefore, unlike while loop and for loop, the do-while
check the condition at the end of loop body. The Java do-while loop is executed at least once because
condition is checked after loop body.

4. Recursion:
Recursion is a process by which a function or a method calls itself again and
again. This function that is called again and again either directly or indirectly
is called the “recursive function”.

We will see various examples to understand recursion. Now let’s see the
syntax of recursion.
Recursion Syntax
Any method that implements Recursion has two basic parts:
1. Method call which can call itself i.e. recursive
2. A precondition that will stop the recursion.
Note that a precondition is necessary for any recursive method as, if we do
not break the recursion then it will keep on running infinitely and result in a
stack overflow.

int fact(int n)
{
if (n < = 1) // base case
return 1;
else
return n*fact(n-1);
}

11. I/O Streams:


Java language offers access to system resources, standard input-output
devices, etc. using a “System” class. This class implements a system-
dependent programming interface to access various resources.

The System class belongs to the “java.lang” package of Java. Apart from
providing standard I/O streams, System class also provides access to
environment variables, external variables, loading files and libraries, and also
a utility method arrayCopy for copying part of an array.

As this tutorial is based on standard I/O only, we will skip the rest of the
facilities provided by the System class here.

1. Standard Input (System.in):


The input stream provided by System class, System.in is used to read the
input data from a standard input device like a keyboard.
The stream remains open and is ready to read the data supplied by the user to
the standard input device.

2. Standard Output (System.out):


The System.out interface of the System class is used to write the program
output to the standard output device like the monitor. In most cases, the
System.out interface writes the command output to the standard output
device.

It uses three methods from the “PrintStream” class as the standard output
derives from this class.

These methods are:

1. print
2. println
3. write

The methods “print” and “println” have the same functionality except for a
single difference that the println method appends a newline character (\n) to
the output.

The write method is not used frequently except in cases where non-ASCII
data is to be displayed.

3. Standard Error Stream (System.err):


The standard error stream, System.err is used to display errors if any during
the execution of the program.

Like System.out stream, the error stream also supports the three methods of
PrintStream class, print, println and writes.

4. Methods To Read Input From The Console:


1. Class BufferRead
2. Console Class
3. Scanner

The following are the variations of the printf function in Java.


No Function prototype Description

1 System.out.printf(string); Prints a string provided in the argument without any formatting

2 System.out.printf(format, arguments); Prints the output using specified format string ‘format’ and
arguments.

3 System.out.printf(locale, format, Prints the output using specified format string by applying locale and
arguments); the arguments.
Note that the function System.out.format () is the same as System.out.printf
().

12. Exceptions:
Try Catch

You simply wrap the Java code which throws the checked exception within
a try catch block. This now allows you to process and deal with the
exception. With this approach it's very easy to swallow the exception and
then carry on like nothing happened. Later in the code when what the method
was doing is required you may find yourself with our good friend
the NullPointerException.
We have now caught the exception and processed the error in a meaningful
way by adding our code to the catch block, the code sequence carries on
crisis averted.
Throws

We use the keyword throws to throw the checked exception up the stack to


the calling method to handle. This is what FileInputStream has just done to
you. This looks and feels great - no messy exception code we are writing and
we no longer need to handle this exception as someone else can deal with it.
The calling method then needs to do something with it ... maybe throw again.
As with try catch be wary of always throwing as you need to think who
SHOULD be handling the error and what piece of code is best placed to
handle it correctly.
1. Checked Exception:
In broad terms, a checked exception (also called a logical exception) in Java
is something that has gone wrong in your code and is potentially recoverable.
For example, if there’s a client error when calling another API, we could
retry from that exception and see if the API is back up and running the
second time. A checked exception is caught at compile time so if something
throws a checked exception the compiler will enforce that you handle it.
2. Unchecked Exception:
An unchecked exception (also known as an runtime exception) in Java is
something that has gone wrong with the program and is unrecoverable. Just
because this is not a compile time exception, meaning you do not need to
handle it, that does not mean you don’t need to be concerned about it.
The most common Java unchecked exception is the good old
NullPointerException which is when you are trying to access a variable or
object that doesn’t exist.
3. Difference between Checked and Unchecked Exceptions:
To summarize, the difference between a checked and unchecked exception is:
 A checked exception is caught at compile time whereas a runtime or
unchecked exception is, as it states, at runtime.
 A checked exception must be handled either by re-throwing or with
a try catch block, whereas an unchecked isn’t required to be handled.
 A runtime exception is a programming error and is fatal whereas a
checked exception is an exception condition within your code’s logic
and can be recovered or re-tried from.
13. Multi-Threading (Basic):
1. Process:
A multi-threaded program contains two or more parts that can run
concurrently and each part can handle a different task at the same time
making optimal use of the available resources specially when your computer
has multiple CPUs.

By definition, multitasking is when multiple processes share common


processing resources such as a CPU. Multi-threading extends the idea of
multitasking into applications where you can subdivide specific operations
within a single application into individual threads. Each of the threads can
run in parallel. The OS divides processing time not only among different
applications, but also among each thread within an application.

Multi-threading enables you to write in a way where multiple activities can


proceed concurrently in the same program.
Following are the stages of the life cycle −
 New − A new thread begins its life cycle in the new state. It
remains in this state until the program starts the thread. It is also
referred to as a born thread.
 Runnable − After a newly born thread is started, the thread
becomes runnable. A thread in this state is considered to be
executing its task.
 Waiting − Sometimes, a thread transitions to the waiting state
while the thread waits for another thread to perform a task. A
thread transitions back to the runnable state only when another
thread signals the waiting thread to continue executing.
 Timed Waiting − A runnable thread can enter the timed waiting
state for a specified interval of time. A thread in this state
transitions back to the runnable state when that time interval
expires or when the event it is waiting for occurs.
 Terminated (Dead) − A runnable thread enters the terminated
state when it completes its task or otherwise terminates.

Threads
Threads allows a program to operate more efficiently by doing multiple
things at the same time.
Threads can be used to perform complicated tasks in the background without
interrupting the main program.

Create a Thread by Implementing a Runnable Interface


If your class is intended to be executed as a thread then you can achieve this
by implementing a Runnable interface. You will need to follow three basic
steps −
Step 1
As a first step, you need to implement a run() method provided by
a Runnable interface. This method provides an entry point for the thread and
you will put your complete business logic inside this method. Following is a
simple syntax of the run() method −
public void run( )
Step 2
As a second step, you will instantiate a Thread object using the following
constructor −
Thread(Runnable threadObj, String threadName);
Where, threadObj is an instance of a class that implements
the Runnable interface and threadName is the name given to the new
thread.
Step 3
Once a Thread object is created, you can start it by calling start() method,
which executes a call to run( ) method. Following is a simple syntax of start()
method −
void start();

Create a Thread by Extending a Thread Class


The second way to create a thread is to create a new class that
extends Thread class using the following two simple steps. This approach
provides more flexibility in handling multiple threads created using available
methods in Thread class.
Step 1
You will need to override run( ) method available in Thread class. This
method provides an entry point for the thread and you will put your complete
business logic inside this method. Following is a simple syntax of run()
method −
public void run( )
Step 2
Once Thread object is created, you can start it by calling start() method,
which executes a call to run( ) method. Following is a simple syntax of start()
method −
void start( );
Thread Methods
Following is the list of important methods available in the Thread class.

Sr.No. Method & Description

1
public void start()
Starts the thread in a separate path of execution, then invokes the run() method on this Thread object.

2
public void run()
If this Thread object was instantiated using a separate Runnable target, the run() method is invoked
on that Runnable object.

3
public final void setName(String name)
Changes the name of the Thread object. There is also a getName() method for retrieving the name.

4
public final void setPriority(int priority)
Sets the priority of this Thread object. The possible values are between 1 and 10.

5
public final void setDaemon(boolean on)
A parameter of true denotes this Thread as a daemon thread.

6
public final void join(long millisec)
The current thread invokes this method on a second thread, causing the current thread to block until
the second thread terminates or the specified number of milliseconds passes.

7
public void interrupt()
Interrupts this thread, causing it to continue execution if it was blocked for any reason.

8
public final boolean isAlive()
Returns true if the thread is alive, which is any time after the thread has been started but before it
runs to completion.
Differences between "extending" and "implementing" Threads
The major difference is that when a class extends the Thread class, you
cannot extend any other class, but by implementing the Runnable interface, it
is possible to extend from another class as well, like: class MyClass extends
OtherClass implements Runnable
2. Fork / Join:
Nowadays, systems are launching with multicore processors. The multicore
processors make the computation faster. Hence, it becomes necessary for a
programmer to use multicore processors effectively so that the result can be
generated in less span of time. Fork/Join in Java is used to make use of the
cores (brain of CPU that process the instructions) in an efficient manner. The
fork/join splits a bigger task into smaller sub-tasks. These sub-tasks are then
distributed among the cores. The results of these subtasks are then joined to
generate the final result. The splitting of a task and joining the results imitates
the divide-and-conquer algorithm. The fork is responsible for splitting the
task, and join is responsible for merging the results of the task to generate the
final result.
3. Mutex:
Java multi threads example to show you how to use Semaphore and Mutex to
limit the number of threads to access resources.

1. Semaphores – Restrict the number of threads that can access a resource.


Example, limit max 10 connections to access a file simultaneously.
2. Mutex – Only one thread to access a resource at once. Example, when a
client is accessing a file, no one else should have access the same file at
the same time.

1. Semaphore

Consider an ATM cubicle with 4 ATMs, Semaphore can make sure only 4


people can access simultaneously.

2. Mutex

Mutex is the Semaphore with an access count of 1. Consider a situation of


using lockers in the bank. Usually, the rule is that only one person is allowed
to enter the locker room.

4. Race Condition:
Race condition occurs in a multi-threaded environment when more than one
thread try to access a shared resource (modify, write) at the same time. Note
that it is safe if multiple threads are trying to read a shared resource as long as
they are not trying to change it. Since multiple threads try to race each other
to finish executing a method thus the name race condition.

Note that multiple threads executing inside a method is not a problem in


itself, problem arises when they try to access the same resource. Examples of
shared resources are class variables, DB record in a table, writing in a file .

14. Generics:

Generics means parameterized types. The idea is to allow type (Integer,


String, … etc., and user-defined types) to be a parameter to methods,
classes, and interfaces. Using Generics, it is possible to create classes that
work with different data types. An entity such as class, interface, or method
that operates on a parameterized type is a generic entity.

Why Generics?

The Object is the superclass of all other classes, and Object reference can
refer to any object. These features lack type safety. Generics add that type of
safety feature. We will discuss that type of safety feature in later examples.
Generics in Java are similar to templates in C++. For example, classes like
HashSet, ArrayList, HashMap, etc., use generics very well. There are some
fundamental differences between the two approaches to generic types. 
The type parameters naming conventions are important to learn generics
thoroughly. The common type parameters are as follows:
 T – Type
 E – Element
 K – Key
 N – Number
 V – Value

Advantages of Generics: 
Programs that use Generics has got many benefits over non-generic code. 
1. Code Reuse: We can write a method/class/interface once and use it for
any type we want.
2. Type Safety: Generics make errors to appear compile time than at run
time (It’s always better to know problems in your code at compile time
rather than making your code fail at run time). Suppose you want to create
an ArrayList that store name of students, and if by mistake the programmer
adds an integer object instead of a string, the compiler allows it. But, when
we retrieve this data from ArrayList, it causes problems at runtime.

15. Differences between C++ and Java:


The main difference between C++ and Java is that C++ is only a compiled
language while Java is both compiled and interpreted. The C++  compiler
converts the source code into machine code and therefore, it is platform
dependent. However, Java source code is converted into bytecode by its
compiler and following that, the Java interpreter executes this bytecode at
runtime and produces output. The fact that Java is interpreted is the reason
as to why it is platform independent .
Differences between Pointers and References:
Following are some of the differences between C/C++ Pointers and
References.
 No Pointer Arithmetic in Java. Pointers are memory addresses and a
pointer points to a memory address of a variable. In C/C++, a pointer
can be incremented/decremented to point to a new address but in Java,
arithmetic operations on references are not allowed.
 No Pointer Manipulation in Java Although a reference internally uses
a pointer but Java does not allow any manipulation to an underlying
pointer using a reference variable. It makes java more secure and
robust. A reference can refer to an object or be null only.
 No Casting of pointers in Java In C/C++, we can cast int* to char*
but in Java, only related objects can be cast e.g. object of the same
hierarchy.
II. Data Structure:
1. Array:
 Arrays are used to store multiple values in a single variable, instead of
declaring separate variables for each value.

2. Collection Interface:
The Collection interface is a member of the Java Collections Framework. It
is a part of java.util package. It is one of the root interfaces of the Collection
Hierarchy. The Collection interface is not directly implemented by any class.
However, it is implemented indirectly via its subtypes or subinterfaces
like List, Queue, and Set. 

For Example, the HashSet class implements the Set interface which is a


subinterface of the Collection interface. If a collection implementation
doesn’t implement a particular operation, it should define the corresponding
method to throw UnsupportedOperationException.
3. List:
The List interface in Java provides a way to store the ordered collection. It is
a child interface of Collection. It is an ordered collection of objects in which
duplicate values can be stored. Since List preserves the insertion order, it
allows positional access and insertion of elements. 

The List interface is found in java.util package and inherits the Collection
interface. It is a factory of ListIterator interface. Through the ListIterator, we
can iterate the list in forward and backward directions. The implementation
classes of the List interface are ArrayList, LinkedList, Stack, and Vector.
ArrayList and LinkedList are widely used in Java programming. The Vector
class is deprecated since Java 5.

Operations in a List interface:

Since List is an interface, it can be used only with a class that implements this
interface. Now, let’s see how to perform a few frequently used operations on
the List.  

Operation 1: Adding elements to List class using add() method

Operation 2: Updating elements in List class using set() method

Operation 3: Removing elements using remove() method

1. ArrayList:
Java ArrayList class uses a dynamic array

for storing the elements. It is like an array, but there is no size limit. We can
add or remove elements anytime. So, it is much more flexible than the
traditional array. It is found in the java.util package. It is like the Vector in
C++.

The ArrayList in Java can have the duplicate elements also. It implements the
List interface so we can use all the methods of the List interface here. The
ArrayList maintains the insertion order internally.
It inherits the AbstractList class and implements List interface.

The important points about the Java ArrayList class are:

o Java ArrayList class can contain duplicate elements.


o Java ArrayList class maintains insertion order.
o Java ArrayList class is non synchronized
o Java ArrayList allows random access because the array works on an
index basis.
o In ArrayList, manipulation is a little bit slower than the LinkedList in
Java because a lot of shifting needs to occur if any element is removed
from the array list.
o We can not create an array list of the primitive types, such as int, float,
char, etc. It is required to use the required wrapper class in such cases.

2. LinkedList:
Java LinkedList class uses a doubly linked list to store the elements. It
provides a linked-list data structure. It inherits the AbstractList class and
implements List and Deque interfaces.

The important points about Java LinkedList are:

o Java LinkedList class can contain duplicate elements.


o Java LinkedList class maintains insertion order.
o Java LinkedList class is non synchronized.
o In Java LinkedList class, manipulation is fast because no shifting needs
to occur.
o Java LinkedList class can be used as a list, stack or queue.

4. Set:
The set is an interface available in the java.util package. The set interface
extends the Collection interface. An unordered collection or list in which
duplicates are not allowed is referred to as a collection interface. The set
interface is used to create the mathematical set. The set interface use
collection interface's methods to avoid the insertion of the same
elements. SortedSet and NavigableSet are two interfaces that extend the set
implementation.
1. HashSet:
Java HashSet class is used to create a collection that uses a hash table for
storage. It inherits the AbstractSet class and implements Set interface.

The important points about Java HashSet class are:

o HashSet stores the elements by using a mechanism called hashing.


o HashSet contains unique elements only.
o HashSet allows null value.
o HashSet class is non synchronized.
o HashSet doesn't maintain the insertion order. Here, elements are
inserted on the basis of their hashcode.
o HashSet is the best approach for search operations.
o The initial default capacity of HashSet is 16, and the load factor is 0.75.

2. TreeSet:
Java TreeSet class implements the Set interface that uses a tree for storage. It
inherits AbstractSet class and implements the NavigableSet interface. The
objects of the TreeSet class are stored in ascending order.
The important points about the Java TreeSet class are:
o Java TreeSet class contains unique elements only like HashSet.
o Java TreeSet class access and retrieval times are quiet fast.
o Java TreeSet class doesn't allow null element.
o Java TreeSet class is non synchronized.
o Java TreeSet class maintains ascending order.
o Java TreeSet class contains unique elements only like HashSet.
o Java TreeSet class access and retrieval times are quite fast.
o Java TreeSet class doesn't allow null elements.
o Java TreeSet class is non-synchronized.
o Java TreeSet class maintains ascending order.
o The TreeSet can only allow those generic types that are comparable.
For example The Comparable interface is being implemented by the
StringBuffer class.

5. Map:
A map contains values on the basis of key, i.e. key and value pair. Each key
and value pair is known as an entry. A Map contains unique keys.
A Map is useful if you have to search, update or delete elements on the basis
of a key.
1. HashMap:
Java HashMap class implements the Map interface which allows us to store
key and value pair, where keys should be unique. If you try to insert the
duplicate key, it will replace the element of the corresponding key. It is easy
to perform operations using the key index like updation, deletion, etc.
HashMap class is found in the java.util package.

HashMap in Java is like the legacy Hashtable class, but it is not


synchronized. It allows us to store the null elements as well, but there should
be only one null key. Since Java 5, it is denoted as HashMap<K,V>, where K
stands for key and V for value. It inherits the AbstractMap class and
implements the Map interface.

Points to remember:
o Java HashMap contains values based on the key.
o Java HashMap contains only unique keys.
o Java HashMap may have one null key and multiple null values.
o Java HashMap is non synchronized.
o Java HashMap maintains no order.
o The initial default capacity of Java HashMap class is 16 with a load
factor of 0.75.

2. TreeMap:
Java TreeMap class is a red-black tree based implementation. It provides an
efficient means of storing key-value pairs in sorted order.

The important points about Java TreeMap class are:

o Java TreeMap contains values based on the key. It implements the


NavigableMap interface and extends AbstractMap class.
o Java TreeMap contains only unique elements.
o Java TreeMap cannot have a null key but can have multiple null values.
o Java TreeMap is non synchronized.
o Java TreeMap maintains ascending order.

6. Stack:
The stack is a linear data structure that is used to store the collection of
objects. It is based on Last-In-First-Out (LIFO). Java collection framework
provides many interfaces and classes to store the collection of objects. One of
them is the Stack class that provides different operations such as push, pop,
search, etc.

The stack data structure has the two most important operations that
are push and pop. The push operation inserts an element into the stack and
pop operation removes an element from the top of the stack. Let's see how
they work on stack.

7. Queue:
The interface Queue is available in the java.util package and does extend the
Collection interface. It is used to keep the elements that are processed in the
First In First Out (FIFO) manner. It is an ordered list of objects, where
insertion of elements occurs at the end of the list, and removal of elements
occur at the beginning of the list.
Being an interface, the queue requires, for the declaration, a concrete class,
and the most common classes are the LinkedList and PriorityQueue in Java.
Implementations done by these classes are not thread safe. If it is required to
have a thread safe implementation, PriorityBlockingQueue is an available
option.

1. Deque:
The interface called Deque is present in java.util package. It is the subtype of
the interface queue. The Deque supports the addition as well as the removal
of elements from both ends of the data structure. Therefore, a deque can be
used as a stack or a queue. We know that the stack supports the Last In First
Out (LIFO) operation, and the operation First In First Out is supported by a
queue. As a deque supports both, either of the mentioned operations can be
performed on it. Deque is an acronym for "double ended queue".

2. Priority Queue:
A PriorityQueue is used when the objects are supposed to be processed based
on the priority. It is known that a Queue follows the First-In-First-Out
algorithm, but sometimes the elements of the queue are needed to be
processed according to the priority, that’s when the PriorityQueue comes into
play.

The PriorityQueue is based on the priority heap. The elements of the priority
queue are ordered according to the natural ordering, or by a Comparator
provided at queue construction time, depending on which constructor is
used.  

A few important points on Priority Queue are as follows: 


 PriorityQueue doesn’t permit null.
 We can’t create a PriorityQueue of Objects that are non-comparable
 PriorityQueue are unbound queues.
 The head of this queue is the least element with respect to the specified
ordering. If multiple elements are tied for the least value, the head is
one of those elements — ties are broken arbitrarily.
 Since PriorityQueue is not thread-safe, java
provides PriorityBlockingQueue class that implements
the BlockingQueue interface to use in a java multithreading
environment.
 The queue retrieval operations poll,  remove,  peek, and element access
the element at the head of the queue.
 It provides O(log(n)) time for add and poll methods.
 It inherits methods
from AbstractQueue, AbstractCollection, Collection, and Object cla
ss.

8. Tree:
o A tree data structure is defined as a collection of objects or entities
known as nodes that are linked together to represent or simulate
hierarchy.
o A tree data structure is a non-linear data structure because it does not
store in a sequential manner. It is a hierarchical structure as elements in
a Tree are arranged in multiple levels.
o In the Tree data structure, the topmost node is known as a root node.
Each node contains some data, and data can be of any type. In the
above tree structure, the node contains the name of the employee, so
the type of data would be a string.
o Each node contains some data and the link or reference of other nodes
that can be called children.
In the above structure, each node is labeled with some number. Each
arrow shown in the above figure is known as a link between the two
nodes.

o Root: The root node is the topmost node in the tree hierarchy. In other
words, the root node is the one that doesn't have any parent. In the
above structure, node numbered 1 is the root node of the tree. If a
node is directly linked to some other node, it would be called a parent-
child relationship.
o Child node: If the node is a descendant of any node, then the node is
known as a child node.
o Parent: If the node contains any sub-node, then that node is said to be
the parent of that sub-node.
o Sibling: The nodes that have the same parent are known as siblings.
o Leaf Node:- The node of the tree, which doesn't have any child node,
is called a leaf node. A leaf node is the bottom-most node of the tree.
There can be any number of leaf nodes present in a general tree. Leaf
nodes can also be called external nodes.
o Internal nodes: A node has atleast one child node known as
an internal
o Ancestor node:- An ancestor of a node is any predecessor node on a
path from the root to that node. The root node doesn't have any
ancestors. In the tree shown in the above image, nodes 1, 2, and 5 are
the ancestors of node 10.
o Descendant: The immediate successor of the given node is known as a
descendant of a node. In the above figure, 10 is the descendant of node
5.

1. Binary Tree:
is a tree type non-linear data structure that are mainly used for sorting and
searching because they store data in hierarchical form. In this section, we will
learn the implementation of binary tree data structure in Java. Also, provides
a short description of binary tree data structure.

A tree in which each node (parent) has at most two-child nodes (left and
right) is called binary tree. The top most node is called the root node. In a
binary tree a node contains the data and the pointer (address) of the left and
right child node.

The height of a binary tree is the number of edges between the tree's


root and its furthest (deepest) leaf. If the tree is empty, the height is 0. The
height of the node is denoted by h.
2. BST:
A binary search tree follows some order to arrange the elements. In a Binary
search tree, the value of left node must be smaller than the parent node, and
the value of right node must be greater than the parent node. This rule is
applied recursively to the left and right subtrees of the root.
Advantages of Binary search tree:
o Searching an element in the Binary search tree is easy as we always
have a hint that which subtree has the desired element.
o As compared to array and linked lists, insertion and deletion operations
are faster in BST.

9. Graph:
A graph is a data structure that stores connected data. In other words, a graph
G (or g) is defined as a set of vertices (V) and edges (E) that connects
vertices. The examples of graph are a social media network, computer
network, Google Maps, etc.
Each graph consists of edges and vertices (also called nodes). Each vertex
and edge have a relation. Where vertex represents the data and edge
represents the relation between them. Vertex is denoted by a circle with a
label on them. Edges are denoted by a line that connects nodes (vertices).
Graph Terminology:
Vertex: Vertices are the point that joints edges. It represents the data. It is
also known as a node. It is denoted by a circle and it must be labeled. To
construct a graph there must be at least a node. For example, house, bus stop,
etc.
Edge: An edge is a line that connects two vertices. It represents the relation
between the vertices. Edges are denoted by a line. For example, a path to the
bus stop from your house.
Weight: It is labeled to edge. For example, the distance between two cities is
100 km, then the distance is called weight for the edge.
Path: The path is a way to reach a destination from the initial point in a
sequence.
1. Directed Graph:
A graph in which edges represent direction is called a directed graph. In a
directed graph, we use arrows instead of lines (edges). Direction denotes the
way to reach from one node to another node. Note that in a directed graph,
we can move either in one direction or in both directions. The following
figure represents a directed graph.

2. Undirected Graph:
A graph in which edges are bidirectional is called an undirected graph. In an
undirected graph, we can traverse in any direction. Note that we can use the
same path for return through which we have traversed. While in the directed
graph we cannot return from the same path.
3. Acyclic Graph:
A graph is called an acyclic graph if zero cycles are present, and an acyclic
graph is the complete opposite of a cyclic graph.

4. DAG:
A DAG for basic block is a directed acyclic graph with the following labels
on nodes:
1. The leaves of graph are labeled by unique identifier and that identifier
can be variable names or constants.
2. Interior nodes of the graph is labeled by an operator symbol.
3. Nodes are also given a sequence of identifiers for labels to store the
computed value.
4. DAGs are a type of data structure. It is used to implement
transformations on basic blocks.
5. DAG provides a good way to determine the common sub-expression.
6. It gives a picture representation of how the value computed by the
statement is used in subsequent statements.

10. Manipulating Data Structures:


1. Lambda Expressions:
Lambda expression is a new and important feature of Java which was
included in Java SE 8. It provides a clear and concise way to represent one
method interface using an expression. It is very useful in collection library. It
helps to iterate, filter and extract data from collection.
The Lambda expression is used to provide the implementation of an interface
which has functional interface. It saves a lot of code. In case of lambda
expression, we don't need to define the method again for providing the
implementation. Here, we just write the implementation code.
Java lambda expression is treated as a function, so compiler does not
create .class file.
Functional Interface:
Lambda expression provides implementation of functional interface. An
interface which has only one abstract method is called functional interface.
Java provides an anotation @FunctionalInterface, which is used to declare an
interface as functional interface.
Why use Lambda Expression
1. To provide the implementation of Functional interface.
2. Less coding.
Java Lambda Expression Syntax
1. (argument-list) -> {body}  
Java lambda expression is consisted of three components.
1) Argument-list: It can be empty or non-empty as well.
2) Arrow-token: It is used to link arguments-list and body of expression.
3) Body: It contains expressions and statements for lambda expression.
No Parameter Syntax
1. () -> {  
2. //Body of no parameter lambda  
3. }  
One Parameter Syntax
1. (p1) -> {  
2. //Body of single parameter lambda  
3. }  
Two Parameter Syntax
1. (p1,p2) -> {  
2. //Body of multiple parameter lambda  
3. }  
2. Streams:
Java provides a new additional package in Java 8 called java.util.stream. This
package consists of classes, interfaces and enum to allows functional-style
operations on the elements. You can use stream by importing java.util.stream
package.

Stream provides following features:

o Stream does not store elements. It simply conveys elements from a


source such as a data structure, an array, or an I/O channel, through a
pipeline of computational operations.
o Stream is functional in nature. Operations performed on a stream does
not modify it's source. For example, filtering a Stream obtained from a
collection produces a new Stream without the filtered elements, rather
than removing elements from the source collection.
o Stream is lazy and evaluates code only when required.
o The elements of a stream are only visited once during the life of a
stream. Like an Iterator, a new stream must be generated to revisit the
same elements of the source.

You can use stream to filter, collect, print, and convert from one data
structure to other etc. In the following examples, we have apply various
operations with the help of stream.
III. Object Oriented Programming:
1. Classes and Objects:
1. What are Classes and Objects?
Classes and Objects are basic concepts of Object Oriented Programming
that revolve around real life entities.
CLASS:
1. Class is a set of object which shares common characteristics/ behavior
and common properties/ attributes.
2. Class is not a real world entity. It is just a template or blueprint or
prototype from which objects are created.
3. Class does not occupy memory.
4. Class is a group of variables of different data types and group of methods.
A class in java can contain:
• data member
• method
• constructor
• nested class and 
• interface
Object

It is a basic unit of Object-Oriented Programming and represents real life


entities. A typical Java program creates many objects, which as you know,
interact by invoking methods. An object consists of : 

1. State: It is represented by attributes of an object. It also reflects the


properties of an object.
2. Behavior: It is represented by methods of an object. It also reflects the
response of an object with other objects.
3. Identity: It gives a unique name to an object and enables one object to
interact with other objects.
2. Instantiation and the life cycle of an object:
As you know that Java programs run on the Java virtual machine (JVM).
When we compile the Java class, it is transformed into byte code which is
platform and machine-independent. The compiled classes are stored as
a .class file on a disk.
Step 1: Creation of .class file on disk
As you know that Java programs run on the Java virtual machine (JVM).
When we compile the Java class, it is transformed into byte code which is
platform and machine-independent. The compiled classes are stored as
a .class file on a disk.
Step 2: Loading .class file into memory
After that, the Java runtime finds out that class on the disk that is in the form
of a .class file. Java classloader loads that class into memory and then  Java
runtime reads it into the memory.
Step 3: Looking for initialized static members of class
Now Java looks for all initialized static members of the class such as static
method, static field, and static block.
You always remember that all the static members of the class do not belong
to any particular instance of the class. It belongs to the class itself and is
shared by all the objects created from the class.
Step 4: Ways to initialize class in java
A class can be initialized in two ways in Java. First, when you access a static
field or static method of the class.
For example, when you run the main method in a class, the class is initialized
because the main method is static and the second way is when object or
instance of the class is created using the new keyword, the class is initialized.
Step 5: Allocation of memory for object and reference variable
In stage 5, Java allocates the memory on the heap for the object and memory
on the stack for object reference variable.
Step 6: Calling of the constructor of class
After allocating the memory, JVM calls the constructor of the class which is
like a method but it is called only once when the object is created.
Thus, the object lives its life and providing access to its fields and methods
whatever we want and need to access them.
Step 7: Removing of object and reference variable from memory
When the accessing of field and methods are completed, the object and its
reference are removed from the memory by the JVM. At this time the object
has died.
You don’t have to destroy objects yourself. Actually, when the object is no
longer in use, Java runtime calls the garbage collector to destroy all the
objects. Thus, objects are born, live, and die.
3. Access Modifiers:
There are two types of modifiers in Java: access modifiers and non-access
modifiers.

The access modifiers in Java specifies the accessibility or scope of a field,


method, constructor, or class. We can change the access level of fields,
constructors, methods, and class by applying the access modifier on it.

There are four types of Java access modifiers:

1. Private: The access level of a private modifier is only within the class.
It cannot be accessed from outside the class.
2. Default: The access level of a default modifier is only within the
package. It cannot be accessed from outside the package. If you do not
specify any access level, it will be the default.
3. Protected: The access level of a protected modifier is within the
package and outside the package through child class. If you do not
make the child class, it cannot be accessed from outside the package.
4. Public: The access level of a public modifier is everywhere. It can be
accessed from within the class, outside the class, within the package
and outside the package.
Access Modifier within class within package outside package by subclass only outside package

Private Y N N N

Default Y Y N N

Protected Y Y Y N

Public Y Y Y Y

4. Methods:
A method is a block of code or collection of statements or a set of code
grouped together to perform a certain task or operation. It is used to achieve
the reusability of code. We write a method once and use it many times. We
do not require to write code again and again. It also provides the easy
modification and readability of code, just by adding or removing a chunk of
code. The method is executed only when we call or invoke it.
5. Types of Constructors:
Java constructors or constructors in Java is a terminology been used to
construct something in our programs. A constructor in Java is a special
method that is used to initialize objects. The constructor is called when an
object of a class is created. It can be used to set initial values for object
attributes. 
In Java, a constructor is a block of codes similar to the method. It is called
when an instance of the class is created. At the time of calling the
constructor, memory for the object is allocated in the memory. It is a special
type of method which is used to initialize the object. Every time an object is
created using the new() keyword, at least one constructor is called.
Types of Constructors in Java
Now is the correct time to discuss the types of the constructor, so primarily
there are two types of constructors in java: 
 No-argument constructor
 Parameterized Constructor
1. No-argument constructor: A constructor that has no parameter is known
as the default constructor. If we don’t define a constructor in a class, then the
compiler creates a default constructor(with no arguments) for the class.
And if we write a constructor with arguments or no arguments then the
compiler does not create a default constructor. 
2. Parameterized Constructor: A constructor that has parameters is known
as parameterized constructor. If we want to initialize fields of the class with
our own values, then use a parameterized constructor.
6. Nested Class:
Java inner class or nested class is a class that is declared inside the class or
interface.

We use inner classes to logically group classes and interfaces in one place to
be more readable and maintainable.

Additionally, it can access all the members of the outer class, including
private data members and methods.

Advantage of Java inner classes

There are three advantages of inner classes in Java. They are as follows:

1. Nested classes represent a particular type of relationship that is it can


access all the members (data members and methods) of the outer
class, including private.
2. Nested classes are used to develop more readable and maintainable
code because it logically group classes and interfaces in one place only.
3. Code Optimization: It requires less code to write.

Need of Java Inner class

Sometimes users need to program a class in such a way so that no other class
can access it. Therefore, it would be better if you include it within other
classes.
If all the class objects are a part of the outer object then it is easier to nest that
class inside the outer class. That way all the outer class can access all the
objects of the inner class.

7. Accessor:
o Getter and Setter are methods used to protect your data and make your
code more secure. Getter returns the value (accessors), it returns the
value of data type int, String, double, float, etc. For the program’s
convenience, getter starts with the word “get” followed by the variable
name.
o While Setter sets or updates the value (mutators). It sets the value for
any variable used in a class’s programs. and starts with the word “set”
followed by the variable name. Getter and Setter make the
programmer convenient in setting and getting the value for a particular
data type. In both getter and setter, the first letter of the variable
should be capital.
8. Static Variable:
If you declare any variable as static, it is known as a static variable.

o The static variable can be used to refer to the common property of all
objects (which is not unique for each object), for example, the company
name of employees, college name of students, etc.
o The static variable gets memory only once in the class area at the time
of class loading.

9. Static Method:
If you apply static keyword with any method, it is known as static method.

o A static method belongs to the class rather than the object of a class.
o A static method can be invoked without the need for creating an
instance of a class.
o A static method can access static data member and can change the
value of it.
10. Final Class:
The final modifier for finalizing the implementations of classes, methods, and
variables.

The main purpose of using a class being declared as final is to prevent the
class from being subclassed. If a class is marked as final then no class can
inherit any feature from the final class.

You cannot extend a final class. If you try it gives you a compile time error.

11. Final Method:


The Method with Final Keyword cannot be overridden in the subclasses. The
purpose of the Final Method is to declare methods of how’s definition can not
be changed by a child or subclass that extends it.

The purpose of creating the final methods is to restrict the unwanted and
improper use of method definition while overriding the method.

Though it is not logically incorrect, it can change the meaning of the method
and the reader might interpret it wrongly. Therefore, in such cases to prevent
the unwanted method definitions, we declare methods as final.

Suppose we make a class Animal and declare a non-final method sound() in


it. Someone creates a Dog class and overrides the sound() method in it and
prints the wrong sound of the Dog such as Meow or Roar, etc.

12. Enum:
The Enum in Java is a data type which contains a fixed set of constants.

It can be used for days of the week (SUNDAY, MONDAY, TUESDAY,


WEDNESDAY, THURSDAY, FRIDAY, and SATURDAY) , directions
(NORTH, SOUTH, EAST, and WEST), season (SPRING, SUMMER,
WINTER, and AUTUMN or FALL), colors (RED, YELLOW, BLUE,
GREEN, WHITE, and BLACK) etc. According to the Java naming
conventions, we should have all constants in capital letters. So, we have
enum constants in capital letters.

Java Enums can be thought of as classes which have a fixed set of constants
(a variable that does not change). The Java enum constants are static and final
implicitly. It is available since JDK 1.5.

Enums are used to create our own data type like classes. The enum data type
(also known as Enumerated Data Type) is used to define an enum in Java.
Unlike C/C++, enum in Java is more powerful. Here, we can define an enum
either inside the class or outside the class.

2. Inheritance:
1. What is Inheritance:
Inheritance in Java is a mechanism in which one object acquires all the
properties and behaviors of a parent object. It is an important part of OOPs

(Object Oriented programming system).

The idea behind inheritance in Java is that you can create new classes

that are built upon existing classes. When you inherit from an existing class,
you can reuse methods and fields of the parent class. Moreover, you can add
new methods and fields in your current class also.
Inheritance represents the IS-A relationship which is also known as
a parent-child relationship.
Why use inheritance in java:
 For method overriding (so runtime polymorphism can be archived).
 For local reusable
Terms used in Inheritance:
o Class: A class is a group of objects which have common properties. It
is a template or blueprint from which objects are created.
o Sub Class/Child Class: Subclass is a class which inherits the other
class. It is also called a derived class, extended class, or child class.
o Super Class/Parent Class: Superclass is the class from where a
subclass inherits the features. It is also called a base class or a parent
class.
o Reusability: As the name specifies, reusability is a mechanism which
facilitates you to reuse the fields and methods of the existing class
when you create a new class. You can use the same fields and methods
already defined in the previous class.

2. Types of Inheritance:

3. Advantages of Inheritance:
Benefits of Inheritance
 Inheritance helps in code reuse. The child class may use the code
defined in the parent class without re-writing it.
 Inheritance can save time and effort as the main code need not be
written again.
 Inheritance provides a clear model structure which is easy to
understand.
 An inheritance leads to less development and maintenance costs.
 With inheritance, we will be able to override the methods of the base
class so that the meaningful implementation of the base class method
can be designed in the derived class. An inheritance leads to less
development and maintenance costs.
 In inheritance base class can decide to keep some data private so that it
cannot be altered by the derived class.
Costs of Inheritance
 Inheritance decreases the execution speed due to the increased time and
effort it takes, the program to jump through all the levels of overloaded
classes.
 Inheritance makes the two classes (base and inherited class) get tightly
coupled. This means one cannot be used independently of each other.
 The changes made in the parent class will affect the behavior of child
class too.
 The overuse of inheritance makes the program more complex.
4. Object Class:

When it comes down to inheritance in Java we are basically dealing with


deriving a class from another class. Now let us understand inheritance a step
deeper so when a particular class inherits a class we do have a keyword that
we use in order to refer to parent constructor via super keyword.

It is used as a suffix followed by the “.” operator in constructor/s in a class.


This property of parent class(super class) also gets initiated well before child
class(sub class) inherits and uses them. 
We already have seen above when we created an object of a subclass, the
constructor of the same class is called, if not made the default constructor is
called while we called superclass constructor without creating an object of
class inside subclass via keyword.  

In inheritance, subclass acquires super class properties. An important point to


note is, when a subclass object is created, a separate object of a superclass
object will not be created. Only a subclass object is created that has
superclass variables. This situation is different from a normal assumption that
a constructor call means an object of the class is created, so we can’t blindly
say that whenever a class constructor is executed, the object of that class is
created or not.

5. Casting and instanceof:


Typecasting is the assessment of the value of one primitive data type to
another type. In java, there are two types of casting namely upcasting and
downcasting as follows:
1. Upcasting is casting a subtype to a super type in an upward direction to
the inheritance tree. It is an automatic procedure for which there are no
efforts poured in to do so where a sub-class object is referred by a
superclass reference variable. One can relate it with dynamic
polymorphism.
 Implicit casting means class typecasting done by the compiler
without cast syntax.
 Explicit casting means class typecasting done by the programmer
with cast syntax.
2. Downcasting refers to the procedure when subclass type refers to the
object of the parent class is known as downcasting. If it is performed
directly compiler gives an error as ClassCastException is thrown at
runtime. It is only achievable with the use of instanceof operator The
object which is already upcast, that object only can be performed
downcast.
In order to perform class type casting we have to follow these two rules as
follows:
1. Classes must be “IS-A-Relationship “
2. An object must have the property of a class in which it is going to cast.

3. Polymorphism:
1. What is Polymorphism:
Polymorphism in Java is a concept by which we can perform a single
action in different ways. Polymorphism is derived from 2 Greek words: poly
and morphs. The word "poly" means many and "morphs" means forms. So
polymorphism means many forms.
There are two types of polymorphism in Java: compile-time polymorphism
and runtime polymorphism. We can perform polymorphism in java by
method overloading and method overriding.
If you overload a static method in Java, it is the example of compile time
polymorphism.
2. Method Overriding:
If subclass (child class) has the same method as declared in the parent class, it
is known as method overriding in Java.
In other words, If a subclass provides the specific implementation of the
method that has been declared by one of its parent class, it is known as
method overriding.
Usage of Java Method Overriding
o Method overriding is used to provide the specific implementation of a
method which is already provided by its superclass.
o Method overriding is used for runtime polymorphism
Rules for Java Method Overriding
1. The method must have the same name as in the parent class
2. The method must have the same parameter as in the parent class.
3. There must be an IS-A relationship (inheritance).
3. Method Overloading:
If a class has multiple methods having same name but different in parameters,
it is known as Method Overloading.

If we have to perform only one operation, having same name of the methods
increases the readability of the program.

Suppose you have to perform addition of the given numbers but there can be
any number of arguments, if you write the method such as a(int,int) for two
parameters, and b(int,int,int) for three parameters then it may be difficult for
you as well as other programmers to understand the behavior of the method
because its name differs.

Advantage of method overloading:


Method overloading increases the readability of the program.
Different ways to overload the method:
There are two ways to overload the method in java
1. By changing number of arguments
2. By changing the data type
4. Static vs. Dynamic Polymorphism:
In Java, polymorphism is a concept of object-oriented programming that
allows us to perform a single action in different forms. In this section, we will
discuss only the dynamic polymorphism in Java.
Types of Polymorphism
There are two types of polymorphism in java:
o Static Polymorphism (Compile Time Polymorphism)
o Dynamic Polymorphism (Run Time Polymorphism)

4. Abstract Classes and Interfaces:

1. Abstraction:
Abstraction is a process of hiding the implementation details and showing
only functionality to the user.
Another way, it shows only essential things to the user and hides the internal
details, for example, sending SMS where you type the text and send the
message. You don't know the internal processing about the message delivery.
Abstraction lets you focus on what the object does instead of how it does it.
Ways to achieve Abstraction
There are two ways to achieve abstraction in java
1. Abstract class (0 to 100%)
2. Interface (100%)

Advantages of Abstraction
1. It reduces the complexity of viewing things.
2. Avoids code duplication and increases reusability.
3. Helps to increase the security of an application or program as only
essential details are provided to the user.
4. It improves the maintainability of the application. 
5. It improves the modularity of the application. 
6. The enhancement will become very easy because without affecting
end-users we can able to perform any type of changes in our internal
system. 
2. Abstract Classes:
A class is declared abstract using the abstract keyword. It can have zero or
more abstract and non-abstract methods. We need to extend the abstract class
and implement its methods. It cannot be instantiated.
Abstract classes and Abstract methods :  
1. An abstract class is a class that is declared with an abstract
keyword.
2. An abstract method is a method that is declared without
implementation.
3. An abstract class may or may not have all abstract methods. Some
of them can be concrete methods
4. A method-defined abstract must always be redefined in the
subclass, thus making overriding compulsory or making the
subclass itself abstract.
5. Any class that contains one or more abstract methods must also be
declared with an abstract keyword.
6. There can be no object of an abstract class. That is, an abstract
class can not be directly instantiated with the new operator.
7. An abstract class can have parameterized constructors and the
default constructor is always present in an abstract class.

3. Abstract Method:
A method declared using the abstract keyword within an abstract class and
does not have a definition (implementation) is called an abstract method.
When we need just the method declaration in a super class, it can be achieved
by declaring the methods as abstracts.
Abstract method is also called subclass responsibility as it doesn't have the
implementation in the super class. Therefore a subclass must override it to
provide the method definition.
Points to Remember
Following points are the important rules for abstract method in Java:
o An abstract method do not have a body (implementation), they just
have a method signature (declaration). The class which extends the
abstract class implements the abstract methods.
o If a non-abstract (concrete) class extends an abstract class, then the
class must implement all the abstract methods of that abstract class. If
not the concrete class has to be declared as abstract as well.
o As the abstract methods just have the signature, it needs to have
semicolon (;) at the end.
o Following are various illegal combinations of other modifiers for
methods with respect to abstract modifier:
o final
o abstract native
o abstract synchronized
o abstract static
o abstract private
o abstract strictfp
o If a class contains abstract method it needs to be abstract and vice versa
is not true.
4. Interfaces:
An interface in Java is a blueprint of a class. It has static constants and
abstract methods.
The interface in Java is a mechanism to achieve abstraction. There can be
only abstract methods in the Java interface, not method body. It is used to
achieve abstraction and multiple inheritance in Java.
In other words, you can say that interfaces can have abstract methods and
variables. It cannot have a method body.
Java Interface also represents the IS-A relationship.
It cannot be instantiated just like the abstract class.
Since Java 8, we can have default and static methods in an interface.
Since Java 9, we can have private methods in an interface.
Why use Java interface?
There are mainly three reasons to use interface. They are given below.
o It is used to achieve abstraction.
o By interface, we can support the functionality of multiple inheritance.
o It can be used to achieve loose coupling.
5. Multiple Inheritance:

Multiple inheritance in java is the capability of creating a single class with


multiple superclasses. Unlike some other popular object oriented
programming languages like C++, java doesn’t provide support for multiple
inheritance in classes. Java doesn’t support multiple inheritances in classes
because it can lead to diamond problem and rather than providing some
complex way to solve it, there are better ways through which we can achieve
the same result as multiple inheritances.
5. Further OOP Principles:

1. Composition:

The composition is a design technique in java to implement a has-


a relationship. Java Inheritance is used for code reuse purposes and the same
we can do by using composition. The composition is achieved by using an
instance variable that refers to other objects. If an object contains the other
object and the contained object cannot exist without the existence of that
object, then it is called composition. In more specific words composition is a
way of describing reference between two or more classes using instance
variable and an instance should be created before it is used. 
The benefits of using Composition is as follows: 
1. Composition allows the reuse of code.
2. Java doesn’t support multiple inheritances but by using composition we
can achieve it.
3. Composition offers better test-ability of a class.
4. By using composition, we are flexible enough to replace the
implementation of a composed class with a better and improved
version.
5. By using composition, we can also change the member objects at run
time, to dynamically change the behaviour of your program.
Do remember the certain key points of composition in java which are as
follows:
 It represents a has-a relationship.
 In composition, both entities are dependent on each other.
 When there is a composition between two entities, the composed object
cannot exist without the other entity. For example, A library can have
no. of books on the same or different subjects. So, If the Library gets
destroyed then All books within that particular library will be
destroyed. This is because books can not exist without a library.
 The composition is achieved by using an instance variable that refers to
other objects.
 We have to favour Composition over Inheritance.

2. Composition vs. Inheritance: Advantages and


Disadvantages:
3. Association:
Association in Java defines the connection between two classes that are set
up through their objects. Association manages one-to-one, one-to-many,
and many-to-many relationships. In Java, the multiplicity between objects is
defined by the Association. It shows how objects communicate with each
other and how they use the functionality and services provided by that
communicated object. Association manages one-to-one, one-to-many, many-
to-one and many-to-many relationships.

Let's take an example of each relationship to manage by the Association.


1. A person can have only one passport. It defines the one-to-one
2. If we talk about the Association between a College and Student, a
College can have many students. It defines the one-to-many
3. A state can have several cities, and those cities are related to that single
state. It defines the many-to-one
4. A single student can associate with multiple teachers, and multiple
students can also be associated with a single teacher. Both are created
or deleted independently, so it defines the many-to-many
4. Aggregation:
Aggregation is a term which is used to refer one way relationship between
two objects. For example, Student class can have reference of Address class
but vice versa does not make sense.
In Java, aggregation represents HAS-A relationship, which means when a
class contains reference of another class known to have aggregation.
The HAS-A relationship is based on usage, rather than inheritance. In other
words, class A has-a relationship with class B, if class A has a reference to an
instance of class B.

5. 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. Another
way to think about encapsulation is, that 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 its
own class in which it is declared.
As in encapsulation, the data in a class is hidden from other classes using the
data hiding concept which is achieved by making the members or methods of
a class private, and the class is exposed to the end-user or the world without
providing any details behind implementation using the abstraction concept,
so it is also known as a combination of data-hiding and abstraction.
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.
It is more defined with the setter and getter method.
Advantages of Encapsulation:
Data Hiding: it is a way of restricting the access of our data members by
hiding the implementation details. Encapsulation also provides a way for data
hiding. The user will have no idea about the inner implementation of the
class. It will not be visible to the user how the class is storing values in the
variables. The user will only know that we are passing the values to a setter
method and variables are getting initialized with that value.
Increased Flexibility: We can make the variables of the class read-only or
write-only depending on our requirement. If we wish to make the variables
read-only then we have to omit the setter methods like setName(), setAge(),
etc. from the above program or if we wish to make the variables write-only
then we have to omit the get methods like getName(), getAge(), etc. from the
above program
Reusability: Encapsulation also improves the re-usability and is easy to
change with new requirements.
Testing code is easy: Encapsulated code is easy to test for unit testing.
6. Single Responsibility Principle:
As the name suggests, this principle states that each class should have one
responsibility, one single purpose. This means that a class will do only one
job, which leads us to conclude it should have only one reason to change.
7. Open/Closed Principle:
As the name suggests, this principle states that software entities should be
open for extension, but closed for modification. As a result, when the
business requirements change then the entity can be extended, but not
modified.

IV. Algorithms:
1. Time Complexity:
The time complexity of an algorithm quantifies the amount of time taken by
an algorithm to run as a function of the length of the input. Note that the time
to run is a function of the length of the input and not the actual execution
time of the machine on which the algorithm is running on.
In order to calculate time complexity on an algorithm, it is assumed that
a constant time c is taken to execute one operation, and then the total
operations for an input length on N are calculated.

The Time Complexity of an algorithm/code is not equal to the actual time


required to execute a particular code, but the number of times a statement
executes. 

2. Space Complexity:
The space complexity of an algorithm or a computer program is the amount
of memory space required to solve an instance of the computational
problem as a function of characteristics of the input. It is the memory
required by an algorithm until it executes completely.
3. Techniques:
1. Brute Force Algorithms:
Brute force algorithm is an algorithm that goes straight forward, through all
cases to get suitable solutions.

For example, we have a dictionary, and we want to find a word in it. With
brute force algorithm, we will go with the natural order - from A to Z of this
dictionary. If this word starts with z character, it takes so much time to find it.
2. Greedy Algorithms:
Greedy is an algorithmic paradigm that builds up a solution piece by piece,
always choosing the next piece that offers the most obvious and immediate
benefit. So the problems where choosing locally optimal also leads to global
solution are the best fit for Greedy.

3. Divide and conquer:


Divide and Conquer is an algorithmic pattern. In algorithmic methods, the
design is to take a dispute on a huge input, break the input into minor pieces,
decide the problem on each of the small pieces, and then merge the piecewise
solutions into a global solution. This mechanism of solving the problem is
called the Divide & Conquer Strategy.

Divide and Conquer algorithm consists of a dispute using the following three
steps.

1. Divide the original problem into a set of subproblems.


2. Conquer: Solve every subproblem individually, recursively.
3. Combine: Put together the solutions of the subproblems to get the
solution to the whole problem.
Generally, we can follow the divide-and-conquer approach in a three-step
process.

Examples: The specific computer algorithms are based on the Divide &


Conquer approach:

1. Maximum and Minimum Problem


2. Binary Search
3. Sorting (merge sort, quick sort)
4. Tower of Hanoi.

4. Two Pointers Technique:


Two pointer approach is an essential part of a programmer’s toolkit,
especially in technical interviews. The name does justice in this case, it
involves using two pointers to save time and space. (Here, pointers are
basically array indexes).
Just like Binary Search is an optimization on the number of trials needed to
achieve the result, this approach is used for the same benefit.

There are primarily two ways of implementing the two-pointer technique:

1. One pointer at each end

One pointer starts from beginning and other from the end and they proceed
towards each other

2. Different Paces

Both pointers start from the beginning but one pointer moves at a faster pace
than the other one.

5. Merge Intervals Technique:


The Merge Intervals pattern is an efficient technique to deal with
overlapping intervals. In a lot of problems involving intervals, you either
need to find overlapping intervals or merge intervals if they overlap. The
pattern works like this:
Given two intervals (‘a’ and ‘b’), there will be six different ways the two
intervals can relate to each other are illustrated in the attached Image.

6. Sliding Window Technique:


Window Sliding Technique is a computational technique which aims to
reduce the use of nested loop and replace it with a single loop, thereby
reducing the time complexity.
What is Sliding Window?
Consider a long chain connected together. Suppose you want to apply oil in
the complete chain with your hands, without pouring the oil from above.
One way to do so is to: 
 pick some oil, 
 apply onto a section of chain, 
 then again pick some oil
 then apply it to the next section where oil is not applied yet
 and so on till the complete chain is oiled.
Another way to do so, is to use a cloth, dip it in oil, and then hold onto one
end of the chain with this cloth. Then instead of re-dipping it again and again,
just slide the cloth with hand onto the next section, and next, and so on till the
other end.
The second way is known as the Sliding window technique and the portion
which is slided from one end to end, is known as Sliding Window.

7. Cyclic Sort Technique:


Cycle sort is an in-place sorting Algorithm, unstable sorting algorithm , and
a comparison sort that is theoretically optimal in terms of the total number
of writes to the original array. 
 
 It is optimal in terms of the number of memory writes. It minimizes
the number of memory writes to sort (Each value is either written
zero times if it’s already in its correct position or written one time
to its correct position.)
 It is based on the idea that the array to be sorted can be divided into
cycles. Cycles can be visualized as a graph. We have n nodes and
an edge directed from node i to node j if the element at i-th index
must be present at j-th index in the sorted array. 
Cycle in arr[] = {2, 4, 5, 1, 3} 

 Cycle in arr[] = {4, 3, 2, 1} 

We one by one consider all cycles. We first consider the cycle that includes
the first element. We find the correct position of the first element, and place
it at its correct position, say j. We consider the old value of arr[j] and find
its correct position, we keep doing this till all elements of the current cycle
are placed at the correct position, i.e., we don’t come back to the cycle
starting point.

8. Topological Sorting:
Topological sorting for Directed Acyclic Graph (DAG) is a linear ordering
of vertices such that for every directed edge u v, vertex u comes before v in
the ordering. Topological Sorting for a graph is not possible if the graph is
not a DAG.
For example, a topological sorting of the following graph is “5 4 2 3 1 0”.
There can be more than one topological sorting for a graph. For example,
another topological sorting of the following graph is “4 5 2 3 1 0”. The first
vertex in topological sorting is always a vertex with in-degree as 0 (a vertex
with no incoming edges).

9. Top K Elements Technique:


Approach: The idea is to store the top k elements with maximum
frequency. To store them a vector or an array can be used. To keep the track
of frequencies of elements creates a HashMap to store element-frequency
pairs. Given a stream of numbers, when a new element appears in the stream
update the frequency of that element in HashMap and put that element at the
end of the list of K numbers (total k+1 elements) now compare adjacent
elements of the list and swap if higher frequency element is stored next to it.
Algorithm: 
1. Create a Hashmap hm, and an array of k + 1 length.
2. Traverse the input array from start to end.
3. Insert the element at k+1 th position of the array, update the
frequency of that element in HashMap.
4. Now, traverse the temp array from start to end – 1
5. For very element, compare the frequency and swap if higher
frequency element is stored next to it, if the frequency is same then
swap is the next element is greater.
6. print the top k element in each traversal of original array.
10. Min Heaps and Max Heaps Technique:
A Heap is a special Tree-based data structure in which the tree is a complete
binary tree. Since a heap is a complete binary tree, a heap with N nodes
has log N height. It is useful to remove the highest or lowest priority
element. It is typically represented as an array. There are two types of Heaps
in the data structure.

Min-Heap

In a Min-Heap the key present at the root node must be less than or equal
among the keys present at all of its children. The same property must be
recursively true for all sub-trees in that Binary Tree. In a Min-Heap the
minimum key element present at the root. Below is the Binary Tree that
satisfies all the property of Min Heap.

Max Heap

In a Max-Heap the key present at the root node must be greater than or


equal among the keys present at all of its children. The same property must
be recursively true for all sub-trees in that Binary Tree. In a Max-Heap the
maximum key element present at the root. Below is the Binary Tree that
satisfies all the property of Max Heap.
4. Sorting:
1. Selection Sort:
The selection sort algorithm sorts an array by repeatedly finding the
minimum element (considering ascending order) from the unsorted part and
putting it at the beginning. 
The algorithm maintains two subarrays in a given array.
 The subarray which already sorted. 
 The remaining subarray was unsorted.
In every iteration of the selection sort, the minimum element (considering
ascending order) from the unsorted subarray is picked and moved to the
sorted subarray. 
2. Bubble Sort:
Bubble Sort is the simplest sorting algorithm that works by repeatedly
swapping the adjacent elements if they are in the wrong order. This
algorithm is not suitable for large data sets as its average and worst-case
time complexity is quite high.

3. Insertion Sort:
Insertion sort is a simple sorting algorithm that works similar to the way
you sort playing cards in your hands. The array is virtually split into a sorted
and an unsorted part. Values from the unsorted part are picked and placed at
the correct position in the sorted part.
Characteristics of Insertion Sort:
 This algorithm is one of the simplest algorithm with simple
implementation
 Basically, Insertion sort is efficient for small data values
 Insertion sort is adaptive in nature, i.e. it is appropriate for data sets
which are already partially sorted.
4. Merge Sort:
The Merge Sort algorithm is a sorting algorithm that is based on the Divide
and Conquer paradigm. In this algorithm, the array is initially divided into
two equal halves and then they are combined in a sorted manner.
Merge Sort Working Process:
Think of it as a recursive algorithm continuously splits the array in half until
it cannot be further divided. This means that if the array becomes empty or
has only one element left, the dividing will stop, i.e. it is the base case to
stop the recursion. If the array has multiple elements, split the array into
halves and recursively invoke the merge sort on each of the halves. Finally,
when both halves are sorted, the merge operation is applied. Merge
operation is the process of taking two smaller sorted arrays and combining
them to eventually make a larger one.
5. Quick Sort:
Like Merge Sort, QuickSort is a Divide and Conquer algorithm . It picks an
element as a pivot and partitions the given array around the picked pivot.
There are many different versions of quickSort that pick pivot in different
ways. 
 Always pick the first element as a pivot.
 Always pick the last element as a pivot (implemented below)
 Pick a random element as a pivot.
 Pick median as the pivot.
The key process in quickSort is a partition(). The target of partitions is,
given an array and an element x of an array as the pivot, put x at its correct
position in a sorted array and put all smaller elements (smaller than x)
before x, and put all greater elements (greater than x) after x. All this should
be done in linear time.
6. Heap Sort:
Heap sort is a comparison-based sorting technique based on Binary
Heap data structure. It is similar to the selection sort where we first find the
minimum element and place the minimum element at the beginning. Repeat
the same process for the remaining elements.
 Heap sort is an in-place algorithm. 
 Its typical implementation is not stable, but can be made stable
(See this)
 Typically 2-3 times slower than well-implemented QuickSort.  The
reason for slowness is a lack of locality of reference.
Advantages of heapsort:
 Efficiency –  The time required to perform Heap sort increases
logarithmically while other algorithms may grow exponentially slower
as the number of items to sort increases. This sorting algorithm is very
efficient.
 Memory Usage – Memory usage is minimal because apart from what
is necessary to hold the initial list of items to be sorted, it needs no
additional memory space to work

 Simplicity –  It is simpler to understand than other equally efficient
sorting algorithms because it does not use advanced computer science
concepts such as recursion

7. Bucket Sort:

Bucket sort is mainly useful when input is uniformly distributed over a range.
For example, consider the following problem. 
Sort a large set of floating point numbers which are in range from 0.0 to 1.0
and are uniformly distributed across the range. How do we sort the numbers
efficiently?
A simple way is to apply a comparison based sorting algorithm. The lower
bound for Comparison based sorting algorithm (Merge Sort, Heap Sort,
Quick-Sort .. etc) is Ω(n Log n), i.e., they cannot do better than nLogn. 
Can we sort the array in linear time? Counting sort can not be applied here as
we use keys as index in counting sort. Here keys are floating point numbers.  
5. Searching:
1. Tree Traversal Algorithms (Pre-order, In-Order, Post-
Order):
Traversal is a process to visit all the nodes of a tree and may print their values
too. Because, all nodes are connected via edges (links) we always start from
the root (head) node. That is, we cannot randomly access a node in a tree.
There are three ways which we use to traverse a tree −
 In-order Traversal
 Pre-order Traversal
 Post-order Traversal
Generally, we traverse a tree to search or locate a given item or key in the
tree or to print all the values it contains.
In-order Traversal
In this traversal method, the left subtree is visited first, then the root and later
the right sub-tree. We should always remember that every node may
represent a subtree itself.
If a binary tree is traversed in-order, the output will produce sorted key
values in an ascending order.
We start from A, and following in-order traversal, we move to its left
subtree B. B is also traversed in-order. The process goes on until all the nodes
are visited. The output of inorder traversal of this tree will be −
D→B→E→A→F→C→G
Pre-order Traversal

In this traversal method, the root node is visited first, then the left subtree and
finally the right subtree.
We start from A, and following pre-order traversal, we first visit A itself and
then move to its left subtree B. B is also traversed pre-order. The process
goes on until all the nodes are visited. The output of pre-order traversal of
this tree will be −

A→B→D→E→C→F→G

Post-order Traversal

In this traversal method, the root node is visited last, hence the name. First we
traverse the left subtree, then the right subtree and finally the root node.

We start from A, and following Post-order traversal, we first visit the left
subtree B. B is also traversed post-order. The process goes on until all the
nodes are visited. The output of post-order traversal of this tree will be −

D→E→B→F→G→C→A

2. Graph Traversal Algorithms (BFS, DFS):


Before looking at the differences between BFS and DFS, we first should
know about BFS and DFS separately.
What is BFS?
stands for Breadth First Search. It is also known as level order traversal.
The Queue data structure is used for the Breadth First Search traversal. When
we use the BFS algorithm for the traversal in a graph, we can consider any
node as a root node.
What is DFS?
stands for Depth First Search. In DFS traversal, the stack data structure is
used, which works on the LIFO (Last In First Out) principle. In DFS,
traversing can be started from any node, or we can say that any node can be
considered as a root node until the root node is not mentioned in the problem.
In the case of BFS, the element which is deleted from the Queue, the adjacent
nodes of the deleted node are added to the Queue. In contrast, in DFS, the
element which is removed from the stack, then only one adjacent node of a
deleted node is added in the stack.

Differences between BFS and DFS


The following are the differences between the BFS and DFS:
BFS DFS

Full form BFS stands for Breadth First Search. DFS stands for Depth First Search.

Technique It a vertex-based technique to find the shortest path in It is an edge-based technique because the vertices
a graph.
along the edge are explored first from the starting to the
end node.

Definition BFS is a traversal technique in which all the nodes of DFS is also a traversal technique in which traversal is
the same level are explored first, and then we move to
the next level. started from the root node and explore the nodes as far
as possible until we reach the node that has no unvisited
adjacent nodes.

Data Structure Queue data structure is used for the BFS traversal. Stack data structure is used for the BFS traversal.

Backtracking BFS does not use the backtracking concept. DFS uses backtracking to traverse all the unvisited
nodes.

Number of edges BFS finds the shortest path having a minimum number In DFS, a greater number of edges
of edges to traverse from the source to the destination
vertex. are required to traverse from the source vertex to the
destination vertex.

Optimality BFS traversal is optimal for those vertices which are DFS traversal is optimal for those
to be searched closer to the source vertex.
graphs in which solutions are away from the source
vertex.

Speed BFS is slower than DFS. DFS is faster than BFS.

Suitability for It is not suitable for the decision tree because it It is suitable for the decision tree. Based on the decision,
decision tree requires exploring all the neighboring nodes first. it explores all the paths. When the goal is found, it stops
its traversal.

Memory efficient It is not memory efficient as it requires more memory It is memory efficient as it requires less memory than
than DFS. BFS.
3. Linear Search:
Linear search is used to search a key element from multiple elements. Linear
search is less used today because it is slower than binary search and hashing.

Algorithm:

o Step 1: Traverse the array


o Step 2: Match the key element with array element
o Step 3: If key element is found, return the index position of the array
element
o Step 4: If key element is not found, return -1

4. Binary Search:
Binary Search is a searching algorithm used in a sorted array
by repeatedly dividing the search interval in half. The idea of binary search
is to use the information that the array is sorted and reduce the time
complexity to O(Log n). 
Binary Search Algorithm: The basic steps to perform Binary Search are:
 Begin with the mid element of the whole array as a search key.
 If the value of the search key is equal to the item then return an index
of the search key.
 Or if the value of the search key is less than the item in the middle of
the interval, narrow the interval to the lower half.
 Otherwise, narrow it to the upper half.
 Repeatedly check from the second point until the value is found or the
interval is empty.
Binary Search Algorithm can be implemented in the following two ways
1. Iterative Method
2. Recursive Method

You might also like