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

What is Java

Java is a high-level, object-oriented programming language known for its platform independence, allowing programs to run on any system with a Java Virtual Machine (JVM). The document covers Java's basic syntax, including data types, variable declaration, control structures, loops, and packages, providing a comprehensive guide for beginners. It also explains the pass-by-value parameter passing mechanism and the significance of the main() method as the entry point for Java applications.

Uploaded by

buddala nandini
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

What is Java

Java is a high-level, object-oriented programming language known for its platform independence, allowing programs to run on any system with a Java Virtual Machine (JVM). The document covers Java's basic syntax, including data types, variable declaration, control structures, loops, and packages, providing a comprehensive guide for beginners. It also explains the pass-by-value parameter passing mechanism and the significance of the main() method as the entry point for Java applications.

Uploaded by

buddala nandini
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 42

What is Java?

Java is a high-level programming language developed by James Gosling in 1995. It follows the principles of object-oriented programming (OOP) and is
widely used for developing large-scale applications.

Why is Java a Platform-Independent Language?

Java is considered platform-independent because it can run on any operating system or hardware that has a Java Virtual Machine (JVM).

When we write a Java program, the Java compiler converts the source code into an intermediate code called bytecode. This bytecode is platform-neutral,
meaning it is not tied to any specific operating system or hardware.

To execute the bytecode, a Java Virtual Machine (JVM) is required. Each operating system has its own implementation of the JVM, which takes the
bytecode and translates it into machine code that the underlying system can understand.

This architecture ensures that the same Java program can run on multiple platforms without needing any modifications, making Java truly
platform-independent.

This guide provides an introduction to the basic syntax in Java. Here's a brief summary of the key concepts:

1. Overview

Java is a statically-typed, object-oriented programming language that is platform-independent. Programs can be written and compiled on one machine and
executed on another without modification.

2. Data Types

Java has two categories of data types:

●​ Primitive Types: These include types like int, long, char, boolean, etc.
●​ Reference Types: These are objects, such as instances of classes like String.

3. Declaring Variables

To declare a variable in Java, specify its type and name:

int a;

double c;

Variables can be initialized when declared:

int a = 10;

4. Arrays

Arrays are used to store collections of values of a specific type. The syntax to declare an array is:

int[] numbers = new int[100];

Access elements using indices:

numbers[0] = 1;

5. Java Keywords

Java has reserved words like public, static, class, etc., that cannot be used as identifiers.

6. Operators

Java includes various types of operators:

●​ Arithmetic: +, -, *, /, %
●​ Logical: && (AND), || (OR), ! (NOT)
●​ Comparison: ==, !=, <, >, <=, >=

7. Java Program Structure

A Java program must include at least one class, and to be executable, it needs a main method. Example:
public class SimpleAddition {

public static void main(String[] args) {

int a = 10;

int b = 5;

double c = a + b;

System.out.println(a + " + " + b + " = " + c);

8. Compiling and Executing a Program

To compile and run the program, use the following commands:

1.​ Compile: javac SimpleAddition.java


2.​ Run: java SimpleAddition

This will produce the output:

10 + 5 = 15.0

This guide introduces basic syntax in Java, including variables, data types, operators, and how to structure and execute a Java program.

Introduction to Java Primitives

Java provides eight primitive data types, which represent raw values and are not considered objects. These types are stored directly in memory (stack) and
have specific size, range, and usage characteristics. Here's an overview:

1. Primitive Data Types:

Java's eight primitive data types are:

●​ byte: 8 bits, range from -128 to 127.


●​ short: 16 bits, range from -32,768 to 32,767.
●​ int: 32 bits, range from -2,147,483,648 to 2,147,483,647.
●​ long: 64 bits, range from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.
●​ float: 32 bits, range from 1.40239846 x 10^−45 to 3.40282347 x 10^38.
●​ double: 64 bits, range from 4.9406564584124654 x 10^−324 to 1.7976931348623157 x 10^308.
●​ boolean: 1 bit, stores true or false.
●​ char: 16 bits, stores a single Unicode character (0 to 65,535).

2. Examples of Each Primitive:

int:​
int x = 424_242;

●​

byte:​
byte b = 100;

●​

short:​
short s = 20_020;

●​

long:​
long l = 1_234_567_890;

●​

float:​
float f = 3.145f;

●​
double:​
double d = 3.13457599923384753929348D;

●​

boolean:​
boolean b = true;

●​

char:​
char c = 'a';

●​

3. Overflow and Underflow:

If a value exceeds the storage limit of a data type, overflow occurs (e.g., int going beyond Integer.MAX_VALUE rolls over to the minimum value). Similarly,
underflow happens when values go below the minimum limit, typically returning 0.0 for floating-point types.

4. Autoboxing:

Autoboxing is the automatic conversion between primitive types and their corresponding wrapper classes. For example:

Converting int to Integer:​


Integer i = 1;

●​

These primitives are the foundation for all data manipulations in Java and understanding them is essential for writing efficient programs.

Java main() Method Explained

In Java, the main() method is the entry point where the execution of a program begins. It's crucial for starting any Java application. Here's a breakdown of
how the main() method works:

1. Common Signature of main() Method

The most commonly used main() method looks like this:

public static void main(String[] args) { }

Let's break down each keyword:

●​ public: This is an access modifier that allows the method to be called from anywhere (i.e., it's globally accessible).
●​ static: This means the method belongs to the class itself rather than an instance of the class. It can be called without creating an object of the class.
●​ void: The method doesn't return any value.
●​ main: The name of the method. The Java Virtual Machine (JVM) looks for this method to start execution.

String[] args: This is an array of Strings that allows you to pass command-line arguments to the program. For example:​
java ProgramName arg1 arg2

●​ In the above case, args[0] will be "arg1" and args[1] will be "arg2".

2. Using Command-Line Arguments

You can use args inside the main() method to access and process command-line arguments. For example:

public static void main(String[] args) {

if (args.length > 0) {

if (args[0].equals("test")) {

// Load test parameters

} else if (args[0].equals("production")) {

// Load production parameters

}
This allows the program to act differently based on the input it receives when executed.

3. Different Variants of main()

The main() method can be written in several valid forms:

Square brackets placement:​


public static void main(String[] args) { }

public static void main(String args[]) { }

●​

Varargs: You can use varargs to pass an unspecified number of arguments:​


public static void main(String... args) { }

●​

Strictfp: This ensures that floating-point operations behave consistently across different platforms:​
public strictfp static void main(String[] args) { }

●​

Final for args: You can declare args as final to prevent modification:​
public static void main(final String[] args) { }

●​

All keywords combined (though rarely used in practice):​


final static synchronized strictfp void main(final String[] args) { }

●​

4. Having More Than One main() Method

You can define multiple main() methods in a project, especially in different classes. When packaging your Java program as an executable JAR file, you
specify the entry point (main class) in the MANIFEST.MF file:

Main-Class: mypackage.ClassWithMainMethod

This tells the JVM which class to execute first.

Every line of code that runs in Java must be inside a class. And the class name should always start with an uppercase first letter.

The main Method

The main() method is required and you will see it in every Java program:

public static void main(String[] args)

Any code inside the main() method will be executed.

Control Structures in Java

Control structures in Java help determine the flow of a program by directing the sequence of execution based on certain conditions. Here's an overview of
the key types:

1. Conditional Branches

Conditional branches allow the program to choose between different paths. The three primary types are:

●​ If/Else/Else If: The most basic conditional structure.

if (count > 2) {

System.out.println("Count is higher than 2");

} else {
System.out.println("Count is lower or equal than 2");

●​ Ternary Operator: A shorthand for if/else that can make code more concise.

System.out.println(count > 2 ? "Count is higher than 2" : "Count is lower or equal than 2");

●​ Switch: Used when there are multiple conditions to choose from.

int count = 3;

switch (count) {

case 0: System.out.println("Count is equal to 0"); break;

case 1: System.out.println("Count is equal to 1"); break;

default: System.out.println("Count is either negative or higher than 1"); break;

2. Loops

Loops are used to repeat a block of code multiple times.

●​ For Loop: Best for iterating through a fixed number of iterations.

for (int i = 1; i <= 50; i++) {

methodToRepeat();

●​ While Loop: Best for repeating a block of code while a condition is true.

int whileCounter = 1;

while (whileCounter <= 50) {

methodToRepeat();

whileCounter++;

3. Branching Statements

Branching statements alter the flow within loops.

●​ Break: Exits the loop early.

List<String> names = getNameList();

String name = "John Doe";

for (int i = 0; i < names.length; i++) {

if (names[i].equals(name)) {

break;

}
●​ Continue: Skips the current iteration of the loop and moves to the next.

List<String> names = getNameList();

String name = "John Doe";

String list = "";

for (int i = 0; i < names.length; i++) {

if (names[i].equals(name)) {

continue;

list += names[i];

Control structures are fundamental in Java, helping programmers decide the flow of execution, handle multiple conditions, and repeat tasks efficiently.

A Guide to Java Loops

Loops are an essential part of programming, allowing for the repeated execution of a set of statements until a specific condition is met. In Java, there are
several types of loops, each suitable for different situations.

1. Overview

In Java, loops are used to execute a block of code repeatedly based on a condition. The loop continues until the Boolean condition evaluates to false. Java
provides the following types of loops:

●​ Simple for loop


●​ Enhanced for-each loop
●​ While loop
●​ Do-while loop

2. For Loop

The for loop is used when you know how many times you want to repeat a statement. It involves initializing a counter, specifying the condition to evaluate,
and incrementing the counter after each iteration.

for (int i = 0; i < 10; i++) {

System.out.println(i); // This will print numbers from 0 to 9

3. While Loop

The while loop runs as long as the specified condition evaluates to true. The condition is evaluated before each iteration, so the loop might not execute at
all if the condition is false initially.

int i = 0;

while (i < 10) {

System.out.println(i); // This will print numbers from 0 to 9

i++;

4. Do-While Loop

The do-while loop is similar to the while loop, but the condition is checked after the loop's body executes. This means that the code inside the loop is
guaranteed to run at least once, even if the condition is false initially.
int i = 0;

do {

System.out.println(i); // This will print numbers from 0 to 9

i++;

} while (i < 10);

Conclusion

Java's looping constructs—for, while, and do-while—allow for flexible and efficient repetition of code. Choosing the right loop depends on whether you
know the number of iterations beforehand and how you want the condition to be evaluated.

Guide to Java Packages

Overview​
In Java, packages are used to group related classes, interfaces, and sub-packages. They help organize code, avoid naming conflicts, and control access
to types. By using packages, developers can create clean and maintainable code structures.

1. Creating a Package

To create a package, add the package statement at the top of your Java file:

package com.baeldung.packages;

If you don't specify a package, the class will belong to the default package. However, it's best practice to always define packages to avoid issues such as:

●​ Losing the benefits of a logical structure.


●​ Inability to import types from other packages.

2. Naming Conventions

Java package names should:

●​ Be in lowercase letters.
●​ Use period delimiters.
●​ Reflect the organization that creates them, often starting with the reverse of a company's domain name, like com.baeldung.

3. Directory Structure

Each package corresponds to a directory structure. For example, the package com.baeldung.packages should have the directory path
com/baeldung/packages/.

4. Using Package Members

To use a class from another package, import it using the import statement:

import com.baeldung.packages.domain.TodoItem;

You can also import all classes in a package using:

import com.baeldung.packages.domain.*;

5. Fully Qualified Name

If you encounter naming conflicts (e.g., using both java.sql.Date and java.util.Date), you can resolve this by using the fully qualified class name:

private List<com.baeldung.packages.domain.TodoItem> todoItems;

6. Compiling Java Classes

When compiling Java classes with packages, remember the directory structure. Use the javac command to compile:
javac com/baeldung/packages/domain/TodoItem.java

For classes that depend on others, use the -classpath flag:

javac -classpath . com/baeldung/packages/*.java

Conclusion

Java packages help organize code efficiently, prevent naming conflicts, and allow for controlled access. Using proper naming conventions and directory
structures ensures your project remains clean and manageable.

Pass-By-Value as a Parameter Passing Mechanism in Java

1. Introduction

In Java, parameters are always passed by value. This means that when a method is called, a copy of the argument is passed to the method. This differs
from pass-by-reference, where the method would operate on the original object. Java uses pass-by-value for both primitive and non-primitive (objects)
types, but the behavior is different based on the type of argument.

2. Pass-by-Value vs Pass-by-Reference

●​ Pass-by-Value: The method gets a copy of the original argument. Any changes made to the parameter inside the method do not affect the original
variable.
●​ Pass-by-Reference: The method receives a reference to the original variable, allowing it to modify the original object directly.

3. Parameter Passing in Java

Java always passes arguments by value:

●​ Primitive Types: A copy of the actual value is passed.


●​ Non-Primitive Types: A copy of the reference is passed, which points to the same object in memory.

3.1. Passing Primitive Types

When you pass a primitive type (like int, float, etc.), the method receives a copy of the value. Modifying the parameter inside the method will not affect the
original variable.

Example:

public class PrimitivesUnitTest {

@Test

public void whenModifyingPrimitives_thenOriginalValuesNotModified() {

int x = 1;

int y = 2;

// Before modification

assertEquals(x, 1);

assertEquals(y, 2);

modify(x, y);

// After modification

assertEquals(x, 1); // original value

assertEquals(y, 2); // original value

}
public static void modify(int x1, int y1) {

x1 = 5;

y1 = 10;

●​ Explanation:
○​ The original variables x and y remain unaffected by changes in the method because copies of x and y are passed, not the actual variables.

3.2. Passing Object References

When you pass objects, what is actually passed is a copy of the reference to the object. The reference points to the same object in heap memory.
Modifying the object through this reference will affect the original object, but reassigning the reference to a new object does not change the original
reference.

Example:

public class NonPrimitivesUnitTest {

@Test

public void whenModifyingObjects_thenOriginalObjectChanged() {

Foo a = new Foo(1);

Foo b = new Foo(1);

// Before modification

assertEquals(a.num, 1);

assertEquals(b.num, 1);

modify(a, b);

// After modification

assertEquals(a.num, 2); // original object modified

assertEquals(b.num, 1); // original object not modified

public static void modify(Foo a1, Foo b1) {

a1.num++; // Modifies the original object

b1 = new Foo(1); // Creates a new object, original 'b' remains unchanged

b1.num++; // Modifies the new object

class Foo {
public int num;

public Foo(int num) {

this.num = num;

●​ Explanation:
○​ The reference a1 points to the same object as a, so modifying a1 changes the original a.
○​ For b1, when we assign a new Foo object, it no longer points to the original b. Thus, the original object b remains unchanged, even though b1
points to a new object.

4. Conclusion

In Java:

●​ Primitives are passed by value, so changes inside the method do not affect the original variable.
●​ Objects are also passed by value, but since what is passed is a reference, modifying the object through the reference will affect the original object.
However, reassigning the reference to a new object does not modify the original reference outside the method.

Java's approach to passing arguments ensures consistency but can sometimes be confusing, especially when dealing with objects. Understanding how
Java handles references and memory allocation helps clarify this concept.

The hashCode() method in Java is crucial for the functioning of hash-based collections like HashMap and HashSet. Here's a summary of how it works:

1. Understanding hashCode()

●​ hashCode() returns an integer generated by a hashing algorithm.


●​ Objects that are equal (via equals()) must have the same hash code.
●​ Unequal objects may have the same or different hash codes, but distinct hash codes improve hash table performance.

2. General Contract of hashCode()

●​ Consistency: If the object's state does not change, hashCode() must return the same value.
●​ Equality: Two equal objects (based on equals()) must have the same hash code.
●​ Unequal objects: While different objects don't need distinct hash codes, distinct codes help improve performance.

3. Naive Implementation

●​ A very simple implementation can return a fixed value (e.g., return 1), but this is inefficient because all objects would be placed in the same bucket in
hash-based collections.

4. Improved Implementation

A better approach is to use fields of the object to generate the hash code:​
@Override

public int hashCode() {

return (int) id * name.hashCode() * email.hashCode();

●​

5. Standard Implementation

A more sophisticated method uses prime numbers to improve distribution:​


@Override

public int hashCode() {

int hash = 7;

hash = 31 * hash + (int) id;

hash = 31 * hash + (name == null ? 0 : name.hashCode());


hash = 31 * hash + (email == null ? 0 : email.hashCode());

return hash;

●​
●​ IDEs like IntelliJ and Eclipse provide built-in tools to generate hashCode() implementations.

6. Handling Hash Collisions

●​ A collision happens when two objects have the same hash code. Java's HashMap resolves this using separate chaining (linked lists).
●​ Java 8 introduced a tree structure for collision handling, allowing better performance with O(log n) lookup in some cases.

7. Testing with HashMap

Example:​
Map<User, User> users = new HashMap<>();

User user1 = new User(1L, "John", "[email protected]");

users.put(user1, user1);

// hashCode() is invoked every time an object is added or looked up

●​

8. Conclusion

●​ Proper implementation of hashCode() is essential for ensuring the performance and reliability of hash-based collections. Efficient hashing algorithms
minimize collisions and maximize the speed of lookup operations.

In practice, using utility methods like Objects.hash() or libraries like Lombok can simplify generating correct hashCode() methods.

A switch statement in Java is used to handle multiple possible conditions based on a single variable. It's more readable than using many if-else statements.
Here’s a simple breakdown:

Basic Syntax:​

switch(variable) {

case value1:

// block of code

break;

case value2:

// block of code

break;

default:

// block of code

●​
●​ The break statement ends the switch block. Without it, the code will "fall through" and run the next case.​

●​ Supported types:​

○​ int, char, String, enum (introduced in Java 5), and wrapper types like Integer.
●​ Switch Expressions (Java 12):​

○​ A more modern approach, removing break and using -> for case blocks.
○​ Requires all cases to be covered, else it won't compile.

This structure makes handling multiple conditions simpler and more maintainable.

The forEach method in Java, introduced in Java 8, is a more concise way to iterate over collections compared to the traditional for loop. Here's a quick
guide:
Key Concepts:

Method Syntax:​

void forEach(Consumer<? super T> action);

1.​
○​ The method takes a Consumer (a functional interface) that accepts an input and performs an action, such as printing values.
2.​ Usage Examples:​

Traditional for-loop:​
for (String name : names) {

System.out.println(name);

○​

Using forEach:​
names.forEach(name -> System.out.println(name));

○​
3.​ Different Ways to Implement Consumer:​

Anonymous Class:​
Consumer<String> printConsumer = new Consumer<String>() {

public void accept(String name) {

System.out.println(name);

};

names.forEach(printConsumer);

○​

Lambda Expression (most common):​


names.forEach(name -> System.out.println(name));

○​

Method Reference:​
names.forEach(System.out::println);

○​
4.​ Using forEach with Collections:​

Works the same way with lists, sets, and queues:​


names.forEach(System.out::println); // List

uniqueNames.forEach(System.out::println); // Set

namesQueue.forEach(System.out::println); // Queue

○​
5.​ Using forEach with Maps:​

Maps require a BiConsumer for key-value pairs:​


Map<Integer, String> namesMap = new HashMap<>();

namesMap.put(1, "Larry");

namesMap.put(2, "Steve");

namesMap.put(3, "James");
namesMap.forEach((key, value) -> System.out.println(key + " " + value));

○​
6.​ Internal vs. External Iterators:​

○​ forEach is an internal iterator: the collection handles the iteration.


○​ Traditional for-loop is an external iterator: the programmer manually controls the iteration.

Pros of forEach:

●​ Simplifies iteration.
●​ Supports lambda expressions and method references, making the code more concise and readable.

Comparison with for-loop:

●​ Both iterate through elements, but forEach abstracts the iteration process, leaving only the action to be defined, while the for-loop mixes iteration
and the action to be performed.

Here’s the organized version of the tutorial:

1. Overview

In this quick tutorial, we’ll look at two basic building blocks of the Java programming language – classes and objects. They’re fundamental concepts of
Object-Oriented Programming (OOP), which we use to model real-life entities.

In OOP:

●​ Classes are blueprints or templates for objects, used to describe types of entities.
●​ Objects are instances of classes, containing certain states within their fields and behaviors through methods.

2. Classes

A class represents the definition or type of an object. In Java, classes can contain:

●​ Fields (attributes of the object)


●​ Constructors (used to initialize objects)
●​ Methods (used to define the behavior of the object)

Example: Car Class

class Car {

// fields

String type;

String model;

String color;

int speed;

// constructor

Car(String type, String model, String color) {

this.type = type;

this.model = model;

this.color = color;

// method to increase speed


int increaseSpeed(int increment) {

this.speed = this.speed + increment;

return this.speed;

This Car class defines a car with fields for type, model, color, and speed. We use the constructor to initialize the fields, and the method increaseSpeed
changes the state of the object.

Default Constructor

Every Java class has an empty constructor by default:

Car(){}

This default constructor initializes fields with their default values: null for strings and 0 for integers. If we define a constructor like above, the fields are
initialized with specific values when we create the object.

3. Objects

Objects are created from classes at runtime. These objects are instances of the class.

Car focus = new Car("Ford", "Focus", "red");

Car auris = new Car("Toyota", "Auris", "blue");

Car golf = new Car("Volkswagen", "Golf", "green");

These are three Car objects, each with different attributes. After creation, we can change the state of the objects using methods like increaseSpeed:

focus.increaseSpeed(10);

auris.increaseSpeed(20);

golf.increaseSpeed(30);

Each object’s state (i.e., its speed) is modified independently, showing the versatility of objects created from the same class.

4. Access Modifiers

In Java, we can control access to classes, constructors, fields, and methods using access modifiers. By default, if no modifier is provided, the class has
package-private access.

Common Access Modifiers:

●​ public: Accessible from any other class


●​ private: Accessible only within the class
●​ protected: Accessible within the same package or subclasses

Example with Access Control

public class Car {

private String type;

private String model;

private String color;


private int speed;

public Car(String type, String model, String color) {

this.type = type;

this.model = model;

this.color = color;

// Getter and Setter methods for color and speed

public String getColor() {

return color;

public void setColor(String color) {

this.color = color;

public int getSpeed() {

return speed;

public void increaseSpeed(int increment) {

this.speed += increment;

In this example:

●​ Fields are private to control access and prevent direct modification.


●​ Constructor is public, so objects can be created from other classes.
●​ Methods like getColor() and setColor() allow controlled access to the color field, while increaseSpeed() modifies the speed.

Summary:

●​ Classes define the blueprint, while objects represent instances of that blueprint.
●​ Access modifiers control how the class and its members can be accessed, providing encapsulation to protect the internal state of an object.

Here’s the organized version of the guide:

1. Introduction

In this quick guide, we’ll discuss the term “concrete class” in Java. First, we’ll define it and then compare it with interfaces and abstract classes.

2. What Is a Concrete Class?


A concrete class is a class that can be instantiated, i.e., you can create an object of it using the new keyword.

In simple terms, a concrete class is a full implementation of its blueprint—everything is defined, and you can create objects directly from it.

Example: Concrete Car Class

public class Car {

public String honk() {

return "beep!";

public String drive() {

return "vroom";

In this example, because all methods (honk and drive) are implemented, Car is a concrete class. You can create an instance of Car like this:

Car car = new Car();

Common concrete classes from the JDK include HashMap, HashSet, ArrayList, and LinkedList.

3. Java Abstraction vs. Concrete Classes

Not all Java types implement all their methods. This flexibility, called abstraction, allows us to think in general terms about the entities we're modeling.

In Java, abstraction can be achieved using interfaces and abstract classes.

Let’s compare concrete classes to these other Java types:

3.1. Interfaces

An interface is a blueprint for a class, containing only method signatures (no implementations).

interface Driveable {

void honk();

void drive();

Because an interface has unimplemented methods, we cannot instantiate it directly using the new keyword.

Concrete classes like Car implement these methods and provide the actual functionality.

The JDK includes several interfaces like Map, List, and Set.

3.2. Abstract Classes

An abstract class is a class that contains both implemented and unimplemented methods. It allows for partial abstraction, meaning some methods can
have implementations while others are left for subclasses to define.

public abstract class Vehicle {

public abstract String honk(); // Unimplemented method

public String drive() {


return "zoom"; // Implemented method

We cannot instantiate an abstract class directly because it has unimplemented methods. However, concrete subclasses can inherit and implement those
methods.

Examples from the JDK include AbstractMap and AbstractList.

3.3. Concrete Classes

In contrast, concrete classes have no unimplemented methods. They can be simple like the Car example, or more complex, like classes that extend
abstract classes or implement interfaces.

public class FancyCar extends Vehicle implements Driveable {

public String honk() {

return "beep";

In this example, FancyCar implements honk() and inherits drive() from Vehicle. Since all methods are implemented, FancyCar is a concrete class, and we
can instantiate it:

FancyCar car = new FancyCar();

Summary:

●​ A concrete class has full implementations for all its methods, allowing us to instantiate objects from it.
●​ Interfaces define method signatures but cannot be instantiated.
●​ Abstract classes may contain both implemented and unimplemented methods, and cannot be instantiated directly.

Here’s the organized version of the tutorial on Access Modifiers in Java:

access modifiers in Java, which control the visibility and access level to classes, variables, methods, and constructors. Java provides four access
modifiers: public, private, protected, and default (no keyword).

●​ Top-level classes can use either public or default access modifiers.


●​ At the member level, all four access modifiers can be used.

2. Default Access Modifier (Package-private)

When no access modifier is specified, Java uses the default access modifier, also called package-private. This means the members are visible within the
same package but not accessible from other packages.

Example:

package com.baeldung.accessmodifiers;

public class SuperPublic {

static void defaultMethod() {

// ...

}
}

In the same package, defaultMethod() is accessible:

package com.baeldung.accessmodifiers;

public class Public {

public Public() {

SuperPublic.defaultMethod(); // Available in the same package

However, it’s not accessible in other packages.

3. Public Access Modifier

The public modifier makes a class, method, or property accessible from any other class, in any package. It is the least restrictive modifier.

Example:

package com.baeldung.accessmodifiers;

public class SuperPublic {

public static void publicMethod() {

// ...

The publicMethod() is accessible from other packages:

package com.baeldung.accessmodifiers.another;

import com.baeldung.accessmodifiers.SuperPublic;

public class AnotherPublic {

public AnotherPublic() {

SuperPublic.publicMethod(); // Available in other packages

4. Private Access Modifier

The private modifier restricts access to members within the same class only. It is the most restrictive access modifier and is key to encapsulation.

Example:

package com.baeldung.accessmodifiers;
public class SuperPublic {

static private void privateMethod() {

// ...

private void anotherPrivateMethod() {

privateMethod(); // Available only within the same class

Private methods and properties are hidden from the outside world, ensuring data encapsulation.

5. Protected Access Modifier

The protected modifier allows access within the same package and in subclasses, even if they are in different packages. It strikes a balance between
public and private.

Example:

package com.baeldung.accessmodifiers;

public class SuperPublic {

static protected void protectedMethod() {

// ...

The protectedMethod() is available in subclasses, even if they are in other packages:

package com.baeldung.accessmodifiers.another;

import com.baeldung.accessmodifiers.SuperPublic;

public class AnotherSubClass extends SuperPublic {

public AnotherSubClass() {

SuperPublic.protectedMethod(); // Available in subclass

6. Comparison of Access Modifiers

The table below summarizes the access levels for each modifier:
Modifier Clas Packag Subcla Worl
s e ss d

public Y Y Y Y

protecte Y Y Y N
d

default Y Y N N

private Y N N N

7. Canonical Order of Modifiers

While Java doesn’t strictly enforce the order of modifiers, the Java Language Specification (JLS) recommends a standard order for modifiers to ensure
consistency and readability.

Recommended Order for Fields:

1.​ Annotation
2.​ Access Modifier (public, protected, private)
3.​ Other Keywords (static, final, transient, volatile)

Example:

@Id

private static final long ID = 1;

Recommended Order for Classes:

1.​ Annotation
2.​ Access Modifier (public, protected, private)
3.​ Other Keywords (abstract, static, final, strictfp)

Recommended Order for Methods:

1.​ Annotation
2.​ Access Modifier (public, protected, private)
3.​ Other Keywords (abstract, static, final, synchronized, native, strictfp)

8. Conclusion

●​ public: Accessible from everywhere.


●​ private: Accessible only within the same class.
●​ protected: Accessible within the same package and subclasses.
●​ default: Accessible only within the same package.

By understanding and using these access modifiers effectively, you can control the visibility and accessibility of your Java classes and their members.

What Are Constructors?

Constructors in Java are special methods used to initialize the state of an object when it is created. They are important for ensuring that objects are
properly set up with the right initial values.

There are several types of constructors that can be used in different scenarios, which we'll explain below.

1. No-Argument Constructor
This type of constructor doesn’t take any parameters. It is useful when you want to create an object with default values.

Example:

class BankAccount {

String name;

LocalDateTime opened;

double balance;

// No-argument constructor

public BankAccount() {

this.name = ""; // Default name

this.opened = LocalDateTime.now(); // Default to current date/time

this.balance = 0.0; // Default balance

●​ Purpose: The no-argument constructor sets default values for all the fields.
●​ Why use it: If you create an object using this constructor (new BankAccount()), it initializes the object without you needing to provide values.

However, if you don’t explicitly define any constructor, Java will add a default one behind the scenes that sets all fields to their default values (e.g., null for
objects, 0 for numeric values).

2. Parameterized Constructor

A parameterized constructor allows you to provide initial values when you create the object. You pass arguments to the constructor, and those values are
used to initialize the object's fields.

Example:

class BankAccount {

String name;

LocalDateTime opened;

double balance;

// Parameterized constructor

public BankAccount(String name, LocalDateTime opened, double balance) {

this.name = name;

this.opened = opened;

this.balance = balance;

●​ Purpose: The constructor allows you to set the name, date, and balance directly when creating the object, instead of using default values.

Why use it: This is useful when you want to create an object with specific data. For example:​
LocalDateTime opened = LocalDateTime.of(2018, Month.JUNE, 29, 06, 30, 00);

BankAccount account = new BankAccount("Tom", opened, 1000.0);

●​
This will create a BankAccount object with the name "Tom", the specified date, and a balance of 1000.0.

3. Copy Constructor

A copy constructor is used to create a new object that is a copy of an existing object. This can be helpful when you want to create a new object with the
same data as an existing one but modify certain values.

Example:

public BankAccount(BankAccount other) {

this.name = other.name;

this.opened = LocalDateTime.now(); // Set new creation date

this.balance = 0.0; // Set a balance of 0

●​ Purpose: The copy constructor allows you to create a new object that has the same data as another object, but you can change specific fields (like
the balance or creation date).

Why use it: This is useful if you want to create a new account from an existing one but with modified properties, like setting the balance to 0. For example:​
BankAccount account = new BankAccount("Tim", opened, 1000.0);

BankAccount newAccount = new BankAccount(account);

●​

In this case, newAccount will have the same name as account, but its balance will be reset to 0.0.

4. Chained Constructor

A chained constructor is used when one constructor calls another constructor in the same class. This helps avoid code duplication and makes your
constructors more flexible.

Example:

public BankAccount(String name, LocalDateTime opened, double balance) {

this.name = name;

this.opened = opened;

this.balance = balance;

public BankAccount(String name) {

this(name, LocalDateTime.now(), 0.0); // Calls the other constructor

●​ Purpose: The second constructor calls the first one (using this()) to avoid repeating the same code for setting default values. It just provides a
default date and balance.

Why use it: If you want to create a bank account with just a name but still want to set default values for the other fields (like date and balance), you can
use this approach. For example:​
BankAccount account = new BankAccount("Alice");

●​

Here, the name is provided, while the opened date is set to the current time, and the balance is set to 0.0.

5. Value Types and Immutability


A value object is an object whose internal state does not change after it is created. This means that once an object is initialized, you cannot modify its
fields.

In Java, you can create immutable objects using constructors. For example, in the Transaction class, once an object is created, its fields can never be
changed.

Example:

class Transaction {

final BankAccount bankAccount;

final LocalDateTime date;

final double amount;

public Transaction(BankAccount account, LocalDateTime date, double amount) {

this.bankAccount = account;

this.date = date;

this.amount = amount;

●​ Purpose: The final keyword ensures that the fields are set once and can’t be changed later. This makes the object immutable.
●​ Why use it: Immutability ensures that the object’s state remains consistent throughout its lifetime, which is important in many scenarios like
transactions where changes in state can cause errors.

Key Takeaways:

●​ No-argument constructor: Used when you don’t need to provide any values for object creation (sets default values).
●​ Parameterized constructor: Allows you to provide values when creating an object.
●​ Copy constructor: Creates a new object by copying the data from an existing object (with possible modifications).
●​ Chained constructor: Calls another constructor from the same class to reduce redundancy.
●​ Immutable objects: Using constructors and final to create objects whose state cannot be changed after creation.

These concepts help structure and initialize objects in Java effectively, making your code cleaner, safer, and easier to maintain.

OBJECTS

1. Overview

Before working with an object in Java, it must be initialized. This involves setting up the object in memory and assigning it values.

2. Declaration vs. Initialization

●​ Declaration: Defines the variable and its type (e.g., int id;).
●​ Initialization: Assigns a value to that variable (e.g., id = 1;).

For example:

public class User {

private String name;

private int id;

3. Objects vs. Primitives

●​ Primitive Types: Variables hold values directly. Java has 8 primitive types like int, char, etc.
●​ Reference Types: Variables hold references (memory addresses) pointing to objects. Objects are instances of classes and require the new keyword
for initialization.

Example:
User user = new User(); // Creates a new User object

4. Creating Objects

●​ Objects are created using the new keyword, which triggers a constructor to initialize the object in memory.
●​ If no constructor is provided, Java provides a default constructor that allocates memory without initialization.

Example with a constructor:

public User(String name, int id) {

this.name = name;

this.id = id;

User user = new User("Alice", 1); // Creates a User object with specific values

5. Variable Scope

●​ Instance Variables: Automatically initialized with default values (e.g., null for objects, 0 for integers).
●​ Local Variables: Must be explicitly initialized before use; they don’t have default values.

6. The final Keyword

The final keyword makes a variable constant. Once initialized, it cannot be changed.

Example:

private static final int YEAR = 2000; // Constant initialization

7. Initializers in Java

●​ Instance Initializers: Blocks of code that initialize instance variables.

id = 0; // Instance initializer

●​ Static Initialization Blocks: Used to initialize static variables.

private static String forum;

static {

forum = "Java";

8. Order of Initialization

Java follows a specific order for initialization:

1.​ Static variables and static initializers.


2.​ Instance variables and instance initializers.
3.​ Constructors.

9. Object Life Cycle

●​ Objects are stored in the heap memory.


●​ When an object is no longer needed (i.e., no references point to it), Java’s garbage collector automatically frees up memory by removing the
object.

10. Other Methods for Creating Objects


●​ Reflection: Allows runtime inspection and object creation.

User user = User.class.getConstructor(String.class, int.class).newInstance("Alice", 2);

●​ Cloning: Creating an exact copy of an object. The class must implement Cloneable.

User clonedUser = (User) user.clone();

●​ Unsafe: A low-level API (sun.misc.Unsafe) to allocate memory without using constructors (typically avoided for most use cases).

Key Concepts:

●​ new Keyword: Used to allocate memory and initialize objects.


●​ Constructors: Initialize objects when they are created.
●​ Static vs. Instance Initialization: Static variables are initialized once, instance variables are initialized per object.
●​ Final Keyword: Used for constants.
●​ Garbage Collection: Java handles memory management, automatically cleaning up unused objects.

This guide gives you a clear understanding of the different ways you can declare, initialize, and manage objects and variables in Java, from simple object
creation to advanced methods like reflection and cloning.

JAVA ARRAYS

What are Arrays in Java?

An array in Java is a data structure that allows you to store multiple values of the same data type in a single variable. Arrays provide a way to organize
data efficiently, making it easy to access and manipulate multiple elements using an index.

Key Features of Arrays

1.​ Fixed Size:​

○​ Once an array is created, its size (number of elements) cannot be changed.


○​ Example: An array of size 5 can only hold 5 elements.
2.​ Homogeneous Data:​

○​ Arrays can only store elements of the same data type (e.g., all integers or all strings).
3.​ Indexed Access:​

○​ Each element in an array can be accessed using an index.


○​ Indexing in Java starts from 0.
○​ Example: For an array arr, arr[0] accesses the first element.
4.​ Memory Allocation:​

○​ Arrays are stored in a contiguous block of memory.


○​ This allows for efficient access to elements but requires knowing the array size in advance.
5.​ Array Type:​

○​ Arrays can be one-dimensional, two-dimensional, or multi-dimensional.


○​ Example:
■​ One-dimensional: A single row of data.
■​ Two-dimensional: Data organized into rows and columns.

Types of Arrays

1.​ One-Dimensional Arrays:​

○​ A linear collection of elements.


○​ Think of it as a list of items.

Example:​
int[] numbers = {1, 2, 3, 4, 5};

○​
2.​ Two-Dimensional Arrays:​

○​ Organized in a table-like structure with rows and columns.

Example:​
int[][] matrix = {

{1, 2, 3},

{4, 5, 6},

{7, 8, 9}

};

○​
3.​ Jagged Arrays:​

○​ A special type of 2D array where rows can have different numbers of elements.

Example:​
int[][] jaggedArray = {

{1, 2},

{3, 4, 5},

{6}

};

○​

Advantages of Arrays

1.​ Random Access:​

○​ Elements can be accessed directly using their index.


2.​ Efficient Storage:​

○​ Arrays are memory-efficient for storing large amounts of data.


3.​ Ease of Manipulation:​

○​ Arrays make it simple to iterate through and manipulate data.

Limitations of Arrays

1.​ Fixed Size:​

○​ You cannot increase or decrease the size of an array after creation.


2.​ Single Data Type:​

○​ Arrays cannot hold multiple types of data (e.g., integers and strings together).
3.​ No Built-in Methods for Manipulation:​

○​ Unlike more advanced data structures (like lists), arrays do not have built-in methods for common operations like adding or removing
elements.

How Do Arrays Work?

Declaration: You declare an array by specifying its type and name.​



int[] numbers;

Initialization: You create an array and allocate memory for it.​



numbers = new int[5]; // Creates an array to hold 5 integers
Accessing Elements: You use an index to access or modify specific elements.​

numbers[0] = 10; // Assigns 10 to the first element

int value = numbers[0]; // Retrieves the first element

Common Array Operations

1.​ Accessing Elements: Use the index to get or set values.​

2.​ Iteration: Use loops (e.g., for, for-each) to process all elements.​

3.​ Sorting: Sort elements using utility methods (like Arrays.sort()).​

4.​ Searching: Search for an element using a loop or methods like binary search.​

Common Array Operations

1.​ Accessing Elements: Use the index to get or set values.​

Example:​
int[] arr = {1, 2, 3, 4};

System.out.println(arr[2]); // Outputs 3

○​
2.​ Iteration: Use loops (e.g., for, for-each) to process all elements.​

Example (for-each loop):​


for (int number : arr) {

System.out.println(number);

○​
3.​ Sorting: Sort elements using utility methods like Arrays.sort().​

Example:​
int[] numbers = {5, 3, 8, 1};

Arrays.sort(numbers); // Sorting the array in ascending order

○​
4.​ Searching: Search for an element using a loop or methods like binarySearch().​

Example:​
int[] sortedNumbers = {1, 3, 5, 8};

int index = Arrays.binarySearch(sortedNumbers, 5); // Returns index of 5 (2)

○​
5.​ Handling Exceptions: Accessing an index that is out of bounds will throw an ArrayIndexOutOfBoundsException.​

Example:​
int[] arr = new int[5];

System.out.println(arr[10]); // Will throw ArrayIndexOutOfBoundsException

○​

Multi-Dimensional Arrays

●​ Two-Dimensional Arrays: Organized as rows and columns.

Example:​
int[][] multiDimArray = {
{1, 2, 3},

{4, 5},

{6, 7, 8, 9}

};

○​

Copying Arrays

●​ Using System.arraycopy(): Efficiently copies data from one array into another.​

Example:​
String[] copyFrom = {"Apple", "Banana", "Cherry"};

String[] copyTo = new String[2];

System.arraycopy(copyFrom, 1, copyTo, 0, 2);

System.out.println(Arrays.toString(copyTo)); // Outputs [Banana, Cherry]

○​
●​ Using Arrays.copyOfRange(): Copies a specified range of elements from one array to another.​

Example:​
String[] copyTo = Arrays.copyOfRange(copyFrom, 1, 3);

System.out.println(Arrays.toString(copyTo)); // Outputs [Banana, Cherry]

○​

Java Array Utilities

The java.util.Arrays class provides several methods to perform common array manipulations:

1.​ Arrays.fill(): Fills an array with a specific value.​

Example:​
int[] arr = new int[5];

Arrays.fill(arr, 10); // Fills the array with 10

○​
2.​ Arrays.equals(): Compares two arrays to check if they are equal.​

Example:​
int[] arr1 = {1, 2, 3};

int[] arr2 = {1, 2, 3};

boolean isEqual = Arrays.equals(arr1, arr2); // true

○​
3.​ Arrays.toString(): Converts an array to a string representation.​

Example:​
int[] arr = {1, 2, 3};

System.out.println(Arrays.toString(arr)); // Outputs [1, 2, 3]

○​
4.​ Arrays.stream(): Converts an array to a stream, allowing operations like map and forEach.​

Example:​
Arrays.stream(arr).map(num -> num * 2).forEach(System.out::print);

In Java, Object[] is an array that can store any type of data because every class in Java is a subclass of Object.
What is an Object?

In Java, Object is the superclass of all classes. This means that any type of data (like int, String, Double, etc.) is ultimately an Object. So, when you use
Object[], you can store any type of object (or primitive data types wrapped in objects).

Object[] Array

●​ Object[] is an array of objects (which can be of any type).


●​ It allows you to store mixed types in the same array, such as integers, strings, and other objects.

Why Use Object[]?

The main reason you'd use an Object[] is when you need an array that can hold different types of data in a single collection.

Why Arrays in Java Are Homogeneous

In Java, arrays are designed to store elements of only one data type. For example:

●​ An array of integers: int[] arr = {1, 2, 3};


●​ An array of strings: String[] arr = {"one", "two", "three"};

You cannot mix integers and strings in the same array, like {1, "two", 3}.

What If You Want to Store Different Data Types Together?

To store mixed types like integers and strings, you can use special techniques. Here’s the simplest way:

Use Object[] to Store Mixed Data

Java has a class called Object. Everything in Java (like integers, strings, etc.) is either an Object or derived from Object. So, an Object[] array can store
mixed data types.

Example:

public class Main {

public static void main(String[] args) {

Object[] mixedArray = {1, "Hello", 3.14}; // Store integer, string, and double

// Access elements

System.out.println(mixedArray[0]); // Output: 1

System.out.println(mixedArray[1]); // Output: Hello

System.out.println(mixedArray[2]); // Output: 3.14

Why Use Object[]?

●​ Flexible: It allows you to store different data types.

Caution Needed: When retrieving values, you might need to specify their original type:​
int number = (int) mixedArray[0]; // Cast back to int

String text = (String) mixedArray[1]; // Cast back to String

●​

Simplified Summary

If you want to mix types (e.g., integers and strings) in a single array:
1.​ Use an Object[] array.
2.​ Be careful when accessing elements, as you may need to cast them back to their original type.

JAVA STRINGS

Here’s a simple summary of the entire document on Java Strings:

Java Strings Overview

●​ Definition: Strings are objects in Java that represent sequences of characters. They are immutable, meaning they cannot be changed after creation.
●​ Creation: Strings can be created using literals (String str = "Hello";) or the new keyword (String str = new String("Hello");).

Key Concepts

1.​ Immutability:​

○​ Strings cannot be modified. Any changes create a new String object.


○​ Benefits include better performance, thread safety, and security (e.g., for sensitive data).
2.​ String Pool:​

○​ A special memory area where literal strings are stored. Identical strings share memory, saving resources.
3.​ Comparison:​

○​ ==: Compares memory references.


○​ equals(): Compares content.
○​ equalsIgnoreCase(): Ignores case when comparing.

Common Operations

●​ Basic Methods:​

○​ length(): Returns string length.


○​ charAt(index): Retrieves a character.
○​ substring(start, end): Extracts a portion.
○​ toUpperCase()/toLowerCase(): Changes case.
○​ trim(): Removes extra spaces.
○​ contains(substring): Checks if a substring exists.
●​ Concatenation:​

○​ Use + operator or concat() to combine strings.

Advanced Concepts

1.​ Mutable Alternatives:​

○​ StringBuilder: Efficient for frequent modifications, but not thread-safe.


○​ StringBuffer: Thread-safe but slower.
2.​ Character Encoding:​

○​ Strings support various encodings like UTF-8 and UTF-16 for international characters.
○​ Proper encoding is critical for avoiding data loss.
3.​ Iterating Over Strings:​

○​ Use for loops, toCharArray(), streams, or iterators to process characters.


4.​ Text Blocks (Java 15+):​

○​ Multiline strings can be created using """.

Special Use Cases

●​ String Templates (Java 21+):


○​ New feature for safer and more readable string interpolation.
●​ Null Strings:
○​ Uninitialized strings are null. Avoid operations on null values to prevent errors.
Performance and Security

●​ Hashing:
○​ Strings are commonly used in collections like HashMap because of their immutability.
●​ Thread Safety:
○​ Immutable strings prevent issues in multi-threaded environments.
●​ Optimization:
○​ JVM reuses string literals to save memory and improve performance.

Practical Applications

●​ File Handling:
○​ Strings are used to read/write files.
●​ Network and Databases:
○​ Ensure proper encoding for smooth data transfer and storage.
●​ Web Applications:
○​ Strings are essential for constructing URLs, handling user inputs, and displaying content.

Here’s a clear comparison between String, StringBuffer, and StringBuilder in a tabular format:

Feature String StringBuffer StringBuilder

Mutability Immutable Mutable (can be Mutable (can be modified)


(cannot be modified)
changed)

Thread Safety Not Synchronized Not synchronized (not


synchronized (thread-safe) thread-safe)
(not
thread-safe)

Performance Slower (due to Slower (due to Faster (no


immutability) synchronization) synchronization overhead)

Use Case Suitable for Suitable for strings that Suitable for strings
fixed strings are modified in multiple modified in a
threads single-threaded
environment

Memory More memory Lower memory usage Lower memory usage


Usage usage (due to (as it is mutable and (similar to StringBuffer,
immutability) modifies in place) but faster)

Methods Limited Extensive methods for Similar methods to


Available methods for modification (like StringBuffer, but faster
modification append, insert, reverse,
(like concat) etc.)

Efficiency in Poor Better performance Best performance


Concatenatio performance (modifies the existing (modifies the existing
n (creates new object) object)
objects)

Example String str = StringBuffer sb = new StringBuilder sb = new


"Hello"; StringBuffer("Hello"); StringBuilder("Hello");
sb.append(" World"); sb.append(" World");
Key Points:

●​ String: Immutability means that once a String is created, it cannot be modified. Any operation that changes a string (like concatenation) creates a
new object, which leads to higher memory usage and slower performance.​

●​ StringBuffer: Allows modifications to the string. It is synchronized, making it thread-safe, but this comes at the cost of performance, as
synchronization adds overhead.​

●​ StringBuilder: Similar to StringBuffer, but not synchronized. This makes it faster than StringBuffer for single-threaded environments but not
thread-safe.​

In general:

●​ Use String when the string value will not change.


●​ Use StringBuffer when you need thread safety and string modifications.
●​ Use StringBuilder when you need fast string modifications in a single-threaded context.

Java Collections - Comprehensive Guide

Java Collections Framework Structure

Here is a hierarchical representation of the Java Collections Framework:

1.​ Collection (Interface)


○​ Represents a group of objects known as elements. Subinterfaces include:
■​ List
■​ Set
■​ Queue
2.​ List (Interface)
○​ Ordered collection (sequence) that allows duplicate elements.
○​ Implementations:
■​ ArrayList: Resizable-array implementation of List.
■​ LinkedList: Doubly-linked list implementation.
■​ Vector: Synchronized resizable-array implementation (legacy).
3.​ Set (Interface)
○​ Collection that does not allow duplicate elements.
○​ Implementations:
■​ HashSet: Unordered, based on a hash table.
■​ LinkedHashSet: Maintains insertion order.
■​ TreeSet: Sorted set, implemented using a red-black tree.
4.​ Queue (Interface)
○​ Collection designed for holding elements prior to processing (FIFO or priority-based).
○​ Subinterface: Deque (Double-ended queue).
○​ Implementations:
■​ PriorityQueue: Priority-based queue.
■​ ArrayDeque: Resizable-array implementation of Deque.
5.​ Map (Interface)
○​ Not a subtype of Collection. Represents key-value pairs.
○​ Implementations:
■​ HashMap: Unordered key-value pairs.
■​ LinkedHashMap: Maintains insertion order.
■​ TreeMap: Sorted map, implemented using a red-black tree.

Java Collections Framework Hierarchical Representation


Java Collections Framework
|
|-- Collection (Interface)
| |-- List (Interface)
| | |-- ArrayList
| | |-- LinkedList
| | |-- Vector
| |
| |-- Set (Interface)
| | |-- HashSet
| | |-- LinkedHashSet
| | |-- TreeSet
| |
| |-- Queue (Interface)
| |-- PriorityQueue
| |-- ArrayDeque
|
|-- Map (Interface)
|-- HashMap
|-- LinkedHashMap
|-- TreeMap

This structure provides an easy way to understand the organization of Java's core data structures and their relationship

The Java Collections Framework provides a unified architecture for managing and manipulating collections of objects. Here's a breakdown of the main
interfaces and classes within it, along with key differences between them:

1. Collection Interface

●​ Purpose: The root interface of the collection hierarchy, designed for a group of objects.
●​ Implementations:
○​ List Interface: An ordered collection that allows duplicate elements.
■​ ArrayList:
■​ Resizable array implementation.
■​ Allows random access, fast retrieval, but slower insertions and deletions in the middle.
■​ LinkedList:
■​ Doubly linked list implementation.
■​ Faster insertions and deletions compared to ArrayList, but slower random access.
■​ Vector:
■​ Similar to ArrayList, but synchronized.
■​ Slower than ArrayList due to thread-safety overhead.
○​ Set Interface: A collection that does not allow duplicate elements.
■​ HashSet:
■​ Backed by a hash table.
■​ No guaranteed order of elements.
■​ LinkedHashSet:
■​ Similar to HashSet, but maintains insertion order.
■​ TreeSet:
■​ Implements a NavigableSet, which is sorted in natural order or by a comparator.
■​ Provides fast access to sorted elements.
○​ Queue Interface: A collection designed for holding elements in a FIFO (first-in, first-out) order.
■​ PriorityQueue:
■​ Elements are ordered based on their natural order or by a custom comparator.
■​ Typically used for implementing priority queues.
■​ ArrayDeque:
■​ Resizable array implementation of the Deque interface.
■​ Faster than LinkedList for queue operations.

2. Map Interface

●​ Purpose: A collection of key-value pairs, where each key maps to exactly one value.
●​ Implementations:
○​ HashMap:
■​ A map backed by a hash table.
■​ Does not guarantee any order of elements.
○​ LinkedHashMap:
■​ Similar to HashMap, but maintains the insertion order of key-value pairs.
○​ TreeMap:
■​ Implements a NavigableMap, which is sorted by keys.
■​ Provides fast access to the sorted keys.

Key Differences:

1.​ Ordering:​

○​ List and Queue maintain a specific order (insertion or natural order).


○​ Set does not allow duplicates and has no inherent order (except LinkedHashSet and TreeSet).
○​ Map maintains key-value pairs with no specific order unless it's LinkedHashMap or TreeMap (sorted by keys).
2.​ Performance:​

○​ ArrayList is faster for random access but slower for insertions/deletions.


○​ LinkedList is faster for insertions/deletions but slower for random access.
○​ HashSet and HashMap provide average constant-time complexity for insertions, lookups, and deletions.
○​ TreeSet and TreeMap have logarithmic time complexity due to the underlying red-black tree structure.
3.​ Thread-Safety:​
○​ Vector is synchronized and thread-safe (but slower than ArrayList).
○​ Other collections like ArrayList, HashSet, and HashMap are not synchronized by default.
4.​ Duplicates:​

○​ List allows duplicates.


○​ Set does not allow duplicates.
○​ Map allows one value per key, but keys must be unique.

Key Points:

●​ ArrayList and LinkedList allow duplicates and maintain insertion order, but ArrayList is faster for random access, and LinkedList is better for
insertions/deletions.
●​ HashSet does not allow duplicates and does not guarantee any order, whereas LinkedHashSet maintains insertion order and TreeSet sorts the
elements.
●​ PriorityQueue orders elements based on their priority, and ArrayDeque implements a double-ended queue with efficient operations for both ends.
●​ HashMap provides no ordering, LinkedHashMap maintains insertion order, and TreeMap sorts elements by keys.

Java Collections - Comprehensive Guide with Detailed Definitions and Examples

Java Collections Overview

Definition: Java Collections Framework is a set of classes and interfaces that provide data structures and algorithms to store, retrieve, and manipulate
data efficiently. It includes Lists, Sets, Maps, Queues, and more.

Key Features:

●​ Unified architecture for representing and manipulating collections.


●​ Reduces programming effort by providing pre-built data structures and algorithms.
●​ Increases code reusability and flexibility.

Java Collections Framework Structure

Here is a hierarchical representation of the Java Collections Framework with detailed definitions and examples:

1.​ Collection (Interface)


○​ Represents a group of objects known as elements.
○​ Subinterfaces include List, Set, and Queue.

List (Interface)

●​ Definition: An ordered collection that allows duplicate elements.


●​ Common Implementations: ArrayList, LinkedList, Vector.

ArrayList

●​ Definition: A resizable-array implementation of the List interface.


●​ Key Points:
○​ Stores elements in a dynamic array that grows as needed.
○​ Best for retrieval operations due to constant time complexity access time.
○​ Not thread-safe.

Advantages: Efficient random access, dynamic resizing. Disadvantages: Slow insertions and deletions for large datasets due to shifting elements.

Example Program:

import java.util.ArrayList;

public class ArrayListExample {

public static void main(String[] args) {

ArrayList<String> list = new ArrayList<>();

list.add("Apple");

list.add("Banana");
list.add("Apple"); // Allows duplicates

System.out.println("ArrayList: " + list);

LinkedList

●​ Definition: A doubly-linked list implementation of the List and Deque interfaces.


●​ Key Points:
○​ Elements are stored in nodes, each containing a reference to the previous and next node.
○​ Best for frequent insertions and deletions.
○​ Takes more memory due to node references.

Advantages: Efficient insertions/deletions. Disadvantages: Slower random access (linear time complexity time complexity).

Example Program:

import java.util.LinkedList;

public class LinkedListExample {

public static void main(String[] args) {

LinkedList<String> linkedList = new LinkedList<>();

linkedList.add("Car");

linkedList.addFirst("Bike");

linkedList.addLast("Bus");

System.out.println("LinkedList: " + linkedList);

Vector

●​ Definition: A synchronized, resizable-array implementation of the List interface (legacy class).


●​ Key Points:
○​ Thread-safe, making it suitable for concurrent access.
○​ Slower than ArrayList due to synchronization overhead.

Advantages: Thread safety. Disadvantages: Lower performance compared to ArrayList.

Example Program:

import java.util.Vector;

public class VectorExample {

public static void main(String[] args) {

Vector<Integer> vector = new Vector<>();

vector.add(10);

vector.add(20);

vector.add(30);
System.out.println("Vector: " + vector);

Set (Interface)

●​ Definition: A collection that does not allow duplicate elements.


●​ Common Implementations: HashSet, LinkedHashSet, TreeSet.

HashSet

●​ Definition: A set implementation backed by a hash table.


●​ Key Points:
○​ Elements are unordered.
○​ Relies on hashCode() and equals() for uniqueness.

Advantages: Fast operations (O(1) on average). Disadvantages: No ordering of elements.

Example Program:

import java.util.HashSet;

public class HashSetExample {

public static void main(String[] args) {

HashSet<String> set = new HashSet<>();

set.add("Dog");

set.add("Cat");

set.add("Dog"); // Duplicate, will not be added

System.out.println("HashSet: " + set);

LinkedHashSet

●​ Definition: A set implementation that maintains insertion order.


●​ Key Points:
○​ Combines the hash table with a linked list to preserve order.

Advantages: Predictable iteration order. Disadvantages: Slightly slower than HashSet due to maintaining order.

Example Program:

import java.util.LinkedHashSet;

public class LinkedHashSetExample {

public static void main(String[] args) {

LinkedHashSet<Integer> set = new LinkedHashSet<>();

set.add(1);

set.add(3);

set.add(2);
System.out.println("LinkedHashSet: " + set);

TreeSet

●​ Definition: A set implementation that maintains elements in sorted order.


●​ Key Points:
○​ Implements the NavigableSet interface.
○​ Uses a red-black tree internally.

Advantages: Sorted elements, range operations. Disadvantages: Slower than HashSet for basic operations.

Example Program:

import java.util.TreeSet;

public class TreeSetExample {

public static void main(String[] args) {

TreeSet<String> treeSet = new TreeSet<>();

treeSet.add("Banana");

treeSet.add("Apple");

treeSet.add("Cherry");

System.out.println("TreeSet: " + treeSet);

Queue (Interface)

●​ Definition: A collection used to hold elements prior to processing. Typically follows FIFO.
●​ Common Implementations: PriorityQueue, ArrayDeque.

PriorityQueue

●​ Definition: A queue implementation where elements are ordered based on their priority.
●​ Key Points:
○​ By default, uses natural ordering.
○​ Custom ordering can be provided using a comparator.

Advantages: Efficient priority-based operations. Disadvantages: Does not allow null elements.

Example Program:

import java.util.PriorityQueue;

public class PriorityQueueExample {

public static void main(String[] args) {

PriorityQueue<Integer> queue = new PriorityQueue<>();

queue.add(10);

queue.add(5);

queue.add(20);
System.out.println("PriorityQueue: " + queue);

ArrayDeque

●​ Definition: A resizable-array implementation of the Deque interface.


●​ Key Points:
○​ Supports both stack and queue operations.
○​ Does not allow null elements.

Advantages: Flexible insertion and removal from both ends. Disadvantages: None significant.

Example Program:

import java.util.ArrayDeque;

public class ArrayDequeExample {

public static void main(String[] args) {

ArrayDeque<String> deque = new ArrayDeque<>();

deque.add("One");

deque.addFirst("Zero");

deque.addLast("Two");

System.out.println("ArrayDeque: " + deque);

Map (Interface)

●​ Definition: A collection that maps keys to values. Each key is unique.


●​ Common Implementations: HashMap, LinkedHashMap, TreeMap.

HashMap

●​ Definition: A map implementation backed by a hash table.


●​ Key Points:
○​ Allows one null key and multiple null values.
○​ Does not maintain insertion order.

Advantages: Fast operations (O(1) on average). Disadvantages: No ordering of keys.

Example Program:

import java.util.HashMap;

public class HashMapExample {

public static void main(String[] args) {

HashMap<Integer, String> map = new HashMap<>();

map.put(1, "One");

map.put(2, "Two");
map.put(1, "Updated");

System.out.println("HashMap: " + map);

LinkedHashMap

●​ Definition: A map implementation that maintains insertion order.


●​ Key Points:
○​ Combines the hash table with a linked list to preserve order.

Advantages: Predictable iteration order. Disadvantages: Slightly slower than HashMap.

Example Program:

import java.util.LinkedHashMap;

public class LinkedHashMapExample {

public static void main(String[] args) {

LinkedHashMap<Integer, String> map = new LinkedHashMap<>();

map.put(1, "One");

map.put(2, "Two");

System.out.println("LinkedHashMap: " + map);

TreeMap

●​ Definition: A map implementation that maintains keys in sorted order.


●​ Key Points:
○​ Implements the NavigableMap interface.
○​ Uses a red-black tree internally.

Advantages: Sorted keys, range-based operations. Disadvantages: Slower than HashMap for basic operations.

Example Program:

import java.util.TreeMap;

public class TreeMapExample {

public static void main(String[] args) {

TreeMap<String, Integer> map = new TreeMap<>();

map.put("Apple", 3);

map.put("Banana", 2);

map.put("Cherry", 1);

System.out.println("TreeMap: " + map);

}
}

Iterator

Definition: An Iterator is an object used to traverse through elements in a collection one by one.

Features:

●​ Provides methods like hasNext() and next() to iterate over elements.


●​ Fail-fast: Throws ConcurrentModificationException if the collection is modified during iteration.

Usage Examples:

// Iterating over a collection

Iterator<String> iterator = collection.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

Comparator and Comparable

Definition:

●​ Comparable: Used to define the natural ordering of objects by implementing the compareTo() method.
●​ Comparator: Used to define custom ordering of objects by implementing the compare() method.

Usage Examples:

// Comparable example

class Student implements Comparable<Student> {

int rollNo;

String name;

@Override

public int compareTo(Student other) {

return this.rollNo - other.rollNo;

// Comparator example

class NameComparator implements Comparator<Student> {

@Override

public int compare(Student s1, Student s2) {

return s1.name.compareTo(s2.name);

}
Generics

Definition: Generics allow you to define classes, interfaces, and methods that work with any data type while ensuring type safety at compile-time.

Features:

●​ Reduces the need for explicit casting.


●​ Ensures type safety.
●​ Provides reusability.

Usage Examples:

// Generic class

class Box<T> {

private T value;

public void setValue(T value) {

this.value = value;

public T getValue() {

return value;

Box<Integer> intBox = new Box<>();

intBox.setValue(10);

Integer value = intBox.getValue();

Diamond Operator

Definition: Introduced in Java 7, the diamond operator (<>) simplifies the syntax of generics by allowing type inference.

Usage Example:

List<String> list = new ArrayList<>(); // Type inferred as String

Converting Between Arrays and Lists

Array to List:

String[] array = {"A", "B", "C"};

List<String> list = Arrays.asList(array);

List to Array:

List<String> list = new ArrayList<>();

list.add("A");

list.add("B");

String[] array = list.toArray(new String[0]);


Advanced Topics

HashMap Internals:

●​ Uses buckets to store key-value pairs.


●​ Collisions are handled by chaining or switching to a balanced tree (Java 8+).
●​ Resizes automatically when the load factor exceeds 0.75.

HashSet Uniqueness:

●​ Elements are unique due to the use of hashCode() and equals() methods.

LinkedList as a Queue:

●​ Supports operations like poll(), peek(), and offer() for queue behavior.

Bounded Generics:

●​ Upper bound: <T extends Number>


●​ Lower bound: <T super Integer>

Fail-Fast Behavior:

●​ Iterators throw ConcurrentModificationException when the collection is modified concurrently.

You might also like