What is Java
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.
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
● 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
int a;
double c;
int a = 10;
4. Arrays
Arrays are used to store collections of values of a specific type. The syntax to declare an array is:
numbers[0] = 1;
5. Java Keywords
Java has reserved words like public, static, class, etc., that cannot be used as identifiers.
6. Operators
● Arithmetic: +, -, *, /, %
● Logical: && (AND), || (OR), ! (NOT)
● Comparison: ==, !=, <, >, <=, >=
A Java program must include at least one class, and to be executable, it needs a main method. Example:
public class SimpleAddition {
int a = 10;
int b = 5;
double c = a + b;
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.
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:
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';
●
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:
●
These primitives are the foundation for all data manipulations in Java and understanding them is essential for writing efficient programs.
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:
● 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".
You can use args inside the main() method to access and process command-line arguments. For example:
if (args.length > 0) {
if (args[0].equals("test")) {
} else if (args[0].equals("production")) {
}
This allows the program to act differently based on the input it receives when executed.
●
●
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) { }
●
●
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
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 is required and you will see it in every Java program:
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 (count > 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");
int count = 3;
switch (count) {
2. Loops
methodToRepeat();
● While Loop: Best for repeating a block of code while a condition is true.
int whileCounter = 1;
methodToRepeat();
whileCounter++;
3. Branching Statements
if (names[i].equals(name)) {
break;
}
● Continue: Skips the current iteration of the loop and moves to the next.
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.
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:
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.
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;
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 {
i++;
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.
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:
2. Naming Conventions
● 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/.
To use a class from another package, import it using the import statement:
import com.baeldung.packages.domain.TodoItem;
import com.baeldung.packages.domain.*;
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:
When compiling Java classes with packages, remember the directory structure. Use the javac command to compile:
javac com/baeldung/packages/domain/TodoItem.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.
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.
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:
@Test
int x = 1;
int y = 2;
// Before modification
assertEquals(x, 1);
assertEquals(y, 2);
modify(x, y);
// After modification
}
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.
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:
@Test
// Before modification
assertEquals(a.num, 1);
assertEquals(b.num, 1);
modify(a, b);
// After modification
class Foo {
public 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()
● 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
●
5. Standard Implementation
int hash = 7;
return hash;
●
● IDEs like IntelliJ and Eclipse provide built-in tools to generate hashCode() implementations.
● 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.
Example:
Map<User, User> users = new HashMap<>();
users.put(user1, user1);
●
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>() {
System.out.println(name);
};
names.forEach(printConsumer);
○
○
Method Reference:
names.forEach(System.out::println);
○
4. Using forEach with Collections:
uniqueNames.forEach(System.out::println); // Set
namesQueue.forEach(System.out::println); // Queue
○
5. Using forEach with Maps:
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:
Pros of forEach:
● Simplifies iteration.
● Supports lambda expressions and method references, making the code more concise and readable.
● 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.
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:
class Car {
// fields
String type;
String model;
String color;
int speed;
// constructor
this.type = type;
this.model = model;
this.color = color;
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
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.
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.
this.type = type;
this.model = model;
this.color = color;
return color;
this.color = color;
return speed;
this.speed += increment;
In this example:
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.
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.
In simple terms, a concrete class is a full implementation of its blueprint—everything is defined, and you can create objects directly from it.
return "beep!";
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:
Common concrete classes from the JDK include HashMap, HashSet, ArrayList, and LinkedList.
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.
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.
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.
We cannot instantiate an abstract class directly because it has unimplemented methods. However, concrete subclasses can inherit and implement those
methods.
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.
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:
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.
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).
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;
// ...
}
}
package com.baeldung.accessmodifiers;
public Public() {
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;
// ...
package com.baeldung.accessmodifiers.another;
import com.baeldung.accessmodifiers.SuperPublic;
public AnotherPublic() {
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 {
// ...
Private methods and properties are hidden from the outside world, ensuring data encapsulation.
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;
// ...
package com.baeldung.accessmodifiers.another;
import com.baeldung.accessmodifiers.SuperPublic;
public AnotherSubClass() {
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
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.
1. Annotation
2. Access Modifier (public, protected, private)
3. Other Keywords (static, final, transient, volatile)
Example:
@Id
1. Annotation
2. Access Modifier (public, protected, private)
3. Other Keywords (abstract, static, final, strictfp)
1. Annotation
2. Access Modifier (public, protected, private)
3. Other Keywords (abstract, static, final, synchronized, native, strictfp)
8. Conclusion
By understanding and using these access modifiers effectively, you can control the visibility and accessibility of your Java classes and their members.
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() {
● 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
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);
●
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:
this.name = other.name;
● 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);
●
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:
this.name = name;
this.opened = opened;
this.balance = balance;
● 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.
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 {
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.
● Declaration: Defines the variable and its type (e.g., int id;).
● Initialization: Assigns a value to that variable (e.g., id = 1;).
For example:
● 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.
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.
The final keyword makes a variable constant. Once initialized, it cannot be changed.
Example:
7. Initializers in Java
id = 0; // Instance initializer
static {
forum = "Java";
8. Order of Initialization
● Cloning: Creating an exact copy of an object. The class must implement Cloneable.
● Unsafe: A low-level API (sun.misc.Unsafe) to allocate memory without using constructors (typically avoided for most use cases).
Key Concepts:
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
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.
○ Arrays can only store elements of the same data type (e.g., all integers or all strings).
3. Indexed Access:
Types of Arrays
Example:
int[] numbers = {1, 2, 3, 4, 5};
○
2. Two-Dimensional Arrays:
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
Limitations of Arrays
○ 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.
2. Iteration: Use loops (e.g., for, for-each) to process all elements.
4. Searching: Search for an element using a loop or methods like binary search.
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.
System.out.println(number);
○
3. Sorting: Sort elements using utility methods like Arrays.sort().
Example:
int[] numbers = {5, 3, 8, 1};
○
4. Searching: Search for an element using a loop or methods like binarySearch().
Example:
int[] sortedNumbers = {1, 3, 5, 8};
○
5. Handling Exceptions: Accessing an index that is out of bounds will throw an ArrayIndexOutOfBoundsException.
Example:
int[] arr = new int[5];
○
Multi-Dimensional Arrays
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"};
○
● Using Arrays.copyOfRange(): Copies a specified range of elements from one array to another.
Example:
String[] copyTo = Arrays.copyOfRange(copyFrom, 1, 3);
○
The java.util.Arrays class provides several methods to perform common array manipulations:
Example:
int[] arr = new int[5];
○
2. Arrays.equals(): Compares two arrays to check if they are equal.
Example:
int[] arr1 = {1, 2, 3};
○
3. Arrays.toString(): Converts an array to a string representation.
Example:
int[] arr = {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
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.
In Java, arrays are designed to store elements of only one data type. For example:
You cannot mix integers and strings in the same array, like {1, "two", 3}.
To store mixed types like integers and strings, you can use special techniques. Here’s the simplest way:
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:
Object[] mixedArray = {1, "Hello", 3.14}; // Store integer, string, and double
// Access elements
System.out.println(mixedArray[0]); // Output: 1
Caution Needed: When retrieving values, you might need to specify their original type:
int number = (int) mixedArray[0]; // Cast back to int
●
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
● 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:
○ A special memory area where literal strings are stored. Identical strings share memory, saving resources.
3. Comparison:
Common Operations
● Basic Methods:
Advanced Concepts
○ 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:
● 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:
Use Case Suitable for Suitable for strings that Suitable for strings
fixed strings are modified in multiple modified in a
threads single-threaded
environment
● 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:
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:
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.
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:
Here is a hierarchical representation of the Java Collections Framework with detailed definitions and examples:
List (Interface)
ArrayList
Advantages: Efficient random access, dynamic resizing. Disadvantages: Slow insertions and deletions for large datasets due to shifting elements.
Example Program:
import java.util.ArrayList;
list.add("Apple");
list.add("Banana");
list.add("Apple"); // Allows duplicates
LinkedList
Advantages: Efficient insertions/deletions. Disadvantages: Slower random access (linear time complexity time complexity).
Example Program:
import java.util.LinkedList;
linkedList.add("Car");
linkedList.addFirst("Bike");
linkedList.addLast("Bus");
Vector
Example Program:
import java.util.Vector;
vector.add(10);
vector.add(20);
vector.add(30);
System.out.println("Vector: " + vector);
Set (Interface)
HashSet
Example Program:
import java.util.HashSet;
set.add("Dog");
set.add("Cat");
LinkedHashSet
Advantages: Predictable iteration order. Disadvantages: Slightly slower than HashSet due to maintaining order.
Example Program:
import java.util.LinkedHashSet;
set.add(1);
set.add(3);
set.add(2);
System.out.println("LinkedHashSet: " + set);
TreeSet
Advantages: Sorted elements, range operations. Disadvantages: Slower than HashSet for basic operations.
Example Program:
import java.util.TreeSet;
treeSet.add("Banana");
treeSet.add("Apple");
treeSet.add("Cherry");
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;
queue.add(10);
queue.add(5);
queue.add(20);
System.out.println("PriorityQueue: " + queue);
ArrayDeque
Advantages: Flexible insertion and removal from both ends. Disadvantages: None significant.
Example Program:
import java.util.ArrayDeque;
deque.add("One");
deque.addFirst("Zero");
deque.addLast("Two");
Map (Interface)
HashMap
Example Program:
import java.util.HashMap;
map.put(1, "One");
map.put(2, "Two");
map.put(1, "Updated");
LinkedHashMap
Example Program:
import java.util.LinkedHashMap;
map.put(1, "One");
map.put(2, "Two");
TreeMap
Advantages: Sorted keys, range-based operations. Disadvantages: Slower than HashMap for basic operations.
Example Program:
import java.util.TreeMap;
map.put("Apple", 3);
map.put("Banana", 2);
map.put("Cherry", 1);
}
}
Iterator
Definition: An Iterator is an object used to traverse through elements in a collection one by one.
Features:
Usage Examples:
while (iterator.hasNext()) {
System.out.println(iterator.next());
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
int rollNo;
String name;
@Override
// Comparator example
@Override
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:
Usage Examples:
// Generic class
class Box<T> {
private T value;
this.value = value;
public T getValue() {
return value;
intBox.setValue(10);
Diamond Operator
Definition: Introduced in Java 7, the diamond operator (<>) simplifies the syntax of generics by allowing type inference.
Usage Example:
Array to List:
List to Array:
list.add("A");
list.add("B");
HashMap Internals:
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:
Fail-Fast Behavior: